DEV Community

Nikith Kaithalapuram
Nikith Kaithalapuram

Posted on

Deploying a Cloud Resource in AWS with Ansible and Terraform

Ansible Approach

In this article, we will use Ansible to launch EC2 instances in different availability zones. The launch_ec2_instances.yml playbook defines three tasks: the first to get the subnet facts, the next to set the subnet IDs and availability zones, and a final task responsible for launching an EC2 instance into two specific subnets.

Outline

ssh cloud_user@<PUBLIC_IP_ADDRESS>

SSH

Launch EC2 Instances with YAML Playbook using Ansible

  1. Launch in us-east-1

  2. Copy the AMI ID into the image:<******> into the launch_ec2_instances.yml file

touch launch_ec2_instances.yml
vim launch_ec2_instances.yml

- name: Launch EC2 Instances
  hosts: localhost
  gather_facts: false
  vars_files:
    - /home/ansible/keys.yml

  tasks:
    - name: Get Subnet Facts
      ec2_vpc_subnet_info:
        aws_access_key: "{{ AWS_ACCESS_KEY_ID }}"
        aws_secret_key: "{{ AWS_SECRET_ACCESS_KEY }}"
        region: us-east-1
      register: subnet_facts

    - name: Set Subnet IDs and Availability Zones
      set_fact:
        subnet_ids: "{{ subnet_facts.subnets|map(attribute='id')|list }}"
        availability_zones: "{{ subnet_facts.subnets|map(attribute='availability_zone')|list }}"

    - name: Launch EC2 Instances in Each Subnet
      ec2:
        aws_access_key: "{{ AWS_ACCESS_KEY_ID }}"
        aws_secret_key: "{{ AWS_SECRET_ACCESS_KEY }}"
        ec2_region: us-east-1
        instance_type: t2.micro
        image: ami-026ebd4cfe2c043b2
        vpc_subnet_id: "{{ item }}"
        wait: true
      loop: "{{ subnet_ids }}"

Enter fullscreen mode Exit fullscreen mode
  1. Execute the Playbook
    ansible-playbook launch_ec2_instances.yml

  2. Since I had 2 subnets Two EC2 instances will be created

EC2 Running

Terraform Approach

I used terraform data source.

You don’t rebuild the building, and you don’t change its structure you simply read where the walls, doors, and wiring already are so your new room fits perfectly.

In the same way, a data source lets Terraform look at existing infrastructure (like a VPC, subnet, or AMI) and use that information to safely build something new without disturbing what’s already there.

CONFIGURE AWS WITH CLI
installed packages

dnf install -y awscli
Enter fullscreen mode Exit fullscreen mode

USED GITHUB CODESPACES

bash-5.1# cat /etc/os-release
NAME="Rocky Linux"
VERSION="9.3 (Blue Onyx)"
ID="rocky"
ID_LIKE="rhel centos fedora"
VERSION_ID="9.3"
PLATFORM_ID="platform:el9"
PRETTY_NAME="Rocky Linux 9.3 (Blue Onyx)"
ANSI_COLOR="0;32"
LOGO="fedora-logo-icon"
CPE_NAME="cpe:/o:rocky:rocky:9::baseos"
HOME_URL="https://rockylinux.org/"
BUG_REPORT_URL="https://bugs.rockylinux.org/"
SUPPORT_END="2032-05-31"
ROCKY_SUPPORT_PRODUCT="Rocky-Linux-9"
ROCKY_SUPPORT_PRODUCT_VERSION="9.3"
REDHAT_SUPPORT_PRODUCT="Rocky Linux"
REDHAT_SUPPORT_PRODUCT_VERSION="9.3"
Enter fullscreen mode Exit fullscreen mode

CODESPACES

main.tf

provider "aws" {
  region = "us-east-1"
}

data "aws_vpc" "selected" {
  id = "vpc-06110468de1ae8d0f"
}

data "aws_subnet" "selected" {
  vpc_id = data.aws_vpc.selected.id
  availability_zone = "us-east-1a"
}

data "aws_ami" "amazon_linux" {
  most_recent = true

  owners = ["amazon"]

  filter {
    name   = "name"
    values = ["amzn2-ami-hvm-*-x86_64-gp2"]
  }

  filter {
    name   = "state"
    values = ["available"]
  }
}

resource "aws_instance" "example" {
  ami           = data.aws_ami.amazon_linux.id
  instance_type = "t2.micro"

  subnet_id              = data.aws_subnet.selected.id
  vpc_security_group_ids = [aws_security_group.ec2_sg.id]

  tags = {
    Name = "terraform-ec2"
  }
}


