Using Ansible to create an inventory of your AWS resources


I was recently at a customer site, to perform an environment review of their AWS real-estate. As part of this engagement, I was going to do an inventory of all their AWS resources. Superficially, this sounds like an easy task, however when you consider the various regions that resources can be provisioned into, the amount of work required for a simple inventory can easily escalate.

Not being a big fan of manual work, I started to look at ways to automate this task. I quickly settled on Ansible as the tool of choice and not long after, I had two ansible playbooks ready (the main and the worker playbook) to perform the inventory.

In this blog, I will introduce the two ansible playbooks that I wrote. The first playbook is the main actor. This is where the variables are defined. This playbook iterates over the specified AWS regions, calling the worker playbook each time, to check if any resources have been provisioned in these regions. The output is written to comma separated value (csv) files (I am using semi-colons instead of commas), which can be easily imported into Microsoft Excel (or any spreadsheet program of your choice) for analysis.

Introducing the Ansible playbooks

The playbooks have been configured to check the following AWS resources

  • Virtual Private Cloud (vpc)
  • Subnets within the VPCs
  • Internet Gateways
  • Route Tables
  • Security Groups
  • Network Access Control Lists
  • Customer Gateways
  • Virtual Private Gateways
  • Elastic IP Addresses
  • Elastic Compute Cloud Instances
  • Amazon Machine Images that were created
  • Elastic Block Store Volumes
  • Elastic Block Store Snapshots
  • Classic Load Balancers
  • Application Load Balancers
  • Relational Database Service Instances
  • Relational Database Service Snapshots
  • Simple Storage Service (S3) Buckets

The table below provides details for the two ansible playbooks.

Filename Purpose
ansible-aws-inventory-main.yml This is the controller playbook. It iterates over each of the specified regions, calling the worker playbook to check for any resources that are provisioned in these regions.
ansible-aws-inventory-worker.yml This playbook does all the heavy lifting. It checks for any provisioned resources in the region that is provided to it by the controller playbook

Let’s go through each of the sections in the main ansible playbook (ansible-aws-inventory-main.yml), to get a better understanding of what it does.

First off, the variables that will be used are defined

hosts: localhost
connection: local
gather_facts: yes
us-east-1 #North Virginia
us-east-2 #Ohio
us-west-1 #North California
us-west-2 #Oregon
ap-south-1 #Mumbai
ap-northeast-2 #Seoul
ap-southeast-1 #Singapore
ap-southeast-2 #Sydney
ap-northeast-1 #Tokyo
ca-central-1 #Canada Central
eu-central-1 #Frankfurt
eu-west-1 #Ireland
eu-west-2 #London
eu-west-3 #Paris
eu-north-1 #Stockholm
sa-east-1 #Sau Paulo
verbose: true #set this to true to display results to screen or false to not display to screen
owner_id: {your aws account number} #this is used to find which ami's belong to you

aws_regions – this defines all the AWS regions which will be checked for provisioned resources

verbose – to display the results both on screen and to write it to file, set this to true. Setting this to false just writes the results to file.

owner_id – this is the account id for the AWS account that is being inventoried. It is used to retrieve all the Amazon Machine Images (AMI) that are owned by this account

Next, the column headers for each of the csv files is defined.

