# Leveraging Ansible to automate AWS instance management

[Ansible](https://www.ansible.com/) is an open-source software automation tool suited for instance configuration and provisioning, enabling an Infrastructure as Code approach to the Cloud.\
In this page we provide a set of [ansible-playbooks](https://docs.ansible.com/ansible/latest/user_guide/playbooks.html) templates to perform the most common task to tune EC2 instance types with Akamas, such as:

* EC2 instance creation
* EC2 instance termination
* EC2 instance resizing

Refer to the [Ansible documentation](https://docs.ansible.com/) and to the [Ansible ec2 module](https://docs.ansible.com/ansible/latest/modules/ec2_module.html) for more details, and make sure to check the concepts behind [inventory management](https://docs.ansible.com/ansible/latest/user_guide/intro_inventory.html) to build a robust automation.

The orchestrator requires access to an account or role linked to the correct policies; this requires managing [AWS Policies](https://docs.akamas.io/akamas-docs/3.6/guidelines-for-optimizing-aws-ec2-instances#aws-policies) and having access to the required security groups.

### Instance Creation <a href="#instance-creation" id="instance-creation"></a>

The following example playbook provisions an EC2 instance using the latest *Ubuntu 18-04 LTS* image and then waits for it to be available.\
The playbook requires the following set of arguments:

* **key**: the name of the SSH key pair to use
* **Name**: the instance name
* **security\_group**: the name of the AWS security group
* **region**: the selected AWS region

You can update the `ec2_ami_info` task to query for a different [AMI](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/AMIs.html) family or specify directly the id under `ec2.image`.

When executing the script we must assign the following arguments as [extra-vars](https://docs.ansible.com/ansible/latest/user_guide/playbooks_variables.html#passing-variables-on-the-command-line):

* **intance\_type**: type of instance to provision
* **volume\_size**: the size of the attached volume

```yaml
# Launch an ubuntu instance and wait for ssh

- name: Create an instance request
  hosts: localhost
  gather_facts: False

  tasks:
    - name: query api
      ec2_ami_info:
        filters:
          name: "ubuntu/images/hvm-ssd/ubuntu-bionic-18.04-amd64-server-*"
          owner-id: "099720109477"  # Canonical Group Limited
      register: amis
    - name: sort by creation date
      set_fact:
        sorted_amis: "{{ amis.images | sort(attribute='creation_date') }}"
    - name: get latest
      set_fact:
        latest_ami: "{{ sorted_amis | last }}"

    - name: Launch instance
      ec2:
         key_name: "{{ key }}"
         instance_type: "{{ instance_type | default('m5.xlarge') }}"
         group:
          - <your-security-groups>
         image: "{{ latest_ami.image_id }}"
         count: "{{ count | default('1') }}"
         wait: yes
         wait_timeout: 500
         region: "{{ region }}"
         spot_wait_timeout: 600
         instance_initiated_shutdown_behavior: terminate
         ebs_optimized: yes
         volumes:
           - device_name: /dev/sda1
             volume_type: gp2
             volume_size: "{{ volume_size | default('20') }}"
             delete_on_termination: yes
         instance_tags:
           Name: "{{ Name }}"
           CNAME: "{{ Name }}.<your-domain>"
      register: ec2

    - name: Wait for SSH to come up
      wait_for:
        host: "{{ item.public_dns_name }}"
        port: 22
        delay: 60
        timeout: 320
        state: started
      with_items: "{{ ec2.instances }}"
```

To apply the EC2 parameters from the [AWS Optimization Pack](https://docs.akamas.io/akamas-docs/3.6/reference/optimization-packs/aws-pack) selected by the Akamas engine you can generate the playbook arguments through a template like the following one, where `ec2` is the name of the component:

```bash
ansible-playbook -i inventory --extra-vars "instance_type=${ec2.aws_ec2_instance_type}.${ec2.aws_ec2_instance_size}" provision.yaml
```

### Instance Termination <a href="#instance-termination" id="instance-termination"></a>

The following playbook terminates all instances with the specified name (or any other tag).\
It requires the following arguments:

* **instance\_name**: the name of the instance
* **region**: the selected AWS region

```yaml
# Terminate an aws instance

- name: Terminate instance
  hosts: localhost
  gather_facts: False
  tasks:
  - name: retrieve instance info
    ec2_instance_info:
      filters:
        "tag:Name": "{{ instance_name }}"
    register: ec2

  - name: terminate the instance
    ec2:
      state: absent
      instance_ids:
        - "{{ item.instance_id }}"
      region: "{{ region }}"
    with_items: "{{ ec2.instances }}"
```

### Instance Resizing <a href="#instance-resizing" id="instance-resizing"></a>

Instance resizing is a little trickier to deploy as it requires you to [install AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html) and setup the [required credentials](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-files.html).\
The following playbook provides a simple way to stop, update, and restart your instance: it is intended as a building block for more elaborate workflows.

It makes use of a list of arguments:

* **instance\_name**: your instance name
* **region**: the selected AWS region

For a successful workflow, it requires:

* The instance to exist
* The instance to be unique

```yaml
# Change instance type, requires AWS CLI

- name: Resize the instance
  hosts: localhost
  gather_facts: no
  connection: local
  tasks:
  - name: save instance info
    ec2_instance_info:
      filters:
        "tag:Name": "{{ instance_name }}"
    register: ec2
  - name: stop the instance
    ec2:
      region: "{{ region | default('us-east-2') }}"
      state: stopped
      instance_ids:
        - "{{ ec2.instances[0].instance_id }}"
      instance_type: "{{ ec2.instances[0].instance_type }}"
      wait: True
  - name: Change the instances ec2 type
    shell: >
       aws ec2 modify-instance-attribute --instance-id "{{ ec2.instances[0].instance_id }}"
       --instance-type "{{ new_instance_type }}"
    delegate_to: localhost
  - name: restart the instance
    ec2:
      region: "{{ region }}"
      state: running
      instance_ids:
        - "{{ ec2.instances[0].instance_id }}"
      wait: True
    register: ec2
  - name: wait for SSH to come up
    wait_for:
      host: "{{ item.public_dns_name }}"
      port: 22
      delay: 60
      timeout: 500
      state: started
    with_items: "{{ ec2.instances }}"
```

To apply the EC2 parameters from the [AWS Optimization Pack](https://docs.akamas.io/akamas-docs/3.6/reference/optimization-packs/aws-pack) selected by the Akamas engine you can generate the playbook arguments through a template like the following, where `ec2` is the name of the component:

```bash
ansible-playbook -i inventory --extra-vars "new_instance_type=${ec2.aws_ec2_instance_type}.${ec2.aws_ec2_instance_size}" resize.yaml
```
