0799bfbae8
Sanitized version of red team infrastructure automation platform. Operational content (implant pipelines, lures, credential capture) replaced with documented stubs. Architecture and infrastructure automation code intact.
480 lines
20 KiB
YAML
480 lines
20 KiB
YAML
---
|
|
# AWS Cleanup Playbook - Comprehensive version with robust VPC removal
|
|
- name: Clean up AWS resources
|
|
hosts: localhost
|
|
gather_facts: false
|
|
connection: local
|
|
vars_files:
|
|
- vars.yaml
|
|
vars:
|
|
aws_region: "{{ aws_region | default(aws_region_choices | random) }}"
|
|
confirm_cleanup: "{{ confirm_cleanup | default(true) }}"
|
|
deployment_id: "{{ deployment_id | default('') }}"
|
|
redirector_name: "{{ redirector_name | default('r-' + deployment_id) }}"
|
|
c2_name: "{{ c2_name | default('s-' + deployment_id) }}"
|
|
tracker_name: "{{ tracker_name | default('t-' + deployment_id) }}"
|
|
cleanup_summary: {}
|
|
|
|
tasks:
|
|
# Confirmation step (if enabled)
|
|
- name: Confirm cleanup
|
|
pause:
|
|
prompt: "Are you sure you want to delete all AWS resources for deployment ID {{ deployment_id }}? This action cannot be undone. Type 'yes' to confirm"
|
|
register: cleanup_confirmation
|
|
when: confirm_cleanup | bool
|
|
|
|
- name: Check confirmation
|
|
assert:
|
|
that:
|
|
- cleanup_confirmation.user_input | default('yes') == 'yes'
|
|
fail_msg: "Cleanup cancelled by user"
|
|
when: confirm_cleanup | bool
|
|
|
|
# STEP 1: Find all instances by deployment ID
|
|
- name: Find all EC2 instances for this deployment
|
|
amazon.aws.ec2_instance_info:
|
|
region: "{{ aws_region }}"
|
|
filters:
|
|
"tag:deployment_id": "{{ deployment_id }}"
|
|
register: deployment_instances
|
|
|
|
- name: Set fact for instances found
|
|
set_fact:
|
|
cleanup_summary: "{{ cleanup_summary | combine({'instances_found': deployment_instances.instances | length}) }}"
|
|
|
|
# STEP 2: Terminate all instances with proper tagging
|
|
- name: Terminate all instances for this deployment
|
|
amazon.aws.ec2_instance:
|
|
instance_ids: "{{ item.instance_id }}"
|
|
region: "{{ aws_region }}"
|
|
state: absent
|
|
loop: "{{ deployment_instances.instances }}"
|
|
register: terminated_instances
|
|
when: deployment_instances.instances | length > 0
|
|
|
|
- name: Wait for instances to be terminated
|
|
pause:
|
|
seconds: 30
|
|
when: deployment_instances.instances | length > 0
|
|
|
|
# STEP 3: Find all security groups by deployment ID
|
|
- name: Find all security groups for this deployment
|
|
amazon.aws.ec2_security_group_info:
|
|
region: "{{ aws_region }}"
|
|
filters:
|
|
"tag:deployment_id": "{{ deployment_id }}"
|
|
register: deployment_sgs
|
|
|
|
# Also find SGs by name pattern
|
|
- name: Find security groups by name pattern
|
|
amazon.aws.ec2_security_group_info:
|
|
region: "{{ aws_region }}"
|
|
register: all_sgs
|
|
|
|
- name: Filter SGs by name pattern
|
|
set_fact:
|
|
named_sgs: "{{ all_sgs.security_groups | selectattr('group_name', 'search', redirector_name + '-sg|' + c2_name + '-sg') | list }}"
|
|
|
|
- name: Combine all security groups to delete
|
|
set_fact:
|
|
all_sgs_to_delete: "{{ deployment_sgs.security_groups + named_sgs }}"
|
|
cleanup_summary: "{{ cleanup_summary | combine({'security_groups_found': (deployment_sgs.security_groups + named_sgs) | length}) }}"
|
|
|
|
# STEP 4: Delete all security groups
|
|
- name: Delete security groups
|
|
amazon.aws.ec2_security_group:
|
|
group_id: "{{ item.group_id }}"
|
|
region: "{{ aws_region }}"
|
|
state: absent
|
|
loop: "{{ all_sgs_to_delete }}"
|
|
when: all_sgs_to_delete | length > 0
|
|
ignore_errors: yes
|
|
register: deleted_sgs
|
|
|
|
# STEP 5: Find and delete all ENIs
|
|
- name: Find network interfaces by tag
|
|
amazon.aws.ec2_eni_info:
|
|
region: "{{ aws_region }}"
|
|
filters:
|
|
"tag:deployment_id": "{{ deployment_id }}"
|
|
register: deployment_enis
|
|
|
|
- name: Delete ENIs
|
|
amazon.aws.ec2_eni:
|
|
region: "{{ aws_region }}"
|
|
eni_id: "{{ item.id }}"
|
|
state: absent
|
|
force_detach: true
|
|
loop: "{{ deployment_enis.network_interfaces }}"
|
|
ignore_errors: yes
|
|
register: deleted_enis
|
|
when: deployment_enis.network_interfaces | length > 0
|
|
|
|
- name: Set ENIs count in summary
|
|
set_fact:
|
|
cleanup_summary: "{{ cleanup_summary | combine({'enis_found': deployment_enis.network_interfaces | length}) }}"
|
|
|
|
# STEP 6: Find all VPCs by deployment ID
|
|
- name: Find all VPCs for this deployment
|
|
amazon.aws.ec2_vpc_net_info:
|
|
region: "{{ aws_region }}"
|
|
filters:
|
|
"tag:deployment_id": "{{ deployment_id }}"
|
|
register: deployment_vpcs
|
|
|
|
# STEP 7: Find VPCs by name pattern as fallback
|
|
- name: Find all VPCs by name pattern
|
|
amazon.aws.ec2_vpc_net_info:
|
|
region: "{{ aws_region }}"
|
|
register: all_vpcs
|
|
|
|
- name: Filter VPCs by name pattern
|
|
set_fact:
|
|
named_vpcs: "{{ all_vpcs.vpcs | selectattr('tags', 'defined') | selectattr('tags.Name', 'defined') | selectattr('tags.Name', 'search', redirector_name + '-vpc|' + c2_name + '-vpc') | list }}"
|
|
|
|
- name: Combine all VPCs to delete
|
|
set_fact:
|
|
all_vpcs_to_delete: "{{ deployment_vpcs.vpcs + named_vpcs | unique(attribute='vpc_id') }}"
|
|
cleanup_summary: "{{ cleanup_summary | combine({'vpcs_found': (deployment_vpcs.vpcs + named_vpcs | unique(attribute='vpc_id')) | length}) }}"
|
|
|
|
# STEP 8: Find and delete NAT Gateways for each VPC separately
|
|
- name: Find NAT gateways in each VPC
|
|
amazon.aws.ec2_vpc_nat_gateway_info:
|
|
region: "{{ aws_region }}"
|
|
filters:
|
|
vpc-id: "{{ item.vpc_id }}"
|
|
register: natgw_results
|
|
loop: "{{ all_vpcs_to_delete }}"
|
|
when: all_vpcs_to_delete | length > 0
|
|
|
|
- name: Delete NAT gateways
|
|
amazon.aws.ec2_vpc_nat_gateway:
|
|
region: "{{ aws_region }}"
|
|
nat_gateway_id: "{{ item.1.nat_gateway_id }}"
|
|
state: absent
|
|
release_eip: true
|
|
loop: "{{ natgw_results.results | default([]) | selectattr('skipped', 'undefined') | selectattr('nat_gateways', 'defined') | subelements('nat_gateways') }}"
|
|
ignore_errors: yes
|
|
register: deleted_natgws
|
|
when: natgw_results.results is defined
|
|
|
|
- name: Wait after NAT deletion
|
|
pause:
|
|
seconds: 15
|
|
when: deleted_natgws.results is defined and deleted_natgws.results | length > 0
|
|
|
|
# STEP 9: Find and delete Internet Gateways
|
|
- name: Find internet gateways for each VPC
|
|
amazon.aws.ec2_vpc_igw_info:
|
|
region: "{{ aws_region }}"
|
|
filters:
|
|
attachment.vpc-id: "{{ item.vpc_id }}"
|
|
register: igw_results
|
|
loop: "{{ all_vpcs_to_delete }}"
|
|
when: all_vpcs_to_delete | length > 0
|
|
|
|
- name: Detach and delete internet gateways
|
|
# Fix the reference to attachment - should use item.1.attachments or similar
|
|
ec2_vpc_igw:
|
|
vpc_id: "{{ item.1.attachments[0].vpc_id }}" # Fix this line
|
|
state: absent
|
|
region: "{{ aws_region }}"
|
|
with_together: "{{ find_igws.results|default([]) }}"
|
|
register: deleted_igws
|
|
ignore_errors: yes
|
|
|
|
- name: Wait after IGW deletion
|
|
pause:
|
|
seconds: 15
|
|
when: deleted_igws.results is defined and deleted_igws.results | length > 0
|
|
|
|
# STEP 10: Find and delete Route Tables
|
|
- name: Find route tables for each VPC
|
|
amazon.aws.ec2_vpc_route_table_info:
|
|
region: "{{ aws_region }}"
|
|
filters:
|
|
vpc-id: "{{ item.vpc_id }}"
|
|
register: rtb_results
|
|
loop: "{{ all_vpcs_to_delete }}"
|
|
when: all_vpcs_to_delete | length > 0
|
|
|
|
- name: Delete non-main route tables
|
|
amazon.aws.ec2_vpc_route_table:
|
|
region: "{{ aws_region }}"
|
|
route_table_id: "{{ item.1.id }}"
|
|
lookup: id
|
|
state: absent
|
|
loop: "{{ rtb_results.results | default([]) | selectattr('skipped', 'undefined') | selectattr('route_tables', 'defined') | subelements('route_tables') }}"
|
|
when: not item.1.associations[0].main | default(false)
|
|
ignore_errors: yes
|
|
register: deleted_rtbs
|
|
|
|
# Add this after your existing route table deletion
|
|
- name: Delete main route tables with AWS CLI
|
|
shell: |
|
|
for rtb in $(aws ec2 describe-route-tables --region {{ aws_region }} --filters "Name=vpc-id,Values={{ item.vpc_id }}" --query 'RouteTables[?Associations[?Main==`true`]].RouteTableId' --output text); do
|
|
aws ec2 delete-route --route-table-id $rtb --destination-cidr-block 0.0.0.0/0 --region {{ aws_region }} || true
|
|
done
|
|
environment:
|
|
AWS_ACCESS_KEY_ID: "{{ aws_access_key }}"
|
|
AWS_SECRET_ACCESS_KEY: "{{ aws_secret_key }}"
|
|
loop: "{{ all_vpcs_to_delete }}"
|
|
ignore_errors: yes
|
|
when: all_vpcs_to_delete | length > 0
|
|
|
|
# STEP 11: Find and delete Subnets
|
|
- name: Find subnets for each VPC
|
|
ec2_vpc_subnet_info:
|
|
filters:
|
|
vpc-id: "{{ item.vpc_id }}" # Fix this line, should be vpc_id not vpc_idvpc
|
|
region: "{{ aws_region }}"
|
|
with_items: "{{ all_vpcs_to_delete }}"
|
|
register: find_subnets
|
|
|
|
- name: Delete subnets
|
|
amazon.aws.ec2_vpc_subnet:
|
|
region: "{{ aws_region }}"
|
|
vpc_id: "{{ item.1.vpc_id }}"
|
|
cidr: "{{ item.1.cidr_block }}"
|
|
state: absent
|
|
loop: "{{ subnet_results.results | default([]) | selectattr('skipped', 'undefined') | selectattr('subnets', 'defined') | subelements('subnets') }}"
|
|
ignore_errors: yes
|
|
register: deleted_subnets
|
|
when: subnet_results.results is defined
|
|
|
|
# STEP 12: Find and delete VPC Endpoints
|
|
- name: Find VPC endpoints for each VPC
|
|
amazon.aws.ec2_vpc_endpoint_info:
|
|
region: "{{ aws_region }}"
|
|
filters:
|
|
vpc-id: "{{ item.vpc_id }}"
|
|
register: endpoint_results
|
|
loop: "{{ all_vpcs_to_delete }}"
|
|
when: all_vpcs_to_delete | length > 0
|
|
|
|
- name: Delete VPC endpoints
|
|
amazon.aws.ec2_vpc_endpoint:
|
|
region: "{{ aws_region }}"
|
|
vpc_endpoint_id: "{{ item.1.vpc_endpoint_id }}"
|
|
state: absent
|
|
loop: "{{ endpoint_results.results | default([]) | selectattr('skipped', 'undefined') | selectattr('vpc_endpoints', 'defined') | subelements('vpc_endpoints') }}"
|
|
ignore_errors: yes
|
|
register: deleted_endpoints
|
|
when: endpoint_results.results is defined
|
|
|
|
# Add before STEP 13
|
|
- name: Check for remaining VPC dependencies
|
|
shell: |
|
|
aws ec2 describe-network-interfaces --region {{ aws_region }} --filters "Name=vpc-id,Values={{ item.vpc_id }}" --output json
|
|
environment:
|
|
AWS_ACCESS_KEY_ID: "{{ aws_access_key }}"
|
|
AWS_SECRET_ACCESS_KEY: "{{ aws_secret_key }}"
|
|
register: remaining_deps
|
|
loop: "{{ all_vpcs_to_delete }}"
|
|
when: all_vpcs_to_delete | length > 0
|
|
|
|
- name: Display any remaining dependencies
|
|
debug:
|
|
msg: "VPC {{ item.item.vpc_id }} still has dependencies that need to be removed"
|
|
loop: "{{ remaining_deps.results }}"
|
|
when: item.stdout | from_json | json_query('NetworkInterfaces') | length > 0
|
|
|
|
# Add this before the force delete of network interfaces
|
|
- name: Detach remaining network interfaces
|
|
shell: |
|
|
aws ec2 detach-network-interface --attachment-id $(aws ec2 describe-network-interfaces --network-interface-ids {{ item.1 }} --query 'NetworkInterfaces[0].Attachment.AttachmentId' --output text) --region {{ aws_region }} --force
|
|
environment:
|
|
AWS_ACCESS_KEY_ID: "{{ aws_access_key }}"
|
|
AWS_SECRET_ACCESS_KEY: "{{ aws_secret_key }}"
|
|
loop: "{{ remaining_deps.results | selectattr('stdout', 'defined') |
|
|
map('attr', 'stdout') | map('from_json') |
|
|
map('json_query', 'NetworkInterfaces[?Status==`in-use`].NetworkInterfaceId') |
|
|
zip(remaining_deps.results | map('attr', 'item')) | list }}"
|
|
ignore_errors: yes
|
|
when: item.0 | length > 0
|
|
|
|
- name: Force delete any remaining network interfaces
|
|
shell: |
|
|
aws ec2 delete-network-interface --network-interface-id {{ item.1 }} --region {{ aws_region }}
|
|
environment:
|
|
AWS_ACCESS_KEY_ID: "{{ aws_access_key }}"
|
|
AWS_SECRET_ACCESS_KEY: "{{ aws_secret_key }}"
|
|
loop: "{{ remaining_deps.results | selectattr('stdout', 'defined') |
|
|
map('attr', 'stdout') | map('from_json') |
|
|
map('json_query', 'NetworkInterfaces[].NetworkInterfaceId') |
|
|
zip(remaining_deps.results | map('attr', 'item')) | list }}"
|
|
ignore_errors: yes
|
|
when: item.0 | length > 0
|
|
|
|
# STEP 13: Final VPC deletion with multiple retries
|
|
- name: Wait for all dependencies to clear
|
|
pause:
|
|
seconds: 20
|
|
when: all_vpcs_to_delete | length > 0
|
|
|
|
# First attempt with normal module - with error display
|
|
- name: Delete all VPCs (first attempt)
|
|
amazon.aws.ec2_vpc_net:
|
|
vpc_id: "{{ item.vpc_id }}"
|
|
region: "{{ aws_region }}"
|
|
state: absent
|
|
loop: "{{ all_vpcs_to_delete }}"
|
|
register: vpc_deletion
|
|
when: all_vpcs_to_delete | length > 0
|
|
ignore_errors: yes
|
|
|
|
- name: Display VPC deletion errors
|
|
debug:
|
|
msg: "Failed to delete VPC {{ item.item.vpc_id }}: {{ item.msg }}"
|
|
loop: "{{ vpc_deletion.results | default([]) }}"
|
|
when: item.failed is defined and item.failed
|
|
|
|
# Direct API call for any VPCs that failed
|
|
- name: Find which VPCs still exist
|
|
amazon.aws.ec2_vpc_net_info:
|
|
region: "{{ aws_region }}"
|
|
vpc_ids: "{{ all_vpcs_to_delete | map(attribute='vpc_id') | list }}"
|
|
register: remaining_vpcs
|
|
when: all_vpcs_to_delete | length > 0
|
|
|
|
# Forcibly delete with direct AWS CLI command
|
|
- name: Force delete remaining VPCs with CLI
|
|
shell: |
|
|
aws ec2 delete-vpc --vpc-id {{ item.vpc_id }} --region {{ aws_region }}
|
|
environment:
|
|
AWS_ACCESS_KEY_ID: "{{ aws_access_key }}"
|
|
AWS_SECRET_ACCESS_KEY: "{{ aws_secret_key }}"
|
|
loop: "{{ remaining_vpcs.vpcs }}"
|
|
ignore_errors: yes
|
|
when: remaining_vpcs is defined and remaining_vpcs.vpcs | length > 0
|
|
register: force_vpc_delete
|
|
|
|
# Add after the VPC deletion attempts - more aggressive approach
|
|
- name: Force delete remaining VPCs with AWS CLI and debug output
|
|
shell: |
|
|
aws ec2 delete-vpc --vpc-id {{ item.vpc_id }} --region {{ aws_region }} 2>&1 || echo "Failed with: $?"
|
|
environment:
|
|
AWS_ACCESS_KEY_ID: "{{ aws_access_key }}"
|
|
AWS_SECRET_ACCESS_KEY: "{{ aws_secret_key }}"
|
|
loop: "{{ remaining_vpcs.vpcs }}"
|
|
register: force_vpc_delete_debug
|
|
when: remaining_vpcs is defined and remaining_vpcs.vpcs | length > 0
|
|
|
|
- name: Display debug output from force delete
|
|
debug:
|
|
msg: "{{ item.stdout }}"
|
|
loop: "{{ force_vpc_delete_debug.results | default([]) }}"
|
|
when: item.stdout is defined and item.stdout | trim != ""
|
|
|
|
# Track deleted VPCs in summary
|
|
- name: Set VPC deletion results in summary
|
|
set_fact:
|
|
cleanup_summary: "{{ cleanup_summary | combine({
|
|
'vpcs_deleted': ((vpc_deletion.results | default([]) | selectattr('failed', 'undefined') | list | length) + (force_vpc_delete.results | default([]) | selectattr('failed', 'undefined') | list | length))}) }}"
|
|
when: all_vpcs_to_delete | length > 0
|
|
|
|
# Add these tasks to confirm VPC deletion
|
|
- name: Verify VPC deletion
|
|
amazon.aws.ec2_vpc_net_info:
|
|
region: "{{ aws_region }}"
|
|
filters:
|
|
"tag:deployment_id": "{{ deployment_id }}"
|
|
register: vpc_check
|
|
|
|
- name: Display cleanup summary
|
|
debug:
|
|
msg:
|
|
- "Cleanup Summary:"
|
|
- "Redirector instance deleted: {{ redirector_deleted | default('N/A') }}"
|
|
- "C2 instance deleted: {{ c2_deleted | default('N/A') }}"
|
|
- "VPC resources deleted: {{ vpc_check.vpcs | length == 0 }}"
|
|
when: not disable_summary | default(false)
|
|
|
|
# STEP 14: Delete key pairs
|
|
- name: Find key pairs by name patterns
|
|
amazon.aws.ec2_key:
|
|
name: "{{ item }}"
|
|
region: "{{ aws_region }}"
|
|
state: present
|
|
register: keys_check
|
|
ignore_errors: yes
|
|
with_items:
|
|
- "{{ redirector_name }}"
|
|
- "{{ c2_name }}"
|
|
- "{{ tracker_name }}"
|
|
|
|
- name: Delete key pairs
|
|
amazon.aws.ec2_key:
|
|
name: "{{ item.invocation.module_args.name }}"
|
|
region: "{{ aws_region }}"
|
|
state: absent
|
|
loop: "{{ keys_check.results }}"
|
|
when: keys_check.results | length > 0 and item.failed is not defined and item.key is defined
|
|
register: deleted_keys
|
|
|
|
- name: Count deleted keys
|
|
set_fact:
|
|
cleanup_summary: "{{ cleanup_summary | combine({
|
|
'keypairs_deleted': (deleted_keys.results | default([]) | selectattr('changed', 'defined') | selectattr('changed') | list | length)}) }}"
|
|
|
|
# Add this after your existing key pair finding task
|
|
- name: Find c2deploy key pairs
|
|
amazon.aws.ec2_key:
|
|
name: "c2deploy_{{ deployment_id }}"
|
|
region: "{{ aws_region }}"
|
|
state: present
|
|
register: c2deploy_key_check
|
|
ignore_errors: yes
|
|
|
|
- name: Delete c2deploy key pairs
|
|
amazon.aws.ec2_key:
|
|
name: "c2deploy_{{ deployment_id }}"
|
|
region: "{{ aws_region }}"
|
|
state: absent
|
|
when: not c2deploy_key_check.failed | default(true)
|
|
register: deleted_c2deploy_key
|
|
|
|
# STEP 15: Delete SSH key files
|
|
- name: Delete SSH key files
|
|
file:
|
|
path: "{{ item }}"
|
|
state: absent
|
|
with_items:
|
|
- "~/.ssh/{{ redirector_name }}.pem"
|
|
- "~/.ssh/{{ c2_name }}.pem"
|
|
- "~/.ssh/{{ tracker_name }}.pem"
|
|
- "~/.ssh/c2deploy_{{ deployment_id }}.pem" # Fix: add .pem extension
|
|
- "~/.ssh/c2deploy_{{ deployment_id }}.pub"
|
|
ignore_errors: yes
|
|
register: deleted_ssh_files
|
|
|
|
- name: Count deleted SSH files
|
|
set_fact:
|
|
cleanup_summary: "{{ cleanup_summary | combine({
|
|
'ssh_files_deleted': (deleted_ssh_files.results | selectattr('changed', 'defined') | selectattr('changed') | list | length)}) }}"
|
|
|
|
# Remove infrastructure state file - fix path to include deployment_id
|
|
- name: Remove infrastructure state file
|
|
file:
|
|
path: "infrastructure_state_{{ deployment_id }}.json"
|
|
state: absent
|
|
ignore_errors: yes
|
|
register: infra_file
|
|
|
|
# STEP 16: Enhanced and Accurate Cleanup Summary
|
|
- name: Enhanced cleanup summary
|
|
debug:
|
|
msg:
|
|
- "=========================================================="
|
|
- " AWS CLEANUP SUMMARY: {{ deployment_id }} "
|
|
- "=========================================================="
|
|
- "EC2 Instances: {{ cleanup_summary.instances_found | default(0) }} found, {{ terminated_instances.results | default([]) | length }} terminated"
|
|
- "Security Groups: {{ cleanup_summary.security_groups_found | default(0) }} found, {{ deleted_sgs.results | default([]) | length }} deleted"
|
|
- "Network Interfaces: {{ cleanup_summary.enis_found | default(0) }} found, {{ deleted_enis.results | default([]) | length }} deleted"
|
|
- "VPCs: {{ cleanup_summary.vpcs_found | default(0) }} found, {{ cleanup_summary.vpcs_deleted | default(0) }} deleted"
|
|
- "Key Pairs: {{ cleanup_summary.keypairs_deleted | default(0) }} deleted"
|
|
- "SSH Key Files: {{ cleanup_summary.ssh_files_deleted | default(0) }} deleted"
|
|
- "Infrastructure file: {{ 'Removed' if infra_file.changed else 'Not found' }}"
|
|
- "=========================================================="
|
|
- "CLEANUP {{ 'COMPLETED' if (cleanup_summary.vpcs_deleted | default(0) == cleanup_summary.vpcs_found | default(0)) else 'PARTIAL - SOME RESOURCES MAY REMAIN' }}"
|
|
- "=========================================================" |