Skip to content

Commit

Permalink
Merge pull request #123 from mandar242/ec2-create-instance-role
Browse files Browse the repository at this point in the history
Add new manage_ec2_instance role
  • Loading branch information
hakbailey authored Dec 9, 2024
2 parents 0237835 + 5a7d83b commit 8bc3076
Show file tree
Hide file tree
Showing 14 changed files with 637 additions and 0 deletions.
155 changes: 155 additions & 0 deletions roles/manage_ec2_instance/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
# manage_ec2_instance

A role to create or delete an EC2 instance in AWS.

Users can specify various parameters for instance configuration, including instance type, AMI ID, key pair, tags, VPC/subnet configuration, and whether to associate an EIP. You can choose to wait for the EC2 instance to finish booting/terminating before continuing.

This role can be combined with the [cloud.aws_ops.ec2_networking_resources role](../ec2_networking_resources/README.md) to create/delete networking resources for the instance, see [examples](#examples).

EC2 instance details and the private key (if a key pair is created) will be displayed as role output. The instance and key pair details are accessible via variables `ec2_instance_manage_create_result` and `ec2_instance_manage_key_pair_result`, respectively.

## Requirements

An AWS account with the following permissions:

* ec2:AllocateAddress
* ec2:AssociateAddress
* ec2:CreateKeyPair
* ec2:DeleteKeyPair
* ec2:DescribeAddresses
* ec2:DescribeInstanceAttribute
* ec2:DescribeInstances
* ec2:DescribeInstanceStatus
* ec2:DescribeKeyPairs
* ec2:DescribeSecurityGroups
* ec2:DescribeSubnets
* ec2:DescribeVpcs
* ec2:DisassociateAddress
* ec2:ModifyInstanceAttribute
* ec2:ReleaseAddress
* ec2:RunInstances
* ec2:TerminateInstances

## Role Variables

The following variables can be set in the role to customize EC2 instance creation and networking configurations:

* **manage_ec2_instance_operation**: (Optional)
Target operation for the ec2 instance role. Choices are ["create", "delete"]. Defaults to "create".

* **manage_ec2_instance_instance_name**: (Required)
The name of the EC2 instance to be created/deleted.

* **manage_ec2_instance_instance_type**: (Optional)
The instance type for the EC2 instance (e.g., `t2.micro`, `m5.large`). Required when `manage_ec2_instance_operation` is `create`

* **manage_ec2_instance_ami_id**: (Optional)
The AMI ID for the EC2 instance. Required when `manage_ec2_instance_operation` is `create`

* **manage_ec2_instance_key_name**: (Optional)
The name of the key pair to use for SSH access to the EC2 instance.
If the key does not exist, a key pair will be created with the name.
If not provided, instance will not be accessible via SSH.
If provided when `manage_ec2_instance_operation` is `delete`, the keypair will also be deleted.

* **manage_ec2_instance_vpc_subnet_id**: (Optional)
The ID of the VPC subnet in which the instance will be launched.
If not provided, instance will be created in the default subnet for the default VPC in the AWS region if present.

* **manage_ec2_instance_tags**: (Optional)
A dictionary of tags to assign to the EC2 instance.

* **manage_ec2_instance_wait_for_state**: (Optional)
Whether to wait for the EC2 instance to be in the "running" (if creating an instance) or "terminated" (if deleting an instance) state before continuing. Default is `true`.

* **manage_ec2_instance_associate_security_groups**: (Optional)
List of security group IDs to associate with the EC2 instance.

* **manage_ec2_instance_associate_eip**: (Optional)
Whether to create an Elastic IP (EIP) and associate it with the EC2 instance. Default is `false`.
If true, EC2 instance must be launched in a VPC with an Internet Gateway (IGW) attached, otherwise this will fail. Use [cloud.aws_ops.ec2_networking_resources role](../ec2_networking_resources/README.md) to create the necessary networking resources.

* **manage_ec2_instance_eip_tags**: (Optional)
Tags to assign to the elastic IP.

## Dependencies

- role: [aws_setup_credentials](../aws_setup_credentials/README.md)

## Examples

Using the role on its own in a playbook:

```yaml
---
- name: Create EC2 instance
hosts: localhost
gather_facts: false
roles:
- role: cloud.aws_ops.manage_ec2_instance
vars:
manage_ec2_instance_operation: create
manage_ec2_instance_aws_region: us-west-2
manage_ec2_instance_instance_name: my-test-instance
manage_ec2_instance_instance_type: t2.micro
manage_ec2_instance_ami_id: ami-066a7fbaa12345678
manage_ec2_instance_vpc_subnet_id: subnet-071443aa123456789
manage_ec2_instance_tags:
Component: my-test-instance
Environment: Testing
manage_ec2_instance_wait_for_state: true
```
Combining the role with [cloud.aws_ops.ec2_networking_resources](../ec2_networking_resources/README.md):
```yaml
---
- name: Create EC2 networking resources and EC2 instance
hosts: localhost
gather_facts: false
roles:
- role: cloud.aws_ops.ec2_networking_resources:
vars:
ec2_networking_resources_vpc_name: my-vpc
ec2_networking_resources_vpc_cidr_block: 10.0.0.0/24
ec2_networking_resources_subnet_cidr_block: 10.0.0.0/25
ec2_networking_resources_sg_internal_name: my-internal-sg
ec2_networking_resources_sg_external_name: my-external-sg
ec2_networking_resources_create_igw: true
- role: cloud.aws_ops.manage_ec2_instance
vars:
manage_ec2_instance_operation: present
manage_ec2_instance_instance_name: my-test-instance
manage_ec2_instance_instance_type: t2.micro
manage_ec2_instance_ami_id: ami-066a7fbaa12345678
manage_ec2_instance_vpc_subnet_id: "{{ ec2_networking_resources_subnet_result.subnet.id }}"
manage_ec2_instance_associate_security_groups:
- my-internal-sg
- my-external-sg
manage_ec2_instance_associate_eip: true
```
Deleting an EC2 instance:
```yaml
---
- name: Delete EC2 instance
hosts: localhost
gather_facts: false
roles:
- role: cloud.aws_ops.manage_ec2_instance
vars:
manage_ec2_instance_operation: delete
manage_ec2_instance_instance_name: my-test-instance
manage_ec2_instance_wait_for_state: true
```
## License
GNU General Public License v3.0 or later
See [LICENSE](../../LICENSE) to see the full text.
## Author Information
- Ansible Cloud Content Team
4 changes: 4 additions & 0 deletions roles/manage_ec2_instance/defaults/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
---
manage_ec2_instance_operation: create
manage_ec2_instance_wait_for_state: true
manage_ec2_instance_associate_eip: false
69 changes: 69 additions & 0 deletions roles/manage_ec2_instance/meta/argument_specs.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
---
argument_specs:
main:
short_description: A role to create or delete an EC2 instance with optional networking resources.
description:
- A role to create or delete an EC2 instance.
- Can optionally attach security groups and associate an Elastic IP with the instance.
- Supports custom configurations for instance settings including instance type, AMI, key pair, tags, VPC/subnet, and networking configurations.
options:
manage_ec2_instance_operation:
description:
- Whether to create or delete resources using the role.
required: false
type: str
default: create
choices: [create, delete]
manage_ec2_instance_instance_name:
description:
- The name of the EC2 instance to be created.
required: true
type: str
manage_ec2_instance_instance_type:
description:
- The instance type for the EC2 instance. Required when `manage_ec2_instance_operation` is `create`.
required: false
type: str
manage_ec2_instance_ami_id:
description:
- The AMI ID for the EC2 instance. Required when `manage_ec2_instance_operation` is `create`.
required: false
type: str
manage_ec2_instance_key_name:
description:
- The name of the key pair to use for SSH access to the EC2 instance. If the key does not exist, a key pair will be created with the name. If not provided, instance will not be accessible via SSH. If provided when `manage_ec2_instance_operation` is `delete`, the keypair will also be deleted.
required: false
type: str
manage_ec2_instance_vpc_subnet_id:
description:
- The ID of the VPC subnet in which the instance will be launched. If not provided, instance will be created in the default subnet for the default VPC in the AWS region, if present.
required: false
type: str
manage_ec2_instance_tags:
description:
- A dictionary of tags to assign to the EC2 instance.
required: false
type: dict
manage_ec2_instance_wait_for_state:
description:
- Whether to wait for the EC2 instance to be in the running/terminated state before continuing.
required: false
default: true
type: bool
manage_ec2_instance_associate_security_groups:
description:
- List of security group names or IDs to associate with the EC2 instance.
required: false
type: list
elements: str
manage_ec2_instance_associate_eip:
description:
- Whether to create and associate an Elastic IP (EIP) with the EC2 instance.
required: false
default: false
type: bool
manage_ec2_instance_eip_tags:
description:
- Tags to assign to the Elastic IP.
required: false
type: dict
3 changes: 3 additions & 0 deletions roles/manage_ec2_instance/meta/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
---
dependencies:
- role: cloud.aws_ops.aws_setup_credentials
66 changes: 66 additions & 0 deletions roles/manage_ec2_instance/tasks/ec2_instance_create_operations.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
---
- name: Verify that an instance with same name does not exist
block:
- name: Get instance info with provided name
amazon.aws.ec2_instance_info:
filters:
tag:Name: "{{ manage_ec2_instance_instance_name }}"
instance-state-name: ["pending", "running", "stopping", "stopped"]
register: ec2_info_result

- name: Print warning and exit if instance exists
ansible.builtin.fail:
msg: "Instance with name {{ manage_ec2_instance_instance_name }} already exists in {{ aws_region }}. Please provide a different name to avoid updating the existing instance."
when: ec2_info_result.instances | length > 0

- name: Create a key pair if required
when: manage_ec2_instance_key_name is defined and manage_ec2_instance_key_name | length > 0
block:
- name: Get key pair info
amazon.aws.ec2_key_info:
names:
- "{{ manage_ec2_instance_key_name }}"
register: key_info_result

- name: Create new key pair
amazon.aws.ec2_key:
name: "{{ manage_ec2_instance_key_name }}"
state: present
when: key_info_result.keypairs | length == 0
register: ec2_instance_manage_key_pair_result

- name: Create EC2 instance with provided configuration
amazon.aws.ec2_instance:
state: running
name: "{{ manage_ec2_instance_instance_name }}"
instance_type: "{{ manage_ec2_instance_instance_type }}"
image_id: "{{ manage_ec2_instance_ami_id }}"
key_name: "{{ manage_ec2_instance_key_name | default(omit) }}"
security_groups: "{{ manage_ec2_instance_associate_security_groups | default(omit, true) }}"
vpc_subnet_id: "{{ manage_ec2_instance_vpc_subnet_id | default(omit) }}"
tags: "{{ manage_ec2_instance_tags | default(omit) }}"
wait: "{{ manage_ec2_instance_wait_for_state }}"
register: ec2_instance

- name: Allocate and associate Elastic IP if enabled
when: manage_ec2_instance_associate_eip is true
amazon.aws.ec2_eip:
device_id: "{{ ec2_instance.instance_ids[0] }}"
state: present
register: instance_eip

- name: Get EC2 instance info
amazon.aws.ec2_instance_info:
instance_ids: "{{ ec2_instance.instance_ids[0] }}"
register: ec2_instance_manage_create_result

- name: Output details of the created EC2 instance
ansible.builtin.debug:
msg:
- "EC2 instance {{ ec2_instance.instance_ids[0] }} created successfully"
- "Instance details: {{ ec2_instance_manage_create_result.instances[0] }}"

- name: Output private key if a new keypair was created
when: ec2_instance_manage_key_pair_result.key is defined
ansible.builtin.debug:
msg: "A new key pair was created for ssh access to the instance. Please save this private key for reference: {{ ec2_instance_manage_key_pair_result.key.private_key }}"
29 changes: 29 additions & 0 deletions roles/manage_ec2_instance/tasks/ec2_instance_delete_operations.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
---
- name: Get instance info with provided name
amazon.aws.ec2_instance_info:
filters:
tag:Name: "{{ manage_ec2_instance_instance_name }}"
instance-state-name: ["pending", "running", "stopping", "stopped"]
register: ec2_info_result

- name: Disassociate and release EIP if present
when: ec2_info_result.instances | length > 0
# and ec2_info_result.instances[0].network_interfaces.association.public_ip is defined
amazon.aws.ec2_eip:
device_id: "{{ ec2_info_result.instances[0].instance_id }}"
state: absent
release_on_disassociation: true

- name: Terminate EC2 Instance if present
when: ec2_info_result.instances | length > 0
amazon.aws.ec2_instance:
state: terminated
wait: "{{ manage_ec2_instance_wait_for_state }}"
instance_ids:
- "{{ ec2_info_result.instances[0].instance_id }}"

- name: Delete keypair if provided
when: manage_ec2_instance_key_name is defined and manage_ec2_instance_key_name | length > 0
amazon.aws.ec2_key:
name: "{{ manage_ec2_instance_key_name }}"
state: absent
12 changes: 12 additions & 0 deletions roles/manage_ec2_instance/tasks/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
---
- name: EC2 Instance creation or deletion based on operation
module_defaults:
group/aws: "{{ aws_setup_credentials__output }}"
block:
- name: Include create operations
ansible.builtin.include_tasks: ec2_instance_create_operations.yml
when: manage_ec2_instance_operation == 'create'

- name: Include delete operations
ansible.builtin.include_tasks: ec2_instance_delete_operations.yml
when: manage_ec2_instance_operation == 'delete'
2 changes: 2 additions & 0 deletions tests/integration/targets/test_manage_ec2_instance/aliases
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
cloud/aws
role/manage_ec2_instance
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
---
aws_security_token: "{{ security_token | default(omit) }}"

# Network Configuration
test_vpc_name: "{{ resource_prefix }}-vpc"
test_vpc_cidr: '101.{{ 255 | random(seed=resource_prefix) }}.0.0/16'
test_subnet_cidr: '101.{{ 255 | random(seed=resource_prefix) }}.0.0/24'
test_security_group_name: "{{ resource_prefix }}-sg"

# EC2 Instance Configuration
test_ec2_instance_name: "{{ resource_prefix }}-ec2-instance"
test_ec2_instance_type: t2.micro
test_ec2_key_name: "{{ resource_prefix }}-ec2-key"
21 changes: 21 additions & 0 deletions tests/integration/targets/test_manage_ec2_instance/tasks/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
---
- name: Integration tests for manage_ec2_instance role
module_defaults:
group/aws:
aws_access_key: "{{ aws_access_key }}"
aws_secret_key: "{{ aws_secret_key }}"
security_token: "{{ security_token | default(omit) }}"
region: "{{ aws_region }}"
block:
- name: Create resources required for test
ansible.builtin.include_tasks: setup.yml

- name: Run tests for case 1 - EC2 instance with required options only
ansible.builtin.include_tasks: tasks/test_ec2_required_options.yml

- name: Run tests for case 2 - EC2 instance with all options
ansible.builtin.include_tasks: tasks/test_ec2_all_options.yml

always:
- name: Delete any leftover resources used in tests
ansible.builtin.include_tasks: teardown.yml
Loading

0 comments on commit 8bc3076

Please sign in to comment.