working on it
This commit is contained in:
@@ -4,3 +4,4 @@ deployment*
|
||||
config.yml
|
||||
logs/
|
||||
domainhunter/
|
||||
infrastructure_state_*.json
|
||||
+26
-7
@@ -9,7 +9,7 @@
|
||||
- vars.yaml
|
||||
vars:
|
||||
# Default values
|
||||
ssh_user: "{{ ssh_user | default('root') }}"
|
||||
ssh_user: "{{ ssh_user | default('kali') }}"
|
||||
aws_region: "{{ aws_region | default(aws_region_choices | random) }}"
|
||||
instance_type: "{{ aws_instance_type | default('t2.medium') }}"
|
||||
deployment_id: "{{ deployment_id | default('') }}"
|
||||
@@ -17,6 +17,10 @@
|
||||
# Check for shared infrastructure
|
||||
# Only use shared when C2 and redirector are in the same region
|
||||
use_shared_infra: "{{ not (c2_region is defined and redirector_region is defined and c2_region != redirector_region) and not c2_only | default(false) | bool and not redirector_only | default(false) | bool }}"
|
||||
# AMI map comes from vars.yaml - add fallback for safety
|
||||
kali_ami_map_fallback:
|
||||
us-east-1: "ami-061b17d332829ab1c"
|
||||
us-east-2: "ami-061b17d332829ab1c" # Fallback to us-east-1 AMI
|
||||
|
||||
tasks:
|
||||
- name: Validate AWS credentials
|
||||
@@ -29,13 +33,13 @@
|
||||
# Load shared infrastructure state if available
|
||||
- name: Check for shared infrastructure state
|
||||
stat:
|
||||
path: "infrastructure_state.json"
|
||||
path: "infrastructure_state_{{ deployment_id }}.json"
|
||||
register: infra_state_file
|
||||
when: use_shared_infra | bool
|
||||
|
||||
- name: Load shared infrastructure state
|
||||
include_vars:
|
||||
file: "infrastructure_state.json"
|
||||
file: "infrastructure_state_{{ deployment_id }}.json"
|
||||
name: shared_infra
|
||||
when: use_shared_infra | bool and infra_state_file.stat.exists | default(false)
|
||||
|
||||
@@ -49,15 +53,30 @@
|
||||
aws_c2_region: "{{ c2_region | default(aws_region) }}"
|
||||
when: not (use_shared_infra | bool and infra_state_file.stat.exists | default(false))
|
||||
|
||||
- name: Set AMI ID for selected region
|
||||
- name: Check if ami_map is provided in vars.yaml
|
||||
debug:
|
||||
msg: "ami_map is {{ 'defined' if ami_map is defined else 'NOT defined' }} in vars.yaml"
|
||||
|
||||
- name: Set AMI ID for selected region (from vars.yaml)
|
||||
set_fact:
|
||||
ami_id: "{{ ami_map[aws_c2_region] }}"
|
||||
when: ami_map is defined and aws_c2_region in ami_map
|
||||
ami_id: "{{ ami_map[aws_c2_region] | default(ami_map.us-east-1) }}"
|
||||
when: ami_map is defined and ami_map
|
||||
|
||||
- name: Set AMI ID for selected region (fallback)
|
||||
set_fact:
|
||||
ami_id: "{{ kali_ami_map_fallback[aws_c2_region] | default(kali_ami_map_fallback['us-east-1']) }}"
|
||||
when: ami_id is not defined or ami_id == ""
|
||||
|
||||
- name: Ensure we have a valid AMI ID
|
||||
assert:
|
||||
that:
|
||||
- ami_id is defined and ami_id != ""
|
||||
fail_msg: "Could not determine a valid AMI ID for region {{ aws_c2_region }}. Please add it to ami_map in vars.yaml."
|
||||
|
||||
# Add AMI username mapping - improved with better detection
|
||||
- name: Determine correct SSH user for the AMI
|
||||
set_fact:
|
||||
ami_ssh_user: "{{ 'root' if (ami_id is defined and ami_id is search('-root-')) or (ami_id is defined and ami_id == 'ami-061b17d332829ab1c') else 'ubuntu' }}"
|
||||
ami_ssh_user: "{{ 'kali' if (ami_id is defined and ami_id is search('-kali-')) or (ami_id is defined and ami_id == 'ami-061b17d332829ab1c') else 'ubuntu' }}"
|
||||
|
||||
- name: Display AMI and user information for debugging
|
||||
debug:
|
||||
|
||||
+408
-284
@@ -1,6 +1,5 @@
|
||||
---
|
||||
# AWS Cleanup Playbook
|
||||
|
||||
# AWS Cleanup Playbook - Comprehensive version with robust VPC removal
|
||||
- name: Clean up AWS resources
|
||||
hosts: localhost
|
||||
gather_facts: false
|
||||
@@ -8,348 +7,473 @@
|
||||
vars_files:
|
||||
- vars.yaml
|
||||
vars:
|
||||
delete_results: {}
|
||||
vpc_cleanup_list: []
|
||||
aws_detailed_logging: true
|
||||
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 prompts
|
||||
# Confirmation step (if enabled)
|
||||
- name: Confirm cleanup
|
||||
pause:
|
||||
prompt: "This will delete all AWS resources for deployment ID {{ deployment_id }}. Continue? (yes/no)"
|
||||
register: confirm_deletion
|
||||
when: confirm_cleanup | default(true) | bool
|
||||
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: confirm_deletion.user_input | default('yes') | lower == 'yes'
|
||||
that:
|
||||
- cleanup_confirmation.user_input | default('yes') == 'yes'
|
||||
fail_msg: "Cleanup cancelled by user"
|
||||
when: confirm_cleanup | default(true) | bool
|
||||
when: confirm_cleanup | bool
|
||||
|
||||
# Display AWS region being used for cleanup
|
||||
- name: Display AWS region being used for cleanup
|
||||
debug:
|
||||
msg: "Cleaning up resources in region: {{ aws_region }}"
|
||||
when: aws_detailed_logging | bool
|
||||
|
||||
# Check for and load shared infrastructure file
|
||||
- name: Check for shared infrastructure state
|
||||
stat:
|
||||
path: "infrastructure_state.json"
|
||||
register: infra_state_file
|
||||
|
||||
- name: Load shared infrastructure state
|
||||
include_vars:
|
||||
file: "infrastructure_state.json"
|
||||
name: shared_infra
|
||||
when: infra_state_file.stat.exists | default(false)
|
||||
|
||||
# Initialize cleaned resources tracking
|
||||
- name: Initialize cleaned resources tracking
|
||||
set_fact:
|
||||
cleaned_resources:
|
||||
instances: []
|
||||
key_pairs: []
|
||||
security_groups: []
|
||||
network_interfaces: []
|
||||
route_tables: []
|
||||
subnets: []
|
||||
internet_gateways: []
|
||||
vpcs: []
|
||||
|
||||
# EC2 Instance cleanup
|
||||
- name: Terminate C2 instances
|
||||
amazon.aws.ec2_instance:
|
||||
state: absent
|
||||
# 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:Name": "{{ c2_name }}"
|
||||
register: c2_instances_result
|
||||
when: cleanup_c2 | default(true) | bool
|
||||
"tag:deployment_id": "{{ deployment_id }}"
|
||||
register: deployment_instances
|
||||
|
||||
- name: Update cleaned instances list for C2
|
||||
- name: Set fact for instances found
|
||||
set_fact:
|
||||
cleaned_resources: "{{ cleaned_resources | combine({'instances': cleaned_resources.instances + c2_instances_result.instance_ids}, recursive=true) }}"
|
||||
when: cleanup_c2 | default(true) | bool and c2_instances_result.instance_ids is defined
|
||||
cleanup_summary: "{{ cleanup_summary | combine({'instances_found': deployment_instances.instances | length}) }}"
|
||||
|
||||
- name: Terminate redirector instances
|
||||
# STEP 2: Terminate all instances with proper tagging
|
||||
- name: Terminate all instances for this deployment
|
||||
amazon.aws.ec2_instance:
|
||||
state: absent
|
||||
instance_ids: "{{ item.instance_id }}"
|
||||
region: "{{ aws_region }}"
|
||||
filters:
|
||||
"tag:Name": "{{ redirector_name }}"
|
||||
register: redirector_instances_result
|
||||
when: cleanup_redirector | default(true) | bool
|
||||
|
||||
- name: Update cleaned instances list for redirector
|
||||
set_fact:
|
||||
cleaned_resources: "{{ cleaned_resources | combine({'instances': cleaned_resources.instances + redirector_instances_result.instance_ids}, recursive=true) }}"
|
||||
when: cleanup_redirector | default(true) | bool and redirector_instances_result.instance_ids is defined
|
||||
|
||||
- name: Terminate tracker instances
|
||||
amazon.aws.ec2_instance:
|
||||
state: absent
|
||||
region: "{{ aws_region }}"
|
||||
filters:
|
||||
"tag:Name": "{{ tracker_name | default('t-' + deployment_id) }}"
|
||||
register: tracker_instances_result
|
||||
when: cleanup_tracker | default(true) | bool and track_deployment | default(false) | bool
|
||||
loop: "{{ deployment_instances.instances }}"
|
||||
register: terminated_instances
|
||||
when: deployment_instances.instances | length > 0
|
||||
|
||||
- name: Update cleaned instances list for tracker
|
||||
set_fact:
|
||||
cleaned_resources: "{{ cleaned_resources | combine({'instances': cleaned_resources.instances + tracker_instances_result.instance_ids}, recursive=true) }}"
|
||||
when: cleanup_tracker | default(true) | bool and track_deployment | default(false) | bool and tracker_instances_result.instance_ids is defined
|
||||
|
||||
# Wait for all instances to terminate before attempting to clean up dependent resources
|
||||
- name: Wait for instances to terminate
|
||||
- name: Wait for instances to be terminated
|
||||
pause:
|
||||
seconds: 30
|
||||
when: (cleanup_c2 | default(true) | bool and c2_instances_result.changed | default(false)) or
|
||||
(cleanup_redirector | default(true) | bool and redirector_instances_result.changed | default(false)) or
|
||||
(cleanup_tracker | default(true) | bool and track_deployment | default(false) | bool and tracker_instances_result.changed | default(false))
|
||||
when: deployment_instances.instances | length > 0
|
||||
|
||||
# List all key pairs to handle different naming patterns
|
||||
- name: List available EC2 key pairs
|
||||
amazon.aws.ec2_key_info:
|
||||
# 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 }}"
|
||||
register: all_key_pairs
|
||||
filters:
|
||||
"tag:deployment_id": "{{ deployment_id }}"
|
||||
register: deployment_sgs
|
||||
|
||||
# Delete C2 deployment key with deployment_id pattern
|
||||
- name: Delete C2 deployment key (deployment_id pattern)
|
||||
amazon.aws.ec2_key:
|
||||
name: "c2deploy_{{ deployment_id }}"
|
||||
# Also find SGs by name pattern
|
||||
- name: Find security groups by name pattern
|
||||
amazon.aws.ec2_security_group_info:
|
||||
region: "{{ aws_region }}"
|
||||
state: absent
|
||||
register: c2_deploy_key_result
|
||||
when: cleanup_c2 | default(true) | bool
|
||||
register: all_sgs
|
||||
|
||||
# Delete C2 EC2 key pair (original pattern)
|
||||
- name: Delete C2 EC2 key pair (original pattern)
|
||||
amazon.aws.ec2_key:
|
||||
name: "{{ c2_name }}"
|
||||
region: "{{ aws_region }}"
|
||||
state: absent
|
||||
register: c2_key_result
|
||||
when: cleanup_c2 | default(true) | bool
|
||||
|
||||
# Update cleaned key pairs list for C2
|
||||
- name: Update cleaned key pairs list for C2
|
||||
- name: Filter SGs by name pattern
|
||||
set_fact:
|
||||
cleaned_resources: "{{ cleaned_resources | combine({'key_pairs': cleaned_resources.key_pairs + [item]}, recursive=true) }}"
|
||||
with_items:
|
||||
- "c2deploy_{{ deployment_id }}"
|
||||
- "{{ c2_name }}"
|
||||
when: cleanup_c2 | default(true) | bool and (c2_key_result.changed or c2_deploy_key_result.changed)
|
||||
named_sgs: "{{ all_sgs.security_groups | selectattr('group_name', 'search', redirector_name + '-sg|' + c2_name + '-sg') | list }}"
|
||||
|
||||
# Delete redirector EC2 key pair
|
||||
- name: Delete redirector EC2 key pair
|
||||
amazon.aws.ec2_key:
|
||||
name: "{{ redirector_name }}"
|
||||
region: "{{ aws_region }}"
|
||||
state: absent
|
||||
register: redirector_key_result
|
||||
when: cleanup_redirector | default(true) | bool
|
||||
|
||||
- name: Update cleaned key pairs list for redirector
|
||||
- name: Combine all security groups to delete
|
||||
set_fact:
|
||||
cleaned_resources: "{{ cleaned_resources | combine({'key_pairs': cleaned_resources.key_pairs + [redirector_name]}, recursive=true) }}"
|
||||
when: cleanup_redirector | default(true) | bool and redirector_key_result.changed | default(false)
|
||||
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}) }}"
|
||||
|
||||
- name: Delete tracker EC2 key pair
|
||||
amazon.aws.ec2_key:
|
||||
name: "{{ tracker_name | default('t-' + deployment_id) }}"
|
||||
region: "{{ aws_region }}"
|
||||
state: absent
|
||||
register: tracker_key_result
|
||||
when: cleanup_tracker | default(true) | bool and track_deployment | default(false) | bool
|
||||
|
||||
- name: Update cleaned key pairs list for tracker
|
||||
set_fact:
|
||||
cleaned_resources: "{{ cleaned_resources | combine({'key_pairs': cleaned_resources.key_pairs + [tracker_name | default('t-' + deployment_id)]}, recursive=true) }}"
|
||||
when: cleanup_tracker | default(true) | bool and track_deployment | default(false) | bool and tracker_key_result.changed | default(false)
|
||||
|
||||
# Security group cleanup
|
||||
- name: Delete C2 security group
|
||||
# STEP 4: Delete all security groups
|
||||
- name: Delete security groups
|
||||
amazon.aws.ec2_security_group:
|
||||
name: "{{ c2_name }}-sg"
|
||||
group_id: "{{ item.group_id }}"
|
||||
region: "{{ aws_region }}"
|
||||
state: absent
|
||||
register: c2_sg_result
|
||||
loop: "{{ all_sgs_to_delete }}"
|
||||
when: all_sgs_to_delete | length > 0
|
||||
ignore_errors: yes
|
||||
when: cleanup_c2 | default(true) | bool
|
||||
register: deleted_sgs
|
||||
|
||||
- name: Update cleaned security groups list for C2
|
||||
set_fact:
|
||||
cleaned_resources: "{{ cleaned_resources | combine({'security_groups': cleaned_resources.security_groups + [c2_sg_result.group_id | default('')]}, recursive=true) }}"
|
||||
when: cleanup_c2 | default(true) | bool and c2_sg_result.changed | default(false) and c2_sg_result.group_id is defined
|
||||
|
||||
- name: Delete redirector security group
|
||||
amazon.aws.ec2_security_group:
|
||||
name: "{{ redirector_name }}-sg"
|
||||
# STEP 5: Find and delete all ENIs
|
||||
- name: Find network interfaces by tag
|
||||
amazon.aws.ec2_eni_info:
|
||||
region: "{{ aws_region }}"
|
||||
state: absent
|
||||
register: redirector_sg_result
|
||||
ignore_errors: yes
|
||||
when: cleanup_redirector | default(true) | bool
|
||||
filters:
|
||||
"tag:deployment_id": "{{ deployment_id }}"
|
||||
register: deployment_enis
|
||||
|
||||
- name: Update cleaned security groups list for redirector
|
||||
set_fact:
|
||||
cleaned_resources: "{{ cleaned_resources | combine({'security_groups': cleaned_resources.security_groups + [redirector_sg_result.group_id | default('')]}, recursive=true) }}"
|
||||
when: cleanup_redirector | default(true) | bool and redirector_sg_result.changed | default(false) and redirector_sg_result.group_id is defined
|
||||
|
||||
- name: Delete tracker security group
|
||||
amazon.aws.ec2_security_group:
|
||||
name: "{{ tracker_name | default('t-' + deployment_id) }}-sg"
|
||||
- name: Delete ENIs
|
||||
amazon.aws.ec2_eni:
|
||||
region: "{{ aws_region }}"
|
||||
eni_id: "{{ item.id }}"
|
||||
state: absent
|
||||
register: tracker_sg_result
|
||||
force_detach: true
|
||||
loop: "{{ deployment_enis.network_interfaces }}"
|
||||
ignore_errors: yes
|
||||
when: cleanup_tracker | default(true) | bool and track_deployment | default(false) | bool
|
||||
register: deleted_enis
|
||||
when: deployment_enis.network_interfaces | length > 0
|
||||
|
||||
- name: Update cleaned security groups list for tracker
|
||||
- name: Set ENIs count in summary
|
||||
set_fact:
|
||||
cleaned_resources: "{{ cleaned_resources | combine({'security_groups': cleaned_resources.security_groups + [tracker_sg_result.group_id | default('')]}, recursive=true) }}"
|
||||
when: cleanup_tracker | default(true) | bool and track_deployment | default(false) | bool and tracker_sg_result.changed | default(false) and tracker_sg_result.group_id is defined
|
||||
cleanup_summary: "{{ cleanup_summary | combine({'enis_found': deployment_enis.network_interfaces | length}) }}"
|
||||
|
||||
# Identify VPCs to clean up (by tag and by name pattern)
|
||||
- name: Get VPCs by deployment tag
|
||||
# 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: tagged_vpcs
|
||||
register: deployment_vpcs
|
||||
|
||||
- name: Add tagged VPCs to cleanup list
|
||||
set_fact:
|
||||
vpc_cleanup_list: "{{ vpc_cleanup_list + [item.vpc_id] }}"
|
||||
with_items: "{{ tagged_vpcs.vpcs }}"
|
||||
when: tagged_vpcs.vpcs | length > 0
|
||||
|
||||
# Find VPCs by name pattern
|
||||
- name: Find VPCs by naming pattern for C2
|
||||
# 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 }}"
|
||||
filters:
|
||||
"tag:Name": "{{ c2_name }}-vpc"
|
||||
register: c2_named_vpcs
|
||||
when: cleanup_c2 | default(true) | bool
|
||||
register: all_vpcs
|
||||
|
||||
- name: Add C2-named VPCs to cleanup list
|
||||
- name: Filter VPCs by name pattern
|
||||
set_fact:
|
||||
vpc_cleanup_list: "{{ vpc_cleanup_list + [item.vpc_id] }}"
|
||||
with_items: "{{ c2_named_vpcs.vpcs }}"
|
||||
when: cleanup_c2 | default(true) | bool and c2_named_vpcs.vpcs | length > 0
|
||||
named_vpcs: "{{ all_vpcs.vpcs | selectattr('tags', 'defined') | selectattr('tags.Name', 'defined') | selectattr('tags.Name', 'search', redirector_name + '-vpc|' + c2_name + '-vpc') | list }}"
|
||||
|
||||
- name: Find VPCs by naming pattern for redirector
|
||||
amazon.aws.ec2_vpc_net_info:
|
||||
- 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:
|
||||
"tag:Name": "{{ redirector_name }}-vpc"
|
||||
register: redirector_named_vpcs
|
||||
when: cleanup_redirector | default(true) | bool
|
||||
vpc-id: "{{ item.vpc_id }}"
|
||||
register: natgw_results
|
||||
loop: "{{ all_vpcs_to_delete }}"
|
||||
when: all_vpcs_to_delete | length > 0
|
||||
|
||||
- name: Add redirector-named VPCs to cleanup list
|
||||
set_fact:
|
||||
vpc_cleanup_list: "{{ vpc_cleanup_list + [item.vpc_id] }}"
|
||||
with_items: "{{ redirector_named_vpcs.vpcs }}"
|
||||
when: cleanup_redirector | default(true) | bool and redirector_named_vpcs.vpcs | length > 0
|
||||
|
||||
# Handle shared infrastructure
|
||||
- name: Add shared VPC to cleanup if both C2 and redirector being cleaned
|
||||
set_fact:
|
||||
vpc_cleanup_list: "{{ vpc_cleanup_list + [shared_infra.vpc_id] }}"
|
||||
when: >
|
||||
infra_state_file.stat.exists | default(false) and
|
||||
shared_infra.vpc_id is defined and
|
||||
cleanup_c2 | default(true) | bool and
|
||||
cleanup_redirector | default(true) | bool
|
||||
|
||||
# Make the VPC cleanup list unique
|
||||
- name: Deduplicate VPC cleanup list
|
||||
set_fact:
|
||||
vpc_cleanup_list: "{{ vpc_cleanup_list | unique }}"
|
||||
when: vpc_cleanup_list | length > 0
|
||||
|
||||
# Display VPCs to be cleaned up
|
||||
- name: Display VPCs to be cleaned
|
||||
debug:
|
||||
msg: "VPCs to be cleaned: {{ vpc_cleanup_list }}"
|
||||
when: aws_detailed_logging | bool and vpc_cleanup_list | length > 0
|
||||
|
||||
# Process each VPC
|
||||
- name: Process VPCs for cleanup
|
||||
block:
|
||||
- name: Process each VPC
|
||||
include_tasks: "process_vpc.yml"
|
||||
vars:
|
||||
vpc_id: "{{ item }}"
|
||||
with_items: "{{ vpc_cleanup_list | unique }}"
|
||||
when: vpc_cleanup_list | length > 0
|
||||
|
||||
# Clean up infrastructure state file if both C2 and redirector are being cleaned
|
||||
- name: Remove infrastructure state file if cleaning up shared infrastructure
|
||||
file:
|
||||
path: "infrastructure_state.json"
|
||||
- name: Delete NAT gateways
|
||||
amazon.aws.ec2_vpc_nat_gateway:
|
||||
region: "{{ aws_region }}"
|
||||
nat_gateway_id: "{{ item.1.nat_gateway_id }}"
|
||||
state: absent
|
||||
when: >
|
||||
infra_state_file.stat.exists | default(false) and
|
||||
((cleanup_c2 | default(true) | bool and cleanup_redirector | default(true) | bool) or
|
||||
(cleanup_c2 | default(true) | bool and not cleanup_redirector | default(true) | bool and
|
||||
c2_region | default(aws_region) == shared_infra.region) or
|
||||
(cleanup_redirector | default(true) | bool and not cleanup_c2 | default(true) | bool and
|
||||
redirector_region | default(aws_region) == shared_infra.region))
|
||||
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
|
||||
|
||||
# Create summary of deleted resources
|
||||
- name: Collect all deleted resources
|
||||
- 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
|
||||
amazon.aws.ec2_vpc_igw:
|
||||
internet_gateway_id: "{{ item.1.internet_gateway_id }}"
|
||||
state: absent
|
||||
region: "{{ aws_region }}"
|
||||
loop: "{{ igw_results.results | default([]) | subelements('internet_gateways') }}"
|
||||
ignore_errors: yes
|
||||
register: deleted_igws
|
||||
|
||||
- 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
|
||||
amazon.aws.ec2_vpc_subnet_info:
|
||||
region: "{{ aws_region }}"
|
||||
filters:
|
||||
vpc-id: "{{ item.vpc_id }}"
|
||||
loop: "{{ all_vpcs_to_delete }}"
|
||||
register: subnet_results
|
||||
|
||||
- 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([]) | 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:
|
||||
resource_summary:
|
||||
ec2_instances:
|
||||
c2: "{{ c2_instances_result.instance_ids | default([]) }}"
|
||||
redirector: "{{ redirector_instances_result.instance_ids | default([]) }}"
|
||||
tracker: "{{ tracker_instances_result.instance_ids | default([]) }}"
|
||||
key_pairs:
|
||||
c2: "{{ cleaned_resources.key_pairs | default([]) | select('search', 'c2deploy_' + deployment_id) | list + cleaned_resources.key_pairs | default([]) | select('search', c2_name) | list }}"
|
||||
redirector: "{{ redirector_key_result.key.name | default('') | ternary([redirector_key_result.key.name], []) }}"
|
||||
tracker: "{{ tracker_key_result.key.name | default('') | ternary([tracker_key_result.key.name], []) }}"
|
||||
security_groups:
|
||||
c2: "{{ c2_sg_result.group_id | default('') | ternary([c2_sg_result.group_id], []) }}"
|
||||
redirector: "{{ redirector_sg_result.group_id | default('') | ternary([redirector_sg_result.group_id], []) }}"
|
||||
tracker: "{{ tracker_sg_result.group_id | default('') | ternary([tracker_sg_result.group_id], []) }}"
|
||||
vpc_resources:
|
||||
route_tables: "{{ cleaned_resources.route_tables | default([]) }}"
|
||||
internet_gateways: "{{ cleaned_resources.internet_gateways | default([]) }}"
|
||||
subnets: "{{ cleaned_resources.subnets | default([]) }}"
|
||||
network_interfaces: "{{ cleaned_resources.network_interfaces | default([]) }}"
|
||||
vpcs: "{{ cleaned_resources.vpcs | default([]) }}"
|
||||
when: not disable_summary | default(false)
|
||||
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
|
||||
|
||||
# Display summary of deleted resources
|
||||
- name: Display cleanup summary
|
||||
debug:
|
||||
msg:
|
||||
- "=========================================================="
|
||||
- "CLEANUP SUMMARY FOR DEPLOYMENT ID: {{ deployment_id }}"
|
||||
- "=========================================================="
|
||||
- "EC2 INSTANCES DELETED:"
|
||||
- " C2: {{ resource_summary.ec2_instances.c2 }}"
|
||||
- " Redirector: {{ resource_summary.ec2_instances.redirector }}"
|
||||
- " Tracker: {{ resource_summary.ec2_instances.tracker }}"
|
||||
- ""
|
||||
- "KEY PAIRS DELETED:"
|
||||
- " C2: {{ resource_summary.key_pairs.c2 }}"
|
||||
- " Redirector: {{ resource_summary.key_pairs.redirector }}"
|
||||
- " Tracker: {{ resource_summary.key_pairs.tracker }}"
|
||||
- ""
|
||||
- "SECURITY GROUPS DELETED:"
|
||||
- " C2: {{ resource_summary.security_groups.c2 }}"
|
||||
- " Redirector: {{ resource_summary.security_groups.redirector }}"
|
||||
- " Tracker: {{ resource_summary.security_groups.tracker }}"
|
||||
- ""
|
||||
- "VPC RESOURCES DELETED:"
|
||||
- " Route Tables: {{ resource_summary.vpc_resources.route_tables }}"
|
||||
- " Internet Gateways: {{ resource_summary.vpc_resources.internet_gateways }}"
|
||||
- " Subnets: {{ resource_summary.vpc_resources.subnets }}"
|
||||
- " Network Interfaces: {{ resource_summary.vpc_resources.network_interfaces }}"
|
||||
- " VPCs: {{ resource_summary.vpc_resources.vpcs }}"
|
||||
- "=========================================================="
|
||||
- "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' }}"
|
||||
- "========================================================="
|
||||
+3
-193
@@ -1,204 +1,14 @@
|
||||
---
|
||||
# VPC Cleanup Process with enhanced dependency handling
|
||||
# Step 1: Network Interfaces
|
||||
- name: Find network interfaces in VPC {{ vpc_id }}
|
||||
amazon.aws.ec2_eni_info:
|
||||
region: "{{ aws_region }}"
|
||||
filters:
|
||||
vpc-id: "{{ vpc_id }}"
|
||||
register: eni_info
|
||||
failed_when: false
|
||||
changed_when: false
|
||||
# Removed detailed cleanup tasks for ENIs, RTs, IGWs, NATs, subnets, SGs to simplify deletion
|
||||
|
||||
- name: Delete network interfaces
|
||||
amazon.aws.ec2_eni:
|
||||
region: "{{ aws_region }}"
|
||||
eni_id: "{{ item.id }}"
|
||||
state: absent
|
||||
force_detach: true
|
||||
loop: "{{ eni_info.network_interfaces }}"
|
||||
register: eni_deleted
|
||||
ignore_errors: yes
|
||||
when: eni_info.network_interfaces | length > 0
|
||||
|
||||
- name: Wait after deleting network interfaces
|
||||
pause:
|
||||
seconds: 10
|
||||
when: eni_info.network_interfaces | length > 0
|
||||
|
||||
# Step 2: Route Tables
|
||||
- name: Find all route tables for VPC {{ vpc_id }}
|
||||
amazon.aws.ec2_vpc_route_table_info:
|
||||
region: "{{ aws_region }}"
|
||||
filters:
|
||||
vpc-id: "{{ vpc_id }}"
|
||||
register: rt_info
|
||||
failed_when: false
|
||||
changed_when: false
|
||||
|
||||
- name: Process route tables
|
||||
when: rt_info.route_tables | length > 0
|
||||
block:
|
||||
- name: Delete non-main route tables
|
||||
amazon.aws.ec2_vpc_route_table:
|
||||
region: "{{ aws_region }}"
|
||||
vpc_id: "{{ vpc_id }}"
|
||||
route_table_id: "{{ item.id }}"
|
||||
state: absent
|
||||
loop: "{{ rt_info.route_tables }}"
|
||||
when: not item.associations or not item.associations[0].main | default(false)
|
||||
register: deleted_rts
|
||||
ignore_errors: yes
|
||||
|
||||
- name: Update cleaned route tables
|
||||
set_fact:
|
||||
cleaned_resources: "{{ cleaned_resources | combine({'route_tables': cleaned_resources.route_tables + [item.id]}, recursive=True) }}"
|
||||
with_items: "{{ rt_info.route_tables }}"
|
||||
when: not item.associations or not item.associations[0].main | default(false)
|
||||
|
||||
- name: Wait after deleting route tables
|
||||
pause:
|
||||
seconds: 10
|
||||
when: rt_info.route_tables | length > 0
|
||||
|
||||
# Step 3: Internet Gateways
|
||||
- name: Find internet gateways for VPC {{ vpc_id }}
|
||||
amazon.aws.ec2_vpc_igw_info:
|
||||
region: "{{ aws_region }}"
|
||||
filters:
|
||||
attachment.vpc-id: "{{ vpc_id }}"
|
||||
register: igw_info
|
||||
failed_when: false
|
||||
changed_when: false
|
||||
|
||||
- name: Process internet gateways
|
||||
when: igw_info.internet_gateways | length > 0
|
||||
block:
|
||||
- name: Detach internet gateways
|
||||
amazon.aws.ec2_vpc_igw:
|
||||
region: "{{ aws_region }}"
|
||||
vpc_id: "{{ vpc_id }}"
|
||||
state: absent
|
||||
register: igw_detached
|
||||
ignore_errors: yes
|
||||
|
||||
- name: Wait after detaching IGW
|
||||
pause:
|
||||
seconds: 15
|
||||
when: igw_detached is changed
|
||||
|
||||
- name: Delete internet gateways
|
||||
amazon.aws.ec2_vpc_igw:
|
||||
region: "{{ aws_region }}"
|
||||
igw_id: "{{ item.id }}"
|
||||
state: absent
|
||||
loop: "{{ igw_info.internet_gateways }}"
|
||||
register: deleted_igws
|
||||
ignore_errors: yes
|
||||
|
||||
- name: Update cleaned internet gateways
|
||||
set_fact:
|
||||
cleaned_resources: "{{ cleaned_resources | combine({'internet_gateways': cleaned_resources.internet_gateways + [item.id]}, recursive=True) }}"
|
||||
with_items: "{{ igw_info.internet_gateways }}"
|
||||
|
||||
# Step 4: Subnets
|
||||
- name: Find subnets for VPC {{ vpc_id }}
|
||||
amazon.aws.ec2_vpc_subnet_info:
|
||||
region: "{{ aws_region }}"
|
||||
filters:
|
||||
vpc-id: "{{ vpc_id }}"
|
||||
register: subnet_info
|
||||
failed_when: false
|
||||
changed_when: false
|
||||
|
||||
- name: Delete subnets
|
||||
amazon.aws.ec2_vpc_subnet:
|
||||
region: "{{ aws_region }}"
|
||||
vpc_id: "{{ vpc_id }}"
|
||||
cidr: "{{ item.cidr_block }}"
|
||||
state: absent
|
||||
loop: "{{ subnet_info.subnets }}"
|
||||
register: deleted_subnets
|
||||
ignore_errors: true
|
||||
when: subnet_info.subnets | length > 0
|
||||
|
||||
- name: Update cleaned subnets
|
||||
set_fact:
|
||||
cleaned_resources: "{{ cleaned_resources | combine({'subnets': cleaned_resources.subnets + [item.id]}, recursive=True) }}"
|
||||
with_items: "{{ subnet_info.subnets }}"
|
||||
when: subnet_info.subnets | length > 0
|
||||
|
||||
- name: Wait after deleting subnets
|
||||
pause:
|
||||
seconds: 15
|
||||
when: subnet_info.subnets | length > 0
|
||||
|
||||
# Step 5: Security Groups (except default)
|
||||
- name: Find non-default security groups for VPC {{ vpc_id }}
|
||||
amazon.aws.ec2_security_group_info:
|
||||
region: "{{ aws_region }}"
|
||||
filters:
|
||||
vpc-id: "{{ vpc_id }}"
|
||||
register: sg_info
|
||||
failed_when: false
|
||||
changed_when: false
|
||||
|
||||
- name: Delete non-default security groups
|
||||
amazon.aws.ec2_security_group:
|
||||
region: "{{ aws_region }}"
|
||||
group_id: "{{ item.group_id }}"
|
||||
state: absent
|
||||
loop: "{{ sg_info.security_groups }}"
|
||||
when: item.group_name != 'default'
|
||||
register: deleted_sgs
|
||||
ignore_errors: true
|
||||
|
||||
- name: Update cleaned security groups
|
||||
set_fact:
|
||||
cleaned_resources: "{{ cleaned_resources | combine({'security_groups': cleaned_resources.security_groups + [item.group_id]}, recursive=True) }}"
|
||||
with_items: "{{ sg_info.security_groups }}"
|
||||
when: item.group_name != 'default'
|
||||
|
||||
- name: Wait after deleting security groups
|
||||
pause:
|
||||
seconds: 15
|
||||
when: sg_info.security_groups | length > 0
|
||||
|
||||
# Step 6: Delete VPC with proper dependencies and retries
|
||||
- name: Wait longer before trying to delete VPC
|
||||
pause:
|
||||
seconds: 30
|
||||
|
||||
- name: Delete VPC with retries
|
||||
block:
|
||||
# Step X: Delete VPC directly
|
||||
- name: Delete VPC {{ vpc_id }}
|
||||
amazon.aws.ec2_vpc_net:
|
||||
region: "{{ aws_region }}"
|
||||
vpc_id: "{{ vpc_id }}"
|
||||
state: absent
|
||||
register: vpc_delete_result
|
||||
retries: 5
|
||||
delay: 15
|
||||
register: vpc_delete_result
|
||||
until: vpc_delete_result is success
|
||||
rescue:
|
||||
- name: Force check dependencies again if VPC deletion failed
|
||||
amazon.aws.ec2_vpc_net_info:
|
||||
region: "{{ aws_region }}"
|
||||
vpc_ids:
|
||||
- "{{ vpc_id }}"
|
||||
register: vpc_check
|
||||
|
||||
- name: Final attempt to delete VPC if it still exists
|
||||
amazon.aws.ec2_vpc_net:
|
||||
region: "{{ aws_region }}"
|
||||
vpc_id: "{{ vpc_id }}"
|
||||
state: absent
|
||||
when: vpc_check.vpcs | length > 0
|
||||
ignore_errors: true
|
||||
register: final_vpc_delete
|
||||
|
||||
- name: Update cleaned VPCs
|
||||
set_fact:
|
||||
cleaned_resources: "{{ cleaned_resources | combine({'vpcs': cleaned_resources.vpcs + [vpc_id]}, recursive=True) }}"
|
||||
when: (vpc_delete_result is defined and vpc_delete_result is success) or
|
||||
(final_vpc_delete is defined and final_vpc_delete is success)
|
||||
+50
-10
@@ -14,7 +14,8 @@
|
||||
instance_type: "{{ aws_instance_type | default('t2.micro') }}"
|
||||
deployment_id: "{{ deployment_id | default('') }}"
|
||||
redirector_name: "{{ redirector_name | default('r-' + deployment_id) }}"
|
||||
shell_handler_port: "{{ shell_handler_port | default(4000 + 60000 | random) }}"
|
||||
# Calculate effective port: use shell_handler_port from vars or default to 8083
|
||||
effective_listen_port: "{{ shell_handler_port | default(8083) }}"
|
||||
# Check for shared infrastructure
|
||||
# Only use shared when C2 and redirector are in the same region
|
||||
use_shared_infra: "{{ not (c2_region is defined and redirector_region is defined and c2_region != redirector_region) and not c2_only | default(false) | bool and not redirector_only | default(false) | bool }}"
|
||||
@@ -43,16 +44,37 @@
|
||||
# Load shared infrastructure state if available
|
||||
- name: Check for shared infrastructure state
|
||||
stat:
|
||||
path: "infrastructure_state.json"
|
||||
path: "infrastructure_state_{{ deployment_id }}.json"
|
||||
register: infra_state_file
|
||||
when: use_shared_infra | bool
|
||||
|
||||
- name: Load shared infrastructure state
|
||||
include_vars:
|
||||
file: "infrastructure_state.json"
|
||||
file: "infrastructure_state_{{ deployment_id }}.json"
|
||||
name: shared_infra
|
||||
when: use_shared_infra | bool and infra_state_file.stat.exists | default(false)
|
||||
|
||||
# After loading shared infrastructure state
|
||||
- name: Validate shared VPC exists
|
||||
amazon.aws.ec2_vpc_net_info:
|
||||
region: "{{ shared_infra.region }}"
|
||||
vpc_ids:
|
||||
- "{{ shared_infra.vpc_id }}"
|
||||
register: vpc_check
|
||||
when: use_shared_infra | bool and infra_state_file.stat.exists | default(false)
|
||||
ignore_errors: yes
|
||||
|
||||
- name: Delete stale infrastructure state file
|
||||
file:
|
||||
path: "infrastructure_state.json"
|
||||
state: absent
|
||||
when: use_shared_infra | bool and vpc_check.vpcs is defined and vpc_check.vpcs | length == 0
|
||||
|
||||
- name: Disable shared infrastructure when VPC doesn't exist
|
||||
set_fact:
|
||||
use_shared_infra: false
|
||||
when: use_shared_infra | bool and vpc_check.vpcs is defined and vpc_check.vpcs | length == 0
|
||||
|
||||
- name: Set region for redirector from shared infra
|
||||
set_fact:
|
||||
aws_redirector_region: "{{ shared_infra.region | default(aws_region) }}"
|
||||
@@ -148,12 +170,6 @@
|
||||
redirector_vpc_id: "{{ vpc_result.vpc.id }}" # Store for cleanup reference
|
||||
when: not (use_shared_infra | bool and infra_state_file.stat.exists | default(false))
|
||||
|
||||
# Set a random shell handler port if not defined
|
||||
- name: Set random shell handler port if not defined
|
||||
set_fact:
|
||||
shell_handler_port: "{{ 4000 + 59000 | random }}"
|
||||
when: shell_handler_port is not defined
|
||||
|
||||
# Create security group
|
||||
- name: Create security group
|
||||
amazon.aws.ec2_security_group:
|
||||
@@ -167,7 +183,7 @@
|
||||
- 22
|
||||
- 80
|
||||
- 443
|
||||
- "{{ shell_handler_port }}"
|
||||
- "{{ effective_listen_port }}"
|
||||
cidr_ip: 0.0.0.0/0
|
||||
rules_egress:
|
||||
- proto: -1
|
||||
@@ -175,6 +191,20 @@
|
||||
state: present
|
||||
register: security_group
|
||||
|
||||
# Save infrastructure state for reuse
|
||||
- name: Save infrastructure state for reuse
|
||||
copy:
|
||||
content: |
|
||||
{
|
||||
"vpc_id": "{{ vpc_id }}",
|
||||
"subnet_id": "{{ subnet_id }}",
|
||||
"security_group_id": "{{ security_group.group_id }}",
|
||||
"region": "{{ aws_redirector_region }}",
|
||||
"deployment_id": "{{ deployment_id }}"
|
||||
}
|
||||
dest: "infrastructure_state_{{ deployment_id }}.json"
|
||||
when: not (use_shared_infra | bool and infra_state_file.stat.exists | default(false))
|
||||
|
||||
# Launch the redirector
|
||||
- name: Launch redirector instance
|
||||
amazon.aws.ec2_instance:
|
||||
@@ -215,6 +245,7 @@
|
||||
ansible_ssh_private_key_file: "~/.ssh/{{ redirector_name }}.pem"
|
||||
ansible_ssh_common_args: "-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o IdentitiesOnly=yes"
|
||||
ansible_python_interpreter: "/usr/bin/python3"
|
||||
shell_handler_port: "{{ effective_listen_port }}"
|
||||
|
||||
# Rest of the playbook for configuring the redirector
|
||||
- name: Configure redirector
|
||||
@@ -225,12 +256,21 @@
|
||||
- vars.yaml
|
||||
vars:
|
||||
c2_ip: "{{ hostvars['localhost']['c2_ip'] | default('127.0.0.1') }}"
|
||||
shell_handler_port: "{{ hostvars['localhost']['effective_listen_port'] }}"
|
||||
|
||||
# Include the rest of your redirector configuration tasks here
|
||||
tasks:
|
||||
- name: Include common redirector configuration tasks
|
||||
include_tasks: "../tasks/configure_redirector.yml"
|
||||
|
||||
- name: Configure shell handler script with listening port
|
||||
template:
|
||||
src: "../files/havoc_shell_handler.sh"
|
||||
dest: "/root/Tools/shell_handler.sh"
|
||||
mode: 0755
|
||||
vars:
|
||||
listen_port: "{{ effective_listen_port }}"
|
||||
|
||||
- name: Print deployment summary
|
||||
debug:
|
||||
msg:
|
||||
|
||||
@@ -128,5 +128,4 @@
|
||||
- "-------------------------------"
|
||||
- "Redirector IP: {{ ansible_host }}"
|
||||
- "Redirector Domain: {{ redirector_subdomain }}.{{ domain }} (Update DNS A record)"
|
||||
- "Shell Handler Port: {{ shell_handler_port }}"
|
||||
when: not disable_summary | default(false)
|
||||
@@ -21,7 +21,7 @@ deployment_id = None
|
||||
# Constants for providers
|
||||
PROVIDERS = ["aws", "linode", "flokinet"]
|
||||
DEFAULT_SSH_USER = {
|
||||
"aws": "root",
|
||||
"aws": "kali",
|
||||
"linode": "root",
|
||||
"flokinet": "root"
|
||||
}
|
||||
@@ -375,7 +375,7 @@ def gather_common_parameters():
|
||||
config['zero_logs'] = input("Enable zero-logs configuration? (y/n) [default: y]: ").lower() != 'n'
|
||||
|
||||
# Add SSH option that was missing
|
||||
config['ssh_after_deploy'] = input("\nSSH into instance after deployment? (y/n) [default: n]: ").lower() == 'y'
|
||||
config['ssh_after_deploy'] = input("\nSSH into instance after deployment? (y/n) [default: y]: ").lower() == 'y'
|
||||
|
||||
return config
|
||||
|
||||
@@ -519,6 +519,14 @@ def execute_deployment(config):
|
||||
print(f"\n{COLORS['RED']}Deployment failed.{COLORS['RESET']}")
|
||||
|
||||
input("\nPress Enter to return to menu...")
|
||||
# Clean up shared infrastructure state file if present
|
||||
try:
|
||||
state_file = os.path.join(os.getcwd(), 'infrastructure_state.json')
|
||||
if os.path.exists(state_file):
|
||||
os.remove(state_file)
|
||||
logging.info('Removed shared infrastructure state file')
|
||||
except Exception as e:
|
||||
logging.warning(f'Failed to remove infra state file: {e}')
|
||||
|
||||
def generate_random_string(length=8):
|
||||
"""Generate a random string of letters and digits."""
|
||||
@@ -526,8 +534,11 @@ def generate_random_string(length=8):
|
||||
|
||||
def generate_deployment_id():
|
||||
"""Generate a consistent deployment ID for all resources in this deployment"""
|
||||
global deployment_id
|
||||
if not deployment_id:
|
||||
rand_suffix = generate_random_string(6)
|
||||
return f"{rand_suffix}"
|
||||
deployment_id = f"{rand_suffix}"
|
||||
return deployment_id
|
||||
|
||||
def setup_logging(deployment_id=None):
|
||||
"""Set up logging for the deployment"""
|
||||
@@ -864,7 +875,7 @@ def interactive_setup(deployment_id=None):
|
||||
config['tracker_create_pixel'] = input("Create tracking pixel? (y/n) [default: y]: ").lower() != 'n'
|
||||
|
||||
# Post-deployment options
|
||||
config['ssh_after_deploy'] = input("\nSSH into instance after deployment? (y/n) [default: n]: ").lower() == 'y'
|
||||
config['ssh_after_deploy'] = input("\nSSH into instance after deployment? (y/n) [default: y]: ").lower() == 'y'
|
||||
|
||||
# Debug mode
|
||||
config['debug'] = input("Enable debug mode? (y/n) [default: n]: ").lower() == 'y'
|
||||
@@ -1326,6 +1337,8 @@ def deploy_infrastructure(config):
|
||||
logging.error(f"{provider} redirector deployment failed")
|
||||
if redirector_config.get('debug'):
|
||||
logging.error(f"Ansible stderr: {stderr}")
|
||||
# Run cleanup before returning
|
||||
cleanup_resources(config, interactive=True)
|
||||
return False
|
||||
|
||||
# Extract and save redirector IP for C2 configuration
|
||||
@@ -1353,6 +1366,8 @@ def deploy_infrastructure(config):
|
||||
logging.error(f"{provider} C2 server deployment failed")
|
||||
if c2_config.get('debug'):
|
||||
logging.error(f"Ansible stderr: {stderr}")
|
||||
# Run cleanup before returning
|
||||
cleanup_resources(config, interactive=True)
|
||||
return False
|
||||
|
||||
# Extract and save C2 IP for reference
|
||||
@@ -1367,7 +1382,8 @@ def deploy_infrastructure(config):
|
||||
logging.error(traceback.format_exc())
|
||||
|
||||
# Clean up any partial resources that were created
|
||||
cleanup_resources(config, interactive=True)
|
||||
# Force interactive to False to ensure cleanup runs without prompting when there's an exception
|
||||
cleanup_resources(config, interactive=False)
|
||||
return False
|
||||
|
||||
def deploy_flokinet_redirector(config):
|
||||
@@ -2212,7 +2228,7 @@ def main():
|
||||
if args.provider == "linode":
|
||||
config['ssh_user'] = "root"
|
||||
elif args.provider == "aws":
|
||||
config['ssh_user'] = "root"
|
||||
config['ssh_user'] = "kali"
|
||||
else:
|
||||
if hasattr(args, 'ssh_user'):
|
||||
config['ssh_user'] = args.ssh_user or vars_data.get('ssh_user') or DEFAULT_SSH_USER.get(args.provider)
|
||||
|
||||
@@ -97,10 +97,12 @@
|
||||
replace: 'C2_HOST="{{ c2_ip }}"'
|
||||
|
||||
- name: Configure shell handler script with listening port
|
||||
replace:
|
||||
path: /root/Tools/shell-handler/persistent-listener.sh
|
||||
regexp: 'LISTEN_PORT=4444'
|
||||
replace: 'LISTEN_PORT={{ shell_handler_port }}'
|
||||
template:
|
||||
src: "../files/havoc_shell_handler.sh"
|
||||
dest: "/root/Tools/shell_handler.sh"
|
||||
mode: 0755
|
||||
vars:
|
||||
listen_port: "{{ shell_handler_port | default(8083) }}"
|
||||
|
||||
- name: Create shell handler service
|
||||
template:
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"vpc_id": "{{ infra_state.vpc_id }}",
|
||||
"subnet_id": "{{ infra_state.subnet_id }}",
|
||||
"security_group_id": "{{ infra_state.security_group_id }}",
|
||||
"region": "{{ infra_state.region }}"
|
||||
}
|
||||
Reference in New Issue
Block a user