How to deploy an EC2 instance using Ansible

Key takeaways:

  • Ansible automates tasks like provisioning and application deployment using modules via SSH.

  • Create a security group to limit traffic to public port 3000.

  • Use a YAML playbook to define tasks for creating the security group and launching the EC2 instance.

  • The security group allows TCP communication on port 3000 with all outbound traffic permitted.

  • Launch an EC2 instance specifying AMI, instance type, and a user data script for setup.

  • The user data script updates packages, installs Node.js, clones a GitHub repository, and runs the Node.js app.

  • AWS credentials are needed to run the code, executed with Ansible commands.

  • The application can take up to 2 minutes to be accessible at the public IP on port 3000.

Ansible is an open-source automation tool designed to streamline provisioning, configuration management, application deployment, and orchestration tasks. It connects nodes and deploys small programs called Ansible modules to execute tasks. These modules run over SSH (or alternative protocols if SSH is unavailable) and are removed once the tasks are completed.

In this Answer, we will create a security group and set up a React application on an EC2 instance, ensuring the instance’s security by using only public port 3000.

We will examine the code, provision the infrastructure, and finally create the security group. After that, we’ll launch an EC2 instance to host the React application, configuring it by specifying its AMIAmazon Machine Image is a template that contains the software configuration, including the operating system and additional software, required to launch an instance in the AWS cloud., instance type, and storage, and attaching the previously created security group.

Here is an architecture diagram of the provisioned infrastructure.

Architecture diagram
Architecture diagram

Create a security group

A security group manages the inbound and outbound traffic for an associated resource. In other words, it acts as a firewall for the resource. We will create a security group to allow limited access to our EC2 instance; the security group instance_sg will only use public port 3000 to ensure the security of the EC2 instance. 

# This playbook creates an AWS Security Group and outputs its ID
- name: Create AWS Security Group
hosts: localhost
gather_facts: no # Facts are not needed for localhost
tasks:
- name: Create a security group
amazon.aws.ec2_group:
name: allow-port-3000 # Name of the security group
description: Allow incoming traffic on port 3000 # Description of the security group
region: us-east-1 # AWS region where the security group will be created; change as needed
rules:
- proto: tcp # Protocol for the rule (TCP in this case)
from_port: 3000 # Starting port for the rule
to_port: 3000 # Ending port for the rule
cidr_ip: 0.0.0.0/0 # CIDR block to allow access from (0.0.0.0/0 means from anywhere)
rules_egress:
- proto: all # Protocol for the egress(outbound) rule (all protocols)
from_port: 0 # Starting port for the egress rule
to_port: 0 # Ending port for the egress rule
cidr_ip: 0.0.0.0/0 # CIDR block to allow egress to (0.0.0.0/0 means to anywhere)
register: sg # Register the result in the variable 'sg'
- name: Output the security group ID
debug:
msg: "Security Group ID is {{ sg.group_id }}" # Output the ID of the created security group

Explanation

Let’s take a look at the above code and understand it. 

  • Line 1: Marks the beginning of the YAML file.

  • Line 2: Name of the playbook. It gives an idea of what the playbook does.

  • Line 3: Specifies that the playbook will run on the local machine (the control node where Ansible is executed).

  • Line 4: Disables fact gathering, a process where Ansible collects details about the hosts. It’s unnecessary here because the tasks only interact with AWS and don’t require information about the local machine.

  • Lines 6–26 : tasks:Create a security group employs amazon.aws.ec2_group. The module defines the group’s attributes, including its name, description, and regional placement. Through specified rules, the playbook configures inbound traffic through specified rules to permit TCP communication on port 3000 from any source IP while allowing all outbound(Egress) traffic. The results are registered in the variable sg.

    • Lines 23–25: After execution, the playbook captures the newly created security group’s details into the variable sg. Subsequently, a debug message displays the security group’s ID,

Create an EC2 instance

widget

Amazon Elastic Compute Cloud (EC2) offers resizable computing capacity in the cloud. It allows users to run virtual servers, known as instances, for various computing tasks. EC2 offers a secure, flexible, and scalable solution, enabling businesses to easily deploy, manage, and scale applications without investing in physical hardware.

We have created a security group that only allows inbound traffic on port 3000. Now, we will create an EC2 instance and add a script to clone the application from GitHub and run it in the user data of our EC2 instance. 

