Repository: gruntwork-io/cloud-nuke
Pull request #632 (2024-01-03 02:39:10)

Implement a new resource types for IPAM resources
<!-- Prepend '[WIP]' to the title if this PR is still a work-in-progress. Remove it when it is ready for review! -->

## Description

Fixes https://github.com/gruntwork-io/cloud-nuke/issues/628.

<!-- Description of the changes introduced by this PR. -->

## TODOs

Read the [Gruntwork contribution guidelines](https://gruntwork.notion.site/Gruntwork-Coding-Methodology-02fdcd6e4b004e818553684760bf691e).

- [ ] Update the docs.
- [ ] Run the relevant tests successfully, including pre-commit checks.
- [ ] Ensure any 3rd party code adheres with our [license policy](https://www.notion.so/gruntwork/Gruntwork-licenses-and-open-source-usage-policy-f7dece1f780341c7b69c1763f22b1378) or delete this line if its not applicable.
- [ ] Include release notes. If this PR is backward incompatible, include a migration guide.
- [ ] Attention Grunts - if this PR adds support for a new resource, ensure the `nuke_sandbox` and `nuke_phxdevops` jobs in `.circleci/config.yml` have been updated with appropriate exclusions (either directly in the job or via the `.circleci/nuke_config.yml` file) to prevent nuking IAM roles, groups, resources, etc that are important for the test accounts.


## Release Notes (draft)

<!-- One-line description of the PR that can be included in the final release notes. -->
Added / Removed / Updated [X].

### Migration Guide

<!-- Important: If you made any backward incompatible changes, then you must write a migration guide! -->




Patch:
diff --git a/README.md b/README.md
index c578e84..fc5eb51 100644
--- a/README.md
+++ b/README.md
@@ -23,12 +23,15 @@ Telemetry can be disabled entirely by setting the `DISABLE_TELEMETRY` environmen
 As an open source tool, you can see the exact statistics being collected by searching the code for
 `telemetry.TrackEvent(...)`
 
-
 # cloud-nuke
 
-This repo contains a CLI tool to delete all resources . cloud-nuke was created for situations when you might have an account you use for testing and need to clean up leftover resources so you're not charged for them. Also great for cleaning out accounts with redundant resources. Also great for removing unnecessary defaults like default VPCs and permissive ingress/egress rules in default security groups.
+This repo contains a CLI tool to delete all resources . cloud-nuke was created for situations when you might have an
+account you use for testing and need to clean up leftover resources so you're not charged for them. Also great for
+cleaning out accounts with redundant resources. Also great for removing unnecessary defaults like default VPCs and
+permissive ingress/egress rules in default security groups.
 
-In addition, cloud-nuke offers non-destructive inspecting functionality that can either be called via the command-line interface, or consumed as library methods, for scripting purposes.
+In addition, cloud-nuke offers non-destructive inspecting functionality that can either be called via the command-line
+interface, or consumed as library methods, for scripting purposes.
 
 The currently supported functionality includes:
 
@@ -37,7 +40,7 @@ The currently supported functionality includes:
 Cloud-nuke suppports 🔎 inspecting and 🔥💀 deleting the following AWS resources:
 
 | Resource Family         | Resource type                                            |
-| ----------------------- | -------------------------------------------------------- |
+|-------------------------|----------------------------------------------------------|
 | EC2                     | Auto scaling groups                                      |
 | EC2                     | Elastic Load Balancers (v1 and v2)                       |
 | EC2                     | EBS Volumes                                              |
@@ -46,6 +49,12 @@ Cloud-nuke suppports 🔎 inspecting and 🔥💀 deleting the following AWS res
 | EC2                     | Snapshots                                                |
 | EC2                     | Elastic IPs                                              |
 | EC2                     | Launch Configurations                                    |
+| EC2                     | IPAM (Amazon VPC IP Address Manager)                     |
+| EC2                     | IPAM Pool															                                 |
+| EC2                     | IPAM Scope														                                 |
+| EC2                     | IPAM Custom Allocation 								                          |
+| EC2                     | IPAM BYOASN	          								                           |
+| EC2                     | IPAM Resource Discovery 							                          |
 | Certificate Manager     | ACM Private CA                                           |
 | Direct Connect          | Transit Gateways                                         |
 | Elasticache             | Clusters                                                 |
@@ -95,15 +104,24 @@ Cloud-nuke suppports 🔎 inspecting and 🔥💀 deleting the following AWS res
 | AWS Certificate Manager | Certificates                                             |
 | CodeDeploy              | Applications                                             |
 
-> **WARNING:** The RDS APIs also interact with neptune and document db resources.  Running `cloud-nuke aws --resource-type rds` without a config file will remove any neptune and document db resources in the account.
+> **WARNING:** The RDS APIs also interact with neptune and document db resources.
+> Running `cloud-nuke aws --resource-type rds` without a config file will remove any neptune and document db resources
+> in
+> the account.
 
-> **NOTE: AWS Backup Resource:** Resources (such as AMIs) created by AWS Backup, while owned by your AWS account, are managed specifically by AWS Backup and cannot be deleted through standard APIs calls for that resource. These resources are tagged by AWS Backup and are filtered out so that `cloud-nuke` does not fail when trying to delete resources it cannot delete.
+> **NOTE: AWS Backup Resource:** Resources (such as AMIs) created by AWS Backup, while owned by your AWS account, are
+> managed specifically by AWS Backup and cannot be deleted through standard APIs calls for that resource. These
+> resources
+> are tagged by AWS Backup and are filtered out so that `cloud-nuke` does not fail when trying to delete resources it
+> cannot delete.
 
 ### BEWARE!
 
-When executed as `cloud-nuke aws`, this tool is **HIGHLY DESTRUCTIVE** and deletes all resources! This mode should never be used in a production environment!
+When executed as `cloud-nuke aws`, this tool is **HIGHLY DESTRUCTIVE** and deletes all resources! This mode should never
+be used in a production environment!
 
-When executed as `cloud-nuke defaults-aws`, this tool deletes all DEFAULT VPCs and the default ingress/egress rule for all default security groups. This should be used in production environments **WITH CAUTION**.
+When executed as `cloud-nuke defaults-aws`, this tool deletes all DEFAULT VPCs and the default ingress/egress rule for
+all default security groups. This should be used in production environments **WITH CAUTION**.
 
 ## Install
 
@@ -116,7 +134,11 @@ When executed as `cloud-nuke defaults-aws`, this tool deletes all DEFAULT VPCs a
 
 ### Install via package manager
 
-Note that package managers are third party. The third party cloud-nuke packages may not be updated with the latest version, but are often close. Please check your version against the latest available on the [releases page](https://github.com/gruntwork-io/cloud-nuke/releases). If you want the latest version, the recommended installation option is to [download from the releases page](https://github.com/gruntwork-io/cloud-nuke/releases).
+Note that package managers are third party. The third party cloud-nuke packages may not be updated with the latest
+version, but are often close. Please check your version against the latest available on
+the [releases page](https://github.com/gruntwork-io/cloud-nuke/releases). If you want the latest version, the
+recommended installation option is
+to [download from the releases page](https://github.com/gruntwork-io/cloud-nuke/releases).
 
 - **macOS:** You can install cloud-nuke using [Homebrew](https://brew.sh/): `brew install cloud-nuke`.
 
@@ -124,45 +146,55 @@ Note that package managers are third party. The third party cloud-nuke packages
 
 ## Usage
 
-Simply running `cloud-nuke aws` will start the process of cleaning up your cloud account. You'll be shown a list of resources that'll be deleted as well as a prompt to confirm before any deletion actually takes place.
-
-In AWS, to delete only the default resources, run `cloud-nuke defaults-aws`. This will remove the default VPCs in each region, and will also revoke the ingress and egress rules associated with the default security group in each VPC. Note that the default security group itself is unable to be deleted.
+Simply running `cloud-nuke aws` will start the process of cleaning up your cloud account. You'll be shown a list of
+resources that'll be deleted as well as a prompt to confirm before any deletion actually takes place.
 
+In AWS, to delete only the default resources, run `cloud-nuke defaults-aws`. This will remove the default VPCs in each
+region, and will also revoke the ingress and egress rules associated with the default security group in each VPC. Note
+that the default security group itself is unable to be deleted.
 
 ### Nuke or inspect resources using AWS Profile
 
-When using `cloud-nuke aws`, or `cloud-nuke inspect-aws`, you can pass in the `AWS_PROFILE` env variable to target resources in certain regions for a specific AWS account. For example the following command will nuke resources only in `ap-south-1` and `ap-south-2` regions in the `gruntwork-dev` AWS account:
+When using `cloud-nuke aws`, or `cloud-nuke inspect-aws`, you can pass in the `AWS_PROFILE` env variable to target
+resources in certain regions for a specific AWS account. For example the following command will nuke resources only
+in `ap-south-1` and `ap-south-2` regions in the `gruntwork-dev` AWS account:
 
 ```shell
 AWS_PROFILE=gruntwork-dev cloud-nuke aws --region ap-south-1 --region ap-south-2
 ```
 
 Similarly, the following command will inspect resources only in `us-east-1`
+
 ```shell
 AWS_PROFILE=gruntwork-dev cloud-nuke inspect-aws --region us-east-1
 ```
 
 ### Nuke or inspect resources in certain regions
 
-When using `cloud-nuke aws`, or `cloud-nuke inspect-aws`, you can use the `--region` flag to target resources in certain regions. For example the following command will nuke resources only in `ap-south-1` and `ap-south-2` regions:
+When using `cloud-nuke aws`, or `cloud-nuke inspect-aws`, you can use the `--region` flag to target resources in certain
+regions. For example the following command will nuke resources only in `ap-south-1` and `ap-south-2` regions:
 
 ```shell
 cloud-nuke aws --region ap-south-1 --region ap-south-2
 ```
 
 Similarly, the following command will inspect resources only in `us-east-1`
+
 ```shell
 cloud-nuke inspect-aws --region us-east-1
 ```
 
 Including regions is available within:
+
 - `cloud-nuke aws`
 - `cloud-nuke defaults-aws`
 - `cloud-nuke inspect-aws`
 
 ### Exclude resources in certain regions
 
-When using `cloud-nuke aws` or `cloud-nuke inspect-aws`, you can use the `--exclude-region` flag to exclude resources in certain regions from being deleted or inspected. For example the following command does not nuke resources in `ap-south-1` and `ap-south-2` regions:
+When using `cloud-nuke aws` or `cloud-nuke inspect-aws`, you can use the `--exclude-region` flag to exclude resources in
+certain regions from being deleted or inspected. For example the following command does not nuke resources
+in `ap-south-1` and `ap-south-2` regions:
 
 ```shell
 cloud-nuke aws --exclude-region ap-south-1 --exclude-region ap-south-2
@@ -177,23 +209,26 @@ cloud-nuke inspect-aws --exclude-region us-west-1
 `--region` and `--exclude-region` flags cannot be specified together i.e. they are mutually exclusive.
 
 Excluding regions is available within:
+
 - `cloud-nuke aws`
 - `cloud-nuke defaults-aws`
 - `cloud-nuke inspect-aws`
 
 ### Excluding Resources by Age
 
-You can use the `--older-than` flag to only nuke resources that were created before a certain period, the possible values are all valid values for [ParseDuration](https://golang.org/pkg/time/#ParseDuration) For example the following command nukes resources that are at least one day old:
+You can use the `--older-than` flag to only nuke resources that were created before a certain period, the possible
+values are all valid values for [ParseDuration](https://golang.org/pkg/time/#ParseDuration) For example the following
+command nukes resources that are at least one day old:
 
 ```shell
 cloud-nuke aws --older-than 24h
 ```
 
 Excluding resources by age is available within:
+
 - `cloud-nuke aws`
 - `cloud-nuke inspect-aws`
 
-
 ### List supported resource types
 
 You can use the `--list-resource-types` flag to list resource types whose termination is currently supported:
@@ -203,10 +238,10 @@ cloud-nuke aws --list-resource-types
 ```
 
 Listing supported resource types is available within:
+
 - `cloud-nuke aws`
 - `cloud-nuke inspect-aws`
 
-
 ### Terminate or inspect specific resource types
 
 If you want to target specific resource types (e.g ec2, ami, etc.) instead of all the supported resources you can
@@ -227,12 +262,14 @@ cloud-nuke inspect-aws --resource-type ec2
 ```
 
 Specifying target resource types is available within:
+
 - `cloud-nuke aws`
 - `cloud-nuke inspect-aws`
 
 ### Exclude terminating specific resource types
 
-Just like you can select which resources to terminate using `--resource-type`, you can select which resources to skip using
+Just like you can select which resources to terminate using `--resource-type`, you can select which resources to skip
+using
 `--exclude-resource-type` flag:
 
 ```shell
@@ -244,6 +281,7 @@ This will terminate all resource types other than S3 and EC2.
 `--resource-type` and `--exclude-resource-type` flags cannot be specified together i.e. they are mutually exclusive.
 
 Specifying resource types to exclude is available within:
+
 - `cloud-nuke aws`
 - `cloud-nuke inspect-aws`
 
@@ -257,13 +295,13 @@ cloud-nuke aws --resource-type ec2 --dry-run
 ```
 
 Dry run mode is only available within:
-- `cloud-nuke aws`
-
 
+- `cloud-nuke aws`
 
 ### Using cloud-nuke as a library
 
-You can import cloud-nuke into other projects and use it as a library for programmatically inspecting and counting resources.
+You can import cloud-nuke into other projects and use it as a library for programmatically inspecting and counting
+resources.
 
 ```golang
 
@@ -341,7 +379,6 @@ func main() {
 }
 ```
 
-
 ## Config file
 
 You can also specify which resources to terminate with more granularity via using config files. The config file is a
@@ -361,7 +398,7 @@ s3:
     ...
 ```
 
-#### Names Regex Filter 
+#### Names Regex Filter
 
 Now given the following config, the s3 buckets that will be nuked are further filtered to only include ones that match
 any of the provided regular expressions. So a bucket named `alb-app-access-logs` would be deleted, but a bucket
@@ -428,7 +465,9 @@ s3:
 
 #### Tag Filter
 
-You can also exclude resources by tags. The following config will exclude all s3 buckets that have a tag with key `foo` and value `true` (case-insensitive).
+You can also exclude resources by tags. The following config will exclude all s3 buckets that have a tag with key `foo`
+and value `true` (case-insensitive).
+
 ```yaml
 s3:
   exclude:
@@ -469,6 +508,10 @@ of the file that are supported are listed here.
 | ec2-dedicated-hosts         | EC2DedicatedHosts            | ✅ (EC2 Name Tag)                      | ✅ (Allocation Time)                 | ❌    |
 | ec2-dhcp-option             | EC2DhcpOption                | ❌                                     | ❌                                   | ❌    |
 | ec2-keypairs                | EC2KeyPairs                  | ✅ (Key Pair Name)                     | ✅ (Creation Time)                   | ✅    |
+| ec2-ipam                	   | EC2IPAM			                   | ✅ (IPAM name)                    	    | ✅ (Creation Time)                   | ✅    |
+| ec2-ipam-pool               | EC2IPAMPool			               | ✅ (IPAM Pool name)                    | ✅ (Creation Time)                   | ✅    |
+| ec2-ipam-resource-discovery | EC2IPAMResourceDiscovery		   | ✅ (IPAM Discovery Name)               | ✅ (Creation Time)                   | ✅    |
+| ec2-ipam-scope              | EC2IPAMScope		 						        | ✅ (IPAM Scope Name)               		  | ✅ (Creation Time)                   | ✅    |
 | ecr                         | ECRRepository                | ✅ (Repository Name)                   | ✅ (Creation Time)                   | ❌    |
 | ecscluster                  | ECSCluster                   | ✅ (Cluster Name)                      | ❌                                   | ❌    |
 | ecsserv                     | ECSService                   | ✅ (Service Name)                      | ✅ (Creation Time)                   | ❌    |
@@ -510,33 +553,38 @@ of the file that are supported are listed here.
 | vpc                         | VPC                          | ✅ (EC2 Name Tag)                      | ✅ (First Seen Tag Time)             | ❌    |
 
 ### How to Use
-Once you created your config file, you can run a command like this to nuke resources with your config file: 
+
+Once you created your config file, you can run a command like this to nuke resources with your config file:
 
 ```shell
 cloud-nuke aws --resource-type s3 --config path/to/file.yaml
 ```
 
 > **CLI options override config file options**
-> 
-> The options provided in the command line take precedence over those provided in any config file that gets passed in. For
-> example, say you provide `--resource-type s3` in the command line, along with a config file that specifies `ec2:` at the
+>
+> The options provided in the command line take precedence over those provided in any config file that gets passed in.
+> For
+> example, say you provide `--resource-type s3` in the command line, along with a config file that specifies `ec2:` at
+> the
 > top level but doesn't specify `s3:`. The command line argument filters the resource types to include only s3, so the
 > rules in the config file for `ec2:` are ignored, and ec2 resources are not nuked. All s3 resources would be nuked.
-> 
+>
 > In the same vein, say you do not provide a `--resource-type` option in the command line, but you do pass in a config
 > file that only lists rules for `s3:`, such as `cloud-nuke aws --config path/to/config.yaml`. In this case _all_
 > resources would be nuked, but among `s3` buckets, only those matching your config file rules would be nuked.
-> 
+>
 > Be careful when nuking and append the `--dry-run` option if you're unsure. Even without `--dry-run`, `cloud-nuke` will
 > list resources that would undergo nuking and wait for your confirmation before carrying it out.
 
-
 ## Log level
-By default, cloud-nuke sends most output to the `Debug` level logger, to enhance legibility, since the results of every deletion attempt will be displayed in the report that cloud-nuke prints after each run.
+
+By default, cloud-nuke sends most output to the `Debug` level logger, to enhance legibility, since the results of every
+deletion attempt will be displayed in the report that cloud-nuke prints after each run.
 
 However, sometimes it's helpful to see all output, such as when you're debugging something.
 
-You can set the log level by specifying the `--log-level` flag as per [logrus](https://github.com/sirupsen/logrus) log levels:
+You can set the log level by specifying the `--log-level` flag as per [logrus](https://github.com/sirupsen/logrus) log
+levels:
 
 ```shell
 cloud-nuke aws --log-level debug
@@ -548,9 +596,11 @@ OR
 LOG_LEVEL=debug cloud-nuke aws
 ```
 
-Default value is - `info`. Acceptable values are `debug, info, warn, error, panic, fatal, trace` as per [logrus log level parser](https://github.com/sirupsen/logrus/blob/master/logrus.go#L25).
+Default value is - `info`. Acceptable values are `debug, info, warn, error, panic, fatal, trace` as
+per [logrus log level parser](https://github.com/sirupsen/logrus/blob/master/logrus.go#L25).
 
 ### Nuking only default security group rules
+
 When deleting defaults with `cloud-nuke defaults-aws`, use the `--sg-only` flag to delete only the default
 security group rules and not the default VPCs.
 
@@ -573,13 +623,16 @@ When nuking VPCs cloud-nuke will attempt to remove dependency resources undernea
 - Network ACLs
 - Security Groups
 - DHCP Option Sets (Will be dissociated from VPC, not deleted. Must be cleaned up separately)
-- Elastic IPs (Supported as a separate resource that gets cleaned up first. If you are filtering what gets nuked, Elastic IPs may prevent VPCs from destroying.)
+- Elastic IPs (Supported as a separate resource that gets cleaned up first. If you are filtering what gets nuked,
+  Elastic IPs may prevent VPCs from destroying.)
 
 All other resources that get created within VPCs must be cleaned up prior to running cloud-nuke on VPC resources.
 
-> VPC resources may not be entirely cleaned up on the first run. We believe this is caused by an eventual consistency error in AWS.
-> 
-> If you see errors like `InvalidParameterValue: Network interface is currently in use.` We recommend waiting 30 minutes and trying again.
+> VPC resources may not be entirely cleaned up on the first run. We believe this is caused by an eventual consistency
+> error in AWS.
+>
+> If you see errors like `InvalidParameterValue: Network interface is currently in use.` We recommend waiting 30 minutes
+> and trying again.
 
 Happy Nuking!!!
 
@@ -587,7 +640,9 @@ Happy Nuking!!!
 
 ### AWS
 
-In order for the `cloud-nuke` CLI tool to access your AWS, you will need to provide your AWS credentials. You can use one of the [standard AWS CLI credential mechanisms](http://docs.aws.amazon.com/cli/latest/userguide/cli-chap-getting-started.html).
+In order for the `cloud-nuke` CLI tool to access your AWS, you will need to provide your AWS credentials. You can use
+one of
+the [standard AWS CLI credential mechanisms](http://docs.aws.amazon.com/cli/latest/userguide/cli-chap-getting-started.html).
 
 ## Running Tests
 
@@ -612,8 +667,10 @@ go run main.go
 
 ### Running tests
 
-**Note**: Many of the tests in the `aws` folder run against a real AWS account and will create and destroy actual resources. DO NOT
-hit `CTRL+C` while the tests are running, as this will prevent them from cleaning up properly. We are not responsible for any
+**Note**: Many of the tests in the `aws` folder run against a real AWS account and will create and destroy actual
+resources. DO NOT
+hit `CTRL+C` while the tests are running, as this will prevent them from cleaning up properly. We are not responsible
+for any
 charges you may incur.
 
 Before running the tests, you must configure your [AWS credentials](#credentials).
@@ -638,7 +695,6 @@ cd aws
 go test -v -run TestListAMIs
 ```
 
-
 And to run a specific test, such as `TestLambdaFunction_GetAll` in package `aws/resources`:
 
 ```bash
@@ -658,22 +714,32 @@ TEST_ACMPCA_EXPENSIVE_ENABLE=1 go test -v ./...
 Every source file in this project should be formatted with `go fmt`.
 
 ### Releasing new versions
-We try to follow the release process as deifned in our [Coding Methodology](https://www.notion.so/gruntwork/Gruntwork-Coding-Methodology-02fdcd6e4b004e818553684760bf691e#08b68ee0e19143e89523dcf483d2bf48).
+
+We try to follow the release process as deifned in
+our [Coding Methodology](https://www.notion.so/gruntwork/Gruntwork-Coding-Methodology-02fdcd6e4b004e818553684760bf691e#08b68ee0e19143e89523dcf483d2bf48).
 
 #### Choosing a new release tag
-If the new release contains any new resources that `cloud-nuke` will support, mark it as a minor version bump (X in v0.X.Y)
+
+If the new release contains any new resources that `cloud-nuke` will support, mark it as a minor version bump (X in
+v0.X.Y)
 to indicate backward incompatibilities.
 
-This is because since version `v0.2.0` `cloud-nuke` has been configured to automatically include new resources (so you have
-to explicitly opt-out). This is inherently not backward compatible, because users with CI practices around `cloud-nuke` would
-be surprised by new resources that are suddenly being picked up for deletion! This surprise is more alarming for resources
+This is because since version `v0.2.0` `cloud-nuke` has been configured to automatically include new resources (so you
+have
+to explicitly opt-out). This is inherently not backward compatible, because users with CI practices around `cloud-nuke`
+would
+be surprised by new resources that are suddenly being picked up for deletion! This surprise is more alarming for
+resources
 that are actively in use for any account, such as IAM Users.
 
-Therefore please mark your release as backward incompatible and bump the **minor version** (`X` in `v0.X.Y`) when it includes
+Therefore please mark your release as backward incompatible and bump the **minor version** (`X` in `v0.X.Y`) when it
+includes
 support for nuking new resources, so that we provide better signals for users when we introduce a new resource.
 
 #### To release a new version
-Go to the [Releases Page](https://github.com/gruntwork-io/cloud-nuke/releases) and create a new release. The CircleCI job for this repo has been configured to:
+
+Go to the [Releases Page](https://github.com/gruntwork-io/cloud-nuke/releases) and create a new release. The CircleCI
+job for this repo has been configured to:
 
 1. Automatically detect new tags.
 1. Build binaries for every OS using that tag as a version number.
diff --git a/aws/resource_registry.go b/aws/resource_registry.go
index 2db76fe..1815de9 100644
--- a/aws/resource_registry.go
+++ b/aws/resource_registry.go
@@ -106,6 +106,12 @@ func getRegisteredRegionalResources() []AwsResource {
 		&resources.TransitGatewaysVpcAttachment{},
 		&resources.TransitGatewaysRouteTables{},
 		&resources.TransitGateways{},
+		&resources.EC2IPAMs{},
+		&resources.EC2IpamScopes{},
+		&resources.EC2IPAMResourceDiscovery{},
+		&resources.EC2IPAMPool{},
+		&resources.EC2IPAMByoasn{},
+		&resources.EC2IPAMCustomAllocation{},
 	}
 }
 
diff --git a/aws/resources/ec2.go b/aws/resources/ec2.go
index 7a34f60..3e7143a 100644
--- a/aws/resources/ec2.go
+++ b/aws/resources/ec2.go
@@ -3,11 +3,12 @@ package resources
 import (
 	"context"
 	"fmt"
+	"time"
+
 	"github.com/gruntwork-io/cloud-nuke/externalcreds"
 	"github.com/gruntwork-io/cloud-nuke/telemetry"
 	"github.com/gruntwork-io/cloud-nuke/util"
 	commonTelemetry "github.com/gruntwork-io/go-commons/telemetry"
-	"time"
 
 	"github.com/hashicorp/go-multierror"
 	"github.com/pterm/pterm"
@@ -80,7 +81,7 @@ func shouldIncludeInstanceId(instance *ec2.Instance, protected bool, configObj c
 
 	// If Name is unset, GetEC2ResourceNameTagValue returns error and zero value string
 	// Ignore this error and pass empty string to config.ShouldInclude
-	instanceName := GetEC2ResourceNameTagValue(instance.Tags)
+	instanceName := util.GetEC2ResourceNameTagValue(instance.Tags)
 	return configObj.EC2.ShouldInclude(config.ResourceValue{
 		Name: instanceName,
 		Time: instance.LaunchTime,
@@ -828,18 +829,3 @@ func NukeDefaultSecurityGroupRules(sgs []DefaultSecurityGroup) error {
 	logging.Debug("Finished nuking default Security Groups in all regions")
 	return nil
 }
-
-// Given an slice of tags, return the value of the Name tag
-func GetEC2ResourceNameTagValue(tags []*ec2.Tag) *string {
-	t := make(map[string]string)
-
-	for _, v := range tags {
-		t[awsgo.StringValue(v.Key)] = awsgo.StringValue(v.Value)
-	}
-
-	if name, ok := t["Name"]; ok {
-		return &name
-	}
-
-	return nil
-}
diff --git a/aws/resources/ec2_dedicated_host.go b/aws/resources/ec2_dedicated_host.go
index dd9a0c9..37a8c23 100644
--- a/aws/resources/ec2_dedicated_host.go
+++ b/aws/resources/ec2_dedicated_host.go
@@ -3,6 +3,7 @@ package resources
 import (
 	"context"
 	"fmt"
+
 	"github.com/aws/aws-sdk-go/aws"
 	awsgo "github.com/aws/aws-sdk-go/aws"
 	"github.com/aws/aws-sdk-go/service/ec2"
@@ -10,6 +11,7 @@ import (
 	"github.com/gruntwork-io/cloud-nuke/logging"
 	"github.com/gruntwork-io/cloud-nuke/report"
 	"github.com/gruntwork-io/cloud-nuke/telemetry"
+	"github.com/gruntwork-io/cloud-nuke/util"
 	"github.com/gruntwork-io/go-commons/errors"
 	commonTelemetry "github.com/gruntwork-io/go-commons/telemetry"
 )
@@ -62,7 +64,7 @@ func shouldIncludeHostId(host *ec2.Host, configObj config.Config) bool {
 
 	// If Name is unset, GetEC2ResourceNameTagValue returns error and zero value string
 	// Ignore this error and pass empty string to config.ShouldInclude
-	hostNameTagValue := GetEC2ResourceNameTagValue(host.Tags)
+	hostNameTagValue := util.GetEC2ResourceNameTagValue(host.Tags)
 
 	return configObj.EC2DedicatedHosts.ShouldInclude(config.ResourceValue{
 		Name: hostNameTagValue,
diff --git a/aws/resources/ec2_ipam.go b/aws/resources/ec2_ipam.go
new file mode 100644
index 0000000..b32f2de
--- /dev/null
+++ b/aws/resources/ec2_ipam.go
@@ -0,0 +1,314 @@
+package resources
+
+import (
+	"context"
+	"fmt"
+	"time"
+
+	"github.com/aws/aws-sdk-go/aws"
+	awsgo "github.com/aws/aws-sdk-go/aws"
+	"github.com/aws/aws-sdk-go/service/ec2"
+	"github.com/gruntwork-io/cloud-nuke/config"
+	"github.com/gruntwork-io/cloud-nuke/logging"
+	"github.com/gruntwork-io/cloud-nuke/report"
+	"github.com/gruntwork-io/cloud-nuke/util"
+	"github.com/gruntwork-io/go-commons/errors"
+)
+
+func (ec2Ipam *EC2IPAMs) setFirstSeenTag(ipam ec2.Ipam, value time.Time) error {
+	_, err := ec2Ipam.Client.CreateTags(&ec2.CreateTagsInput{
+		Resources: []*string{ipam.IpamId},
+		Tags: []*ec2.Tag{
+			{
+				Key:   awsgo.String(util.FirstSeenTagKey),
+				Value: awsgo.String(util.FormatTimestamp(value)),
+			},
+		},
+	})
+	if err != nil {
+		return errors.WithStackTrace(err)
+	}
+
+	return nil
+}
+
+func (ec2Ipam *EC2IPAMs) getFirstSeenTag(ipam ec2.Ipam) (*time.Time, error) {
+	tags := ipam.Tags
+	for _, tag := range tags {
+		if util.IsFirstSeenTag(tag.Key) {
+			firstSeenTime, err := util.ParseTimestamp(tag.Value)
+			if err != nil {
+				return nil, errors.WithStackTrace(err)
+			}
+
+			return firstSeenTime, nil
+		}
+	}
+
+	return nil, nil
+}
+
+func shouldIncludeIpamID(ipam *ec2.Ipam, firstSeenTime *time.Time, configObj config.Config) bool {
+	var ipamName string
+	// get the tags as map
+	tagMap := util.ConvertEC2TagsToMap(ipam.Tags)
+	if name, ok := tagMap["Name"]; ok {
+		ipamName = name
+	}
+
+	return configObj.EC2IPAM.ShouldInclude(config.ResourceValue{
+		Name: &ipamName,
+		Time: firstSeenTime,
+		Tags: tagMap,
+	})
+}
+
+// Returns a formatted string of IPAM URLs
+func (ec2Ipam *EC2IPAMs) getAll(c context.Context, configObj config.Config) ([]*string, error) {
+	result := []*string{}
+	paginator := func(output *ec2.DescribeIpamsOutput, lastPage bool) bool {
+		for _, ipam := range output.Ipams {
+			// check first seen tag
+			firstSeenTime, err := ec2Ipam.getFirstSeenTag(*ipam)
+			if err != nil {
+				logging.Errorf(
+					"Unable to retrieve tags for IPAM: %s, with error: %s", *ipam.IpamId, err)
+				continue
+			}
+
+			// if the first seen tag is not there, then create one
+			if firstSeenTime == nil {
+				now := time.Now().UTC()
+				firstSeenTime = &now
+				if err := ec2Ipam.setFirstSeenTag(*ipam, time.Now().UTC()); err != nil {
+					logging.Errorf(
+						"Unable to apply first seen tag IPAM: %s, with error: %s", *ipam.IpamId, err)
+					continue
+				}
+			}
+
+			// Check for include this ipam
+			if shouldIncludeIpamID(ipam, firstSeenTime, configObj) {
+				result = append(result, ipam.IpamId)
+			}
+		}
+		return !lastPage
+	}
+
+	params := &ec2.DescribeIpamsInput{
+		MaxResults: awsgo.Int64(10),
+	}
+
+	err := ec2Ipam.Client.DescribeIpamsPages(params, paginator)
+	if err != nil {
+		return nil, errors.WithStackTrace(err)
+	}
+
+	return result, nil
+}
+
+// deProvisionPoolCIDRs : Detach the CIDR provisiond on the pool
+func (ec2Ipam *EC2IPAMs) deProvisionPoolCIDRs(poolID *string) error {
+	output, err := ec2Ipam.Client.GetIpamPoolCidrs(&ec2.GetIpamPoolCidrsInput{
+		IpamPoolId: poolID,
+		Filters: []*ec2.Filter{
+			{
+				Name:   awsgo.String("state"),
+				Values: awsgo.StringSlice([]string{"provisioned"}),
+			},
+		},
+	})
+	if err != nil {
+		return errors.WithStackTrace(err)
+	}
+
+	for _, poolCidr := range output.IpamPoolCidrs {
+		_, err := ec2Ipam.Client.DeprovisionIpamPoolCidr(&ec2.DeprovisionIpamPoolCidrInput{
+			IpamPoolId: poolID,
+			Cidr:       poolCidr.Cidr,
+		})
+
+		if err != nil {
+			return errors.WithStackTrace(err)
+		}
+		logging.Debugf("De-Provisioned CIDR(s) from IPAM Pool %s", aws.StringValue(poolID))
+	}
+
+	return nil
+}
+
+// releaseCustomAllocations : Release the custom allocated CIDR(s) from the pool
+func (ec2Ipam *EC2IPAMs) releaseCustomAllocations(poolID *string) error {
+	output, err := ec2Ipam.Client.GetIpamPoolAllocations(&ec2.GetIpamPoolAllocationsInput{
+		IpamPoolId: poolID,
+	})
+	if err != nil {
+		return errors.WithStackTrace(err)
+	}
+
+	for _, poolAllocation := range output.IpamPoolAllocations {
+		// we only can release the custom allocations
+		if *poolAllocation.ResourceType != "custom" {
+			continue
+		}
+		_, err := ec2Ipam.Client.ReleaseIpamPoolAllocation(&ec2.ReleaseIpamPoolAllocationInput{
+			IpamPoolId:           poolID,
+			IpamPoolAllocationId: poolAllocation.IpamPoolAllocationId,
+			Cidr:                 poolAllocation.Cidr,
+		})
+
+		if err != nil {
+			return errors.WithStackTrace(err)
+		}
+		logging.Debugf("Release custom allocated CIDR(s) from IPAM Pool %s", aws.StringValue(poolID))
+	}
+
+	return nil
+}
+
+// nukePublicIPAMPools : Nuke the pools on an IPAM
+// Before deleting the IPAM, it is necessary to manually remove any pools within our public scope,
+// as the deleteIPAM operation will not handle their deletion with cascade option.
+//
+// We cannot delete an IPAM pool if there are allocations in it or CIDRs provisioned to it. We must first release the allocations and Deprovision CIDRs
+// from a pool before we can delete the pool
+func (ec2Ipam *EC2IPAMs) nukePublicIPAMPools(ipamID *string) error {
+	ipam, err := ec2Ipam.Client.DescribeIpams(&ec2.DescribeIpamsInput{
+		IpamIds: aws.StringSlice([]string{*ipamID}),
+	})
+	if err != nil {
+		logging.Errorf(fmt.Sprintf("Error describing IPAM %s: %s", *ipamID, err.Error()))
+		return errors.WithStackTrace(err)
+	}
+
+	// Describe the scope to read the scope arn
+	scope, err := ec2Ipam.Client.DescribeIpamScopes(&ec2.DescribeIpamScopesInput{
+		IpamScopeIds: aws.StringSlice([]string{
+			*ipam.Ipams[0].PublicDefaultScopeId,
+		}),
+	})
+
+	if err != nil {
+		logging.Errorf(fmt.Sprintf("Error describing IPAM Public scope %s: %s", *ipamID, err.Error()))
+		return errors.WithStackTrace(err)
+	}
+
+	// get the pools which is assigned on the public scope of the IPAM
+	output, err := ec2Ipam.Client.DescribeIpamPools(&ec2.DescribeIpamPoolsInput{
+		Filters: []*ec2.Filter{
+			{
+				Name: aws.String("ipam-scope-arn"),
+				Values: aws.StringSlice([]string{
+					*scope.IpamScopes[0].IpamScopeArn,
+				}),
+			},
+		},
+	})
+	if err != nil {
+		logging.Errorf(fmt.Sprintf("Error describing IPAM Pools on public scope %s: %s", *ipamID, err.Error()))
+		return errors.WithStackTrace(err)
+	}
+
+	for _, pool := range output.IpamPools {
+		// Remove associated CIDRs before deleting IPAM pools to complete de-provisioning.
+		err := ec2Ipam.deProvisionPoolCIDRs(pool.IpamPoolId)
+		if err != nil {
+			logging.Errorf(fmt.Sprintf("Error de-provisioning Pools CIDR  on Pool %s : %s", *pool.IpamPoolId, err.Error()))
+			return errors.WithStackTrace(err)
+		}
+
+		// Release custom allocation from the pool
+		err = ec2Ipam.releaseCustomAllocations(pool.IpamPoolId)
+		if err != nil {
+			logging.Errorf(fmt.Sprintf("Error Release custom allocations of Pool %s : %s", *pool.IpamPoolId, err.Error()))
+			return errors.WithStackTrace(err)
+		}
+
+		// delete ipam pool
+		_, err = ec2Ipam.Client.DeleteIpamPool(&ec2.DeleteIpamPoolInput{
+			IpamPoolId: pool.IpamPoolId,
+		})
+		if err != nil {
+			logging.Errorf("[Failed] Delete IPAM Pool %s", err)
+			return errors.WithStackTrace(err)
+		}
+		logging.Debugf("Deleted IPAM Pool %s from IPAM %s", aws.StringValue(pool.IpamPoolId), aws.StringValue(ipamID))
+	}
+
+	return nil
+}
+
+// deleteIPAM : Delete the IPAM
+func (ec2Ipam *EC2IPAMs) deleteIPAM(id *string) error {
+	params := &ec2.DeleteIpamInput{
+		IpamId: id,
+		// NOTE : Enables you to quickly delete an IPAM, private scopes, pools in private scopes, and any allocations in the pools in private scopes.
+		// You cannot delete the IPAM with this option if there is a pool in your public scope.
+		// IPAM does the following when this is enabled
+		//
+		// * Deallocates any CIDRs allocated to VPC resources (such as VPCs) in pools
+		// * Deprovisions all IPv4 CIDRs provisioned to IPAM pools in private scopes.
+		// * Deletes all IPAM pools in private scopes.
+		// * Deletes all non-default private scopes in the IPAM.
+		// * Deletes the default public and private scopes and the IPAM.
+		Cascade: aws.Bool(true),
+	}
+
+	_, err := ec2Ipam.Client.DeleteIpam(params)
+
+	return err
+}
+
+func (ec2Ipam *EC2IPAMs) nukeIPAM(id *string) error {
+	// Functions used to really nuke an IPAM as an IPAM can have many attached
+	// items we need delete/detach them before actually deleting it.
+	// NOTE: The actual IPAM deletion should always be the last one. This way we
+	// can guarantee that it will fail if we forgot to delete/detach an item.
+	functions := []func(userName *string) error{
+		ec2Ipam.nukePublicIPAMPools,
+		ec2Ipam.deleteIPAM,
+	}
+
+	for _, fn := range functions {
+		if err := fn(id); err != nil {
+			return err
+		}
+	}
+
+	return nil
+}
+
+// Deletes all IPAMs
+func (ec2Ipam *EC2IPAMs) nukeAll(ids []*string) error {
+	if len(ids) == 0 {
+		logging.Debugf("No IPAM ids to nuke in region %s", ec2Ipam.Region)
+		return nil
+	}
+
+	logging.Debugf("Deleting all IPAM ids in region %s", ec2Ipam.Region)
+	var deletedAddresses []*string
+
+	for _, id := range ids {
+
+		err := ec2Ipam.nukeIPAM(id)
+
+		// Record status of this resource
+		e := report.Entry{
+			Identifier:   aws.StringValue(id),
+			ResourceType: "IPAM",
+			Error:        err,
+		}
+		report.Record(e)
+
+		if err != nil {
+			logging.Debugf("[Failed] %s", err)
+		} else {
+			deletedAddresses = append(deletedAddresses, id)
+			logging.Debugf("Deleted IPAM: %s", *id)
+		}
+	}
+
+	logging.Debugf("[OK] %d IPAM address(s) deleted in %s", len(deletedAddresses), ec2Ipam.Region)
+
+	return nil
+}
diff --git a/aws/resources/ec2_ipam_byoasn.go b/aws/resources/ec2_ipam_byoasn.go
new file mode 100644
index 0000000..47aa6cf
--- /dev/null
+++ b/aws/resources/ec2_ipam_byoasn.go
@@ -0,0 +1,68 @@
+package resources
+
+import (
+	"context"
+
+	"github.com/aws/aws-sdk-go/aws"
+	"github.com/aws/aws-sdk-go/service/ec2"
+	"github.com/gruntwork-io/cloud-nuke/config"
+	"github.com/gruntwork-io/cloud-nuke/logging"
+	"github.com/gruntwork-io/cloud-nuke/report"
+	"github.com/gruntwork-io/go-commons/errors"
+)
+
+// Returns a formatted string of IPAM Byoasns
+func (byoasn *EC2IPAMByoasn) getAll(c context.Context, configObj config.Config) ([]*string, error) {
+	result := []*string{}
+	params := &ec2.DescribeIpamByoasnInput{
+		MaxResults: &MaxResultCount,
+	}
+
+	output, err := byoasn.Client.DescribeIpamByoasn(params)
+	if err != nil {
+		return nil, errors.WithStackTrace(err)
+	}
+
+	for _, out := range output.Byoasns {
+		result = append(result, out.Asn)
+	}
+	return result, nil
+}
+
+// Deletes all IPAMs Byoasns
+func (byoasn *EC2IPAMByoasn) nukeAll(asns []*string) error {
+	if len(asns) == 0 {
+		logging.Debugf("No IPAM Byoasn to nuke in region %s", byoasn.Region)
+		return nil
+	}
+
+	logging.Debugf("Deleting all IPAM Byoasn in region %s", byoasn.Region)
+	var list []*string
+
+	for _, id := range asns {
+		params := &ec2.DisassociateIpamByoasnInput{
+			Asn: id,
+		}
+
+		_, err := byoasn.Client.DisassociateIpamByoasn(params)
+
+		// Record status of this resource
+		e := report.Entry{
+			Identifier:   aws.StringValue(id),
+			ResourceType: "IPAM Byoasn",
+			Error:        err,
+		}
+		report.Record(e)
+
+		if err != nil {
+			logging.Debugf("[Failed] %s", err)
+		} else {
+			list = append(list, id)
+			logging.Debugf("Deleted IPAM Pool: %s", *id)
+		}
+	}
+
+	logging.Debugf("[OK] %d IPAM Pool(s) deleted in %s", len(list), byoasn.Region)
+
+	return nil
+}
diff --git a/aws/resources/ec2_ipam_byoasn_types.go b/aws/resources/ec2_ipam_byoasn_types.go
new file mode 100644
index 0000000..925734a
--- /dev/null
+++ b/aws/resources/ec2_ipam_byoasn_types.go
@@ -0,0 +1,59 @@
+package resources
+
+import (
+	"context"
+
+	awsgo "github.com/aws/aws-sdk-go/aws"
+	"github.com/aws/aws-sdk-go/aws/session"
+	"github.com/aws/aws-sdk-go/service/ec2"
+	"github.com/aws/aws-sdk-go/service/ec2/ec2iface"
+	"github.com/gruntwork-io/cloud-nuke/config"
+	"github.com/gruntwork-io/go-commons/errors"
+)
+
+// IPAM Byoasn- represents all IPAMs
+type EC2IPAMByoasn struct {
+	Client ec2iface.EC2API
+	Region string
+	Pools  []string
+}
+
+var MaxResultCount = int64(10)
+
+func (byoasn *EC2IPAMByoasn) Init(session *session.Session) {
+	byoasn.Client = ec2.New(session)
+}
+
+// ResourceName - the simple name of the aws resource
+func (byoasn *EC2IPAMByoasn) ResourceName() string {
+	return "ipam-byoasn"
+}
+
+func (byoasn *EC2IPAMByoasn) MaxBatchSize() int {
+	// Tentative batch size to ensure AWS doesn't throttle
+	return 49
+}
+
+// ResourceIdentifiers - The ids of the IPAMs
+func (byoasn *EC2IPAMByoasn) ResourceIdentifiers() []string {
+	return byoasn.Pools
+}
+
+func (byoasn *EC2IPAMByoasn) GetAndSetIdentifiers(c context.Context, configObj config.Config) ([]string, error) {
+	identifiers, err := byoasn.getAll(c, configObj)
+	if err != nil {
+		return nil, err
+	}
+
+	byoasn.Pools = awsgo.StringValueSlice(identifiers)
+	return byoasn.Pools, nil
+}
+
+// Nuke - nuke 'em all!!!
+func (byoasn *EC2IPAMByoasn) Nuke(identifiers []string) error {
+	if err := byoasn.nukeAll(awsgo.StringSlice(identifiers)); err != nil {
+		return errors.WithStackTrace(err)
+	}
+
+	return nil
+}
diff --git a/aws/resources/ec2_ipam_custom_allocation.go b/aws/resources/ec2_ipam_custom_allocation.go
new file mode 100644
index 0000000..b861a0f
--- /dev/null
+++ b/aws/resources/ec2_ipam_custom_allocation.go
@@ -0,0 +1,148 @@
+package resources
+
+import (
+	"context"
+	"fmt"
+
+	"github.com/aws/aws-sdk-go/aws"
+	awsgo "github.com/aws/aws-sdk-go/aws"
+	"github.com/aws/aws-sdk-go/service/ec2"
+	"github.com/gruntwork-io/cloud-nuke/config"
+	"github.com/gruntwork-io/cloud-nuke/logging"
+	"github.com/gruntwork-io/cloud-nuke/report"
+	"github.com/gruntwork-io/go-commons/errors"
+)
+
+// Get all active pools
+func (cs *EC2IPAMCustomAllocation) getPools() ([]*string, error) {
+	result := []*string{}
+	paginator := func(output *ec2.DescribeIpamPoolsOutput, lastPage bool) bool {
+		for _, p := range output.IpamPools {
+			result = append(result, p.IpamPoolId)
+		}
+		return !lastPage
+	}
+
+	params := &ec2.DescribeIpamPoolsInput{
+		MaxResults: awsgo.Int64(10),
+	}
+
+	err := cs.Client.DescribeIpamPoolsPages(params, paginator)
+	if err != nil {
+		return nil, errors.WithStackTrace(err)
+	}
+
+	return result, nil
+}
+
+// getPoolAllocationCIDR retrieves the CIDR block associated with an IPAM pool allocation in AWS.
+func (cs *EC2IPAMCustomAllocation) getPoolAllocationCIDR(allocationID *string) (*string, error) {
+	// get the pool id curresponding to the allocation id
+	allocationIPAMPoolID, ok := cs.PoolAndAllocationMap[*allocationID]
+	if !ok {
+		return nil, errors.WithStackTrace(fmt.Errorf("unable to find the pool allocation with %s", *allocationID))
+	}
+
+	output, err := cs.Client.GetIpamPoolAllocations(&ec2.GetIpamPoolAllocationsInput{
+		IpamPoolId:           &allocationIPAMPoolID,
+		IpamPoolAllocationId: allocationID,
+	})
+
+	if err != nil {
+		return nil, errors.WithStackTrace(err)
+	}
+
+	if !(len(output.IpamPoolAllocations) > 0) {
+		return nil, errors.WithStackTrace(fmt.Errorf("unable to find the pool allocation with %s", *allocationID))
+	}
+
+	return output.IpamPoolAllocations[0].Cidr, nil
+}
+
+// Get all the allocations
+func (cs *EC2IPAMCustomAllocation) getAll(c context.Context, configObj config.Config) ([]*string, error) {
+	activePools, errPool := cs.getPools()
+	if errPool != nil {
+		return nil, errors.WithStackTrace(errPool)
+	}
+
+	// check though the filters and see the custom allocations
+	result := []*string{}
+	for _, pool := range activePools {
+		paginator := func(output *ec2.GetIpamPoolAllocationsOutput, lastPage bool) bool {
+			for _, allocation := range output.IpamPoolAllocations {
+				if *allocation.ResourceType == "custom" {
+					result = append(result, allocation.IpamPoolAllocationId)
+					cs.PoolAndAllocationMap[*allocation.IpamPoolAllocationId] = *pool
+				}
+			}
+			return !lastPage
+		}
+
+		// prepare the params
+		params := &ec2.GetIpamPoolAllocationsInput{
+			MaxResults: awsgo.Int64(int64(cs.MaxBatchSize())),
+			IpamPoolId: pool,
+		}
+
+		err := cs.Client.GetIpamPoolAllocationsPages(params, paginator)
+		if err != nil {
+			logging.Debugf("[Failed] %s", err)
+			continue
+		}
+	}
+
+	return result, nil
+}
+
+// Deletes all IPAMs
+func (cs *EC2IPAMCustomAllocation) nukeAll(ids []*string) error {
+	if len(ids) == 0 {
+		logging.Debugf("No IPAM Custom Allocation to nuke in region %s", cs.Region)
+		return nil
+	}
+
+	logging.Debugf("Deleting all IPAM Custom Allocation in region %s", cs.Region)
+	var deletedAddresses []*string
+
+	for _, id := range ids {
+
+		// get the IPamPool details
+		cidr, err := cs.getPoolAllocationCIDR(id)
+		if err != nil {
+			logging.Errorf("[Failed] %s", err)
+			continue
+		}
+		allocationIPAMPoolID, ok := cs.PoolAndAllocationMap[*id]
+		if !ok {
+			logging.Errorf("[Failed] %s", fmt.Errorf("unable to find the pool allocation with %s", *id))
+			continue
+		}
+
+		// Release the allocation
+		_, err = cs.Client.ReleaseIpamPoolAllocation(&ec2.ReleaseIpamPoolAllocationInput{
+			IpamPoolId:           &allocationIPAMPoolID,
+			IpamPoolAllocationId: id,
+			Cidr:                 cidr,
+		})
+
+		// Record status of this resource
+		e := report.Entry{
+			Identifier:   aws.StringValue(id),
+			ResourceType: "IPAM",
+			Error:        err,
+		}
+		report.Record(e)
+
+		if err != nil {
+			logging.Debugf("[Failed] %s", err)
+		} else {
+			deletedAddresses = append(deletedAddresses, id)
+			logging.Debugf("Deleted IPAM: %s", *id)
+		}
+	}
+
+	logging.Debugf("[OK] %d IPAM Custom Allocation(s) deleted in %s", len(deletedAddresses), cs.Region)
+
+	return nil
+}
diff --git a/aws/resources/ec2_ipam_custom_allocation_types.go b/aws/resources/ec2_ipam_custom_allocation_types.go
new file mode 100644
index 0000000..809c287
--- /dev/null
+++ b/aws/resources/ec2_ipam_custom_allocation_types.go
@@ -0,0 +1,59 @@
+package resources
+
+import (
+	"context"
+
+	awsgo "github.com/aws/aws-sdk-go/aws"
+	"github.com/aws/aws-sdk-go/aws/session"
+	"github.com/aws/aws-sdk-go/service/ec2"
+	"github.com/aws/aws-sdk-go/service/ec2/ec2iface"
+	"github.com/gruntwork-io/cloud-nuke/config"
+	"github.com/gruntwork-io/go-commons/errors"
+)
+
+// IPAM Byoasn- represents all IPAMs
+type EC2IPAMCustomAllocation struct {
+	Client               ec2iface.EC2API
+	Region               string
+	Allocations          []string
+	PoolAndAllocationMap map[string]string
+}
+
+func (cs *EC2IPAMCustomAllocation) Init(session *session.Session) {
+	cs.Client = ec2.New(session)
+	cs.PoolAndAllocationMap = make(map[string]string)
+}
+
+// ResourceName - the simple name of the aws resource
+func (cs *EC2IPAMCustomAllocation) ResourceName() string {
+	return "ipam-custom-allocation"
+}
+
+func (cs *EC2IPAMCustomAllocation) MaxBatchSize() int {
+	// Tentative batch size to ensure AWS doesn't throttle
+	return 1000
+}
+
+// ResourceIdentifiers - The ids of the IPAMs
+func (cs *EC2IPAMCustomAllocation) ResourceIdentifiers() []string {
+	return cs.Allocations
+}
+
+func (cs *EC2IPAMCustomAllocation) GetAndSetIdentifiers(c context.Context, configObj config.Config) ([]string, error) {
+	identifiers, err := cs.getAll(c, configObj)
+	if err != nil {
+		return nil, err
+	}
+
+	cs.Allocations = awsgo.StringValueSlice(identifiers)
+	return cs.Allocations, nil
+}
+
+// Nuke - nuke 'em all!!!
+func (cs *EC2IPAMCustomAllocation) Nuke(identifiers []string) error {
+	if err := cs.nukeAll(awsgo.StringSlice(identifiers)); err != nil {
+		return errors.WithStackTrace(err)
+	}
+
+	return nil
+}
diff --git a/aws/resources/ec2_ipam_pool.go b/aws/resources/ec2_ipam_pool.go
new file mode 100644
index 0000000..d3752bb
--- /dev/null
+++ b/aws/resources/ec2_ipam_pool.go
@@ -0,0 +1,150 @@
+package resources
+
+import (
+	"context"
+	"time"
+
+	"github.com/aws/aws-sdk-go/aws"
+	awsgo "github.com/aws/aws-sdk-go/aws"
+	"github.com/aws/aws-sdk-go/service/ec2"
+	"github.com/gruntwork-io/cloud-nuke/config"
+	"github.com/gruntwork-io/cloud-nuke/logging"
+	"github.com/gruntwork-io/cloud-nuke/report"
+	"github.com/gruntwork-io/cloud-nuke/util"
+	"github.com/gruntwork-io/go-commons/errors"
+)
+
+func (discovery *EC2IPAMPool) setFirstSeenTag(ipam ec2.IpamPool, value time.Time) error {
+	_, err := discovery.Client.CreateTags(&ec2.CreateTagsInput{
+		Resources: []*string{ipam.IpamPoolId},
+		Tags: []*ec2.Tag{
+			{
+				Key:   awsgo.String(util.FirstSeenTagKey),
+				Value: awsgo.String(util.FormatTimestamp(value)),
+			},
+		},
+	})
+	if err != nil {
+		return errors.WithStackTrace(err)
+	}
+
+	return nil
+}
+
+func (discovery *EC2IPAMPool) getFirstSeenTag(ipam ec2.IpamPool) (*time.Time, error) {
+	tags := ipam.Tags
+	for _, tag := range tags {
+		if util.IsFirstSeenTag(tag.Key) {
+			firstSeenTime, err := util.ParseTimestamp(tag.Value)
+			if err != nil {
+				return nil, errors.WithStackTrace(err)
+			}
+
+			return firstSeenTime, nil
+		}
+	}
+
+	return nil, nil
+}
+
+func shouldIncludeIpamPoolID(ipam *ec2.IpamPool, firstSeenTime *time.Time, configObj config.Config) bool {
+	var ipamPoolName string
+	// get the tags as map
+	tagMap := util.ConvertEC2TagsToMap(ipam.Tags)
+	if name, ok := tagMap["Name"]; ok {
+		ipamPoolName = name
+	}
+
+	return configObj.EC2IPAMPool.ShouldInclude(config.ResourceValue{
+		Name: &ipamPoolName,
+		Time: firstSeenTime,
+		Tags: tagMap,
+	})
+}
+
+// Returns a formatted string of IPAM URLs
+func (ec2Pool *EC2IPAMPool) getAll(c context.Context, configObj config.Config) ([]*string, error) {
+	result := []*string{}
+	paginator := func(output *ec2.DescribeIpamPoolsOutput, lastPage bool) bool {
+		for _, pool := range output.IpamPools {
+			// check first seen tag
+			firstSeenTime, err := ec2Pool.getFirstSeenTag(*pool)
+			if err != nil {
+				logging.Errorf(
+					"Unable to retrieve tags for IPAM Pool: %s, with error: %s", *pool.IpamPoolId, err)
+				continue
+			}
+
+			// if the first seen tag is not there, then create one
+			if firstSeenTime == nil {
+				now := time.Now().UTC()
+				firstSeenTime = &now
+				if err := ec2Pool.setFirstSeenTag(*pool, time.Now().UTC()); err != nil {
+					logging.Errorf(
+						"Unable to apply first seen tag IPAM Pool: %s, with error: %s", *pool.IpamPoolId, err)
+					continue
+				}
+			}
+			// Check for include this ipam
+			if shouldIncludeIpamPoolID(pool, firstSeenTime, configObj) {
+				result = append(result, pool.IpamPoolId)
+			}
+		}
+		return !lastPage
+	}
+
+	params := &ec2.DescribeIpamPoolsInput{
+		MaxResults: awsgo.Int64(10),
+		Filters: []*ec2.Filter{
+			{
+				Name:   awsgo.String("state"),
+				Values: awsgo.StringSlice([]string{"create-complete"}),
+			},
+		},
+	}
+
+	err := ec2Pool.Client.DescribeIpamPoolsPages(params, paginator)
+	if err != nil {
+		return nil, errors.WithStackTrace(err)
+	}
+
+	return result, nil
+}
+
+// Deletes all IPAMs
+func (pool *EC2IPAMPool) nukeAll(ids []*string) error {
+	if len(ids) == 0 {
+		logging.Debugf("No IPAM ids to nuke in region %s", pool.Region)
+		return nil
+	}
+
+	logging.Debugf("Deleting all IPAM ids in region %s", pool.Region)
+	var deletedAddresses []*string
+
+	for _, id := range ids {
+		params := &ec2.DeleteIpamPoolInput{
+			IpamPoolId: id,
+		}
+
+		_, err := pool.Client.DeleteIpamPool(params)
+
+		// Record status of this resource
+		e := report.Entry{
+			Identifier:   aws.StringValue(id),
+			ResourceType: "IPAM Pool",
+			Error:        err,
+		}
+		report.Record(e)
+
+		if err != nil {
+			logging.Debugf("[Failed] %s", err)
+		} else {
+			deletedAddresses = append(deletedAddresses, id)
+			logging.Debugf("Deleted IPAM Pool: %s", *id)
+		}
+	}
+
+	logging.Debugf("[OK] %d IPAM Pool(s) deleted in %s", len(deletedAddresses), pool.Region)
+
+	return nil
+}
diff --git a/aws/resources/ec2_ipam_pool_types.go b/aws/resources/ec2_ipam_pool_types.go
new file mode 100644
index 0000000..6c2c54f
--- /dev/null
+++ b/aws/resources/ec2_ipam_pool_types.go
@@ -0,0 +1,57 @@
+package resources
+
+import (
+	"context"
+
+	awsgo "github.com/aws/aws-sdk-go/aws"
+	"github.com/aws/aws-sdk-go/aws/session"
+	"github.com/aws/aws-sdk-go/service/ec2"
+	"github.com/aws/aws-sdk-go/service/ec2/ec2iface"
+	"github.com/gruntwork-io/cloud-nuke/config"
+	"github.com/gruntwork-io/go-commons/errors"
+)
+
+// IPAM Pool- represents all IPAMs
+type EC2IPAMPool struct {
+	Client ec2iface.EC2API
+	Region string
+	Pools  []string
+}
+
+func (pool *EC2IPAMPool) Init(session *session.Session) {
+	pool.Client = ec2.New(session)
+}
+
+// ResourceName - the simple name of the aws resource
+func (pool *EC2IPAMPool) ResourceName() string {
+	return "ipam-pool"
+}
+
+func (pool *EC2IPAMPool) MaxBatchSize() int {
+	// Tentative batch size to ensure AWS doesn't throttle
+	return 49
+}
+
+// ResourceIdentifiers - The ids of the IPAMs
+func (pool *EC2IPAMPool) ResourceIdentifiers() []string {
+	return pool.Pools
+}
+
+func (pool *EC2IPAMPool) GetAndSetIdentifiers(c context.Context, configObj config.Config) ([]string, error) {
+	identifiers, err := pool.getAll(c, configObj)
+	if err != nil {
+		return nil, err
+	}
+
+	pool.Pools = awsgo.StringValueSlice(identifiers)
+	return pool.Pools, nil
+}
+
+// Nuke - nuke 'em all!!!
+func (pool *EC2IPAMPool) Nuke(identifiers []string) error {
+	if err := pool.nukeAll(awsgo.StringSlice(identifiers)); err != nil {
+		return errors.WithStackTrace(err)
+	}
+
+	return nil
+}
diff --git a/aws/resources/ec2_ipam_resource_discovery.go b/aws/resources/ec2_ipam_resource_discovery.go
new file mode 100644
index 0000000..f7e249b
--- /dev/null
+++ b/aws/resources/ec2_ipam_resource_discovery.go
@@ -0,0 +1,148 @@
+package resources
+
+import (
+	"context"
+	"time"
+
+	awsgo "github.com/aws/aws-sdk-go/aws"
+	"github.com/aws/aws-sdk-go/service/ec2"
+	"github.com/gruntwork-io/cloud-nuke/config"
+	"github.com/gruntwork-io/cloud-nuke/logging"
+	"github.com/gruntwork-io/cloud-nuke/report"
+	"github.com/gruntwork-io/cloud-nuke/util"
+	"github.com/gruntwork-io/go-commons/errors"
+)
+
+func (discovery *EC2IPAMResourceDiscovery) setFirstSeenTag(ipam ec2.IpamResourceDiscovery, value time.Time) error {
+	_, err := discovery.Client.CreateTags(&ec2.CreateTagsInput{
+		Resources: []*string{ipam.IpamResourceDiscoveryId},
+		Tags: []*ec2.Tag{
+			{
+				Key:   awsgo.String(util.FirstSeenTagKey),
+				Value: awsgo.String(util.FormatTimestamp(value)),
+			},
+		},
+	})
+	if err != nil {
+		return errors.WithStackTrace(err)
+	}
+
+	return nil
+}
+
+func (discovery *EC2IPAMResourceDiscovery) getFirstSeenTag(ipam ec2.IpamResourceDiscovery) (*time.Time, error) {
+	tags := ipam.Tags
+	for _, tag := range tags {
+		if util.IsFirstSeenTag(tag.Key) {
+			firstSeenTime, err := util.ParseTimestamp(tag.Value)
+			if err != nil {
+				return nil, errors.WithStackTrace(err)
+			}
+
+			return firstSeenTime, nil
+		}
+	}
+
+	return nil, nil
+}
+
+func shouldIncludeIpamResourceID(ipam *ec2.IpamResourceDiscovery, firstSeenTime *time.Time, configObj config.Config) bool {
+	var ipamResourceName string
+	// get the tags as map
+	tagMap := util.ConvertEC2TagsToMap(ipam.Tags)
+	if name, ok := tagMap["Name"]; ok {
+		ipamResourceName = name
+	}
+
+	return configObj.EC2IPAMResourceDiscovery.ShouldInclude(config.ResourceValue{
+		Name: &ipamResourceName,
+		Time: firstSeenTime,
+		Tags: tagMap,
+	})
+}
+
+// Returns a formatted string of IPAM Resource discovery
+func (discovery *EC2IPAMResourceDiscovery) getAll(c context.Context, configObj config.Config) ([]*string, error) {
+	result := []*string{}
+	paginator := func(output *ec2.DescribeIpamResourceDiscoveriesOutput, lastPage bool) bool {
+		for _, d := range output.IpamResourceDiscoveries {
+			// check first seen tag
+			firstSeenTime, err := discovery.getFirstSeenTag(*d)
+			if err != nil {
+				logging.Errorf(
+					"Unable to retrieve tags for IPAM: %s, with error: %s", *d.IpamResourceDiscoveryId, err)
+				continue
+			}
+
+			// if the first seen tag is not there, then create one
+			if firstSeenTime == nil {
+				now := time.Now().UTC()
+				firstSeenTime = &now
+				if err := discovery.setFirstSeenTag(*d, time.Now().UTC()); err != nil {
+					logging.Errorf(
+						"Unable to apply first seen tag IPAM: %s, with error: %s", *d.IpamResourceDiscoveryId, err)
+					continue
+				}
+			}
+			// Check for include this ipam resource ID
+			if shouldIncludeIpamResourceID(d, firstSeenTime, configObj) {
+				result = append(result, d.IpamResourceDiscoveryId)
+			}
+		}
+		return !lastPage
+	}
+
+	params := &ec2.DescribeIpamResourceDiscoveriesInput{
+		MaxResults: awsgo.Int64(10),
+		Filters: []*ec2.Filter{
+			{
+				Name:   awsgo.String("is-default"),
+				Values: awsgo.StringSlice([]string{"false"}),
+			},
+		},
+	}
+
+	err := discovery.Client.DescribeIpamResourceDiscoveriesPages(params, paginator)
+	if err != nil {
+		return nil, errors.WithStackTrace(err)
+	}
+
+	return result, nil
+}
+
+// Deletes all IPAM Resource Discoveries
+func (discovery *EC2IPAMResourceDiscovery) nukeAll(ids []*string) error {
+	if len(ids) == 0 {
+		logging.Debugf("No IPAM Resource Discovery ids to nuke in region %s", discovery.Region)
+		return nil
+	}
+
+	logging.Debugf("Deleting all IPAM resource Discovery ids in region %s", discovery.Region)
+	var deletedAddresses []*string
+
+	for _, id := range ids {
+		params := &ec2.DeleteIpamResourceDiscoveryInput{
+			IpamResourceDiscoveryId: id,
+		}
+
+		_, err := discovery.Client.DeleteIpamResourceDiscovery(params)
+		// Record status of this resource
+		e := report.Entry{
+			Identifier:   awsgo.StringValue(id),
+			ResourceType: "IPAM Resource Discovery",
+			Error:        err,
+		}
+		report.Record(e)
+
+		if err != nil {
+			logging.Debugf("[Failed] %s", err)
+		} else {
+			deletedAddresses = append(deletedAddresses, id)
+			logging.Debugf("Deleted IPAM Resource Discovery: %s", *id)
+		}
+	}
+
+	logging.Debugf("[OK] %d IPAM Resource Discovery(s) deleted in %s", len(deletedAddresses), discovery.Region)
+
+	return nil
+}
diff --git a/aws/resources/ec2_ipam_resource_discovery_types.go b/aws/resources/ec2_ipam_resource_discovery_types.go
new file mode 100644
index 0000000..d7eb396
--- /dev/null
+++ b/aws/resources/ec2_ipam_resource_discovery_types.go
@@ -0,0 +1,57 @@
+package resources
+
+import (
+	"context"
+
+	awsgo "github.com/aws/aws-sdk-go/aws"
+	"github.com/aws/aws-sdk-go/aws/session"
+	"github.com/aws/aws-sdk-go/service/ec2"
+	"github.com/aws/aws-sdk-go/service/ec2/ec2iface"
+	"github.com/gruntwork-io/cloud-nuke/config"
+	"github.com/gruntwork-io/go-commons/errors"
+)
+
+// IPAM - represents all IPAMs
+type EC2IPAMResourceDiscovery struct {
+	Client       ec2iface.EC2API
+	Region       string
+	DiscoveryIDs []string
+}
+
+func (ipam *EC2IPAMResourceDiscovery) Init(session *session.Session) {
+	ipam.Client = ec2.New(session)
+}
+
+// ResourceName - the simple name of the aws resource
+func (ipam *EC2IPAMResourceDiscovery) ResourceName() string {
+	return "ipam-resource-discovery"
+}
+
+func (ipam *EC2IPAMResourceDiscovery) MaxBatchSize() int {
+	// Tentative batch size to ensure AWS doesn't throttle
+	return 49
+}
+
+// ResourceIdentifiers - The ids of the IPAMs
+func (ipam *EC2IPAMResourceDiscovery) ResourceIdentifiers() []string {
+	return ipam.DiscoveryIDs
+}
+
+func (ipam *EC2IPAMResourceDiscovery) GetAndSetIdentifiers(c context.Context, configObj config.Config) ([]string, error) {
+	identifiers, err := ipam.getAll(c, configObj)
+	if err != nil {
+		return nil, err
+	}
+
+	ipam.DiscoveryIDs = awsgo.StringValueSlice(identifiers)
+	return ipam.DiscoveryIDs, nil
+}
+
+// Nuke - nuke 'em all!!!
+func (ipam *EC2IPAMResourceDiscovery) Nuke(identifiers []string) error {
+	if err := ipam.nukeAll(awsgo.StringSlice(identifiers)); err != nil {
+		return errors.WithStackTrace(err)
+	}
+
+	return nil
+}
diff --git a/aws/resources/ec2_ipam_scope.go b/aws/resources/ec2_ipam_scope.go
new file mode 100644
index 0000000..53418fb
--- /dev/null
+++ b/aws/resources/ec2_ipam_scope.go
@@ -0,0 +1,150 @@
+package resources
+
+import (
+	"context"
+	"time"
+
+	"github.com/aws/aws-sdk-go/aws"
+	awsgo "github.com/aws/aws-sdk-go/aws"
+	"github.com/aws/aws-sdk-go/service/ec2"
+	"github.com/gruntwork-io/cloud-nuke/config"
+	"github.com/gruntwork-io/cloud-nuke/logging"
+	"github.com/gruntwork-io/cloud-nuke/report"
+	"github.com/gruntwork-io/cloud-nuke/util"
+	"github.com/gruntwork-io/go-commons/errors"
+)
+
+func (scope *EC2IpamScopes) setFirstSeenTag(ipam ec2.IpamScope, value time.Time) error {
+	_, err := scope.Client.CreateTags(&ec2.CreateTagsInput{
+		Resources: []*string{ipam.IpamScopeId},
+		Tags: []*ec2.Tag{
+			{
+				Key:   awsgo.String(util.FirstSeenTagKey),
+				Value: awsgo.String(util.FormatTimestamp(value)),
+			},
+		},
+	})
+	if err != nil {
+		return errors.WithStackTrace(err)
+	}
+
+	return nil
+}
+
+func (scope *EC2IpamScopes) getFirstSeenTag(ipam ec2.IpamScope) (*time.Time, error) {
+	tags := ipam.Tags
+	for _, tag := range tags {
+		if util.IsFirstSeenTag(tag.Key) {
+			firstSeenTime, err := util.ParseTimestamp(tag.Value)
+			if err != nil {
+				return nil, errors.WithStackTrace(err)
+			}
+
+			return firstSeenTime, nil
+		}
+	}
+
+	return nil, nil
+}
+
+func shouldIncludeIpamScopeID(ipam *ec2.IpamScope, firstSeenTime *time.Time, configObj config.Config) bool {
+	var ipamScopeName string
+	// get the tags as map
+	tagMap := util.ConvertEC2TagsToMap(ipam.Tags)
+	if name, ok := tagMap["Name"]; ok {
+		ipamScopeName = name
+	}
+
+	return configObj.EC2IPAMScope.ShouldInclude(config.ResourceValue{
+		Name: &ipamScopeName,
+		Time: firstSeenTime,
+		Tags: tagMap,
+	})
+}
+
+// Returns a formatted string of IPAM URLs
+func (ec2Scope *EC2IpamScopes) getAll(c context.Context, configObj config.Config) ([]*string, error) {
+	result := []*string{}
+	paginator := func(output *ec2.DescribeIpamScopesOutput, lastPage bool) bool {
+		for _, scope := range output.IpamScopes {
+			// check first seen tag
+			firstSeenTime, err := ec2Scope.getFirstSeenTag(*scope)
+			if err != nil {
+				logging.Errorf(
+					"Unable to retrieve tags for IPAM: %s, with error: %s", *scope.IpamScopeId, err)
+				continue
+			}
+
+			// if the first seen tag is not there, then create one
+			if firstSeenTime == nil {
+				now := time.Now().UTC()
+				firstSeenTime = &now
+				if err := ec2Scope.setFirstSeenTag(*scope, time.Now().UTC()); err != nil {
+					logging.Errorf(
+						"Unable to apply first seen tag IPAM: %s, with error: %s", *scope.IpamScopeId, err)
+					continue
+				}
+			}
+			// Check for include this ipam
+			if shouldIncludeIpamScopeID(scope, firstSeenTime, configObj) {
+				result = append(result, scope.IpamScopeId)
+			}
+		}
+		return !lastPage
+	}
+
+	params := &ec2.DescribeIpamScopesInput{
+		MaxResults: awsgo.Int64(10),
+		Filters: []*ec2.Filter{
+			{
+				Name:   awsgo.String("is-default"),
+				Values: awsgo.StringSlice([]string{"false"}),
+			},
+		},
+	}
+
+	err := ec2Scope.Client.DescribeIpamScopesPages(params, paginator)
+	if err != nil {
+		return nil, errors.WithStackTrace(err)
+	}
+
+	return result, nil
+}
+
+// Deletes all IPAMs
+func (scope *EC2IpamScopes) nukeAll(ids []*string) error {
+	if len(ids) == 0 {
+		logging.Debugf("No IPAM Scopes ID's to nuke in region %s", scope.Region)
+		return nil
+	}
+
+	logging.Debugf("Deleting all IPAM Scopes in region %s", scope.Region)
+	var deletedList []*string
+
+	for _, id := range ids {
+		params := &ec2.DeleteIpamScopeInput{
+			IpamScopeId: id,
+		}
+
+		_, err := scope.Client.DeleteIpamScope(params)
+
+		// Record status of this resource
+		e := report.Entry{
+			Identifier:   aws.StringValue(id),
+			ResourceType: "IPAM Scopes",
+			Error:        err,
+		}
+		report.Record(e)
+
+		if err != nil {
+			logging.Debugf("[Failed] %s", err)
+		} else {
+			deletedList = append(deletedList, id)
+			logging.Debugf("Deleted IPAM Scope: %s", *id)
+		}
+	}
+
+	logging.Debugf("[OK] %d IPAM Scope(s) deleted in %s", len(deletedList), scope.Region)
+
+	return nil
+}
diff --git a/aws/resources/ec2_ipam_scope_types.go b/aws/resources/ec2_ipam_scope_types.go
new file mode 100644
index 0000000..112d73d
--- /dev/null
+++ b/aws/resources/ec2_ipam_scope_types.go
@@ -0,0 +1,57 @@
+package resources
+
+import (
+	"context"
+
+	awsgo "github.com/aws/aws-sdk-go/aws"
+	"github.com/aws/aws-sdk-go/aws/session"
+	"github.com/aws/aws-sdk-go/service/ec2"
+	"github.com/aws/aws-sdk-go/service/ec2/ec2iface"
+	"github.com/gruntwork-io/cloud-nuke/config"
+	"github.com/gruntwork-io/go-commons/errors"
+)
+
+// scope - represents all scopes
+type EC2IpamScopes struct {
+	Client    ec2iface.EC2API
+	Region    string
+	ScopreIDs []string
+}
+
+func (scope *EC2IpamScopes) Init(session *session.Session) {
+	scope.Client = ec2.New(session)
+}
+
+// ResourceName - the simple name of the aws resource
+func (scope *EC2IpamScopes) ResourceName() string {
+	return "ipam-scope"
+}
+
+func (scope *EC2IpamScopes) MaxBatchSize() int {
+	// Tentative batch size to ensure AWS doesn't throttle
+	return 49
+}
+
+// ResourceIdentifiers - The ids of the scopes
+func (scope *EC2IpamScopes) ResourceIdentifiers() []string {
+	return scope.ScopreIDs
+}
+
+func (scope *EC2IpamScopes) GetAndSetIdentifiers(c context.Context, configObj config.Config) ([]string, error) {
+	identifiers, err := scope.getAll(c, configObj)
+	if err != nil {
+		return nil, err
+	}
+
+	scope.ScopreIDs = awsgo.StringValueSlice(identifiers)
+	return scope.ScopreIDs, nil
+}
+
+// Nuke - nuke 'em all!!!
+func (scope *EC2IpamScopes) Nuke(identifiers []string) error {
+	if err := scope.nukeAll(awsgo.StringSlice(identifiers)); err != nil {
+		return errors.WithStackTrace(err)
+	}
+
+	return nil
+}
diff --git a/aws/resources/ec2_ipam_types.go b/aws/resources/ec2_ipam_types.go
new file mode 100644
index 0000000..61625ea
--- /dev/null
+++ b/aws/resources/ec2_ipam_types.go
@@ -0,0 +1,57 @@
+package resources
+
+import (
+	"context"
+
+	awsgo "github.com/aws/aws-sdk-go/aws"
+	"github.com/aws/aws-sdk-go/aws/session"
+	"github.com/aws/aws-sdk-go/service/ec2"
+	"github.com/aws/aws-sdk-go/service/ec2/ec2iface"
+	"github.com/gruntwork-io/cloud-nuke/config"
+	"github.com/gruntwork-io/go-commons/errors"
+)
+
+// IPAM - represents all IPAMs
+type EC2IPAMs struct {
+	Client ec2iface.EC2API
+	Region string
+	IDs    []string
+}
+
+func (ipam *EC2IPAMs) Init(session *session.Session) {
+	ipam.Client = ec2.New(session)
+}
+
+// ResourceName - the simple name of the aws resource
+func (ipam *EC2IPAMs) ResourceName() string {
+	return "ipam"
+}
+
+func (ipam *EC2IPAMs) MaxBatchSize() int {
+	// Tentative batch size to ensure AWS doesn't throttle
+	return 49
+}
+
+// ResourceIdentifiers - The ids of the IPAMs
+func (ipam *EC2IPAMs) ResourceIdentifiers() []string {
+	return ipam.IDs
+}
+
+func (ipam *EC2IPAMs) GetAndSetIdentifiers(c context.Context, configObj config.Config) ([]string, error) {
+	identifiers, err := ipam.getAll(c, configObj)
+	if err != nil {
+		return nil, err
+	}
+
+	ipam.IDs = awsgo.StringValueSlice(identifiers)
+	return ipam.IDs, nil
+}
+
+// Nuke - nuke 'em all!!!
+func (ipam *EC2IPAMs) Nuke(identifiers []string) error {
+	if err := ipam.nukeAll(awsgo.StringSlice(identifiers)); err != nil {
+		return errors.WithStackTrace(err)
+	}
+
+	return nil
+}
diff --git a/aws/resources/ec2_vpc.go b/aws/resources/ec2_vpc.go
index 330f70b..ac02902 100644
--- a/aws/resources/ec2_vpc.go
+++ b/aws/resources/ec2_vpc.go
@@ -4,11 +4,12 @@ import (
 	"context"
 	cerrors "errors"
 	"fmt"
+	"strings"
+	"time"
+
 	"github.com/aws/aws-sdk-go/service/ec2/ec2iface"
 	"github.com/gruntwork-io/cloud-nuke/util"
 	"github.com/pterm/pterm"
-	"strings"
-	"time"
 
 	"github.com/gruntwork-io/cloud-nuke/telemetry"
 	commonTelemetry "github.com/gruntwork-io/go-commons/telemetry"
@@ -89,7 +90,7 @@ func (v *EC2VPCs) getAll(c context.Context, configObj config.Config) ([]*string,
 
 		if configObj.VPC.ShouldInclude(config.ResourceValue{
 			Time: firstSeenTime,
-			Name: GetEC2ResourceNameTagValue(vpc.Tags),
+			Name: util.GetEC2ResourceNameTagValue(vpc.Tags),
 		}) {
 			ids = append(ids, vpc.VpcId)
 		}
diff --git a/aws/resources/eip.go b/aws/resources/eip.go
index 7a8b679..c9325e6 100644
--- a/aws/resources/eip.go
+++ b/aws/resources/eip.go
@@ -2,10 +2,11 @@ package resources
 
 import (
 	"context"
+	"time"
+
 	"github.com/gruntwork-io/cloud-nuke/telemetry"
 	"github.com/gruntwork-io/cloud-nuke/util"
 	commonTelemetry "github.com/gruntwork-io/go-commons/telemetry"
-	"time"
 
 	"github.com/aws/aws-sdk-go/aws"
 	awsgo "github.com/aws/aws-sdk-go/aws"
@@ -83,7 +84,7 @@ func (ea *EIPAddresses) getAll(c context.Context, configObj config.Config) ([]*s
 func (ea *EIPAddresses) shouldInclude(address *ec2.Address, firstSeenTime time.Time, configObj config.Config) bool {
 	// If Name is unset, GetEC2ResourceNameTagValue returns error and zero value string
 	// Ignore this error and pass empty string to config.ShouldInclude
-	allocationName := GetEC2ResourceNameTagValue(address.Tags)
+	allocationName := util.GetEC2ResourceNameTagValue(address.Tags)
 	return configObj.ElasticIP.ShouldInclude(config.ResourceValue{
 		Time: &firstSeenTime,
 		Name: allocationName,
diff --git a/config/config.go b/config/config.go
index e935644..2fc9e6f 100644
--- a/config/config.go
+++ b/config/config.go
@@ -39,6 +39,10 @@ type Config struct {
 	EC2DedicatedHosts               ResourceType               `yaml:"EC2DedicatedHosts"`
 	EC2DHCPOption                   ResourceType               `yaml:"EC2DhcpOption"`
 	EC2KeyPairs                     ResourceType               `yaml:"EC2KeyPairs"`
+	EC2IPAM                         ResourceType               `yaml:"EC2IPAM"`
+	EC2IPAMPool                     ResourceType               `yaml:"EC2IPAMPool"`
+	EC2IPAMResourceDiscovery        ResourceType               `yaml:"EC2IPAMResourceDiscovery"`
+	EC2IPAMScope                    ResourceType               `yaml:"EC2IPAMScope"`
 	ECRRepository                   ResourceType               `yaml:"ECRRepository"`
 	ECSCluster                      ResourceType               `yaml:"ECSCluster"`
 	ECSService                      ResourceType               `yaml:"ECSService"`
diff --git a/go.mod b/go.mod
index b4e319d..5c2fb5a 100644
--- a/go.mod
+++ b/go.mod
@@ -3,8 +3,8 @@ module github.com/gruntwork-io/cloud-nuke
 go 1.21
 
 require (
-	github.com/aws/aws-sdk-go v1.44.304
-	github.com/aws/aws-sdk-go-v2 v1.18.1
+	github.com/aws/aws-sdk-go v1.49.13
+	github.com/aws/aws-sdk-go-v2 v1.24.0
 	github.com/charmbracelet/lipgloss v0.6.0
 	github.com/gruntwork-io/go-commons v0.17.0
 	github.com/gruntwork-io/gruntwork-cli v0.7.0
@@ -19,7 +19,7 @@ require (
 require (
 	atomicgo.dev/cursor v0.1.1 // indirect
 	atomicgo.dev/keyboard v0.2.8 // indirect
-	github.com/aws/smithy-go v1.13.5 // indirect
+	github.com/aws/smithy-go v1.19.0 // indirect
 	github.com/containerd/console v1.0.3 // indirect
 	github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
 	github.com/davecgh/go-spew v1.1.1 // indirect
@@ -41,7 +41,6 @@ require (
 	github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 // indirect
 	github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
 	golang.org/x/exp v0.0.0-20221106115401-f9659909a136 // indirect
-	golang.org/x/net v0.17.0 // indirect
 	golang.org/x/sys v0.13.0 // indirect
 	golang.org/x/term v0.13.0 // indirect
 	golang.org/x/text v0.13.0 // indirect
diff --git a/go.sum b/go.sum
index 18e1f47..8f9efe1 100644
--- a/go.sum
+++ b/go.sum
@@ -13,12 +13,12 @@ github.com/MarvinJWendt/testza v0.3.0/go.mod h1:eFcL4I0idjtIx8P9C6KkAuLgATNKpX4/
 github.com/MarvinJWendt/testza v0.4.2 h1:Vbw9GkSB5erJI2BPnBL9SVGV9myE+XmUSFahBGUhW2Q=
 github.com/MarvinJWendt/testza v0.4.2/go.mod h1:mSdhXiKH8sg/gQehJ63bINcCKp7RtYewEjXsvsVUPbE=
 github.com/atomicgo/cursor v0.0.1/go.mod h1:cBON2QmmrysudxNBFthvMtN32r3jxVRIvzkUiF/RuIk=
-github.com/aws/aws-sdk-go v1.44.304 h1:crcJBVeewWcVAXDQChzJWZYGFq9i1TYLycAtQ6Xpi4c=
-github.com/aws/aws-sdk-go v1.44.304/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI=
-github.com/aws/aws-sdk-go-v2 v1.18.1 h1:+tefE750oAb7ZQGzla6bLkOwfcQCEtC5y2RqoqCeqKo=
-github.com/aws/aws-sdk-go-v2 v1.18.1/go.mod h1:uzbQtefpm44goOPmdKyAlXSNcwlRgF3ePWVW6EtJvvw=
-github.com/aws/smithy-go v1.13.5 h1:hgz0X/DX0dGqTYpGALqXJoRKRj5oQ7150i5FdTePzO8=
-github.com/aws/smithy-go v1.13.5/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA=
+github.com/aws/aws-sdk-go v1.49.13 h1:f4mGztsgnx2dR9r8FQYa9YW/RsKb+N7bgef4UGrOW1Y=
+github.com/aws/aws-sdk-go v1.49.13/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk=
+github.com/aws/aws-sdk-go-v2 v1.24.0 h1:890+mqQ+hTpNuw0gGP6/4akolQkSToDJgHfQE7AwGuk=
+github.com/aws/aws-sdk-go-v2 v1.24.0/go.mod h1:LNh45Br1YAkEKaAqvmE1m8FUx6a5b/V0oAKV7of29b4=
+github.com/aws/smithy-go v1.19.0 h1:KWFKQV80DpP3vJrrA9sVAHQ5gc2z8i4EzrLhLlWXcBM=
+github.com/aws/smithy-go v1.19.0/go.mod h1:NukqUGpCZIILqqiV0NIjeFh24kd/FAa4beRb6nbIUPE=
 github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
 github.com/charmbracelet/lipgloss v0.6.0 h1:1StyZB9vBSOyuZxQUcUwGr17JmojPNm87inij9N3wJY=
 github.com/charmbracelet/lipgloss v0.6.0/go.mod h1:tHh2wr34xcHjC2HCXIlGSG1jaDF0S0atAUvBMP6Ppuk=
@@ -82,7 +82,6 @@ github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s=
 github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8=
 github.com/muesli/termenv v0.11.1-0.20220204035834-5ac8409525e0 h1:STjmj0uFfRryL9fzRA/OupNppeAID6QJYPMavTL7jtY=
 github.com/muesli/termenv v0.11.1-0.20220204035834-5ac8409525e0/go.mod h1:Bd5NYQ7pd+SrtBSrSNoBBmXlcY8+Xj4BMJgh8qcZrvs=
-github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 github.com/pterm/pterm v0.12.27/go.mod h1:PhQ89w4i95rhgE+xedAoqous6K9X+r6aSOI2eFF7DZI=
@@ -127,21 +126,10 @@ github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 h1:QldyIu/L63oPpyvQmHg
 github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778/go.mod h1:2MuV+tbUrU1zIOPMxZ5EncGwgmMJsa+9ucAQZXxsObs=
 github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU=
 github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8=
-github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
-golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
-golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
 golang.org/x/exp v0.0.0-20221106115401-f9659909a136 h1:Fq7F/w7MAa1KJ5bt2aJ62ihqp9HDcRuyILskkpIAurw=
 golang.org/x/exp v0.0.0-20221106115401-f9659909a136/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
-golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
-golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
-golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
-golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
 golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
 golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
-golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -152,28 +140,17 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc
 golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20211013075003-97ac67df715c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
 golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
 golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
-golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
 golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek=
 golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
-golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
-golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
-golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
 golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
 golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
 golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
-golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
-golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
 gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
diff --git a/util/tag.go b/util/tag.go
index 29858bd..3ab9960 100644
--- a/util/tag.go
+++ b/util/tag.go
@@ -61,3 +61,12 @@ func ConvertRDSTagsToMap(tags []*rds.Tag) map[string]string {
 
 	return tagMap
 }
+
+func GetEC2ResourceNameTagValue(tags []*ec2.Tag) *string {
+	tagMap := ConvertEC2TagsToMap(tags)
+
+	if name, ok := tagMap["Name"]; ok {
+		return &name
+	}
+	return nil
+}


Test patch:
diff --git a/aws/resources/ec2_ipam_byoasn_test.go b/aws/resources/ec2_ipam_byoasn_test.go
new file mode 100644
index 0000000..8c3bac5
--- /dev/null
+++ b/aws/resources/ec2_ipam_byoasn_test.go
@@ -0,0 +1,86 @@
+package resources
+
+import (
+	"context"
+	"testing"
+
+	"github.com/aws/aws-sdk-go-v2/aws"
+	awsgo "github.com/aws/aws-sdk-go/aws"
+	"github.com/aws/aws-sdk-go/service/ec2"
+	"github.com/aws/aws-sdk-go/service/ec2/ec2iface"
+	"github.com/gruntwork-io/cloud-nuke/config"
+	"github.com/stretchr/testify/require"
+)
+
+type mockedIPAMByoASN struct {
+	ec2iface.EC2API
+	DescribeIpamByoasnOutput     ec2.DescribeIpamByoasnOutput
+	DisassociateIpamByoasnOutput ec2.DisassociateIpamByoasnOutput
+}
+
+func (m mockedIPAMByoASN) DescribeIpamByoasn(params *ec2.DescribeIpamByoasnInput) (*ec2.DescribeIpamByoasnOutput, error) {
+	return &m.DescribeIpamByoasnOutput, nil
+}
+func (m mockedIPAMByoASN) DisassociateIpamByoasn(params *ec2.DisassociateIpamByoasnInput) (*ec2.DisassociateIpamByoasnOutput, error) {
+	return &m.DisassociateIpamByoasnOutput, nil
+}
+
+func TestIPAMByoASN_GetAll(t *testing.T) {
+	t.Parallel()
+
+	var (
+		testId1   = "ipam-byoasn-0dfc56f901b2c3462"
+		testId2   = "ipam-byoasn-0dfc56f901b2c3463"
+		testName1 = "test-ipam-byoasn-id1"
+		testName2 = "test-ipam-byoasn-id2"
+	)
+
+	ipam := EC2IPAMByoasn{
+		Client: mockedIPAMByoASN{
+			DescribeIpamByoasnOutput: ec2.DescribeIpamByoasnOutput{
+				Byoasns: []*ec2.Byoasn{
+					{
+						Asn:    aws.String(testId1),
+						IpamId: aws.String(testName1),
+					},
+					{
+						Asn:    aws.String(testId2),
+						IpamId: aws.String(testName2),
+					},
+				},
+			},
+		},
+	}
+
+	tests := map[string]struct {
+		configObj config.ResourceType
+		expected  []string
+	}{
+		"emptyFilter": {
+			configObj: config.ResourceType{},
+			expected:  []string{testId1, testId2},
+		},
+	}
+	for name, tc := range tests {
+		t.Run(name, func(t *testing.T) {
+			ids, err := ipam.getAll(context.Background(), config.Config{
+				EC2IPAM: tc.configObj,
+			})
+			require.NoError(t, err)
+			require.Equal(t, tc.expected, awsgo.StringValueSlice(ids))
+		})
+	}
+}
+
+func TestIPAMByoASN_NukeAll(t *testing.T) {
+	t.Parallel()
+
+	ipam := EC2IPAMByoasn{
+		Client: mockedIPAMByoASN{
+			DisassociateIpamByoasnOutput: ec2.DisassociateIpamByoasnOutput{},
+		},
+	}
+
+	err := ipam.nukeAll([]*string{aws.String("test")})
+	require.NoError(t, err)
+}
diff --git a/aws/resources/ec2_ipam_custom_allocation_test.go b/aws/resources/ec2_ipam_custom_allocation_test.go
new file mode 100644
index 0000000..51718f3
--- /dev/null
+++ b/aws/resources/ec2_ipam_custom_allocation_test.go
@@ -0,0 +1,139 @@
+package resources
+
+import (
+	"context"
+	"testing"
+
+	"github.com/aws/aws-sdk-go/aws"
+	awsgo "github.com/aws/aws-sdk-go/aws"
+	"github.com/aws/aws-sdk-go/service/ec2"
+	"github.com/aws/aws-sdk-go/service/ec2/ec2iface"
+	"github.com/gruntwork-io/cloud-nuke/config"
+	"github.com/stretchr/testify/require"
+)
+
+type mockedIPAMCustomAllocations struct {
+	ec2iface.EC2API
+	GetIpamPoolAllocationsOutput    ec2.GetIpamPoolAllocationsOutput
+	ReleaseIpamPoolAllocationOutput ec2.ReleaseIpamPoolAllocationOutput
+	DescribeIpamPoolsOutput         ec2.DescribeIpamPoolsOutput
+}
+
+func (m mockedIPAMCustomAllocations) GetIpamPoolAllocationsPages(input *ec2.GetIpamPoolAllocationsInput, callback func(*ec2.GetIpamPoolAllocationsOutput, bool) bool) error {
+	callback(&m.GetIpamPoolAllocationsOutput, true)
+	return nil
+}
+
+func (m mockedIPAMCustomAllocations) DescribeIpamPoolsPages(input *ec2.DescribeIpamPoolsInput, callback func(*ec2.DescribeIpamPoolsOutput, bool) bool) error {
+	callback(&m.DescribeIpamPoolsOutput, true)
+	return nil
+}
+
+func (m mockedIPAMCustomAllocations) GetIpamPoolAllocations(params *ec2.GetIpamPoolAllocationsInput) (*ec2.GetIpamPoolAllocationsOutput, error) {
+	return &m.GetIpamPoolAllocationsOutput, nil
+}
+
+func (m mockedIPAMCustomAllocations) ReleaseIpamPoolAllocation(params *ec2.ReleaseIpamPoolAllocationInput) (*ec2.ReleaseIpamPoolAllocationOutput, error) {
+	return &m.ReleaseIpamPoolAllocationOutput, nil
+}
+
+func TestIPAMCustomAllocation_GetAll(t *testing.T) {
+	t.Parallel()
+
+	var (
+		testId1 = "ipam-pool-alloc-0a3b799e43ee044cf8750f69b8329ee27"
+		testId2 = "ipam-pool-alloc-0a3b799e43ee044cf8750f69b8329ee28"
+
+		testPool1 = "ipam-pool-0a3b799e43ee044cf8750f69b8329ee28"
+	)
+
+	ipam := EC2IPAMCustomAllocation{
+		Client: mockedIPAMCustomAllocations{
+			DescribeIpamPoolsOutput: ec2.DescribeIpamPoolsOutput{
+				IpamPools: []*ec2.IpamPool{
+					{
+						IpamPoolId: aws.String(testPool1),
+					},
+				},
+			},
+			GetIpamPoolAllocationsOutput: ec2.GetIpamPoolAllocationsOutput{
+				IpamPoolAllocations: []*ec2.IpamPoolAllocation{
+					{
+
+						Cidr:                 aws.String("10.0.0.0/24"),
+						IpamPoolAllocationId: aws.String(testId1),
+						ResourceType:         aws.String("custom"),
+					},
+					{
+						Cidr:                 aws.String("10.0.0.16/24"),
+						IpamPoolAllocationId: aws.String(testId2),
+						ResourceType:         aws.String("custom"),
+					},
+				},
+			},
+		},
+		PoolAndAllocationMap: map[string]string{},
+	}
+
+	tests := map[string]struct {
+		configObj config.ResourceType
+		expected  []string
+	}{
+		"emptyFilter": {
+			configObj: config.ResourceType{},
+			expected:  []string{testId1, testId2},
+		},
+	}
+	for name, tc := range tests {
+		t.Run(name, func(t *testing.T) {
+			ids, err := ipam.getAll(context.Background(), config.Config{
+				EC2IPAM: tc.configObj,
+			})
+			require.NoError(t, err)
+			require.Equal(t, tc.expected, awsgo.StringValueSlice(ids))
+		})
+	}
+}
+
+func TestIPAMCustomAllocation_NukeAll(t *testing.T) {
+	t.Parallel()
+
+	var (
+		testId1 = "ipam-pool-alloc-0a3b799e43ee044cf8750f69b8329ee27"
+		testId2 = "ipam-pool-alloc-0a3b799e43ee044cf8750f69b8329ee28"
+
+		testPool1 = "ipam-pool-0a3b799e43ee044cf8750f69b8329ee28"
+	)
+
+	ipam := EC2IPAMCustomAllocation{
+		Client: mockedIPAMCustomAllocations{
+			DescribeIpamPoolsOutput: ec2.DescribeIpamPoolsOutput{
+				IpamPools: []*ec2.IpamPool{
+					{
+						IpamPoolId: aws.String(testPool1),
+					},
+				},
+			},
+			GetIpamPoolAllocationsOutput: ec2.GetIpamPoolAllocationsOutput{
+				IpamPoolAllocations: []*ec2.IpamPoolAllocation{
+					{
+
+						Cidr:                 aws.String("10.0.0.0/24"),
+						IpamPoolAllocationId: aws.String(testId1),
+						ResourceType:         aws.String("custom"),
+					},
+					{
+						Cidr:                 aws.String("10.0.0.16/24"),
+						IpamPoolAllocationId: aws.String(testId2),
+						ResourceType:         aws.String("custom"),
+					},
+				},
+			},
+		},
+		PoolAndAllocationMap: map[string]string{
+			"test": testId1,
+		},
+	}
+	err := ipam.nukeAll([]*string{aws.String("test")})
+	require.NoError(t, err)
+}
diff --git a/aws/resources/ec2_ipam_pool_test.go b/aws/resources/ec2_ipam_pool_test.go
new file mode 100644
index 0000000..c10882d
--- /dev/null
+++ b/aws/resources/ec2_ipam_pool_test.go
@@ -0,0 +1,125 @@
+package resources
+
+import (
+	"context"
+	"regexp"
+	"testing"
+	"time"
+
+	"github.com/aws/aws-sdk-go-v2/aws"
+	awsgo "github.com/aws/aws-sdk-go/aws"
+	"github.com/aws/aws-sdk-go/service/ec2"
+	"github.com/aws/aws-sdk-go/service/ec2/ec2iface"
+	"github.com/gruntwork-io/cloud-nuke/config"
+	"github.com/gruntwork-io/cloud-nuke/util"
+	"github.com/stretchr/testify/require"
+)
+
+type mockedIPAMPools struct {
+	ec2iface.EC2API
+	DescribeIpamPoolsOutput ec2.DescribeIpamPoolsOutput
+	DeleteIpamPoolOutput    ec2.DeleteIpamPoolOutput
+}
+
+func (m mockedIPAMPools) DescribeIpamPoolsPages(input *ec2.DescribeIpamPoolsInput, callback func(*ec2.DescribeIpamPoolsOutput, bool) bool) error {
+	callback(&m.DescribeIpamPoolsOutput, true)
+	return nil
+}
+func (m mockedIPAMPools) DeleteIpamPool(params *ec2.DeleteIpamPoolInput) (*ec2.DeleteIpamPoolOutput, error) {
+	return &m.DeleteIpamPoolOutput, nil
+}
+
+func TestIPAMPool_GetAll(t *testing.T) {
+	t.Parallel()
+
+	var (
+		now       = time.Now()
+		testId1   = "ipam-pool-0dfc56f901b2c3462"
+		testId2   = "ipam-pool-0dfc56f901b2c3463"
+		testName1 = "test-ipam-pool-id1"
+		testName2 = "test-ipam-pool-id2"
+	)
+
+	ipam := EC2IPAMPool{
+		Client: mockedIPAMPools{
+			DescribeIpamPoolsOutput: ec2.DescribeIpamPoolsOutput{
+				IpamPools: []*ec2.IpamPool{
+					{
+						IpamPoolId: aws.String(testId1),
+						Tags: []*ec2.Tag{
+							{
+								Key:   awsgo.String("Name"),
+								Value: awsgo.String(testName1),
+							},
+							{
+								Key:   awsgo.String(util.FirstSeenTagKey),
+								Value: awsgo.String(util.FormatTimestamp(now)),
+							},
+						},
+					},
+					{
+						IpamPoolId: aws.String(testId2),
+						Tags: []*ec2.Tag{
+							{
+								Key:   awsgo.String("Name"),
+								Value: awsgo.String(testName2),
+							},
+							{
+								Key:   awsgo.String(util.FirstSeenTagKey),
+								Value: awsgo.String(util.FormatTimestamp(now)),
+							},
+						},
+					},
+				},
+			},
+		},
+	}
+
+	tests := map[string]struct {
+		configObj config.ResourceType
+		expected  []string
+	}{
+		"emptyFilter": {
+			configObj: config.ResourceType{},
+			expected:  []string{testId1, testId2},
+		},
+		"nameExclusionFilter": {
+			configObj: config.ResourceType{
+				ExcludeRule: config.FilterRule{
+					NamesRegExp: []config.Expression{{
+						RE: *regexp.MustCompile(testName1),
+					}}},
+			},
+			expected: []string{testId2},
+		},
+		"timeAfterExclusionFilter": {
+			configObj: config.ResourceType{
+				ExcludeRule: config.FilterRule{
+					TimeAfter: aws.Time(now.Add(-1 * time.Hour)),
+				}},
+			expected: []string{},
+		},
+	}
+	for name, tc := range tests {
+		t.Run(name, func(t *testing.T) {
+			ids, err := ipam.getAll(context.Background(), config.Config{
+				EC2IPAMPool: tc.configObj,
+			})
+			require.NoError(t, err)
+			require.Equal(t, tc.expected, awsgo.StringValueSlice(ids))
+		})
+	}
+}
+
+func TestIPAMPool_NukeAll(t *testing.T) {
+	t.Parallel()
+
+	ipam := EC2IPAMPool{
+		Client: mockedIPAMPools{
+			DeleteIpamPoolOutput: ec2.DeleteIpamPoolOutput{},
+		},
+	}
+
+	err := ipam.nukeAll([]*string{aws.String("test")})
+	require.NoError(t, err)
+}
diff --git a/aws/resources/ec2_ipam_resource_discovery_test.go b/aws/resources/ec2_ipam_resource_discovery_test.go
new file mode 100644
index 0000000..cf01a13
--- /dev/null
+++ b/aws/resources/ec2_ipam_resource_discovery_test.go
@@ -0,0 +1,125 @@
+package resources
+
+import (
+	"context"
+	"regexp"
+	"testing"
+	"time"
+
+	"github.com/aws/aws-sdk-go-v2/aws"
+	awsgo "github.com/aws/aws-sdk-go/aws"
+	"github.com/aws/aws-sdk-go/service/ec2"
+	"github.com/aws/aws-sdk-go/service/ec2/ec2iface"
+	"github.com/gruntwork-io/cloud-nuke/config"
+	"github.com/gruntwork-io/cloud-nuke/util"
+	"github.com/stretchr/testify/require"
+)
+
+type mockedIPAMResourceDiscovery struct {
+	ec2iface.EC2API
+	DescribeIpamResourceDiscoveriesOutput ec2.DescribeIpamResourceDiscoveriesOutput
+	DeleteIpamResourceDiscoveryOutput     ec2.DeleteIpamResourceDiscoveryOutput
+}
+
+func (m mockedIPAMResourceDiscovery) DescribeIpamResourceDiscoveriesPages(input *ec2.DescribeIpamResourceDiscoveriesInput, callback func(*ec2.DescribeIpamResourceDiscoveriesOutput, bool) bool) error {
+	callback(&m.DescribeIpamResourceDiscoveriesOutput, true)
+	return nil
+}
+func (m mockedIPAMResourceDiscovery) DeleteIpamResourceDiscovery(params *ec2.DeleteIpamResourceDiscoveryInput) (*ec2.DeleteIpamResourceDiscoveryOutput, error) {
+	return &m.DeleteIpamResourceDiscoveryOutput, nil
+}
+
+func TestIPAMRDiscovery_GetAll(t *testing.T) {
+	t.Parallel()
+
+	var (
+		now       = time.Now()
+		testId1   = "ipam-res-disco-0dfc56f901b2c3462"
+		testId2   = "ipam-res-disco-0dfc56f901b2c3463"
+		testName1 = "test-ipam-resource-id1"
+		testName2 = "test-ipam-resource-id2"
+	)
+
+	ipam := EC2IPAMResourceDiscovery{
+		Client: mockedIPAMResourceDiscovery{
+			DescribeIpamResourceDiscoveriesOutput: ec2.DescribeIpamResourceDiscoveriesOutput{
+				IpamResourceDiscoveries: []*ec2.IpamResourceDiscovery{
+					{
+						IpamResourceDiscoveryId: aws.String(testId1),
+						Tags: []*ec2.Tag{
+							{
+								Key:   awsgo.String("Name"),
+								Value: awsgo.String(testName1),
+							},
+							{
+								Key:   awsgo.String(util.FirstSeenTagKey),
+								Value: awsgo.String(util.FormatTimestamp(now)),
+							},
+						},
+					},
+					{
+						IpamResourceDiscoveryId: aws.String(testId2),
+						Tags: []*ec2.Tag{
+							{
+								Key:   awsgo.String("Name"),
+								Value: awsgo.String(testName2),
+							},
+							{
+								Key:   awsgo.String(util.FirstSeenTagKey),
+								Value: awsgo.String(util.FormatTimestamp(now)),
+							},
+						},
+					},
+				},
+			},
+		},
+	}
+
+	tests := map[string]struct {
+		configObj config.ResourceType
+		expected  []string
+	}{
+		"emptyFilter": {
+			configObj: config.ResourceType{},
+			expected:  []string{testId1, testId2},
+		},
+		"nameExclusionFilter": {
+			configObj: config.ResourceType{
+				ExcludeRule: config.FilterRule{
+					NamesRegExp: []config.Expression{{
+						RE: *regexp.MustCompile(testName1),
+					}}},
+			},
+			expected: []string{testId2},
+		},
+		"timeAfterExclusionFilter": {
+			configObj: config.ResourceType{
+				ExcludeRule: config.FilterRule{
+					TimeAfter: aws.Time(now.Add(-1 * time.Hour)),
+				}},
+			expected: []string{},
+		},
+	}
+	for name, tc := range tests {
+		t.Run(name, func(t *testing.T) {
+			ids, err := ipam.getAll(context.Background(), config.Config{
+				EC2IPAMResourceDiscovery: tc.configObj,
+			})
+			require.NoError(t, err)
+			require.Equal(t, tc.expected, awsgo.StringValueSlice(ids))
+		})
+	}
+}
+
+func TestIPAMRDiscovery_NukeAll(t *testing.T) {
+	t.Parallel()
+
+	ipam := EC2IPAMResourceDiscovery{
+		Client: mockedIPAMResourceDiscovery{
+			DeleteIpamResourceDiscoveryOutput: ec2.DeleteIpamResourceDiscoveryOutput{},
+		},
+	}
+
+	err := ipam.nukeAll([]*string{aws.String("test")})
+	require.NoError(t, err)
+}
diff --git a/aws/resources/ec2_ipam_scope_test.go b/aws/resources/ec2_ipam_scope_test.go
new file mode 100644
index 0000000..541d2de
--- /dev/null
+++ b/aws/resources/ec2_ipam_scope_test.go
@@ -0,0 +1,126 @@
+package resources
+
+import (
+	"context"
+	"regexp"
+	"testing"
+	"time"
+
+	"github.com/aws/aws-sdk-go-v2/aws"
+	awsgo "github.com/aws/aws-sdk-go/aws"
+	"github.com/aws/aws-sdk-go/service/ec2"
+	"github.com/aws/aws-sdk-go/service/ec2/ec2iface"
+	"github.com/gruntwork-io/cloud-nuke/config"
+	"github.com/gruntwork-io/cloud-nuke/util"
+	"github.com/stretchr/testify/require"
+)
+
+type mockedIPAMScope struct {
+	ec2iface.EC2API
+	DescribeIpamScopesOutput ec2.DescribeIpamScopesOutput
+	DeleteIpamScopeOutput    ec2.DeleteIpamScopeOutput
+}
+
+func (m mockedIPAMScope) DescribeIpamScopesPages(input *ec2.DescribeIpamScopesInput, callback func(*ec2.DescribeIpamScopesOutput, bool) bool) error {
+	callback(&m.DescribeIpamScopesOutput, true)
+	return nil
+}
+func (m mockedIPAMScope) DeleteIpamScope(params *ec2.DeleteIpamScopeInput) (*ec2.DeleteIpamScopeOutput, error) {
+	return &m.DeleteIpamScopeOutput, nil
+}
+
+func TestIPAMScope_GetAll(t *testing.T) {
+	t.Parallel()
+
+	var (
+		now       = time.Now()
+		testId1   = "ipam-scope-0dfc56f901b2c3462"
+		testId2   = "ipam-scope-0dfc56f901b2c3463"
+		testName1 = "test-ipam-id1"
+		testName2 = "test-ipam-id2"
+	)
+
+	ipam := EC2IpamScopes{
+		Client: mockedIPAMScope{
+			DescribeIpamScopesOutput: ec2.DescribeIpamScopesOutput{
+				IpamScopes: []*ec2.IpamScope{
+					{
+						IpamScopeId: &testId1,
+						Tags: []*ec2.Tag{
+							{
+								Key:   awsgo.String("Name"),
+								Value: awsgo.String(testName1),
+							},
+							{
+								Key:   awsgo.String(util.FirstSeenTagKey),
+								Value: awsgo.String(util.FormatTimestamp(now)),
+							},
+						},
+					},
+					{
+						IpamScopeId: &testId2,
+						Tags: []*ec2.Tag{
+							{
+								Key:   awsgo.String("Name"),
+								Value: awsgo.String(testName2),
+							},
+							{
+								Key:   awsgo.String(util.FirstSeenTagKey),
+								Value: awsgo.String(util.FormatTimestamp(now)),
+							},
+						},
+					},
+				},
+			},
+		},
+	}
+
+	tests := map[string]struct {
+		configObj config.ResourceType
+		expected  []string
+	}{
+		"emptyFilter": {
+			configObj: config.ResourceType{},
+			expected:  []string{testId1, testId2},
+		},
+		"nameExclusionFilter": {
+			configObj: config.ResourceType{
+				ExcludeRule: config.FilterRule{
+					NamesRegExp: []config.Expression{{
+						RE: *regexp.MustCompile(testName1),
+					}}},
+			},
+			expected: []string{testId2},
+		},
+		"timeAfterExclusionFilter": {
+			configObj: config.ResourceType{
+				ExcludeRule: config.FilterRule{
+					TimeAfter: aws.Time(now.Add(-1 * time.Hour)),
+				}},
+			expected: []string{},
+		},
+	}
+
+	for name, tc := range tests {
+		t.Run(name, func(t *testing.T) {
+			ids, err := ipam.getAll(context.Background(), config.Config{
+				EC2IPAMScope: tc.configObj,
+			})
+			require.NoError(t, err)
+			require.Equal(t, tc.expected, awsgo.StringValueSlice(ids))
+		})
+	}
+}
+
+func TestIPAMScope_NukeAll(t *testing.T) {
+	t.Parallel()
+
+	ipam := EC2IpamScopes{
+		Client: mockedIPAMScope{
+			DeleteIpamScopeOutput: ec2.DeleteIpamScopeOutput{},
+		},
+	}
+
+	err := ipam.nukeAll([]*string{aws.String("test")})
+	require.NoError(t, err)
+}
diff --git a/aws/resources/ec2_ipam_test.go b/aws/resources/ec2_ipam_test.go
new file mode 100644
index 0000000..139c99a
--- /dev/null
+++ b/aws/resources/ec2_ipam_test.go
@@ -0,0 +1,230 @@
+package resources
+
+import (
+	"context"
+	"regexp"
+	"testing"
+	"time"
+
+	"github.com/aws/aws-sdk-go-v2/aws"
+	awsgo "github.com/aws/aws-sdk-go/aws"
+	"github.com/aws/aws-sdk-go/service/ec2"
+	"github.com/aws/aws-sdk-go/service/ec2/ec2iface"
+	"github.com/gruntwork-io/cloud-nuke/config"
+	"github.com/gruntwork-io/cloud-nuke/util"
+	"github.com/stretchr/testify/require"
+)
+
+type mockedIPAM struct {
+	ec2iface.EC2API
+	DescribeIpamsOutput          ec2.DescribeIpamsOutput
+	DeleteIpamOutput             ec2.DeleteIpamOutput
+	DescribeIpamScopesOutput     ec2.DescribeIpamScopesOutput
+	DescribeIpamPoolsOutput      ec2.DescribeIpamPoolsOutput
+	GetIpamPoolCidrsOutput       ec2.GetIpamPoolCidrsOutput
+	DeprovisionIpamPoolCidrOuput ec2.DeprovisionIpamPoolCidrOutput
+	GetIpamPoolAllocationsOutput ec2.GetIpamPoolAllocationsOutput
+}
+
+func (m mockedIPAM) DeleteIpamPool(input *ec2.DeleteIpamPoolInput) (*ec2.DeleteIpamPoolOutput, error) {
+	return nil, nil
+}
+func (m mockedIPAM) ReleaseIpamPoolAllocation(input *ec2.ReleaseIpamPoolAllocationInput) (*ec2.ReleaseIpamPoolAllocationOutput, error) {
+	return nil, nil
+}
+func (m mockedIPAM) GetIpamPoolAllocations(input *ec2.GetIpamPoolAllocationsInput) (*ec2.GetIpamPoolAllocationsOutput, error) {
+	return &m.GetIpamPoolAllocationsOutput, nil
+}
+func (m mockedIPAM) DeprovisionIpamPoolCidr(input *ec2.DeprovisionIpamPoolCidrInput) (*ec2.DeprovisionIpamPoolCidrOutput, error) {
+	return &m.DeprovisionIpamPoolCidrOuput, nil
+}
+func (m mockedIPAM) GetIpamPoolCidrs(input *ec2.GetIpamPoolCidrsInput) (*ec2.GetIpamPoolCidrsOutput, error) {
+	return &m.GetIpamPoolCidrsOutput, nil
+}
+func (m mockedIPAM) DescribeIpamScopes(input *ec2.DescribeIpamScopesInput) (*ec2.DescribeIpamScopesOutput, error) {
+	return &m.DescribeIpamScopesOutput, nil
+}
+func (m mockedIPAM) DescribeIpamPools(input *ec2.DescribeIpamPoolsInput) (*ec2.DescribeIpamPoolsOutput, error) {
+	return &m.DescribeIpamPoolsOutput, nil
+}
+func (m mockedIPAM) DescribeIpams(input *ec2.DescribeIpamsInput) (*ec2.DescribeIpamsOutput, error) {
+	return &m.DescribeIpamsOutput, nil
+}
+func (m mockedIPAM) DescribeIpamsPages(input *ec2.DescribeIpamsInput, callback func(*ec2.DescribeIpamsOutput, bool) bool) error {
+	callback(&m.DescribeIpamsOutput, true)
+	return nil
+}
+func (m mockedIPAM) DeleteIpam(params *ec2.DeleteIpamInput) (*ec2.DeleteIpamOutput, error) {
+	return &m.DeleteIpamOutput, nil
+}
+
+func TestIPAM_GetAll(t *testing.T) {
+	t.Parallel()
+
+	var (
+		now       = time.Now()
+		testId1   = "ipam-0dfc56f901b2c3462"
+		testId2   = "ipam-0dfc56f901b2c3463"
+		testName1 = "test-ipam-id1"
+		testName2 = "test-ipam-id2"
+	)
+
+	ipam := EC2IPAMs{
+		Client: mockedIPAM{
+			DescribeIpamsOutput: ec2.DescribeIpamsOutput{
+				Ipams: []*ec2.Ipam{
+					{
+						IpamId: &testId1,
+						Tags: []*ec2.Tag{
+							{
+								Key:   awsgo.String("Name"),
+								Value: awsgo.String(testName1),
+							},
+							{
+								Key:   awsgo.String(util.FirstSeenTagKey),
+								Value: awsgo.String(util.FormatTimestamp(now)),
+							},
+						},
+					},
+					{
+						IpamId: &testId2,
+						Tags: []*ec2.Tag{
+							{
+								Key:   awsgo.String("Name"),
+								Value: awsgo.String(testName2),
+							},
+							{
+								Key:   awsgo.String(util.FirstSeenTagKey),
+								Value: awsgo.String(util.FormatTimestamp(now)),
+							},
+						},
+					},
+				},
+			},
+		},
+	}
+
+	tests := map[string]struct {
+		configObj config.ResourceType
+		expected  []string
+	}{
+		"emptyFilter": {
+			configObj: config.ResourceType{},
+			expected:  []string{testId1, testId2},
+		},
+		"nameExclusionFilter": {
+			configObj: config.ResourceType{
+				ExcludeRule: config.FilterRule{
+					NamesRegExp: []config.Expression{{
+						RE: *regexp.MustCompile(testName1),
+					}}},
+			},
+			expected: []string{testId2},
+		},
+		"timeAfterExclusionFilter": {
+			configObj: config.ResourceType{
+				ExcludeRule: config.FilterRule{
+					TimeAfter: aws.Time(now.Add(-1 * time.Hour)),
+				}},
+			expected: []string{},
+		},
+	}
+
+	for name, tc := range tests {
+		t.Run(name, func(t *testing.T) {
+			ids, err := ipam.getAll(context.Background(), config.Config{
+				EC2IPAM: tc.configObj,
+			})
+			require.NoError(t, err)
+			require.Equal(t, tc.expected, awsgo.StringValueSlice(ids))
+		})
+	}
+}
+
+func TestIPAM_NukeAll(t *testing.T) {
+	t.Parallel()
+
+	var (
+		now       = time.Now()
+		testId1   = "ipam-0dfc56f901b2c3462"
+		testId2   = "ipam-0dfc56f901b2c3463"
+		testName1 = "test-ipam-id1"
+		testName2 = "test-ipam-id2"
+
+		publicScope = "ipam-scope-001277b300c015f14"
+		// privateScope = "ipam-scope-0d49ce2576b99615a"
+
+		publicScopeARN = "arn:aws:ec2::499213733106:ipam-scope/ipam-scope-001277b300c015f14"
+	)
+
+	ipam := EC2IPAMs{
+		Client: mockedIPAM{
+			DeleteIpamOutput: ec2.DeleteIpamOutput{},
+			DescribeIpamsOutput: ec2.DescribeIpamsOutput{
+				Ipams: []*ec2.Ipam{
+					{
+						IpamId:               &testId1,
+						PublicDefaultScopeId: &publicScope,
+						Tags: []*ec2.Tag{
+							{
+								Key:   awsgo.String("Name"),
+								Value: awsgo.String(testName1),
+							},
+							{
+								Key:   awsgo.String(util.FirstSeenTagKey),
+								Value: awsgo.String(util.FormatTimestamp(now)),
+							},
+						},
+					},
+					{
+						IpamId:               &testId2,
+						PublicDefaultScopeId: &publicScope,
+						Tags: []*ec2.Tag{
+							{
+								Key:   awsgo.String("Name"),
+								Value: awsgo.String(testName2),
+							},
+							{
+								Key:   awsgo.String(util.FirstSeenTagKey),
+								Value: awsgo.String(util.FormatTimestamp(now)),
+							},
+						},
+					},
+				},
+			},
+			DescribeIpamScopesOutput: ec2.DescribeIpamScopesOutput{
+				IpamScopes: []*ec2.IpamScope{
+					{
+						IpamScopeId:  aws.String(publicScope),
+						IpamScopeArn: aws.String(publicScopeARN),
+					},
+				},
+			},
+			DescribeIpamPoolsOutput: ec2.DescribeIpamPoolsOutput{
+				IpamPools: []*ec2.IpamPool{
+					{
+						IpamPoolId: aws.String(testId1),
+					},
+				},
+			},
+			GetIpamPoolCidrsOutput: ec2.GetIpamPoolCidrsOutput{
+				IpamPoolCidrs: []*ec2.IpamPoolCidr{
+					{
+						Cidr: aws.String("10.0.0.0/24"),
+					},
+				},
+			},
+			GetIpamPoolAllocationsOutput: ec2.GetIpamPoolAllocationsOutput{
+				IpamPoolAllocations: []*ec2.IpamPoolAllocation{
+					{
+						ResourceType:         aws.String("custom"),
+						IpamPoolAllocationId: aws.String("ipam-pool-alloc-001277b300c015f14"),
+					},
+				},
+			},
+		},
+	}
+
+	err := ipam.nukeAll([]*string{aws.String("test")})
+	require.NoError(t, err)
+}
diff --git a/config/config_test.go b/config/config_test.go
index 9109a44..ce37388 100644
--- a/config/config_test.go
+++ b/config/config_test.go
@@ -54,6 +54,10 @@ func emptyConfig() *Config {
 		ResourceType{FilterRule{}, FilterRule{}},
 		ResourceType{FilterRule{}, FilterRule{}},
 		ResourceType{FilterRule{}, FilterRule{}},
+		ResourceType{FilterRule{}, FilterRule{}},
+		ResourceType{FilterRule{}, FilterRule{}},
+		ResourceType{FilterRule{}, FilterRule{}},
+		ResourceType{FilterRule{}, FilterRule{}},
 		KMSCustomerKeyResourceType{false, ResourceType{FilterRule{}, FilterRule{}}},
 		ResourceType{FilterRule{}, FilterRule{}},
 		ResourceType{FilterRule{}, FilterRule{}},