#define all output file headers
vpc_outputfile_header: "Region;VPC ID;Is_Default;State;CIDR Block;Enable DNS Hostnames;Enable DNS Support;DHCP Options ID;Instance Tenancy"
subnet_outputfile_header: "Region;Subnet ID;VPC ID;avaialability zone;cidr_block;available_ip_address_count;default_for_az;map_public_ip_on_launch;state"
igw_outputfile_header: "Region;IGW ID;VPC ID;State;Tags"
cgw_outputfile_header: "Region;CGW ID;BGP ASN;ip address;state;type;tags"
vgw_outputfile_header: "Region;VGW ID;State;type;attachments;Tags"
ami_outputfile_header: "Region;image_id;name;creation_date;state;is_public;description"
eip_outputfile_header: "Region;allocation_id;association_id;domain;attached to(instance_id);network_interface_id;private_ip_address;public_ip;public_ipv4_pool"
snapshot_outputfile_header: "Region;snapshot_id;owner_id;start_time;progress;state;encrypted;volume_id;volume_size;description"
volume_outputfile_header: "Region;volume_id;volume_type;size;iops;encrypted;status;region;zone;create_time;attach_time;attached_to;attached_as;delete on termination;volume_status"
routetable_outputfile_header: "Region;Routetable ID;VPC ID;Routes (use"
securitygroup_outputfile_header: "Region;SG Name;SG ID;VPC ID;Description;Ingress Rules (use; Egress Rules (use"
nacl_outputfile_header: "Region;NACL ID;VPC ID;Is Default;Subnets Associated with;Ingress Rules (use; Egress Rules (use"
ec2_outputfile_header: "Region;EC2_Instance_ID;EC2_Instance_Name;EC2_Instance_Type;EC2_Image_ID;Private IP;Availability Zone;Public IP;SubnetID;Source Dest Check;Security Groups;VPC ID;EC2_Launch_Time;EC2_Current_State"
elb_outputfile_header: "Region;ELB_Type;ELB_Name;ELB_DNS_Name;Zones;Subnets;VPC_ID;Instances;Scheme;Security Groups;Listeners;State"
rds_instance_outputfile_header: "Region;db_instance_identifier;availability_zone;allocated_storage;auto_minor_version_upgrade;availability_zone;backup_retention_period;instance_class;db_instance_port;db_instance_status;db_parameter_groups;db_security_groups;db_subnet_group;engine;engine_version;preferred_backup_window;preferred_maintenance_window;publicly_accessible;storage_type;security_groups;tags"
rds_snapshot_outputfile_header: "Region;db_snapshot_identifier;snapshot_create_time;snapshot_type;db_instance_identifier;encrypted;percent_progress;allocated_storage;availability_zone;tags"
s3_outputfile_header: "Region;Bucket Name;Creation Date"

After this, the output filenames are defined. Do note that the filenames use timestamps (for when the playbook is run) as prefixes. This ensures that they don’t overwrite any output files from previous runs.

#define output file names. These will be prepended with run date/time in iso6801 format
output_root_folder: /Users/nivleshc/Documents/OneDrive/Personal/Studies/AWS/output/raw/
outputfile_variablename_suffix: "_outputfile"
outputfileheader_variablename_suffix: "_outputfile_header"
vpc_outputfile: "{{ output_root_folder }}{{ ansible_date_time.iso8601 }}_vpc.csv"
subnet_outputfile: "{{ output_root_folder }}{{ ansible_date_time.iso8601 }}_subnet.csv"
igw_outputfile: "{{ output_root_folder }}{{ ansible_date_time.iso8601 }}_igw.csv"
cgw_outputfile: "{{ output_root_folder }}{{ ansible_date_time.iso8601 }}_cgw.csv"
vgw_outputfile: "{{ output_root_folder }}{{ ansible_date_time.iso8601 }}_vgw.csv"
ami_outputfile: "{{ output_root_folder }}{{ ansible_date_time.iso8601 }}_ami.csv"
eip_outputfile: "{{ output_root_folder }}{{ ansible_date_time.iso8601 }}_eip.csv"
snapshot_outputfile: "{{ output_root_folder }}{{ ansible_date_time.iso8601 }}_snapshot.csv"
volume_outputfile: "{{ output_root_folder }}{{ ansible_date_time.iso8601 }}_volume.csv"
routetable_outputfile: "{{ output_root_folder }}{{ ansible_date_time.iso8601 }}_routetable.csv"
securitygroup_outputfile: "{{ output_root_folder }}{{ ansible_date_time.iso8601 }}_securitygroup.csv"
nacl_outputfile: "{{ output_root_folder }}{{ ansible_date_time.iso8601 }}_nacl.csv"
ec2_outputfile: "{{ output_root_folder }}{{ ansible_date_time.iso8601 }}_ec2.csv"
elb_outputfile: "{{ output_root_folder }}{{ ansible_date_time.iso8601 }}_elb.csv"
rds_instance_outputfile: "{{ output_root_folder }}{{ ansible_date_time.iso8601 }}_rds_instance.csv"
rds_snapshot_outputfile: "{{ output_root_folder }}{{ ansible_date_time.iso8601 }}_rds_snapshot.csv"
s3_outputfile: "{{ output_root_folder }}{{ ansible_date_time.iso8601 }}_s3.csv"