---
- name: Launch AWS EC2 Instance # Name of the playbook
hosts: localhost # Run on the local machine
gather_facts: no # Disable gathering facts about the local machine
tasks:
- name: Launch EC2 instance # Task to launch an EC2 instance
amazon.aws.ec2_instance: # Using the EC2_instance module from the AWS collection
ami: "ami-0fa1ca9559f1892ec" # ID of the Amazon Machine Image (AMI)
instance_type: "t2.micro" # Type of EC2 instance
key_name: null # Name of the key pair for SSH authentication (null means none)
security_groups: # List of security groups for the instance
- "{{ aws_security_group.instance_sg.name }}" # Reference to the security group created earlier
user_data: | # User data script to be executed on instance launch
#!/bin/bash
sudo yum -y update && \
sudo yum -y install git && \
sudo yum install https://rpm.nodesource.com/pub_16.x/nodistro/repo/nodesource-release-nodistro-1.noarch.rpm -y && \
sudo yum install nodejs -y --setopt=nodesource-nodejs.module_hotfixes=1 && \
git clone https://github.com/theHaziqali/my-reactapp.git && \
cd my-reactapp && \
npm install && \
npm start
tags: # Tags for the EC2 instance
Name: "clab-app" # Name tag for identifying the instance
register: ec2
post_tasks:
- name: Output the public IP # Task to output the public IP of the instance
debug:
msg: "Public IP is {{ ec2.instances[0].public_ip }}" # Output the public IP of the first instance created

Explanation

Let’s take a look at the above code and understand it. 

  • Lines 6–25: Orchestrates the launch of an AWS EC2 instance, specifying the AMIAn Amazon Machine Image (AMI) is a pre-configured template for EC2 instances that includes the operating system, application server, and applications. It allows you to quickly launch new instances with identical configurations., instance type, and security group. A user data script is provided to execute commands upon instance launch, such as updating packages, installing Node.js, cloning a GitHub repository, and starting a Node.js application.

  • Lines 27–30: After the task execution, a debug message outputs the public IP address of the launched EC2 instance.

Now that we clearly understand each Ansible task we will use, let’s provision the infrastructure.

Configure resources

We have assembled the above-discussed code and used AWS credentials to access the account and deploy infrastructure in the specified region.

Before we deploy the infrastructure on AWS, we need credentials to log in. Enter your AWS access_key_id and secret_access_key in the widget below before running any commands.

- name: Create AWS Security Group and Launch EC2 Instance
  hosts: localhost
  gather_facts: no
  tasks:
    - name: Create AWS Security Group
      amazon.aws.ec2_group:
        name: allow-port-3000
        description: Allow incoming traffic on port 3000
        region: us-east-1  # Change to your desired region
        rules:
          - proto: tcp
            from_port: 3000
            to_port: 3000
            cidr_ip: 0.0.0.0/0
        rules_egress:
          - proto: all
            from_port: 0
            to_port: 0
            cidr_ip: 0.0.0.0/0
      register: sg

    - name: Launch EC2 instance
      amazon.aws.ec2_instance:
        image_id: "ami-0fa1ca9559f1892ec"
        instance_type: "t2.micro"
        key_name: null
        security_groups:
          - "{{ sg.group_id }}"
        user_data: |
          #!/bin/bash
          sudo yum -y update && \
          sudo yum -y install git && \
          sudo yum install https://rpm.nodesource.com/pub_16.x/nodistro/repo/nodesource-release-nodistro-1.noarch.rpm -y && \
          sudo yum install nodejs -y --setopt=nodesource-nodejs.module_hotfixes=1 && \
          git clone https://github.com/theHaziqali/my-reactapp.git && \
          cd my-reactapp && \
          npm install && \
          npm start
        tags:
          Name: "clab-app"
      register: ec2

    - name: Output the public IP of the EC2 instance
      debug:
        msg: "Public IP is {{ ec2.instances[0].public_ip_address }}"

Click the “Run” button and type the following command to execute the Ansible playbook.

ansible-playbook main.yaml

Note: The application may take upto two minutes to be available after running the EC2 instance.

Copy the public IP from the terminal, paste it into the address bar of the new web page, and add the port 3000 at the end.

<Public_IP_Address>:3000

Congratulations!. Our application is live.

Conclusion

Ansible provides a streamlined method to deploy applications on AWS EC2, ensuring security through careful traffic management. By following the steps outlined, users can efficiently set up and run their applications in the cloud, leveraging Ansible's automation capabilities for a smoother deployment experience.

Frequently asked questions

Haven’t found what you were looking for? Contact Us


Why is Terraform better than Ansible?

Terraform excels in infrastructure management with a declarative approach for provisioning and life cycle management. Ansible focuses on configuration management and application deployment using an imperative style, making it less suited for full infrastructure control than Terraform.


How can you stop an EC2 instance using an Ansible Playbook?

Use the amazon.aws.ec2 module to stop an EC2 instance by specifying its instance ID. Example:

- name: Stop EC2 instance
  amazon.aws.ec2:
    instance_ids:
          - i-xxxxxx
    state: stopped

How do I automatically shut down my EC2 instance?

Schedule an AWS Lambda function using CloudWatch Events to automatically stop your EC2 instance at designated times. This setup allows for efficient management without manual intervention.


Free Resources

Copyright ©2025 Educative, Inc. All rights reserved