resource "aws_security_group" "ec2_sg" {
  name        = "terraform-ec2-sg"
  description = "Allow SSH"
  vpc_id      = data.aws_vpc.selected.id

  ingress {
    from_port   = 22
    to_port     = 22
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }

  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }
}

Enter fullscreen mode Exit fullscreen mode

cli

bash-5.1# terraform apply
data.aws_vpc.selected: Reading...
data.aws_ami.amazon_linux: Reading...
data.aws_vpc.selected: Read complete after 0s [id=vpc-06110468de1ae8d0f]
data.aws_subnet.selected: Reading...
data.aws_subnet.selected: Read complete after 0s [id=subnet-00e24cf4555b3ca63]
data.aws_ami.amazon_linux: Read complete after 0s [id=ami-03f9680ef0c07a3d1]

Terraform used the selected providers to generate
the following execution plan. Resource actions are
indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # aws_instance.example will be created
  + resource "aws_instance" "example" {
      + ami                                  = "ami-03f9680ef0c07a3d1"
      + arn                                  = (known after apply)
      + associate_public_ip_address          = (known after apply)
      + availability_zone                    = (known after apply)
      + disable_api_stop                     = (known after apply)
      + disable_api_termination              = (known after apply)
      + ebs_optimized                        = (known after apply)
      + enable_primary_ipv6                  = (known after apply)
      + force_destroy                        = false
      + get_password_data                    = false
      + host_id                              = (known after apply)
      + host_resource_group_arn              = (known after apply)
      + iam_instance_profile                 = (known after apply)
      + id                                   = (known after apply)
      + instance_initiated_shutdown_behavior = (known after apply)
      + instance_lifecycle                   = (known after apply)
      + instance_state                       = (known after apply)
      + instance_type                        = "t2.micro"
      + ipv6_address_count                   = (known after apply)
      + ipv6_addresses                       = (known after apply)
      + key_name                             = (known after apply)
      + monitoring                           = (known after apply)
      + outpost_arn                          = (known after apply)
      + password_data                        = (known after apply)
      + placement_group                      = (known after apply)
      + placement_group_id                   = (known after apply)
      + placement_partition_number           = (known after apply)
      + primary_network_interface_id         = (known after apply)
      + private_dns                          = (known after apply)
      + private_ip                           = (known after apply)
      + public_dns                           = (known after apply)
      + public_ip                            = (known after apply)
      + region                               = "us-east-1"
      + secondary_private_ips                = (known after apply)
      + security_groups                      = (known after apply)
      + source_dest_check                    = true
      + spot_instance_request_id             = (known after apply)
      + subnet_id                            = "subnet-00e24cf4555b3ca63"
      + tags                                 = {
          + "Name" = "terraform-ec2"
        }
      + tags_all                             = {
          + "Name" = "terraform-ec2"
        }
      + tenancy                              = (known after apply)
      + user_data_base64                     = (known after apply)
      + user_data_replace_on_change          = false
      + vpc_security_group_ids               = (known after apply)

      + capacity_reservation_specification (known after apply)

      + cpu_options (known after apply)

      + ebs_block_device (known after apply)

      + enclave_options (known after apply)

      + ephemeral_block_device (known after apply)

      + instance_market_options (known after apply)

      + maintenance_options (known after apply)

      + metadata_options (known after apply)

      + network_interface (known after apply)

      + primary_network_interface (known after apply)

      + private_dns_name_options (known after apply)

      + root_block_device (known after apply)
    }

  # aws_security_group.ec2_sg will be created
  + resource "aws_security_group" "ec2_sg" {
      + arn                    = (known after apply)
      + description            = "Allow SSH"
      + egress                 = [
          + {
              + cidr_blocks      = [
                  + "0.0.0.0/0",
                ]
              + from_port        = 0
              + ipv6_cidr_blocks = []
              + prefix_list_ids  = []
              + protocol         = "-1"
              + security_groups  = []
              + self             = false
              + to_port          = 0
                # (1 unchanged attribute hidden)
            },
        ]
      + id                     = (known after apply)
      + ingress                = [
          + {
              + cidr_blocks      = [
                  + "0.0.0.0/0",
                ]
              + from_port        = 22
              + ipv6_cidr_blocks = []
              + prefix_list_ids  = []
              + protocol         = "tcp"
              + security_groups  = []
              + self             = false
              + to_port          = 22
                # (1 unchanged attribute hidden)
            },
        ]
      + name                   = "terraform-ec2-sg"
      + name_prefix            = (known after apply)
      + owner_id               = (known after apply)
      + region                 = "us-east-1"
      + revoke_rules_on_delete = false
      + tags_all               = (known after apply)
      + vpc_id                 = "vpc-06110468de1ae8d0f"
    }

