Files
oam/examples/ansible/ec2.clone-instance.yml

133 lines
5.6 KiB
YAML

---
- name: Properly clone a running EC2 instance
hosts: localhost
gather_facts: false
vars:
original_instance_name_tag: "Use ME!!"
pre_tasks:
- name: Get information from the original instance
tags:
- gather_information
- pre_task
amazon.aws.ec2_instance_info:
filters:
"tag:Name": "{{ original_instance_name_tag }}"
"instance-state-name": ["running"]
register: original_instance_information
- name: Check a running instance with Name tag '{{ original_instance_name_tag }}' has been found
tags:
- gather_information
- pre_task
ansible.builtin.assert:
that: original_instance_information.instances | length > 0
fail_msg: No running instances found with Name tag '{{ original_instance_name_tag }}'
success_msg: At least one running instance has been found with Name tag '{{ original_instance_name_tag }}'
tasks:
- name: Create a Security Group with only the connections required for testing
tags: security_group
amazon.aws.ec2_security_group:
name: Clone EC2 Instance SG
description: Temporary SG for cloning EC2 Instances
rules_egress:
- cidr_ip: 0.0.0.0/0
ports: 443
rule_desc: Required by SSM, but could be stricter
register: clone_instance_security_group_information
- name: Create snapshots of the instance's volumes
# Allows for more control over the snapshot, namely to avoid recreating snapshot of massive volumes and lose hours
tags: snapshot
amazon.aws.ec2_snapshot:
volume_id: "{{ item }}"
description: Temporary snapshot for cloning EC2 Instances
last_snapshot_min_age: 1440 # 1d
wait_timeout: 7200 # 2h might still be not enough for big boi volumes
loop: "{{ original_instance_information.instances[0].block_device_mappings | map(attribute='ebs.volume_id') }}"
register: original_instance_snapshots
- name: Create an AMI from the snapshot
tags: ami
amazon.aws.ec2_ami:
# no_reboot: false # set to true if one does *not* want to have the original instance shut down
wait: true
name: temp-{{ original_instance_name_tag | regex_replace(' ', '-') | lower }}-ami
description: Temporary AMI for cloning EC2 Instances
tags:
Name: Clone EC2 Instance AMI
root_device_name: "{{ original_instance_information.instances[0].root_device_name }}"
device_mapping:
# Refer https://jinja.palletsprojects.com/en/3.0.x/templates/#assignments for the namespace object's reason
>-
{%- set ns = namespace(devices_list = []) -%}
{%- for result in original_instance_snapshots.results -%}
{%- for device in original_instance_information.instances[0].block_device_mappings
| selectattr('ebs.volume_id', 'equalto', result.volume_id) -%}
{{-
ns.devices_list.append({
'device_name': device.device_name,
'snapshot_id': result.snapshots | sort(attribute='start_time') | last | json_query('snapshot_id'),
'volume_type': 'gp3',
'delete_on_termination': true,
})
-}}
{%- endfor -%}
{%- endfor -%}
{{ ns.devices_list }}
register: original_instance_ami
- name: Use the AMI to launch a clone
tags:
- clone
- instance
when: original_instance_ami.image_id is defined
amazon.aws.ec2_instance:
name: Clone EC2 Instance
vpc_subnet_id: "{{ original_instance_information.instances[0].subnet_id }}"
instance_type: "{{ original_instance_information.instances[0].instance_type }}"
image:
id: "{{ original_instance_ami.image_id }}"
security_group: "{{ clone_instance_security_group_information.group_id }}"
iam_instance_profile: "{{ original_instance_information.instances[0].iam_instance_profile.arn }}"
register: clone_instance_information
- name: Wait for the instance to be ready
tags:
- clone
- instance
- check
when: clone_instance_information.instance_ids is defined
block:
- name: Just pause enough for the instance to initialize
# Because of course there seems to be no effing way to distinguish between just running and ready, and of
# course the SSM connection plugin crashes badly instead of just erroring the task out (ノಠ益ಠ)ノ彡┻━┻
ansible.builtin.pause:
minutes: 3
- name: Try connecting with SSM
delegate_to: "{{ clone_instance_information.instances[0].instance_id }}"
vars:
ansible_connection: community.aws.aws_ssm
ansible_aws_ssm_bucket_name: some-bucket
ansible_aws_ssm_region: eu-west-1
ansible_aws_ssm_timeout: 300
ansible.builtin.ping:
- name: Ready!
ansible.builtin.debug:
msg: The clone instance is ready!
post_tasks:
- name: Remove the clone
tags:
- clone
- instance
- cleanup
- post_task
when: clone_instance_information.instance_ids is defined
amazon.aws.ec2_instance:
instance_ids: "{{ clone_instance_information.instance_ids }}"
state: absent
- name: Remove the AMI
tags:
- always # stupid ami module fails if already existing
- ami
- cleanup
- post_task
when: original_instance_ami.image_id is defined
amazon.aws.ec2_ami:
image_id: "{{ original_instance_ami.image_id }}"
state: absent