When I was generating the inventory list, at times I found that I needed only a subset of resource types inventoried, instead of all (for instance when I was looking for only EC2 instances). For this reason, I found it beneficial to have boolean variables to either enable or disable inventory checks for specific resource types.

The next section lists boolean variables that control if a particular resource type should be checked or not. Set this to true if it is to be checked and false if it is to be skipped. You can set this to your own preference.

#the following variables are used to enable or disable inventory for a particular resource. Use true to enable and false to disable
inventory_vpc: true
inventory_subnet: true
inventory_igw: true
inventory_cgw: true
inventory_vgw: true
inventory_ami: true
inventory_eip: true
inventory_snapshot: true
inventory_volume: true
inventory_routetable: true
inventory_securitygroup: true
inventory_nacl: true
inventory_ec2: true
inventory_elb: true
inventory_rds_instance: true
inventory_rds_snapshot: true
inventory_s3: true

After all the variables have been defined, the tasks that will be carried out are configured.

The first task initialises the output csv files with the column headers.

name: initialise output files with headers
state: present
create: yes
path: "{{ lookup('vars', item + outputfile_variablename_suffix) }}"
line: "{{ lookup('vars', item + outputfileheader_variablename_suffix) }}"
insertbefore: BOF

Once the initialisation has been completed, the inventory process is started by looping through each of the specified AWS regions and calling the worker ansible playbook to check for provisioned resources.

name: find all applicable resources within region
include_tasks: ansible-aws-inventory-worker.yml
loop: "{{ aws_regions }}"
loop_var: aws_region
label: "{{ aws_region }}"

The last task displays the path for the output files.

name: print out the output filenames for each of the resoureces inventoried
"{{ item }} output filename: {{ lookup('vars', item + outputfile_variablename_suffix) }}"

The main ansible playbook (ansible-aws-inventory-main.yml) can be downloaded from

The worker playbook (ansible-aws-inventory-worker.yml) has the following format

  • go through each of the defined resource types and confirm that it is to be checked (checks for a particular resource type are enabled using the boolean variable that is defined in the main playbook)
  • If checks are enabled for that particular resource type, find all provisioned resources of that type in the region provided by the main ansible playbook
  • write the results to the respective output file
  • if verbose is enabled, write the results to screen

The worker file (ansible-aws-inventory-worker.yml) can be downloaded from

Running the ansible playbooks

Use the following steps to run the above mentioned ansible playbooks to perform an inventory of your AWS account.

1. On a computer that has ansible installed, create a folder and name it appropriately (for instance inventory)

2. Download ansible-aws-inventory-main.yml from and put it in the folder that was created in step 1 above

3. Download ansible-aws-inventory-worker.yml from and put it in the folder that was created in step 1 above

4. Download the ansible inventory file from , rename it to hosts and put it in the folder that was created in step 1 above

5. Customise ansible-aws-inventory-main.yml by adding your account id as the owner_id and change the output folder by updating the output_root_folder variable. If you need to disable inventory for certain resource types, you can set the respective boolean variable to false.

6. Create a user account with access keys enabled within your AWS account. For checking all the resources defined in the playbook, at a minimum, the account must have the following permissions assigned


7. Open a command line and then run the following to configure environment variables with credentials of the account that was created in step 6 above (the following commands are specific to MacOS)

export AWS_ACCESS_KEY_ID="xxxxx"
export AWS_SECRET_ACCESS_KEY="xxxxxxx"

8. There is a possibility that you might encounter an error with boto complaining that it is unable to access region us-west-3. To fix this, define the following environment variable as well


9. Run the ansible playbook using the following command line

ansible-playbook -i hosts ansible-aws-inventory-main.yml

Depending on how many resources are being inventoried, the playbook can take anywhere from five to ten minutes to complete. So, sit back and relax, while the playbook runs.

I found a bug with the ansible “aws s3 bucket facts” module. It ignores the region parameter and instead of returning s3 buckets in a specific region, it returns buckets in all regions. Due to this, the s3 buckets csv file will have the same buckets repeated in all the regions.

Hope you enjoy the above ansible playbooks and they make your life much easier when trying to find all resources that are deployed within your AWS account.

Till the next time, enjoy!

Feature Photo by Samuel Zeller on Unsplash