Plan: 2 to add, 0 to change, 0 to destroy.

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes

aws_security_group.ec2_sg: Creating...
aws_security_group.ec2_sg: Creation complete after 1s [id=sg-09d80c75597b8624d]
aws_instance.example: Creating...
aws_instance.example: Still creating... [00m10s elapsed]
aws_instance.example: Still creating... [00m20s elapsed]
aws_instance.example: Still creating... [00m30s elapsed]
aws_instance.example: Creation complete after 32s [id=i-07e8658ca12915abf]
Enter fullscreen mode Exit fullscreen mode

Fetch VPC-ID

bash-5.1# aws ec2 describe-vpcs
{
    "Vpcs": [
        {
            "OwnerId": "080763862064",
            "InstanceTenancy": "default",
            "CidrBlockAssociationSet": [
                {
                    "AssociationId": "vpc-cidr-assoc-0c2a3ddbd5c6aa364",
                    "CidrBlock": "10.0.0.0/16",
                    "CidrBlockState": {
                        "State": "associated"
                    }
                }
            ],
            "IsDefault": false,
            "Tags": [
                {
                    "Key": "aws:cloudformation:logical-id",
                    "Value": "VPC"
                },
                {
                    "Key": "Name",
                    "Value": "LinuxAcademy"
                },
                {
                    "Key": "aws:cloudformation:stack-id",
                    "Value": "arn:aws:cloudformation:us-east-1:080763862064:stack/cfst-4150-d757e6fc0ddc7949940f7ec1f4710dd8b2bdf1fecd5f43946c84cb1649b24e2a/1d83daf0-dc6a-11f0-8dbd-123b45f64b33"
                },
                {
                    "Key": "Network",
                    "Value": "VPC"
                },
                {
                    "Key": "Application",
                    "Value": "cfst-4150-d757e6fc0ddc7949940f7ec1f4710dd8b2bdf1fecd5f43946c84cb1649b24e2a"
                },
                {
                    "Key": "UserId",
                    "Value": "24701544"
                },
                {
                    "Key": "aws:cloudformation:stack-name",
                    "Value": "cfst-4150-d757e6fc0ddc7949940f7ec1f4710dd8b2bdf1fecd5f43946c84cb1649b24e2a"
                }
            ],
            "BlockPublicAccessStates": {
                "InternetGatewayBlockMode": "off"
            },
            "VpcId": "vpc-0b76c79b6a607cb38",
            "State": "available",
            "CidrBlock": "10.0.0.0/16",
            "DhcpOptionsId": "dopt-079e42f1be6dd68a4"
        }
    ]
}
Enter fullscreen mode Exit fullscreen mode

fetch subnet

                {
                    "Key": "aws:cloudformation:stack-name",
                    "Value": "cfst-4150-cf678142718819a066a9339ffe1e112471fc9e300512621571a366cc45a4129d"
                },
                {
                    "Key": "Application",
                    "Value": "cfst-4150-cf678142718819a066a9339ffe1e112471fc9e300512621571a366cc45a4129d"
                },
                {
                    "Key": "Network",
                    "Value": "Public2"
                }
            ],
            "SubnetArn": "arn:aws:ec2:us-east-1:143108796152:subnet/subnet-0dcaed3327867a837",
            "EnableDns64": false,
            "Ipv6Native": false,
            "PrivateDnsNameOptionsOnLaunch": {
                "HostnameType": "ip-name",
                "EnableResourceNameDnsARecord": false,
                "EnableResourceNameDnsAAAARecord": false
            },
            "BlockPublicAccessStates": {
                "InternetGatewayBlockMode": "off"
            },
            "SubnetId": "subnet-0dcaed3327867a837",
            "State": "available",
            "VpcId": "vpc-06110468de1ae8d0f",
            "CidrBlock": "10.0.2.0/24",
            "AvailableIpAddressCount": 251,
            "AvailabilityZone": "us-east-1b",
            "DefaultForAz": false,
            "MapPublicIpOnLaunch": false
        }
    ]
Enter fullscreen mode Exit fullscreen mode

Successfully launched EC2 instance with terraform approach

Terraform EC2

References

  1. Pluralsight Hands-on Lab – AWS / Cloud Infrastructure Lab https://app.pluralsight.com/hands-on/labs/4dd1d104-d54e-4351-bc6c-bc8be053ec42

Top comments (0)