The Ansible development environment in AWS will comprise the following:

  • A Virtual Private Cloud(VPC)
  • A subnet
  • An internet gateway
  • A route for public traffic into the VPC
  • Windows EC2 instance with Windows Server 2019 Amazon Machine Image(AMI)
  • Linux EC2 instance with Red Hat Enterprise Linux 8 Amazon Machine Image(AMI)

Free Tier

Both the AMIs used are within the free tier.

Ansible Modules

The following Ansible Modules can be used to deploy the resources on AWS using Ansible playbooks:

AWS Resource Ansible Module
VPC ec2_vpc_net
Subnet ec2_vpc_subnet
Internet Gateway ec2_vpc_igw
Route Table ec2_vpc_route_table
Security Group ec2_group
Key Pair ec2_key
EC2 Instance ec2
Elastic IP Address ec2_eip

Ansible codifies your infrastructure in YAML files called Ansible playbooks. You will use pre-written Ansible playbooks to deploy the Ansible development environment to AWS.

Resource Dependency
Several of the AWS resources depend on other resources. These dependencies mean that you have to run the playbooks in the right order.

Let’s start exploring the playbooks we will cover in this lesson one by one:

Create a VPC

Before you can deploy an Elastic Compute Cloud(EC2) instance, you have to provision a VPC. You will provision a VPC with a subnet, an internet gateway, and a route table entry for public traffic. Review the aws_create_vpc.yaml playbook below:

---
- hosts: localhost
  gather_facts: false
  connection: local

  tasks: 
    - name: create vpc
      ec2_vpc_net:
        name: ansible
        cidr_block: 10.0.0.0/16
        region: us-east-1
        tags:
          Name: ansible-vpc
          app: ansible
          env: dev
        state: present
      register: ansible_vpc

    - name: create subnet
      ec2_vpc_subnet:
        region: us-east-1
        vpc_id: "{{ ansible_vpc.vpc.id }}"
        cidr: 10.0.1.0/24
        map_public: yes
        tags:
          Name: ansible-subnet
          app: ansible
          env: dev
        state: present
      register: ansible_subnet

    - name: create internet gateway
      ec2_vpc_igw:
        vpc_id: "{{ ansible_vpc.vpc.id }}"
        region: us-east-1
        state: present
        tags:
          Name: "ansible-igw"
          app: ansible
          env: dev
      register: igw

    - name: Route IGW
      ec2_vpc_route_table:
        vpc_id: "{{ ansible_vpc.vpc.id }}"
        region: us-east-1
        subnets:
          - "{{ ansible_subnet.subnet.id }}"
        routes:
          - dest: 0.0.0.0/0
            gateway_id: "{{ igw.gateway_id  }}"
        tags:
          Name: ansible-public
Create a VPC

Ansible playbook

Ansible playbooks are written in YAML and have two main sections:

  • Hosts
  • Tasks

Hosts

Hosts determine which hosts are targeted by the playbook. Refer to Line 2-4 for hosts in the above playbook.

Tasks

Tasks define what Ansible will execute sequentially. From Line 6 onwards, you can observe all the tasks that the Ansible will perform.

You have four tasks in the playbook. create vpc is the first task’s name. It uses the Ansible module ec2_vpc_net to create a VPC in AWS. This Ansible module provides an interface to configure the VPC using parameters and arguments. Line 10 in the playbook represents the argument passed to the ec2_vpc_net module.

Ansible Modules Ansible modules are reusable, standalone scripts that Ansible executes. A module provides a defined interface, accepting arguments, and returning information to Ansible through a JSON string to stdout as output.

You can execute the playbook by clicking on the Run button. The Run button executes the following command in the environment:

Press + to interact
ansible-playbook aws_create_vpc.yaml

Deploy a Windows EC2 instance

Before you can create a Windows Server 2019 EC2 instance, you need the following AWS resources:

  • Virtual Private Cloud
    • Previously created by executing the aws_create_vpc.yaml.
  • Security Group
    • Virtual firewall for your instance to control inbound and outbound traffic.
  • Key Pair
    • To encrypt and decrypt login information.
  • EC2 Instance
    • Virtual machine running Windows Server 2019 Operating System.
  • Ami Id
    • The id of Microsoft Windows Server 2019 Core image from the AWS marketplace.

Note: If we want to use the latest or a different image, we can find it here.

Each of these AWS resources correlates to an Ansible Module. Review the aws_create_windows_ec2_instance.yaml playbook below:

---
- hosts: localhost
  gather_facts: false
  connection: local

  tasks: 
    - pause:
        prompt: "Enter password"
        echo: no
      when: password is undefined
      register: password_input

    - set_fact:
        password: "{{ password_input.user_input }}"
      when: password is undefined

    - ec2_vpc_net_info:
        region: us-east-1
        filters:
          "tag:Name": ansible
      register: ansible_vpc

    - ec2_vpc_subnet_info:
        region: us-east-1
        filters:
          vpc-id: "{{ ansible_vpc.vpcs[0].id }}"
      register: ansible_subnet

    - name: webserver security group
      ec2_group:
        name: windows
        description: windows sg
        vpc_id: "{{ ansible_vpc.vpcs[0].id }}"
        region: us-east-1
        tags:
          Name: windows
          app: ansible
          env: dev
        rules:
          - proto: tcp
            from_port: 80
            to_port: 80
            cidr_ip: 0.0.0.0/0
            rule_desc: allow all on port 80
          - proto: tcp
            from_port: 3389
            to_port: 3389
            cidr_ip: 0.0.0.0/0
            rule_desc: allow all RDP on port 3389
          - proto: tcp
            from_port: 5986
            to_port: 5986
            cidr_ip: 0.0.0.0/0
            rule_desc: allow all HTTPS via WinRM on 5986
          - proto: tcp
            from_port: 5985
            to_port: 5985
            cidr_ip: 0.0.0.0/0
            rule_desc: allow all HTTP via WinRM on 5985

    - name: create a new ec2 key
      ec2_key:
        name: aws-ansible-key
        region: us-east-1
        state: present
      register: ec2_key

    - name: Save private key to disk
      copy: content="{{ ec2_key.key.private_key }}" dest="./aws-ansible-key.pem" mode=0600
      when: ec2_key.changed

    - name: windows - create ec2 instance
      ec2:
        key_name: aws-ansible-key
        instance_type: t2.micro
        image: {{Ami_Id}}
        region: us-east-1
        group: windows
        count: 1
        vpc_subnet_id: "{{ ansible_subnet.subnets[0].id }}"
        user_data: |
          <powershell>
          $content = (Invoke-WebRequest -Uri 'https://raw.githubusercontent.com/ansible/ansible/devel/examples/scripts/ConfigureRemotingForAnsible.ps1' -UseBasicParsing).content
          iex $content
          $password = "{{ password }}" | ConvertTo-SecureString -AsPlainText -Force
          New-LocalUser 'ansible' -Password $password
          Add-LocalGroupMember -Group 'Administrators' -Member 'ansible'
          </powershell>
          <persist>true</persist>
        wait: yes
        assign_public_ip: yes
        instance_tags:
          Name: winweb01
          app: ansible
          env: dev
          os: windows
      register: ec2

    - name: associate new elastic IPs with each of the instances
      ec2_eip:
        device_id: "{{ item }}"
        release_on_disassociation: yes
        region: us-east-1
      loop: "{{ ec2.instance_ids }}"

# - name: get the Administrator password
#   ec2_win_password:
#     profile: my-boto-profile
#     instance_id: i-XXXXXX
#     region: us-east-1
#     key_file: "~/aws-creds/my_test_key.pem"
Deploy a Windows EC2 instance

Once again, you can execute the playbook by clicking on the Run button.

aws-ansible-key.pem

When the aws_create_windows_ec2_instance.yaml playbook ends, it will output a file called aws-ansible-key.pem. This is the private key for the ansible_key key pair in AWS. The private key is used to get the login information for the EC2 instance.

The Run button executes the following command,

Press + to interact
ansible-playbook aws_create_windows_ec2_instance.yaml

Once run, you will be prompted for a password. You can use a password of your own choice.

Password Strength

By default, there is a password policy on all Windows servers. Use a strong password with at least 8 characters that are a mixture of the following:

  • English uppercase characters (A through Z).
  • English lowercase characters (a through z).
  • Base 10 digits (0 through 9).
  • Non-alphabetic characters (for example, !, $, #, %).

The password provided will be used later to connect to the virtual machine.

Gather Information

The ec2_vpc_net_info and ec2_vpc_subnet_info are used to gather information about the VPC. You will learn more about this technique in the upcoming chapters.

Deploy a Linux EC2 instance

Deploying a Linux EC2 instance with Ansible is identical to that of a Windows EC2 instance. Review the aws_create_linux_ec2_instance.yaml playbook below. It uses the same modules as before and only requires the arguments to be changed:

  • The AMI number
  • Security Group Rules
---
- hosts: localhost
  gather_facts: false
  connection: local

  tasks: 
    - pause:
        prompt: "Enter password"
        echo: no
      when: password is undefined
      register: password_input

    - set_fact:
        password: "{{ password_input.user_input }}"
      when: password is undefined

    - ec2_vpc_net_info:
        region: us-east-1
        filters:
          "tag:Name": ansible
      register: ansible_vpc

    - ec2_vpc_subnet_info:
        region: us-east-1
        filters:
          vpc-id: "{{ ansible_vpc.vpcs[0].id }}"
      register: ansible_subnet

    - name: linux security group
      ec2_group:
        name: linux
        description: linux sg
        vpc_id: "{{ ansible_vpc.vpcs[0].id }}"
        region: us-east-1
        tags:
          Name: linux
          app: ansible
          env: dev
        rules:
          - proto: tcp
            from_port: 22
            to_port: 22
            cidr_ip: 0.0.0.0/0
            rule_desc: allow all on port 22
          - proto: tcp
            from_port: 80
            to_port: 80
            cidr_ip: 0.0.0.0/0
            rule_desc: allow all on port 80

    - name: create a new ec2 key
      ec2_key:
        name: aws-ansible-key
        region: us-east-1
        state: present
      register: ec2_key

    - name: Save private key to disk
      copy: content="{{ ec2_key.key.private_key }}" dest="./aws-ansible-key.pem" mode=0600
      when: ec2_key.changed

    - name: linux - create ec2 instance
      ec2:
        key_name: aws-ansible-key
        instance_type: t2.micro
        image: ami-0c322300a1dd5dc79
        region: us-east-1
        group: linux
        count: 1
        vpc_subnet_id: "{{ ansible_subnet.subnets[0].id }}"
        user_data: |
          #!/bin/bash
          sudo adduser ansible
          sudo echo "{{ password }}" | passwd --stdin ansible
          echo 'ansible        ALL=(ALL)       NOPASSWD: ALL' >> /etc/sudoers
          sudo sed -n 'H;${x;s/\PasswordAuthentication no/PasswordAuthentication yes/;p;}' /etc/ssh/sshd_config > tmp_sshd_config
          sudo cat tmp_sshd_config > /etc/ssh/sshd_config
          rm -f tmp_sshd_config
          sudo service sshd restart
        wait: yes
        assign_public_ip: yes
        instance_tags:
          Name: linuxweb01
          app: ansible
          env: dev
          os: linux
      register: ec2

    - name: associate new elastic IPs with each of the instances
      ec2_eip:
        device_id: "{{ item }}"
        region: us-east-1
        release_on_disassociation: yes
      loop: "{{ ec2.instance_ids }}"
Create a Linux EC2 instance

Execute the playbook by clicking on the Run button. Once again, use the password of your own choice when prompted. The following command is executed when you click the Run button:

Press + to interact
ansible-playbook aws_create_linux_ec2_instance.yaml

Delete the environment

All the above infrastructure lies in the free tier. You will be using these resources in the upcoming lessons and chapters. In case you are going to visit the next lessons later, you can take down the resources to avoid any unexpected bills.

You can take these resources down by executing the playbook below.

Disclaimer: _Run the playbook at your own risk!

It is highly recommended you use a development AWS account.

Review the playbook:

---
- hosts: localhost
  gather_facts: false
  connection: local
  
  vars:
    ec2_ids: []
    sg_ids: []

  tasks:

    - ec2_vpc_net_info:
        region: us-east-1
        filters:
          "tag:Name": ansible
      register: ansible_vpc

    - name: get ec2 instance info
      ec2_instance_info:
        region: us-east-1
        filters:
          "tag:app": ansible
          "tag:env": dev
          instance-state-name: [ "running" ]
      register: ec2

    - set_fact:
        ec2_ids: "{{ ec2_ids }} + [ '{{ item.instance_id }}' ]"
      loop: "{{ ec2['instances'] }}"
      loop_control:
        label: "{{ item.instance_id }}"

    - name: disassociate an elastic IP from an instance
      ec2_eip:
        region: us-east-1
        release_on_disassociation: yes
        device_id: "{{ item }}"
        state: absent
      with_items: "{{ ec2_ids }}"

    - name: terminate ec2 instances
      ec2:
        state: 'absent'
        region: us-east-1
        instance_ids: "{{ item.instance_id }}"
      with_items: "{{ ec2.instances }}"

    - name: sleep for 30 seconds
      wait_for:
        timeout: 30
      delegate_to: localhost

    - ec2_group_info:
        region: us-east-1
        filters:
          group_name: 
            - linux
            - windows
          vpc-id: "{{ ansible_vpc.vpcs[0].id }}"
      register: sgs

    - set_fact:
        sg_ids: "{{ sg_ids }} + [ '{{ item.group_id }}' ]"
      loop: "{{ sgs['security_groups'] }}"
      loop_control:
        label: "{{ item.group_id }}"

    - name: delete security groups
      ec2_group:
        region: us-east-1
        group_id: "{{ item }}"
        state: absent
      loop: "{{ sg_ids }}"

    - name: delete internet gateway
      ec2_vpc_igw:
        vpc_id: "{{ ansible_vpc.vpcs[0].id }}"
        region: us-east-1
        state: absent
        tags:
          Name: "ansible-igw"
          app: ansible
          env: dev

    - name: delete subnet
      ec2_vpc_subnet:
        region: us-east-1
        cidr: 10.0.1.0/24
        vpc_id: "{{ ansible_vpc.vpcs[0].id }}"
        tags:
          Name: ansible-subnet
          app: ansible
          env: dev
        state: absent

    - name: delete route IGW
      ec2_vpc_route_table:
        vpc_id: "{{ ansible_vpc.vpcs[0].id }}"
        region: us-east-1
        tags:
          Name: ansible-public
        state: absent

    - name: delete VPC
      ec2_vpc_net:
        name: ansible
        region: us-east-1
        cidr_block: 10.0.0.0/16
        tags:
          Name: ansible-vpc
          app: ansible
          env: dev
        state: absent
Delete the Ansible Environment

Execute the playbook by clicking on the Run button. It will execute the following command:

Press + to interact
ansible-playbook aws_delete_ansible_env.yaml

In this lesson, we introduced Ansible playbooks and modules to create the VPC and the EC2 instances with Linux and Windows operating systems.


Download the Source Code

You can download the playbooks for this lesson from the Github repository, become Ansible.

Get hands-on with 1300+ tech skills courses.