Repository: gruntwork-io/cloud-nuke
Pull request #694 (2024-05-03 00:53:34)

Option to not add 'cloud-nuke-first-seen' tag + Refactoring
<!-- 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/675

<!-- 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/aws/aws.go b/aws/aws.go
index 66020c2..10ab59b 100644
--- a/aws/aws.go
+++ b/aws/aws.go
@@ -24,7 +24,6 @@ func GetAllResources(c context.Context, query *Query, configObj config.Config) (
 	configObj.AddExcludeAfterTime(query.ExcludeAfter)
 	configObj.AddIncludeAfterTime(query.IncludeAfter)
 	configObj.AddTimeout(query.Timeout)
-
 	configObj.KMSCustomerKeys.IncludeUnaliasedKeys = query.ListUnaliasedKMSKeys
 
 	// Setting the DefaultOnly field
@@ -35,6 +34,7 @@ func GetAllResources(c context.Context, query *Query, configObj config.Config) (
 		Resources: make(map[string]AwsResources),
 	}
 
+	c = context.WithValue(c, util.ExcludeFirstSeenTagKey, query.ExcludeFirstSeen)
 	spinner, _ := pterm.DefaultSpinner.WithRemoveWhenDone(true).Start()
 	for _, region := range query.Regions {
 		cloudNukeSession := NewSession(region)
diff --git a/aws/query.go b/aws/query.go
index 680ebaf..8fdd954 100644
--- a/aws/query.go
+++ b/aws/query.go
@@ -14,11 +14,16 @@ type Query struct {
 	IncludeAfter         *time.Time
 	ListUnaliasedKMSKeys bool
 	Timeout              *time.Duration
+	ExcludeFirstSeen     bool
 	DefaultOnly          bool
 }
 
 // NewQuery configures and returns a Query struct that can be passed into the InspectResources method
-func NewQuery(regions, excludeRegions, resourceTypes, excludeResourceTypes []string, excludeAfter, includeAfter *time.Time, listUnaliasedKMSKeys bool, timeout *time.Duration, defaultOnly bool) (*Query, error) {
+func NewQuery(regions, excludeRegions, resourceTypes, excludeResourceTypes []string,
+	excludeAfter, includeAfter *time.Time,
+	listUnaliasedKMSKeys bool, timeout *time.Duration,
+	defaultOnly, excludeFirstSeen bool,
+) (*Query, error) {
 	q := &Query{
 		Regions:              regions,
 		ExcludeRegions:       excludeRegions,
@@ -29,6 +34,7 @@ func NewQuery(regions, excludeRegions, resourceTypes, excludeResourceTypes []str
 		ListUnaliasedKMSKeys: listUnaliasedKMSKeys,
 		Timeout:              timeout,
 		DefaultOnly:          defaultOnly,
+		ExcludeFirstSeen:     excludeFirstSeen,
 	}
 
 	validationErr := q.Validate()
diff --git a/aws/resources/ec2_egress_only_igw.go b/aws/resources/ec2_egress_only_igw.go
index 945bdac..8fc38d6 100644
--- a/aws/resources/ec2_egress_only_igw.go
+++ b/aws/resources/ec2_egress_only_igw.go
@@ -15,39 +15,6 @@ import (
 	"github.com/gruntwork-io/go-commons/errors"
 )
 
-func (egigw *EgressOnlyInternetGateway) setFirstSeenTag(eoig ec2.EgressOnlyInternetGateway, value time.Time) error {
-	_, err := egigw.Client.CreateTags(&ec2.CreateTagsInput{
-		Resources: []*string{eoig.EgressOnlyInternetGatewayId},
-		Tags: []*ec2.Tag{
-			{
-				Key:   awsgo.String(util.FirstSeenTagKey),
-				Value: awsgo.String(util.FormatTimestamp(value)),
-			},
-		},
-	})
-	if err != nil {
-		return errors.WithStackTrace(err)
-	}
-
-	return nil
-}
-
-func (egigw *EgressOnlyInternetGateway) getFirstSeenTag(eoig ec2.EgressOnlyInternetGateway) (*time.Time, error) {
-	tags := eoig.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 shouldIncludeEgressOnlyInternetGateway(gateway *ec2.EgressOnlyInternetGateway, firstSeenTime *time.Time, configObj config.Config) bool {
 	var gatewayName string
 	// get the tags as map
@@ -62,8 +29,10 @@ func shouldIncludeEgressOnlyInternetGateway(gateway *ec2.EgressOnlyInternetGatew
 	})
 }
 
-func (egigw *EgressOnlyInternetGateway) getAll(_ context.Context, configObj config.Config) ([]*string, error) {
+func (egigw *EgressOnlyInternetGateway) getAll(c context.Context, configObj config.Config) ([]*string, error) {
 	var result []*string
+	var firstSeenTime *time.Time
+	var err error
 
 	output, err := egigw.Client.DescribeEgressOnlyInternetGateways(&ec2.DescribeEgressOnlyInternetGatewaysInput{})
 	if err != nil {
@@ -71,39 +40,23 @@ func (egigw *EgressOnlyInternetGateway) getAll(_ context.Context, configObj conf
 	}
 
 	for _, igw := range output.EgressOnlyInternetGateways {
-		// check first seen tag
-		firstSeenTime, err := egigw.getFirstSeenTag(*igw)
+		firstSeenTime, err = util.GetOrCreateFirstSeen(c, egigw.Client, igw.EgressOnlyInternetGatewayId, util.ConvertEC2TagsToMap(igw.Tags))
 		if err != nil {
-			logging.Errorf(
-				"Unable to retrieve tags for Egress IGW: %s, with error: %s", *igw.EgressOnlyInternetGatewayId, err)
-			continue
-		}
-
-		// if the first seen tag is not there, then create one
-		if firstSeenTime == nil {
-			now := time.Now().UTC()
-			firstSeenTime = &now
-			if err := egigw.setFirstSeenTag(*igw, time.Now().UTC()); err != nil {
-				logging.Errorf(
-					"Unable to apply first seen tag Egress IGW: %s, with error: %s", *igw.EgressOnlyInternetGatewayId, err)
-				continue
-			}
+			logging.Error("Unable to retrieve tags")
+			return nil, errors.WithStackTrace(err)
 		}
-
 		if shouldIncludeEgressOnlyInternetGateway(igw, firstSeenTime, configObj) {
 			result = append(result, igw.EgressOnlyInternetGatewayId)
 		}
 	}
-
 	// checking the nukable permissions
 	egigw.VerifyNukablePermissions(result, func(id *string) error {
 		_, err := egigw.Client.DeleteEgressOnlyInternetGateway(&ec2.DeleteEgressOnlyInternetGatewayInput{
 			EgressOnlyInternetGatewayId: id,
 			DryRun:                      awsgo.Bool(true),
 		})
-		return err
+		return errors.WithStackTrace(err)
 	})
-
 	return result, nil
 }
 
diff --git a/aws/resources/ec2_endpoints.go b/aws/resources/ec2_endpoints.go
index 90157bf..3694682 100644
--- a/aws/resources/ec2_endpoints.go
+++ b/aws/resources/ec2_endpoints.go
@@ -16,38 +16,6 @@ import (
 	"github.com/gruntwork-io/go-commons/retry"
 )
 
-func (e *EC2Endpoints) setFirstSeenTag(endpoint ec2.VpcEndpoint, value time.Time) error {
-	_, err := e.Client.CreateTags(&ec2.CreateTagsInput{
-		Resources: []*string{endpoint.VpcEndpointId},
-		Tags: []*ec2.Tag{
-			{
-				Key:   awsgo.String(util.FirstSeenTagKey),
-				Value: awsgo.String(util.FormatTimestamp(value)),
-			},
-		},
-	})
-	if err != nil {
-		return errors.WithStackTrace(err)
-	}
-
-	return nil
-}
-
-func (e *EC2Endpoints) getFirstSeenTag(endpoint ec2.VpcEndpoint) (*time.Time, error) {
-	for _, tag := range endpoint.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 ShouldIncludeVpcEndpoint(endpoint *ec2.VpcEndpoint, firstSeenTime *time.Time, configObj config.Config) bool {
 	var endpointName string
 	// get the tags as map
@@ -63,36 +31,26 @@ func ShouldIncludeVpcEndpoint(endpoint *ec2.VpcEndpoint, firstSeenTime *time.Tim
 	})
 }
 
-func (e *EC2Endpoints) getAll(_ context.Context, configObj config.Config) ([]*string, error) {
+func (e *EC2Endpoints) getAll(c context.Context, configObj config.Config) ([]*string, error) {
 	var result []*string
+	var firstSeenTime *time.Time
+	var err error
 	endpoints, err := e.Client.DescribeVpcEndpoints(&ec2.DescribeVpcEndpointsInput{})
 	if err != nil {
 		return nil, errors.WithStackTrace(err)
 	}
 
 	for _, endpoint := range endpoints.VpcEndpoints {
-		// check first seen tag
-		firstSeenTime, err := e.getFirstSeenTag(*endpoint)
+		firstSeenTime, err = util.GetOrCreateFirstSeen(c, e.Client, endpoint.VpcEndpointId, util.ConvertEC2TagsToMap(endpoint.Tags))
 		if err != nil {
-			logging.Errorf(
-				"Unable to retrieve tags for Vpc Endpoint: %s, with error: %s", *endpoint.VpcEndpointId, err)
-			continue
-		}
-
-		// if the first seen tag is not there, then create one
-		if firstSeenTime == nil {
-			now := time.Now().UTC()
-			firstSeenTime = &now
-			if err := e.setFirstSeenTag(*endpoint, time.Now().UTC()); err != nil {
-				logging.Errorf(
-					"Unable to apply first seen tag Vpc Endpoint: %s, with error: %s", *endpoint.VpcEndpointId, err)
-				continue
-			}
+			logging.Error("Unable to retrieve tags")
+			return nil, errors.WithStackTrace(err)
 		}
 
 		if ShouldIncludeVpcEndpoint(endpoint, firstSeenTime, configObj) {
 			result = append(result, endpoint.VpcEndpointId)
 		}
+
 	}
 
 	e.VerifyNukablePermissions(result, func(id *string) error {
diff --git a/aws/resources/ec2_internet_gateway.go b/aws/resources/ec2_internet_gateway.go
index 27a92c9..43f834a 100644
--- a/aws/resources/ec2_internet_gateway.go
+++ b/aws/resources/ec2_internet_gateway.go
@@ -15,39 +15,6 @@ import (
 	"github.com/gruntwork-io/go-commons/errors"
 )
 
-func (igw *InternetGateway) setFirstSeenTag(gateway ec2.InternetGateway, value time.Time) error {
-	_, err := igw.Client.CreateTags(&ec2.CreateTagsInput{
-		Resources: []*string{gateway.InternetGatewayId},
-		Tags: []*ec2.Tag{
-			{
-				Key:   awsgo.String(util.FirstSeenTagKey),
-				Value: awsgo.String(util.FormatTimestamp(value)),
-			},
-		},
-	})
-	if err != nil {
-		return errors.WithStackTrace(err)
-	}
-
-	return nil
-}
-
-func (igw *InternetGateway) getFirstSeenTag(gateway ec2.InternetGateway) (*time.Time, error) {
-	tags := gateway.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 shouldIncludeGateway(ig *ec2.InternetGateway, firstSeenTime *time.Time, configObj config.Config) bool {
 	var internetGateway string
 	// get the tags as map
@@ -63,8 +30,10 @@ func shouldIncludeGateway(ig *ec2.InternetGateway, firstSeenTime *time.Time, con
 	})
 }
 
-func (igw *InternetGateway) getAll(_ context.Context, configObj config.Config) ([]*string, error) {
+func (igw *InternetGateway) getAll(c context.Context, configObj config.Config) ([]*string, error) {
 	var identifiers []*string
+	var firstSeenTime *time.Time
+	var err error
 
 	input := &ec2.DescribeInternetGatewaysInput{}
 	resp, err := igw.Client.DescribeInternetGateways(input)
@@ -72,25 +41,11 @@ func (igw *InternetGateway) getAll(_ context.Context, configObj config.Config) (
 		logging.Debugf("[Internet Gateway] Failed to list internet gateways: %s", err)
 		return nil, err
 	}
-
 	for _, ig := range resp.InternetGateways {
-		// check first seen tag
-		firstSeenTime, err := igw.getFirstSeenTag(*ig)
+		firstSeenTime, err = util.GetOrCreateFirstSeen(c, igw.Client, ig.InternetGatewayId, util.ConvertEC2TagsToMap(ig.Tags))
 		if err != nil {
-			logging.Errorf(
-				"Unable to retrieve tags for Internet gateway: %s, with error: %s", *ig.InternetGatewayId, err)
-			continue
-		}
-
-		// if the first seen tag is not there, then create one
-		if firstSeenTime == nil {
-			now := time.Now().UTC()
-			firstSeenTime = &now
-			if err := igw.setFirstSeenTag(*ig, time.Now().UTC()); err != nil {
-				logging.Errorf(
-					"Unable to apply first seen tag Internet gateway: %s, with error: %s", *ig.InternetGatewayId, err)
-				continue
-			}
+			logging.Error("Unable to retrieve tags")
+			return nil, errors.WithStackTrace(err)
 		}
 
 		if shouldIncludeGateway(ig, firstSeenTime, configObj) {
diff --git a/aws/resources/ec2_ipam.go b/aws/resources/ec2_ipam.go
index b32f2de..23a7322 100644
--- a/aws/resources/ec2_ipam.go
+++ b/aws/resources/ec2_ipam.go
@@ -15,39 +15,6 @@ import (
 	"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
@@ -66,27 +33,16 @@ func shouldIncludeIpamID(ipam *ec2.Ipam, firstSeenTime *time.Time, configObj con
 // Returns a formatted string of IPAM URLs
 func (ec2Ipam *EC2IPAMs) getAll(c context.Context, configObj config.Config) ([]*string, error) {
 	result := []*string{}
+	var firstSeenTime *time.Time
+	var err error
+
 	paginator := func(output *ec2.DescribeIpamsOutput, lastPage bool) bool {
 		for _, ipam := range output.Ipams {
-			// check first seen tag
-			firstSeenTime, err := ec2Ipam.getFirstSeenTag(*ipam)
+			firstSeenTime, err = util.GetOrCreateFirstSeen(c, ec2Ipam.Client, ipam.IpamId, util.ConvertEC2TagsToMap(ipam.Tags))
 			if err != nil {
-				logging.Errorf(
-					"Unable to retrieve tags for IPAM: %s, with error: %s", *ipam.IpamId, err)
+				logging.Error("Unable to retrieve tags")
 				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)
@@ -99,7 +55,7 @@ func (ec2Ipam *EC2IPAMs) getAll(c context.Context, configObj config.Config) ([]*
 		MaxResults: awsgo.Int64(10),
 	}
 
-	err := ec2Ipam.Client.DescribeIpamsPages(params, paginator)
+	err = ec2Ipam.Client.DescribeIpamsPages(params, paginator)
 	if err != nil {
 		return nil, errors.WithStackTrace(err)
 	}
diff --git a/aws/resources/ec2_ipam_pool.go b/aws/resources/ec2_ipam_pool.go
index d3752bb..402c2f8 100644
--- a/aws/resources/ec2_ipam_pool.go
+++ b/aws/resources/ec2_ipam_pool.go
@@ -14,39 +14,6 @@ import (
 	"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
@@ -65,27 +32,16 @@ func shouldIncludeIpamPoolID(ipam *ec2.IpamPool, firstSeenTime *time.Time, confi
 // Returns a formatted string of IPAM URLs
 func (ec2Pool *EC2IPAMPool) getAll(c context.Context, configObj config.Config) ([]*string, error) {
 	result := []*string{}
+	var firstSeenTime *time.Time
+	var err error
+
 	paginator := func(output *ec2.DescribeIpamPoolsOutput, lastPage bool) bool {
 		for _, pool := range output.IpamPools {
-			// check first seen tag
-			firstSeenTime, err := ec2Pool.getFirstSeenTag(*pool)
+			firstSeenTime, err = util.GetOrCreateFirstSeen(c, ec2Pool.Client, pool.IpamPoolId, util.ConvertEC2TagsToMap(pool.Tags))
 			if err != nil {
-				logging.Errorf(
-					"Unable to retrieve tags for IPAM Pool: %s, with error: %s", *pool.IpamPoolId, err)
+				logging.Error("unable to retrieve firstseen tag")
 				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)
 			}
@@ -103,7 +59,7 @@ func (ec2Pool *EC2IPAMPool) getAll(c context.Context, configObj config.Config) (
 		},
 	}
 
-	err := ec2Pool.Client.DescribeIpamPoolsPages(params, paginator)
+	err = ec2Pool.Client.DescribeIpamPoolsPages(params, paginator)
 	if err != nil {
 		return nil, errors.WithStackTrace(err)
 	}
diff --git a/aws/resources/ec2_ipam_resource_discovery.go b/aws/resources/ec2_ipam_resource_discovery.go
index f7e249b..f19047a 100644
--- a/aws/resources/ec2_ipam_resource_discovery.go
+++ b/aws/resources/ec2_ipam_resource_discovery.go
@@ -13,39 +13,6 @@ import (
 	"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
@@ -64,27 +31,16 @@ func shouldIncludeIpamResourceID(ipam *ec2.IpamResourceDiscovery, firstSeenTime
 // Returns a formatted string of IPAM Resource discovery
 func (discovery *EC2IPAMResourceDiscovery) getAll(c context.Context, configObj config.Config) ([]*string, error) {
 	result := []*string{}
+	var firstSeenTime *time.Time
+	var err error
+
 	paginator := func(output *ec2.DescribeIpamResourceDiscoveriesOutput, lastPage bool) bool {
 		for _, d := range output.IpamResourceDiscoveries {
-			// check first seen tag
-			firstSeenTime, err := discovery.getFirstSeenTag(*d)
+			firstSeenTime, err = util.GetOrCreateFirstSeen(c, discovery.Client, d.IpamResourceDiscoveryId, util.ConvertEC2TagsToMap(d.Tags))
 			if err != nil {
-				logging.Errorf(
-					"Unable to retrieve tags for IPAM: %s, with error: %s", *d.IpamResourceDiscoveryId, err)
+				logging.Error("unable to retrieve firstseen tag")
 				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)
 			}
@@ -102,7 +58,7 @@ func (discovery *EC2IPAMResourceDiscovery) getAll(c context.Context, configObj c
 		},
 	}
 
-	err := discovery.Client.DescribeIpamResourceDiscoveriesPages(params, paginator)
+	err = discovery.Client.DescribeIpamResourceDiscoveriesPages(params, paginator)
 	if err != nil {
 		return nil, errors.WithStackTrace(err)
 	}
diff --git a/aws/resources/ec2_ipam_scope.go b/aws/resources/ec2_ipam_scope.go
index 53418fb..228f110 100644
--- a/aws/resources/ec2_ipam_scope.go
+++ b/aws/resources/ec2_ipam_scope.go
@@ -14,39 +14,6 @@ import (
 	"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
@@ -65,26 +32,18 @@ func shouldIncludeIpamScopeID(ipam *ec2.IpamScope, firstSeenTime *time.Time, con
 // Returns a formatted string of IPAM URLs
 func (ec2Scope *EC2IpamScopes) getAll(c context.Context, configObj config.Config) ([]*string, error) {
 	result := []*string{}
+	var firstSeenTime *time.Time
+	var err error
+
 	paginator := func(output *ec2.DescribeIpamScopesOutput, lastPage bool) bool {
 		for _, scope := range output.IpamScopes {
-			// check first seen tag
-			firstSeenTime, err := ec2Scope.getFirstSeenTag(*scope)
+
+			firstSeenTime, err = util.GetOrCreateFirstSeen(c, ec2Scope.Client, scope.IpamScopeId, util.ConvertEC2TagsToMap(scope.Tags))
 			if err != nil {
-				logging.Errorf(
-					"Unable to retrieve tags for IPAM: %s, with error: %s", *scope.IpamScopeId, err)
+				logging.Error("unable to retrieve firstseen tag")
 				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)
@@ -103,7 +62,7 @@ func (ec2Scope *EC2IpamScopes) getAll(c context.Context, configObj config.Config
 		},
 	}
 
-	err := ec2Scope.Client.DescribeIpamScopesPages(params, paginator)
+	err = ec2Scope.Client.DescribeIpamScopesPages(params, paginator)
 	if err != nil {
 		return nil, errors.WithStackTrace(err)
 	}
diff --git a/aws/resources/ec2_network_acl.go b/aws/resources/ec2_network_acl.go
index a818e2b..657dbf8 100644
--- a/aws/resources/ec2_network_acl.go
+++ b/aws/resources/ec2_network_acl.go
@@ -14,38 +14,6 @@ import (
 	"github.com/gruntwork-io/go-commons/errors"
 )
 
-func (nacl *NetworkACL) setFirstSeenTag(networkAcl ec2.NetworkAcl, value time.Time) error {
-	_, err := nacl.Client.CreateTags(&ec2.CreateTagsInput{
-		Resources: []*string{networkAcl.NetworkAclId},
-		Tags: []*ec2.Tag{
-			{
-				Key:   awsgo.String(util.FirstSeenTagKey),
-				Value: awsgo.String(util.FormatTimestamp(value)),
-			},
-		},
-	})
-	if err != nil {
-		return errors.WithStackTrace(err)
-	}
-
-	return nil
-}
-
-func (nacl *NetworkACL) getFirstSeenTag(networkAcl ec2.NetworkAcl) (*time.Time, error) {
-	for _, tag := range networkAcl.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 shouldIncludeNetworkACL(networkAcl *ec2.NetworkAcl, firstSeenTime *time.Time, configObj config.Config) bool {
 	var naclName string
 	// get the tags as map
@@ -60,8 +28,11 @@ func shouldIncludeNetworkACL(networkAcl *ec2.NetworkAcl, firstSeenTime *time.Tim
 	})
 }
 
-func (nacl *NetworkACL) getAll(_ context.Context, configObj config.Config) ([]*string, error) {
+func (nacl *NetworkACL) getAll(c context.Context, configObj config.Config) ([]*string, error) {
 	var identifiers []*string
+	var firstSeenTime *time.Time
+	var err error
+
 	resp, err := nacl.Client.DescribeNetworkAcls(&ec2.DescribeNetworkAclsInput{
 		Filters: []*ec2.Filter{
 			{
@@ -78,25 +49,12 @@ func (nacl *NetworkACL) getAll(_ context.Context, configObj config.Config) ([]*s
 	}
 
 	for _, networkAcl := range resp.NetworkAcls {
-		// check first seen tag
-		firstSeenTime, err := nacl.getFirstSeenTag(*networkAcl)
+		firstSeenTime, err = util.GetOrCreateFirstSeen(c, nacl.Client, networkAcl.NetworkAclId, util.ConvertEC2TagsToMap(networkAcl.Tags))
 		if err != nil {
-			logging.Errorf(
-				"Unable to retrieve tags for network acl: %s, with error: %s", awsgo.StringValue(networkAcl.NetworkAclId), err)
+			logging.Error("unable to retrieve first seen tag")
 			continue
 		}
 
-		// if the first seen tag is not there, then create one
-		if firstSeenTime == nil {
-			now := time.Now().UTC()
-			firstSeenTime = &now
-			if err := nacl.setFirstSeenTag(*networkAcl, time.Now().UTC()); err != nil {
-				logging.Errorf(
-					"Unable to apply first seen tag network acl: %s, with error: %s", awsgo.StringValue(networkAcl.NetworkAclId), err)
-				continue
-			}
-		}
-
 		if shouldIncludeNetworkACL(networkAcl, firstSeenTime, configObj) {
 			identifiers = append(identifiers, networkAcl.NetworkAclId)
 		}
diff --git a/aws/resources/ec2_network_interface.go b/aws/resources/ec2_network_interface.go
index cfb9d64..c0a9edc 100644
--- a/aws/resources/ec2_network_interface.go
+++ b/aws/resources/ec2_network_interface.go
@@ -14,39 +14,6 @@ import (
 	"github.com/gruntwork-io/go-commons/errors"
 )
 
-func (ni *NetworkInterface) setFirstSeenTag(networkInterface ec2.NetworkInterface, value time.Time) error {
-	_, err := ni.Client.CreateTags(&ec2.CreateTagsInput{
-		Resources: []*string{networkInterface.NetworkInterfaceId},
-		Tags: []*ec2.Tag{
-			{
-				Key:   awsgo.String(util.FirstSeenTagKey),
-				Value: awsgo.String(util.FormatTimestamp(value)),
-			},
-		},
-	})
-	if err != nil {
-		return errors.WithStackTrace(err)
-	}
-
-	return nil
-}
-
-func (igw *NetworkInterface) getFirstSeenTag(networkInterface ec2.NetworkInterface) (*time.Time, error) {
-	tags := networkInterface.TagSet
-	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 shouldIncludeNetworkInterface(networkInterface *ec2.NetworkInterface, firstSeenTime *time.Time, configObj config.Config) bool {
 	var interfaceName string
 	// get the tags as map
@@ -61,8 +28,11 @@ func shouldIncludeNetworkInterface(networkInterface *ec2.NetworkInterface, first
 	})
 }
 
-func (ni *NetworkInterface) getAll(_ context.Context, configObj config.Config) ([]*string, error) {
+func (ni *NetworkInterface) getAll(c context.Context, configObj config.Config) ([]*string, error) {
 	var identifiers []*string
+	var firstSeenTime *time.Time
+	var err error
+
 	resp, err := ni.Client.DescribeNetworkInterfaces(&ec2.DescribeNetworkInterfacesInput{})
 	if err != nil {
 		logging.Debugf("[Internet Gateway] Failed to list internet gateways: %s", err)
@@ -70,25 +40,12 @@ func (ni *NetworkInterface) getAll(_ context.Context, configObj config.Config) (
 	}
 
 	for _, networkInterface := range resp.NetworkInterfaces {
-		// check first seen tag
-		firstSeenTime, err := ni.getFirstSeenTag(*networkInterface)
+		firstSeenTime, err = util.GetOrCreateFirstSeen(c, ni.Client, networkInterface.NetworkInterfaceId, util.ConvertEC2TagsToMap(networkInterface.TagSet))
 		if err != nil {
-			logging.Errorf(
-				"Unable to retrieve tags for Internet gateway: %s, with error: %s", *networkInterface.NetworkInterfaceId, err)
+			logging.Error("unable to retrieve first seen tag")
 			continue
 		}
 
-		// if the first seen tag is not there, then create one
-		if firstSeenTime == nil {
-			now := time.Now().UTC()
-			firstSeenTime = &now
-			if err := ni.setFirstSeenTag(*networkInterface, time.Now().UTC()); err != nil {
-				logging.Errorf(
-					"Unable to apply first seen tag Internet gateway: %s, with error: %s", *networkInterface.NetworkInterfaceId, err)
-				continue
-			}
-		}
-
 		if shouldIncludeNetworkInterface(networkInterface, firstSeenTime, configObj) {
 			identifiers = append(identifiers, networkInterface.NetworkInterfaceId)
 		}
diff --git a/aws/resources/ec2_subnet.go b/aws/resources/ec2_subnet.go
index 545f005..d1fd833 100644
--- a/aws/resources/ec2_subnet.go
+++ b/aws/resources/ec2_subnet.go
@@ -16,39 +16,6 @@ import (
 	"github.com/gruntwork-io/go-commons/errors"
 )
 
-func (ec2subnet *EC2Subnet) setFirstSeenTag(sb ec2.Subnet, value time.Time) error {
-	_, err := ec2subnet.Client.CreateTags(&ec2.CreateTagsInput{
-		Resources: []*string{sb.SubnetId},
-		Tags: []*ec2.Tag{
-			{
-				Key:   awsgo.String(util.FirstSeenTagKey),
-				Value: awsgo.String(util.FormatTimestamp(value)),
-			},
-		},
-	})
-	if err != nil {
-		return errors.WithStackTrace(err)
-	}
-
-	return nil
-}
-
-func (ec2subnet *EC2Subnet) getFirstSeenTag(sb ec2.Subnet) (*time.Time, error) {
-	tags := sb.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 shouldIncludeEc2Subnet(subnet *ec2.Subnet, firstSeenTime *time.Time, configObj config.Config) bool {
 	var subnetName string
 	tagMap := util.ConvertEC2TagsToMap(subnet.Tags)
@@ -64,14 +31,17 @@ func shouldIncludeEc2Subnet(subnet *ec2.Subnet, firstSeenTime *time.Time, config
 }
 
 // Returns a formatted string of EC2 subnets
-func (ec2subnet *EC2Subnet) getAll(_ context.Context, configObj config.Config) ([]*string, error) {
+func (ec2subnet *EC2Subnet) getAll(c context.Context, configObj config.Config) ([]*string, error) {
 	result := []*string{}
+	var firstSeenTime *time.Time
+	var err error
+
 	// Note: This filter initially handles non-default resources and can be overridden by passing the only-default filter to choose default subnets.
 	if configObj.EC2Subnet.DefaultOnly {
 		logging.Debugf("[default only] Retrieving the default subnets")
 	}
 
-	err := ec2subnet.Client.DescribeSubnetsPages(&ec2.DescribeSubnetsInput{
+	err = ec2subnet.Client.DescribeSubnetsPages(&ec2.DescribeSubnetsInput{
 		Filters: []*ec2.Filter{
 			{
 				Name: awsgo.String("default-for-az"),
@@ -82,26 +52,11 @@ func (ec2subnet *EC2Subnet) getAll(_ context.Context, configObj config.Config) (
 		},
 	}, func(pages *ec2.DescribeSubnetsOutput, lastPage bool) bool {
 		for _, subnet := range pages.Subnets {
-
-			// check first seen tag
-			firstSeenTime, err := ec2subnet.getFirstSeenTag(*subnet)
+			firstSeenTime, err = util.GetOrCreateFirstSeen(c, ec2subnet.Client, subnet.SubnetId, util.ConvertEC2TagsToMap(subnet.Tags))
 			if err != nil {
-				logging.Errorf(
-					"Unable to retrieve tags for Subnet: %s, with error: %s", *subnet.SubnetId, err)
+				logging.Error("unable to retrieve first seen tag")
 				continue
 			}
-
-			// if the first seen tag is not there, then create one
-			if firstSeenTime == nil {
-				now := time.Now().UTC()
-				firstSeenTime = &now
-				if err := ec2subnet.setFirstSeenTag(*subnet, time.Now().UTC()); err != nil {
-					logging.Errorf(
-						"Unable to apply first seen tag Subnet: %s, with error: %s", *subnet.SubnetId, err)
-					continue
-				}
-			}
-
 			if shouldIncludeEc2Subnet(subnet, firstSeenTime, configObj) {
 				result = append(result, subnet.SubnetId)
 			}
diff --git a/aws/resources/ec2_vpc.go b/aws/resources/ec2_vpc.go
index 43278ba..652ab8b 100644
--- a/aws/resources/ec2_vpc.go
+++ b/aws/resources/ec2_vpc.go
@@ -26,41 +26,9 @@ import (
 	"github.com/hashicorp/go-multierror"
 )
 
-func (v *EC2VPCs) setFirstSeenTag(vpc ec2.Vpc, value time.Time) error {
-	// We set a first seen tag because an Elastic IP doesn't contain an attribute that gives us it's creation time
-	_, err := v.Client.CreateTags(&ec2.CreateTagsInput{
-		Resources: []*string{vpc.VpcId},
-		Tags: []*ec2.Tag{
-			{
-				Key:   awsgo.String(util.FirstSeenTagKey),
-				Value: awsgo.String(util.FormatTimestamp(value)),
-			},
-		},
-	})
-	if err != nil {
-		return errors.WithStackTrace(err)
-	}
-
-	return nil
-}
-
-func (v *EC2VPCs) getFirstSeenTag(vpc ec2.Vpc) (*time.Time, error) {
-	tags := vpc.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 (v *EC2VPCs) getAll(_ context.Context, configObj config.Config) ([]*string, error) {
+func (v *EC2VPCs) getAll(c context.Context, configObj config.Config) ([]*string, error) {
+	var firstSeenTime *time.Time
+	var err error
 	// Note: This filter initially handles non-default resources and can be overridden by passing the only-default filter to choose default VPCs.
 	result, err := v.Client.DescribeVpcs(&ec2.DescribeVpcsInput{
 		Filters: []*ec2.Filter{
@@ -78,20 +46,12 @@ func (v *EC2VPCs) getAll(_ context.Context, configObj config.Config) ([]*string,
 
 	var ids []*string
 	for _, vpc := range result.Vpcs {
-		firstSeenTime, err := v.getFirstSeenTag(*vpc)
+		firstSeenTime, err = util.GetOrCreateFirstSeen(c, v.Client, vpc.VpcId, util.ConvertEC2TagsToMap(vpc.Tags))
 		if err != nil {
 			logging.Error("Unable to retrieve tags")
 			return nil, errors.WithStackTrace(err)
 		}
 
-		if firstSeenTime == nil {
-			now := time.Now().UTC()
-			firstSeenTime = &now
-			if err := v.setFirstSeenTag(*vpc, time.Now().UTC()); err != nil {
-				return nil, err
-			}
-		}
-
 		if configObj.VPC.ShouldInclude(config.ResourceValue{
 			Time: firstSeenTime,
 			Name: util.GetEC2ResourceNameTagValue(vpc.Tags),
diff --git a/aws/resources/ecs_cluster.go b/aws/resources/ecs_cluster.go
index 4d4aabc..7d685d8 100644
--- a/aws/resources/ecs_cluster.go
+++ b/aws/resources/ecs_cluster.go
@@ -100,23 +100,29 @@ func (clusters *ECSClusters) getAll(c context.Context, configObj config.Config)
 		return nil, errors.WithStackTrace(err)
 	}
 
+	excludeFirstSeenTag, err := util.GetBoolFromContext(c, util.ExcludeFirstSeenTagKey)
+	if err != nil {
+		return nil, errors.WithStackTrace(err)
+	}
+
 	var filteredEcsClusters []*string
 	for _, clusterArn := range clusterArns {
-
-		firstSeenTime, err := clusters.getFirstSeenTag(clusterArn)
-		if err != nil {
-			logging.Debugf("Error getting the `cloud-nuke-first-seen` tag for ECS cluster with ARN %s", aws.StringValue(clusterArn))
-			return nil, errors.WithStackTrace(err)
-		}
-
-		if firstSeenTime == nil {
-			err := clusters.setFirstSeenTag(clusterArn, time.Now().UTC())
+		if !excludeFirstSeenTag {
+			firstSeenTime, err := clusters.getFirstSeenTag(clusterArn)
 			if err != nil {
-				logging.Debugf("Error tagging the ECS cluster with ARN %s", aws.StringValue(clusterArn))
+				logging.Debugf("Error getting the `cloud-nuke-first-seen` tag for ECS cluster with ARN %s", aws.StringValue(clusterArn))
 				return nil, errors.WithStackTrace(err)
 			}
-		} else if configObj.ECSCluster.ShouldInclude(config.ResourceValue{Time: firstSeenTime}) {
-			filteredEcsClusters = append(filteredEcsClusters, clusterArn)
+
+			if firstSeenTime == nil {
+				err := clusters.setFirstSeenTag(clusterArn, time.Now().UTC())
+				if err != nil {
+					logging.Debugf("Error tagging the ECS cluster with ARN %s", aws.StringValue(clusterArn))
+					return nil, errors.WithStackTrace(err)
+				}
+			} else if configObj.ECSCluster.ShouldInclude(config.ResourceValue{Time: firstSeenTime}) {
+				filteredEcsClusters = append(filteredEcsClusters, clusterArn)
+			}
 		}
 	}
 	return filteredEcsClusters, nil
diff --git a/aws/resources/eip.go b/aws/resources/eip.go
index 50ad353..58fa2e4 100644
--- a/aws/resources/eip.go
+++ b/aws/resources/eip.go
@@ -5,7 +5,6 @@ import (
 	"time"
 
 	"github.com/aws/aws-sdk-go/aws"
-	awsgo "github.com/aws/aws-sdk-go/aws"
 	"github.com/aws/aws-sdk-go/aws/awserr"
 	"github.com/aws/aws-sdk-go/service/ec2"
 	"github.com/gruntwork-io/cloud-nuke/config"
@@ -15,42 +14,10 @@ import (
 	"github.com/gruntwork-io/go-commons/errors"
 )
 
-func (ea *EIPAddresses) setFirstSeenTag(address ec2.Address, value time.Time) error {
-	// We set a first seen tag because an Elastic IP doesn't contain an attribute that gives us it's creation time
-	_, err := ea.Client.CreateTags(&ec2.CreateTagsInput{
-		Resources: []*string{address.AllocationId},
-		Tags: []*ec2.Tag{
-			{
-				Key:   awsgo.String(util.FirstSeenTagKey),
-				Value: awsgo.String(util.FormatTimestamp(value)),
-			},
-		},
-	})
-	if err != nil {
-		return errors.WithStackTrace(err)
-	}
-
-	return nil
-}
-
-func (ea *EIPAddresses) getFirstSeenTag(address ec2.Address) (*time.Time, error) {
-	tags := address.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
-}
-
 // Returns a formatted string of EIP allocation ids
 func (ea *EIPAddresses) getAll(c context.Context, configObj config.Config) ([]*string, error) {
+	var firstSeenTime *time.Time
+	var err error
 	result, err := ea.Client.DescribeAddresses(&ec2.DescribeAddressesInput{})
 	if err != nil {
 		return nil, errors.WithStackTrace(err)
@@ -58,18 +25,12 @@ func (ea *EIPAddresses) getAll(c context.Context, configObj config.Config) ([]*s
 
 	var allocationIds []*string
 	for _, address := range result.Addresses {
-		firstSeenTime, err := ea.getFirstSeenTag(*address)
+		firstSeenTime, err = util.GetOrCreateFirstSeen(c, ea.Client, address.AllocationId, util.ConvertEC2TagsToMap(address.Tags))
 		if err != nil {
+			logging.Error("unable to retrieve first seen tag")
 			return nil, errors.WithStackTrace(err)
 		}
 
-		if firstSeenTime == nil {
-			now := time.Now().UTC()
-			firstSeenTime = &now
-			if err := ea.setFirstSeenTag(*address, *firstSeenTime); err != nil {
-				return nil, err
-			}
-		}
 		if ea.shouldInclude(address, *firstSeenTime, configObj) {
 			allocationIds = append(allocationIds, address.AllocationId)
 		}
diff --git a/aws/resources/network_firewall.go b/aws/resources/network_firewall.go
index 0556096..7b82345 100644
--- a/aws/resources/network_firewall.go
+++ b/aws/resources/network_firewall.go
@@ -14,34 +14,6 @@ import (
 	"github.com/gruntwork-io/go-commons/errors"
 )
 
-func (nfw *NetworkFirewall) setFirstSeenTag(resource *networkfirewall.Firewall, value time.Time) error {
-	_, err := nfw.Client.TagResource(&networkfirewall.TagResourceInput{
-		ResourceArn: resource.FirewallArn,
-		Tags: []*networkfirewall.Tag{
-			{
-				Key:   awsgo.String(util.FirstSeenTagKey),
-				Value: awsgo.String(util.FormatTimestamp(value)),
-			},
-		},
-	})
-	return errors.WithStackTrace(err)
-}
-
-func (nfw *NetworkFirewall) getFirstSeenTag(resource *networkfirewall.Firewall) (*time.Time, error) {
-	for _, tag := range resource.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 shouldIncludeNetworkFirewall(firewall *networkfirewall.Firewall, firstSeenTime *time.Time, configObj config.Config) bool {
 	var identifierName string
 	tags := util.ConvertNetworkFirewallTagsToMap(firewall.Tags)
@@ -58,8 +30,10 @@ func shouldIncludeNetworkFirewall(firewall *networkfirewall.Firewall, firstSeenT
 	})
 }
 
-func (nfw *NetworkFirewall) getAll(_ context.Context, configObj config.Config) ([]*string, error) {
+func (nfw *NetworkFirewall) getAll(c context.Context, configObj config.Config) ([]*string, error) {
 	var identifiers []*string
+	var firstSeenTime *time.Time
+	var err error
 
 	metaOutput, err := nfw.Client.ListFirewalls(nil)
 	if err != nil {
@@ -82,23 +56,10 @@ func (nfw *NetworkFirewall) getAll(_ context.Context, configObj config.Config) (
 			continue
 		}
 
-		// check first seen tag
-		firstSeenTime, err := nfw.getFirstSeenTag(output.Firewall)
+		firstSeenTime, err = util.GetOrCreateFirstSeen(c, nfw.Client, firewall.FirewallArn, util.ConvertNetworkFirewallTagsToMap(output.Firewall.Tags))
 		if err != nil {
-			logging.Errorf(
-				"Unable to retrieve tags for Firewall: %s, with error: %s", awsgo.StringValue(firewall.FirewallName), err)
-			continue
-		}
-
-		// if the first seen tag is not there, then create one
-		if firstSeenTime == nil {
-			now := time.Now().UTC()
-			firstSeenTime = &now
-			if err := nfw.setFirstSeenTag(output.Firewall, time.Now().UTC()); err != nil {
-				logging.Errorf(
-					"Unable to apply first seen tag Firewall: %s, with error: %s", awsgo.StringValue(firewall.FirewallName), err)
-				continue
-			}
+			logging.Error("Unable to retrieve tags")
+			return nil, errors.WithStackTrace(err)
 		}
 
 		// check the resource is delete protected
diff --git a/aws/resources/network_firewall_policy.go b/aws/resources/network_firewall_policy.go
index 4ed3938..796d102 100644
--- a/aws/resources/network_firewall_policy.go
+++ b/aws/resources/network_firewall_policy.go
@@ -13,34 +13,6 @@ import (
 	"github.com/gruntwork-io/go-commons/errors"
 )
 
-func (nfw *NetworkFirewallPolicy) setFirstSeenTag(resource *networkfirewall.FirewallPolicyResponse, value time.Time) error {
-	_, err := nfw.Client.TagResource(&networkfirewall.TagResourceInput{
-		ResourceArn: resource.FirewallPolicyArn,
-		Tags: []*networkfirewall.Tag{
-			{
-				Key:   awsgo.String(util.FirstSeenTagKey),
-				Value: awsgo.String(util.FormatTimestamp(value)),
-			},
-		},
-	})
-	return errors.WithStackTrace(err)
-}
-
-func (nfw *NetworkFirewallPolicy) getFirstSeenTag(resource *networkfirewall.FirewallPolicyResponse) (*time.Time, error) {
-	for _, tag := range resource.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 shouldIncludeNetworkFirewallPolicy(firewall *networkfirewall.FirewallPolicyResponse, firstSeenTime *time.Time, configObj config.Config) bool {
 	// if the firewall policy has any attachments, then we can't remove that policy
 	if awsgo.Int64Value(firewall.NumberOfAssociations) > 0 {
@@ -61,8 +33,12 @@ func shouldIncludeNetworkFirewallPolicy(firewall *networkfirewall.FirewallPolicy
 	})
 }
 
-func (nfw *NetworkFirewallPolicy) getAll(_ context.Context, configObj config.Config) ([]*string, error) {
-	var identifiers []*string
+func (nfw *NetworkFirewallPolicy) getAll(c context.Context, configObj config.Config) ([]*string, error) {
+	var (
+		identifiers   []*string
+		firstSeenTime *time.Time
+		err           error
+	)
 
 	metaOutput, err := nfw.Client.ListFirewallPolicies(nil)
 	if err != nil {
@@ -84,23 +60,10 @@ func (nfw *NetworkFirewallPolicy) getAll(_ context.Context, configObj config.Con
 			continue
 		}
 
-		// check first seen tag
-		firstSeenTime, err := nfw.getFirstSeenTag(output.FirewallPolicyResponse)
+		firstSeenTime, err = util.GetOrCreateFirstSeen(c, nfw.Client, policy.Arn, util.ConvertNetworkFirewallTagsToMap(output.FirewallPolicyResponse.Tags))
 		if err != nil {
-			logging.Errorf(
-				"Unable to retrieve tags for Rule group: %s, with error: %s", awsgo.StringValue(policy.Name), err)
-			continue
-		}
-
-		// if the first seen tag is not there, then create one
-		if firstSeenTime == nil {
-			now := time.Now().UTC()
-			firstSeenTime = &now
-			if err := nfw.setFirstSeenTag(output.FirewallPolicyResponse, time.Now().UTC()); err != nil {
-				logging.Errorf(
-					"Unable to apply first seen tag Rule group: %s, with error: %s", awsgo.StringValue(policy.Name), err)
-				continue
-			}
+			logging.Error("Unable to retrieve tags")
+			return nil, errors.WithStackTrace(err)
 		}
 
 		if shouldIncludeNetworkFirewallPolicy(output.FirewallPolicyResponse, firstSeenTime, configObj) {
diff --git a/aws/resources/network_firewall_rule_group.go b/aws/resources/network_firewall_rule_group.go
index 670fb56..7cb52ee 100644
--- a/aws/resources/network_firewall_rule_group.go
+++ b/aws/resources/network_firewall_rule_group.go
@@ -14,34 +14,6 @@ import (
 	"github.com/gruntwork-io/go-commons/errors"
 )
 
-func (nfw *NetworkFirewallRuleGroup) setFirstSeenTag(resource *networkfirewall.RuleGroupResponse, value time.Time) error {
-	_, err := nfw.Client.TagResource(&networkfirewall.TagResourceInput{
-		ResourceArn: resource.RuleGroupArn,
-		Tags: []*networkfirewall.Tag{
-			{
-				Key:   awsgo.String(util.FirstSeenTagKey),
-				Value: awsgo.String(util.FormatTimestamp(value)),
-			},
-		},
-	})
-	return errors.WithStackTrace(err)
-}
-
-func (nfw *NetworkFirewallRuleGroup) getFirstSeenTag(resource *networkfirewall.RuleGroupResponse) (*time.Time, error) {
-	for _, tag := range resource.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 shouldIncludeNetworkFirewallRuleGroup(group *networkfirewall.RuleGroupResponse, firstSeenTime *time.Time, configObj config.Config) bool {
 	// if the firewall policy has any attachments, then we can't remove that policy
 	if awsgo.Int64Value(group.NumberOfAssociations) > 0 {
@@ -64,8 +36,12 @@ func shouldIncludeNetworkFirewallRuleGroup(group *networkfirewall.RuleGroupRespo
 	})
 }
 
-func (nfw *NetworkFirewallRuleGroup) getAll(_ context.Context, configObj config.Config) ([]*string, error) {
-	var identifiers []*string
+func (nfw *NetworkFirewallRuleGroup) getAll(c context.Context, configObj config.Config) ([]*string, error) {
+	var (
+		identifiers   []*string
+		firstSeenTime *time.Time
+		err           error
+	)
 
 	meta, err := nfw.Client.ListRuleGroups(nil)
 	if err != nil {
@@ -86,23 +62,10 @@ func (nfw *NetworkFirewallRuleGroup) getAll(_ context.Context, configObj config.
 			continue
 		}
 
-		// check first seen tag
-		firstSeenTime, err := nfw.getFirstSeenTag(output.RuleGroupResponse)
+		firstSeenTime, err = util.GetOrCreateFirstSeen(c, nfw.Client, group.Arn, util.ConvertNetworkFirewallTagsToMap(output.RuleGroupResponse.Tags))
 		if err != nil {
-			logging.Errorf(
-				"Unable to retrieve tags for Rule group: %s, with error: %s", awsgo.StringValue(group.Name), err)
-			continue
-		}
-
-		// if the first seen tag is not there, then create one
-		if firstSeenTime == nil {
-			now := time.Now().UTC()
-			firstSeenTime = &now
-			if err := nfw.setFirstSeenTag(output.RuleGroupResponse, time.Now().UTC()); err != nil {
-				logging.Errorf(
-					"Unable to apply first seen tag Rule group: %s, with error: %s", awsgo.StringValue(group.Name), err)
-				continue
-			}
+			logging.Error("Unable to retrieve tags")
+			return nil, errors.WithStackTrace(err)
 		}
 
 		if shouldIncludeNetworkFirewallRuleGroup(output.RuleGroupResponse, firstSeenTime, configObj) {
diff --git a/aws/resources/network_firewall_tls_config.go b/aws/resources/network_firewall_tls_config.go
index cc3059d..4a8176f 100644
--- a/aws/resources/network_firewall_tls_config.go
+++ b/aws/resources/network_firewall_tls_config.go
@@ -13,34 +13,6 @@ import (
 	"github.com/gruntwork-io/go-commons/errors"
 )
 
-func (nfw *NetworkFirewallTLSConfig) setFirstSeenTag(resource *networkfirewall.TLSInspectionConfigurationResponse, value time.Time) error {
-	_, err := nfw.Client.TagResource(&networkfirewall.TagResourceInput{
-		ResourceArn: resource.TLSInspectionConfigurationArn,
-		Tags: []*networkfirewall.Tag{
-			{
-				Key:   awsgo.String(util.FirstSeenTagKey),
-				Value: awsgo.String(util.FormatTimestamp(value)),
-			},
-		},
-	})
-	return errors.WithStackTrace(err)
-}
-
-func (nfw *NetworkFirewallTLSConfig) getFirstSeenTag(resource *networkfirewall.TLSInspectionConfigurationResponse) (*time.Time, error) {
-	for _, tag := range resource.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 shouldIncludeNetworkFirewallTLSConfig(tlsconfig *networkfirewall.TLSInspectionConfigurationResponse, firstSeenTime *time.Time, configObj config.Config) bool {
 
 	var identifierName string
@@ -58,8 +30,13 @@ func shouldIncludeNetworkFirewallTLSConfig(tlsconfig *networkfirewall.TLSInspect
 	})
 }
 
-func (nftc *NetworkFirewallTLSConfig) getAll(_ context.Context, configObj config.Config) ([]*string, error) {
-	var identifiers []*string
+func (nftc *NetworkFirewallTLSConfig) getAll(c context.Context, configObj config.Config) ([]*string, error) {
+	var (
+		identifiers   []*string
+		firstSeenTime *time.Time
+		err           error
+	)
+
 	meta, err := nftc.Client.ListTLSInspectionConfigurations(nil)
 	if err != nil {
 		return nil, errors.WithStackTrace(err)
@@ -79,23 +56,10 @@ func (nftc *NetworkFirewallTLSConfig) getAll(_ context.Context, configObj config
 			continue
 		}
 
-		// check first seen tag
-		firstSeenTime, err := nftc.getFirstSeenTag(output.TLSInspectionConfigurationResponse)
+		firstSeenTime, err = util.GetOrCreateFirstSeen(c, nftc.Client, tlsconfig.Arn, util.ConvertNetworkFirewallTagsToMap(output.TLSInspectionConfigurationResponse.Tags))
 		if err != nil {
-			logging.Errorf(
-				"Unable to retrieve tags for TLS inspection configurations: %s, with error: %s", awsgo.StringValue(tlsconfig.Name), err)
-			continue
-		}
-
-		// if the first seen tag is not there, then create one
-		if firstSeenTime == nil {
-			now := time.Now().UTC()
-			firstSeenTime = &now
-			if err := nftc.setFirstSeenTag(output.TLSInspectionConfigurationResponse, time.Now().UTC()); err != nil {
-				logging.Errorf(
-					"Unable to apply first seen tag TLS inspection configurations: %s, with error: %s", awsgo.StringValue(tlsconfig.Name), err)
-				continue
-			}
+			logging.Error("Unable to retrieve tags")
+			return nil, errors.WithStackTrace(err)
 		}
 
 		if shouldIncludeNetworkFirewallTLSConfig(output.TLSInspectionConfigurationResponse, firstSeenTime, configObj) {
diff --git a/aws/resources/opensearch.go b/aws/resources/opensearch.go
index bb68839..ee47a83 100644
--- a/aws/resources/opensearch.go
+++ b/aws/resources/opensearch.go
@@ -3,10 +3,11 @@ package resources
 import (
 	"context"
 	"fmt"
-	"github.com/gruntwork-io/cloud-nuke/util"
 	"sync"
 	"time"
 
+	"github.com/gruntwork-io/cloud-nuke/util"
+
 	"github.com/aws/aws-sdk-go/aws"
 	"github.com/aws/aws-sdk-go/service/opensearchservice"
 	"github.com/gruntwork-io/cloud-nuke/config"
@@ -22,27 +23,34 @@ import (
 // use the first-seen tagging pattern to track which OpenSearch Domains should be nuked based on time. This routine will
 // tag resources with the first-seen tag if it does not have one.
 func (osd *OpenSearchDomains) getAll(c context.Context, configObj config.Config) ([]*string, error) {
+	var firstSeenTime *time.Time
+	var err error
 	domains, err := osd.getAllActiveOpenSearchDomains()
 	if err != nil {
 		return nil, errors.WithStackTrace(err)
 	}
 
+	excludeFirstSeenTag, err := util.GetBoolFromContext(c, util.ExcludeFirstSeenTagKey)
+	if err != nil {
+		return nil, errors.WithStackTrace(err)
+	}
+
 	domainsToNuke := []*string{}
 	for _, domain := range domains {
-
-		firstSeenTime, err := osd.getFirstSeenTag(domain.ARN)
-		if err != nil {
-			return nil, errors.WithStackTrace(err)
-		}
-
-		if firstSeenTime == nil {
-			err := osd.setFirstSeenTag(domain.ARN, time.Now().UTC())
+		if !excludeFirstSeenTag {
+			firstSeenTime, err = osd.getFirstSeenTag(domain.ARN)
 			if err != nil {
-				logging.Errorf("Error tagging the OpenSearch Domain with ARN %s with error: %s", aws.StringValue(domain.ARN), err.Error())
 				return nil, errors.WithStackTrace(err)
 			}
-		}
 
+			if firstSeenTime == nil {
+				err := osd.setFirstSeenTag(domain.ARN, time.Now().UTC())
+				if err != nil {
+					logging.Errorf("Error tagging the OpenSearch Domain with ARN %s with error: %s", aws.StringValue(domain.ARN), err.Error())
+					return nil, errors.WithStackTrace(err)
+				}
+			}
+		}
 		if configObj.OpenSearchDomain.ShouldInclude(config.ResourceValue{
 			Name: domain.DomainName,
 			Time: firstSeenTime,
diff --git a/aws/resources/rds_cluster.go b/aws/resources/rds_cluster.go
index a1826c5..6e0cd22 100644
--- a/aws/resources/rds_cluster.go
+++ b/aws/resources/rds_cluster.go
@@ -2,9 +2,8 @@ package resources
 
 import (
 	"context"
-	"time"
-
 	"github.com/gruntwork-io/cloud-nuke/util"
+	"time"
 
 	awsgo "github.com/aws/aws-sdk-go/aws"
 
@@ -91,6 +90,7 @@ func (instance *DBClusters) nukeAll(names []*string) error {
 
 	if len(deletedNames) > 0 {
 		for _, name := range deletedNames {
+
 			err := instance.waitUntilRdsClusterDeleted(&rds.DescribeDBClustersInput{
 				DBClusterIdentifier: name,
 			})
diff --git a/aws/resources/security_group.go b/aws/resources/security_group.go
index 92f0905..d67a7e7 100644
--- a/aws/resources/security_group.go
+++ b/aws/resources/security_group.go
@@ -15,37 +15,6 @@ import (
 	"github.com/gruntwork-io/go-commons/errors"
 )
 
-func (sg *SecurityGroup) setFirstSeenTag(securityGroup ec2.SecurityGroup, value time.Time) error {
-	_, err := sg.Client.CreateTags(&ec2.CreateTagsInput{
-		Resources: []*string{securityGroup.GroupId},
-		Tags: []*ec2.Tag{
-			{
-				Key:   awsgo.String(util.FirstSeenTagKey),
-				Value: awsgo.String(util.FormatTimestamp(value)),
-			},
-		},
-	})
-	if err != nil {
-		return errors.WithStackTrace(err)
-	}
-	return nil
-}
-
-func (sg *SecurityGroup) getFirstSeenTag(securityGroup ec2.SecurityGroup) (*time.Time, error) {
-	tags := securityGroup.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
-}
-
 // shouldIncludeSecurityGroup determines whether a security group should be included for deletion based on the provided configuration.
 func shouldIncludeSecurityGroup(sg *ec2.SecurityGroup, firstSeenTime *time.Time, configObj config.Config) bool {
 	var groupName = sg.GroupName
@@ -63,8 +32,10 @@ func shouldIncludeSecurityGroup(sg *ec2.SecurityGroup, firstSeenTime *time.Time,
 }
 
 // getAll retrieves all security group identifiers based on the provided configuration.
-func (sg *SecurityGroup) getAll(_ context.Context, configObj config.Config) ([]*string, error) {
+func (sg *SecurityGroup) getAll(c context.Context, configObj config.Config) ([]*string, error) {
 	var identifiers []*string
+	var firstSeenTime *time.Time
+	var err error
 
 	var filters []*ec2.Filter
 	if configObj.SecurityGroup.DefaultOnly {
@@ -90,24 +61,12 @@ func (sg *SecurityGroup) getAll(_ context.Context, configObj config.Config) ([]*
 	}
 
 	for _, group := range resp.SecurityGroups {
-		// check first seen tag
-		firstSeenTime, err := sg.getFirstSeenTag(*group)
+		firstSeenTime, err = util.GetOrCreateFirstSeen(c, sg.Client, group.GroupId, util.ConvertEC2TagsToMap(group.Tags))
 		if err != nil {
-			logging.Errorf(
-				"Unable to retrieve tags for Security group: %s, with error: %s", *group.GroupId, err)
-			continue
+			logging.Error("unable to retrieve first seen tag")
+			return nil, errors.WithStackTrace(err)
 		}
 
-		// if the first seen tag is not there, then create one
-		if firstSeenTime == nil {
-			now := time.Now().UTC()
-			firstSeenTime = &now
-			if err := sg.setFirstSeenTag(*group, time.Now().UTC()); err != nil {
-				logging.Errorf(
-					"Unable to apply first seen tag Security group: %s, with error: %s", *group.GroupId, err)
-				continue
-			}
-		}
 		if shouldIncludeSecurityGroup(group, firstSeenTime, configObj) {
 			identifiers = append(identifiers, group.GroupId)
 		}
diff --git a/aws/resources/sns.go b/aws/resources/sns.go
index d5e5509..453b0a4 100644
--- a/aws/resources/sns.go
+++ b/aws/resources/sns.go
@@ -2,12 +2,13 @@ package resources
 
 import (
 	"context"
-	"github.com/aws/aws-sdk-go/service/sns"
-	"github.com/gruntwork-io/cloud-nuke/util"
 	"strings"
 	"sync"
 	"time"
 
+	"github.com/aws/aws-sdk-go/service/sns"
+	"github.com/gruntwork-io/cloud-nuke/util"
+
 	"github.com/aws/aws-sdk-go/aws"
 	"github.com/gruntwork-io/cloud-nuke/config"
 	"github.com/gruntwork-io/cloud-nuke/logging"
@@ -22,25 +23,33 @@ import (
 func (s *SNSTopic) getAll(c context.Context, configObj config.Config) ([]*string, error) {
 
 	var snsTopics []*string
-	err := s.Client.ListTopicsPages(&sns.ListTopicsInput{}, func(page *sns.ListTopicsOutput, lastPage bool) bool {
-		for _, topic := range page.Topics {
-			firstSeenTime, err := s.getFirstSeenTag(*topic.TopicArn)
-			if err != nil {
-				logging.Errorf(
-					"Unable to retrieve tags for SNS Topic: %s, with error: %s", *topic.TopicArn, err)
-				continue
-			}
+	var firstSeenTime *time.Time
+	var err error
 
-			if firstSeenTime == nil {
-				now := time.Now().UTC()
-				firstSeenTime = &now
-				if err := s.setFirstSeenTag(*topic.TopicArn, now); err != nil {
+	excludeFirstSeenTag, err := util.GetBoolFromContext(c, util.ExcludeFirstSeenTagKey)
+	if err != nil {
+		return nil, errors.WithStackTrace(err)
+	}
+	err = s.Client.ListTopicsPages(&sns.ListTopicsInput{}, func(page *sns.ListTopicsOutput, lastPage bool) bool {
+		for _, topic := range page.Topics {
+			if !excludeFirstSeenTag {
+				firstSeenTime, err = s.getFirstSeenTag(*topic.TopicArn)
+				if err != nil {
 					logging.Errorf(
-						"Unable to apply first seen tag SNS Topic: %s, with error: %s", *topic.TopicArn, err)
+						"Unable to retrieve tags for SNS Topic: %s, with error: %s", *topic.TopicArn, err)
 					continue
 				}
-			}
 
+				if firstSeenTime == nil {
+					now := time.Now().UTC()
+					firstSeenTime = &now
+					if err := s.setFirstSeenTag(*topic.TopicArn, now); err != nil {
+						logging.Errorf(
+							"Unable to apply first seen tag SNS Topic: %s, with error: %s", *topic.TopicArn, err)
+						continue
+					}
+				}
+			}
 			// a topic arn is of the form arn:aws:sns:us-east-1:123456789012:MyTopic
 			// so we can search for the index of the last colon, then slice the string to get the topic name
 			nameIndex := strings.LastIndex(*topic.TopicArn, ":")
diff --git a/aws/resources/sqs.go b/aws/resources/sqs.go
index f9d433d..812eb89 100644
--- a/aws/resources/sqs.go
+++ b/aws/resources/sqs.go
@@ -2,6 +2,9 @@ package resources
 
 import (
 	"context"
+	"strconv"
+	"time"
+
 	"github.com/aws/aws-sdk-go/aws"
 	awsgo "github.com/aws/aws-sdk-go/aws"
 	"github.com/aws/aws-sdk-go/service/sqs"
@@ -9,8 +12,6 @@ import (
 	"github.com/gruntwork-io/cloud-nuke/logging"
 	"github.com/gruntwork-io/cloud-nuke/report"
 	"github.com/gruntwork-io/go-commons/errors"
-	"strconv"
-	"time"
 )
 
 // Returns a formatted string of SQS Queue URLs
diff --git a/commands/cli.go b/commands/cli.go
index 3e26529..abe4e96 100644
--- a/commands/cli.go
+++ b/commands/cli.go
@@ -173,6 +173,10 @@ func CreateCli(version string) *cli.App {
 					Usage:   "Set log level",
 					EnvVars: []string{"LOG_LEVEL"},
 				},
+				&cli.BoolFlag{
+					Name:  "exclude-first-seen",
+					Usage: "Set a flag for excluding first-seen-tag",
+				},
 			},
 		},
 	}
@@ -396,6 +400,7 @@ func generateQuery(c *cli.Context, includeUnaliasedKmsKeys bool, overridingResou
 		includeUnaliasedKmsKeys,
 		timeout,
 		onlyDefault,
+		c.Bool("exclude-first-seen"),
 	)
 }
 
diff --git a/util/context.go b/util/context.go
new file mode 100644
index 0000000..9bc97d7
--- /dev/null
+++ b/util/context.go
@@ -0,0 +1,19 @@
+package util
+
+import (
+	"context"
+	"errors"
+)
+
+const (
+	ExcludeFirstSeenTagKey = "exclude-first-seen-tag"
+)
+
+func GetBoolFromContext(ctx context.Context, key string) (bool, error) {
+	result, ok := ctx.Value(key).(bool)
+	if !ok {
+		return result, errors.New("unable to find the boolean context value or correct type mismatch.")
+	}
+
+	return result, nil
+}
diff --git a/util/time.go b/util/time.go
index bffa5df..d2eceea 100644
--- a/util/time.go
+++ b/util/time.go
@@ -1,9 +1,16 @@
 package util
 
 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/aws/aws-sdk-go/service/ec2/ec2iface"
+	"github.com/aws/aws-sdk-go/service/networkfirewall"
+	"github.com/aws/aws-sdk-go/service/networkfirewall/networkfirewalliface"
 	"github.com/gruntwork-io/cloud-nuke/logging"
 	"github.com/gruntwork-io/go-commons/errors"
 )
@@ -42,3 +49,60 @@ func ParseTimestamp(timestamp *string) (*time.Time, error) {
 func FormatTimestamp(timestamp time.Time) string {
 	return timestamp.Format(firstSeenTimeFormat)
 }
+
+func GetOrCreateFirstSeen(ctx context.Context, client interface{}, identifier *string, tags map[string]string) (*time.Time, error) {
+
+	var firstSeenTime *time.Time
+	var err error
+
+	excludeFirstSeenTag, err := GetBoolFromContext(ctx, ExcludeFirstSeenTagKey)
+	if err != nil {
+		return nil, errors.WithStackTrace(err)
+	}
+
+	if excludeFirstSeenTag {
+		return nil, nil
+	}
+
+	// check the first seen already exists in the given map
+	for key, value := range tags {
+		if IsFirstSeenTag(awsgo.String(key)) {
+			firstSeenTime, err = ParseTimestamp(awsgo.String(value))
+			if err != nil {
+				return nil, errors.WithStackTrace(err)
+			}
+		}
+	}
+
+	if firstSeenTime == nil {
+		now := time.Now().UTC()
+		firstSeenTime = &now
+
+		switch v := client.(type) {
+		case ec2iface.EC2API:
+			_, err = v.CreateTags(&ec2.CreateTagsInput{
+				Resources: []*string{identifier},
+				Tags: []*ec2.Tag{
+					{
+						Key:   awsgo.String(FirstSeenTagKey),
+						Value: awsgo.String(FormatTimestamp(now)),
+					},
+				},
+			})
+		case networkfirewalliface.NetworkFirewallAPI:
+			_, err = v.TagResource(&networkfirewall.TagResourceInput{
+				ResourceArn: identifier,
+				Tags: []*networkfirewall.Tag{
+					{
+						Key:   awsgo.String(FirstSeenTagKey),
+						Value: awsgo.String(FormatTimestamp(now)),
+					},
+				},
+			})
+		default:
+			return nil, errors.WithStackTrace(fmt.Errorf("invalid type %v for first seen tag", v))
+		}
+	}
+
+	return firstSeenTime, err
+}


Test patch:
diff --git a/aws/query_test.go b/aws/query_test.go
index 8cc4b4c..e217277 100644
--- a/aws/query_test.go
+++ b/aws/query_test.go
@@ -53,7 +53,9 @@ func TestNewQueryAcceptsValidExcludeAfterEntries(t *testing.T) {
 				tc.IncludeAfter,
 				false,
 				nil,
-				false)
+				false,
+				false,
+			)
 			require.NoError(t, err)
 		})
 	}
diff --git a/aws/resources/ec2_egress_only_igw_test.go b/aws/resources/ec2_egress_only_igw_test.go
index edcaab7..fbd0964 100644
--- a/aws/resources/ec2_egress_only_igw_test.go
+++ b/aws/resources/ec2_egress_only_igw_test.go
@@ -33,6 +33,9 @@ func TestEgressOnlyInternetGateway_GetAll(t *testing.T) {
 
 	t.Parallel()
 
+	// Set excludeFirstSeenTag to false for testing
+	ctx := context.WithValue(context.Background(), util.ExcludeFirstSeenTagKey, false)
+
 	var (
 		now      = time.Now()
 		gateway1 = "igw-0b44cfa6103932e1d001"
@@ -76,14 +79,17 @@ func TestEgressOnlyInternetGateway_GetAll(t *testing.T) {
 	object.BaseAwsResource.Init(nil)
 
 	tests := map[string]struct {
+		ctx       context.Context
 		configObj config.ResourceType
 		expected  []string
 	}{
 		"emptyFilter": {
+			ctx:       ctx,
 			configObj: config.ResourceType{},
 			expected:  []string{gateway1, gateway2},
 		},
 		"nameExclusionFilter": {
+			ctx: ctx,
 			configObj: config.ResourceType{
 				ExcludeRule: config.FilterRule{
 					NamesRegExp: []config.Expression{{
@@ -93,6 +99,7 @@ func TestEgressOnlyInternetGateway_GetAll(t *testing.T) {
 			expected: []string{gateway2},
 		},
 		"timeAfterExclusionFilter": {
+			ctx: ctx,
 			configObj: config.ResourceType{
 				ExcludeRule: config.FilterRule{
 					TimeAfter: awsgo.Time(now),
@@ -100,6 +107,7 @@ func TestEgressOnlyInternetGateway_GetAll(t *testing.T) {
 			expected: []string{gateway1},
 		},
 		"timeBeforeExclusionFilter": {
+			ctx: ctx,
 			configObj: config.ResourceType{
 				ExcludeRule: config.FilterRule{
 					TimeBefore: awsgo.Time(now.Add(1)),
@@ -109,7 +117,7 @@ func TestEgressOnlyInternetGateway_GetAll(t *testing.T) {
 	}
 	for name, tc := range tests {
 		t.Run(name, func(t *testing.T) {
-			names, err := object.getAll(context.Background(), config.Config{
+			names, err := object.getAll(tc.ctx, config.Config{
 				EgressOnlyInternetGateway: tc.configObj,
 			})
 			require.NoError(t, err)
diff --git a/aws/resources/ec2_endpoints_test.go b/aws/resources/ec2_endpoints_test.go
index 8299b5a..8ef8c16 100644
--- a/aws/resources/ec2_endpoints_test.go
+++ b/aws/resources/ec2_endpoints_test.go
@@ -33,6 +33,9 @@ func TestVcpEndpoint_GetAll(t *testing.T) {
 
 	t.Parallel()
 
+	// Set excludeFirstSeenTag to false for testing
+	ctx := context.WithValue(context.Background(), util.ExcludeFirstSeenTagKey, false)
+
 	var (
 		now       = time.Now()
 		endpoint1 = "vpce-0b201b2dcd4f77a2f001"
@@ -76,14 +79,17 @@ func TestVcpEndpoint_GetAll(t *testing.T) {
 	vpcEndpoint.BaseAwsResource.Init(nil)
 
 	tests := map[string]struct {
+		ctx       context.Context
 		configObj config.ResourceType
 		expected  []string
 	}{
 		"emptyFilter": {
+			ctx:       ctx,
 			configObj: config.ResourceType{},
 			expected:  []string{endpoint1, endpoint2},
 		},
 		"nameExclusionFilter": {
+			ctx: ctx,
 			configObj: config.ResourceType{
 				ExcludeRule: config.FilterRule{
 					NamesRegExp: []config.Expression{{
@@ -93,6 +99,7 @@ func TestVcpEndpoint_GetAll(t *testing.T) {
 			expected: []string{endpoint2},
 		},
 		"timeAfterExclusionFilter": {
+			ctx: ctx,
 			configObj: config.ResourceType{
 				ExcludeRule: config.FilterRule{
 					TimeAfter: aws.Time(now),
@@ -100,6 +107,7 @@ func TestVcpEndpoint_GetAll(t *testing.T) {
 			expected: []string{endpoint1},
 		},
 		"timeBeforeExclusionFilter": {
+			ctx: ctx,
 			configObj: config.ResourceType{
 				ExcludeRule: config.FilterRule{
 					TimeBefore: aws.Time(now.Add(1)),
@@ -109,7 +117,7 @@ func TestVcpEndpoint_GetAll(t *testing.T) {
 	}
 	for name, tc := range tests {
 		t.Run(name, func(t *testing.T) {
-			names, err := vpcEndpoint.getAll(context.Background(), config.Config{
+			names, err := vpcEndpoint.getAll(tc.ctx, config.Config{
 				EC2Endpoint: tc.configObj,
 			})
 			require.NoError(t, err)
diff --git a/aws/resources/ec2_internet_gateway_test.go b/aws/resources/ec2_internet_gateway_test.go
index 79998fb..49c8ec3 100644
--- a/aws/resources/ec2_internet_gateway_test.go
+++ b/aws/resources/ec2_internet_gateway_test.go
@@ -38,6 +38,9 @@ func TestEc2InternetGateway_GetAll(t *testing.T) {
 
 	t.Parallel()
 
+	// Set excludeFirstSeenTag to false for testing
+	ctx := context.WithValue(context.Background(), util.ExcludeFirstSeenTagKey, false)
+
 	var (
 		now      = time.Now()
 		gateway1 = "igw-0b44cfa6103932e1d001"
@@ -82,14 +85,17 @@ func TestEc2InternetGateway_GetAll(t *testing.T) {
 	igw.BaseAwsResource.Init(nil)
 
 	tests := map[string]struct {
+		ctx       context.Context
 		configObj config.ResourceType
 		expected  []string
 	}{
 		"emptyFilter": {
+			ctx:       ctx,
 			configObj: config.ResourceType{},
 			expected:  []string{gateway1, gateway2},
 		},
 		"nameExclusionFilter": {
+			ctx: ctx,
 			configObj: config.ResourceType{
 				ExcludeRule: config.FilterRule{
 					NamesRegExp: []config.Expression{{
@@ -99,6 +105,7 @@ func TestEc2InternetGateway_GetAll(t *testing.T) {
 			expected: []string{gateway2},
 		},
 		"timeAfterExclusionFilter": {
+			ctx: ctx,
 			configObj: config.ResourceType{
 				ExcludeRule: config.FilterRule{
 					TimeAfter: awsgo.Time(now.Add(-1 * time.Hour)),
@@ -108,7 +115,7 @@ func TestEc2InternetGateway_GetAll(t *testing.T) {
 	}
 	for name, tc := range tests {
 		t.Run(name, func(t *testing.T) {
-			names, err := igw.getAll(context.Background(), config.Config{
+			names, err := igw.getAll(tc.ctx, config.Config{
 				InternetGateway: tc.configObj,
 			})
 			require.NoError(t, err)
diff --git a/aws/resources/ec2_ipam_pool_test.go b/aws/resources/ec2_ipam_pool_test.go
index c10882d..ef5d209 100644
--- a/aws/resources/ec2_ipam_pool_test.go
+++ b/aws/resources/ec2_ipam_pool_test.go
@@ -32,6 +32,9 @@ func (m mockedIPAMPools) DeleteIpamPool(params *ec2.DeleteIpamPoolInput) (*ec2.D
 func TestIPAMPool_GetAll(t *testing.T) {
 	t.Parallel()
 
+	// Set excludeFirstSeenTag to false for testing
+	ctx := context.WithValue(context.Background(), util.ExcludeFirstSeenTagKey, false)
+
 	var (
 		now       = time.Now()
 		testId1   = "ipam-pool-0dfc56f901b2c3462"
@@ -76,14 +79,17 @@ func TestIPAMPool_GetAll(t *testing.T) {
 	}
 
 	tests := map[string]struct {
+		ctx       context.Context
 		configObj config.ResourceType
 		expected  []string
 	}{
 		"emptyFilter": {
+			ctx:       ctx,
 			configObj: config.ResourceType{},
 			expected:  []string{testId1, testId2},
 		},
 		"nameExclusionFilter": {
+			ctx: ctx,
 			configObj: config.ResourceType{
 				ExcludeRule: config.FilterRule{
 					NamesRegExp: []config.Expression{{
@@ -93,6 +99,7 @@ func TestIPAMPool_GetAll(t *testing.T) {
 			expected: []string{testId2},
 		},
 		"timeAfterExclusionFilter": {
+			ctx: ctx,
 			configObj: config.ResourceType{
 				ExcludeRule: config.FilterRule{
 					TimeAfter: aws.Time(now.Add(-1 * time.Hour)),
@@ -102,7 +109,7 @@ func TestIPAMPool_GetAll(t *testing.T) {
 	}
 	for name, tc := range tests {
 		t.Run(name, func(t *testing.T) {
-			ids, err := ipam.getAll(context.Background(), config.Config{
+			ids, err := ipam.getAll(tc.ctx, config.Config{
 				EC2IPAMPool: tc.configObj,
 			})
 			require.NoError(t, err)
diff --git a/aws/resources/ec2_ipam_resource_discovery_test.go b/aws/resources/ec2_ipam_resource_discovery_test.go
index cf01a13..9925ed5 100644
--- a/aws/resources/ec2_ipam_resource_discovery_test.go
+++ b/aws/resources/ec2_ipam_resource_discovery_test.go
@@ -32,6 +32,9 @@ func (m mockedIPAMResourceDiscovery) DeleteIpamResourceDiscovery(params *ec2.Del
 func TestIPAMRDiscovery_GetAll(t *testing.T) {
 	t.Parallel()
 
+	// Set excludeFirstSeenTag to false for testing
+	ctx := context.WithValue(context.Background(), util.ExcludeFirstSeenTagKey, false)
+
 	var (
 		now       = time.Now()
 		testId1   = "ipam-res-disco-0dfc56f901b2c3462"
@@ -76,14 +79,17 @@ func TestIPAMRDiscovery_GetAll(t *testing.T) {
 	}
 
 	tests := map[string]struct {
+		ctx       context.Context
 		configObj config.ResourceType
 		expected  []string
 	}{
 		"emptyFilter": {
+			ctx:       ctx,
 			configObj: config.ResourceType{},
 			expected:  []string{testId1, testId2},
 		},
 		"nameExclusionFilter": {
+			ctx: ctx,
 			configObj: config.ResourceType{
 				ExcludeRule: config.FilterRule{
 					NamesRegExp: []config.Expression{{
@@ -93,6 +99,7 @@ func TestIPAMRDiscovery_GetAll(t *testing.T) {
 			expected: []string{testId2},
 		},
 		"timeAfterExclusionFilter": {
+			ctx: ctx,
 			configObj: config.ResourceType{
 				ExcludeRule: config.FilterRule{
 					TimeAfter: aws.Time(now.Add(-1 * time.Hour)),
@@ -102,7 +109,7 @@ func TestIPAMRDiscovery_GetAll(t *testing.T) {
 	}
 	for name, tc := range tests {
 		t.Run(name, func(t *testing.T) {
-			ids, err := ipam.getAll(context.Background(), config.Config{
+			ids, err := ipam.getAll(tc.ctx, config.Config{
 				EC2IPAMResourceDiscovery: tc.configObj,
 			})
 			require.NoError(t, err)
diff --git a/aws/resources/ec2_ipam_scope_test.go b/aws/resources/ec2_ipam_scope_test.go
index 541d2de..19726a9 100644
--- a/aws/resources/ec2_ipam_scope_test.go
+++ b/aws/resources/ec2_ipam_scope_test.go
@@ -32,6 +32,9 @@ func (m mockedIPAMScope) DeleteIpamScope(params *ec2.DeleteIpamScopeInput) (*ec2
 func TestIPAMScope_GetAll(t *testing.T) {
 	t.Parallel()
 
+	// Set excludeFirstSeenTag to false for testing
+	ctx := context.WithValue(context.Background(), util.ExcludeFirstSeenTagKey, false)
+
 	var (
 		now       = time.Now()
 		testId1   = "ipam-scope-0dfc56f901b2c3462"
@@ -76,14 +79,17 @@ func TestIPAMScope_GetAll(t *testing.T) {
 	}
 
 	tests := map[string]struct {
+		ctx       context.Context
 		configObj config.ResourceType
 		expected  []string
 	}{
 		"emptyFilter": {
+			ctx:       ctx,
 			configObj: config.ResourceType{},
 			expected:  []string{testId1, testId2},
 		},
 		"nameExclusionFilter": {
+			ctx: ctx,
 			configObj: config.ResourceType{
 				ExcludeRule: config.FilterRule{
 					NamesRegExp: []config.Expression{{
@@ -93,6 +99,7 @@ func TestIPAMScope_GetAll(t *testing.T) {
 			expected: []string{testId2},
 		},
 		"timeAfterExclusionFilter": {
+			ctx: ctx,
 			configObj: config.ResourceType{
 				ExcludeRule: config.FilterRule{
 					TimeAfter: aws.Time(now.Add(-1 * time.Hour)),
@@ -103,7 +110,7 @@ func TestIPAMScope_GetAll(t *testing.T) {
 
 	for name, tc := range tests {
 		t.Run(name, func(t *testing.T) {
-			ids, err := ipam.getAll(context.Background(), config.Config{
+			ids, err := ipam.getAll(tc.ctx, config.Config{
 				EC2IPAMScope: tc.configObj,
 			})
 			require.NoError(t, err)
diff --git a/aws/resources/ec2_ipam_test.go b/aws/resources/ec2_ipam_test.go
index 139c99a..46f4dcb 100644
--- a/aws/resources/ec2_ipam_test.go
+++ b/aws/resources/ec2_ipam_test.go
@@ -61,6 +61,9 @@ func (m mockedIPAM) DeleteIpam(params *ec2.DeleteIpamInput) (*ec2.DeleteIpamOutp
 func TestIPAM_GetAll(t *testing.T) {
 	t.Parallel()
 
+	// Set excludeFirstSeenTag to false for testing
+	ctx := context.WithValue(context.Background(), util.ExcludeFirstSeenTagKey, false)
+
 	var (
 		now       = time.Now()
 		testId1   = "ipam-0dfc56f901b2c3462"
@@ -105,14 +108,17 @@ func TestIPAM_GetAll(t *testing.T) {
 	}
 
 	tests := map[string]struct {
+		ctx       context.Context
 		configObj config.ResourceType
 		expected  []string
 	}{
 		"emptyFilter": {
+			ctx:       ctx,
 			configObj: config.ResourceType{},
 			expected:  []string{testId1, testId2},
 		},
 		"nameExclusionFilter": {
+			ctx: ctx,
 			configObj: config.ResourceType{
 				ExcludeRule: config.FilterRule{
 					NamesRegExp: []config.Expression{{
@@ -122,6 +128,7 @@ func TestIPAM_GetAll(t *testing.T) {
 			expected: []string{testId2},
 		},
 		"timeAfterExclusionFilter": {
+			ctx: ctx,
 			configObj: config.ResourceType{
 				ExcludeRule: config.FilterRule{
 					TimeAfter: aws.Time(now.Add(-1 * time.Hour)),
@@ -132,7 +139,7 @@ func TestIPAM_GetAll(t *testing.T) {
 
 	for name, tc := range tests {
 		t.Run(name, func(t *testing.T) {
-			ids, err := ipam.getAll(context.Background(), config.Config{
+			ids, err := ipam.getAll(tc.ctx, config.Config{
 				EC2IPAM: tc.configObj,
 			})
 			require.NoError(t, err)
diff --git a/aws/resources/ec2_network_acl_test.go b/aws/resources/ec2_network_acl_test.go
index 0d50222..1ee9757 100644
--- a/aws/resources/ec2_network_acl_test.go
+++ b/aws/resources/ec2_network_acl_test.go
@@ -37,6 +37,9 @@ func (m mockedNetworkACL) ReplaceNetworkAclAssociation(*ec2.ReplaceNetworkAclAss
 
 func TestNetworkAcl_GetAll(t *testing.T) {
 
+	// Set excludeFirstSeenTag to false for testing
+	ctx := context.WithValue(context.Background(), util.ExcludeFirstSeenTagKey, false)
+
 	var (
 		now     = time.Now()
 		testId1 = "acl-09e36c45cbdbfb001"
@@ -83,14 +86,17 @@ func TestNetworkAcl_GetAll(t *testing.T) {
 	resourceObject.BaseAwsResource.Init(nil)
 
 	tests := map[string]struct {
+		ctx       context.Context
 		configObj config.ResourceType
 		expected  []string
 	}{
 		"emptyFilter": {
+			ctx:       ctx,
 			configObj: config.ResourceType{},
 			expected:  []string{testId1, testId2},
 		},
 		"nameExclusionFilter": {
+			ctx: ctx,
 			configObj: config.ResourceType{
 				ExcludeRule: config.FilterRule{
 					NamesRegExp: []config.Expression{{
@@ -100,6 +106,7 @@ func TestNetworkAcl_GetAll(t *testing.T) {
 			expected: []string{testId2},
 		},
 		"nameInclusionFilter": {
+			ctx: ctx,
 			configObj: config.ResourceType{
 				IncludeRule: config.FilterRule{
 					NamesRegExp: []config.Expression{{
@@ -109,6 +116,7 @@ func TestNetworkAcl_GetAll(t *testing.T) {
 			expected: []string{testId1},
 		},
 		"timeAfterExclusionFilter": {
+			ctx: ctx,
 			configObj: config.ResourceType{
 				ExcludeRule: config.FilterRule{
 					TimeAfter: aws.Time(now),
@@ -120,7 +128,7 @@ func TestNetworkAcl_GetAll(t *testing.T) {
 	}
 	for name, tc := range tests {
 		t.Run(name, func(t *testing.T) {
-			names, err := resourceObject.getAll(context.Background(), config.Config{
+			names, err := resourceObject.getAll(tc.ctx, config.Config{
 				NetworkACL: tc.configObj,
 			})
 			require.NoError(t, err)
diff --git a/aws/resources/ec2_network_interface_test.go b/aws/resources/ec2_network_interface_test.go
index 97f652e..e466e57 100644
--- a/aws/resources/ec2_network_interface_test.go
+++ b/aws/resources/ec2_network_interface_test.go
@@ -50,6 +50,9 @@ func (m mockedNetworkInterface) WaitUntilInstanceTerminated(*ec2.DescribeInstanc
 
 func TestNetworkInterface_GetAll(t *testing.T) {
 
+	// Set excludeFirstSeenTag to false for testing
+	ctx := context.WithValue(context.Background(), util.ExcludeFirstSeenTagKey, false)
+
 	var (
 		now     = time.Now()
 		testId1 = "eni-09e36c45cbdbfb001"
@@ -96,14 +99,17 @@ func TestNetworkInterface_GetAll(t *testing.T) {
 	resourceObject.BaseAwsResource.Init(nil)
 
 	tests := map[string]struct {
+		ctx       context.Context
 		configObj config.ResourceType
 		expected  []string
 	}{
 		"emptyFilter": {
+			ctx:       ctx,
 			configObj: config.ResourceType{},
 			expected:  []string{testId1, testId2},
 		},
 		"nameExclusionFilter": {
+			ctx: ctx,
 			configObj: config.ResourceType{
 				ExcludeRule: config.FilterRule{
 					NamesRegExp: []config.Expression{{
@@ -113,6 +119,7 @@ func TestNetworkInterface_GetAll(t *testing.T) {
 			expected: []string{testId2},
 		},
 		"nameInclusionFilter": {
+			ctx: ctx,
 			configObj: config.ResourceType{
 				IncludeRule: config.FilterRule{
 					NamesRegExp: []config.Expression{{
@@ -122,6 +129,7 @@ func TestNetworkInterface_GetAll(t *testing.T) {
 			expected: []string{testId1},
 		},
 		"timeAfterExclusionFilter": {
+			ctx: ctx,
 			configObj: config.ResourceType{
 				ExcludeRule: config.FilterRule{
 					TimeAfter: awsgo.Time(now),
@@ -133,7 +141,7 @@ func TestNetworkInterface_GetAll(t *testing.T) {
 	}
 	for name, tc := range tests {
 		t.Run(name, func(t *testing.T) {
-			names, err := resourceObject.getAll(context.Background(), config.Config{
+			names, err := resourceObject.getAll(tc.ctx, config.Config{
 				NetworkInterface: tc.configObj,
 			})
 			require.NoError(t, err)
diff --git a/aws/resources/ec2_subnet_test.go b/aws/resources/ec2_subnet_test.go
index 9e7b07f..46dd5c8 100644
--- a/aws/resources/ec2_subnet_test.go
+++ b/aws/resources/ec2_subnet_test.go
@@ -32,6 +32,9 @@ func TestEc2Subnets_GetAll(t *testing.T) {
 
 	t.Parallel()
 
+	// Set excludeFirstSeenTag to false for testing
+	ctx := context.WithValue(context.Background(), util.ExcludeFirstSeenTagKey, false)
+
 	var (
 		now       = time.Now()
 		subnet1   = "subnet-0631b58700ba3db41"
@@ -75,10 +78,12 @@ func TestEc2Subnets_GetAll(t *testing.T) {
 	ec2subnet.BaseAwsResource.Init(nil)
 
 	tests := map[string]struct {
+		ctx       context.Context
 		configObj config.EC2ResourceType
 		expected  []string
 	}{
 		"emptyFilter": {
+			ctx:       ctx,
 			configObj: config.EC2ResourceType{},
 			expected:  []string{subnet1, subnet2},
 		},
@@ -95,6 +100,7 @@ func TestEc2Subnets_GetAll(t *testing.T) {
 			expected: []string{subnet2},
 		},
 		"timeAfterExclusionFilter": {
+			ctx: ctx,
 			configObj: config.EC2ResourceType{
 				ResourceType: config.ResourceType{
 					ExcludeRule: config.FilterRule{
@@ -106,7 +112,7 @@ func TestEc2Subnets_GetAll(t *testing.T) {
 	}
 	for name, tc := range tests {
 		t.Run(name, func(t *testing.T) {
-			names, err := ec2subnet.getAll(context.Background(), config.Config{
+			names, err := ec2subnet.getAll(tc.ctx, config.Config{
 				EC2Subnet: tc.configObj,
 			})
 			require.NoError(t, err)
diff --git a/aws/resources/ec2_vpc_test.go b/aws/resources/ec2_vpc_test.go
index 9e1e2c7..d68e9db 100644
--- a/aws/resources/ec2_vpc_test.go
+++ b/aws/resources/ec2_vpc_test.go
@@ -60,6 +60,9 @@ func TestEC2VPC_GetAll(t *testing.T) {
 
 	t.Parallel()
 
+	// Set excludeFirstSeenTag to false for testing
+	ctx := context.WithValue(context.Background(), util.ExcludeFirstSeenTagKey, false)
+
 	testName1 := "test-vpc-name1"
 	testName2 := "test-vpc-name2"
 	now := time.Now()
@@ -101,14 +104,17 @@ func TestEC2VPC_GetAll(t *testing.T) {
 	}
 
 	tests := map[string]struct {
+		ctx       context.Context
 		configObj config.EC2ResourceType
 		expected  []string
 	}{
 		"emptyFilter": {
+			ctx:       ctx,
 			configObj: config.EC2ResourceType{},
 			expected:  []string{testId1, testId2},
 		},
 		"nameExclusionFilter": {
+			ctx: ctx,
 			configObj: config.EC2ResourceType{
 				ResourceType: config.ResourceType{
 					ExcludeRule: config.FilterRule{
@@ -121,6 +127,7 @@ func TestEC2VPC_GetAll(t *testing.T) {
 			expected: []string{testId2},
 		},
 		"timeAfterExclusionFilter": {
+			ctx: ctx,
 			configObj: config.EC2ResourceType{
 				ResourceType: config.ResourceType{
 					ExcludeRule: config.FilterRule{
@@ -132,7 +139,7 @@ func TestEC2VPC_GetAll(t *testing.T) {
 	}
 	for name, tc := range tests {
 		t.Run(name, func(t *testing.T) {
-			names, err := vpc.getAll(context.Background(), config.Config{
+			names, err := vpc.getAll(tc.ctx, config.Config{
 				VPC: tc.configObj,
 			})
 			require.NoError(t, err)
diff --git a/aws/resources/ecs_cluster_test.go b/aws/resources/ecs_cluster_test.go
index e9824ee..ba3910f 100644
--- a/aws/resources/ecs_cluster_test.go
+++ b/aws/resources/ecs_cluster_test.go
@@ -56,6 +56,9 @@ func TestEC2Cluster_GetAll(t *testing.T) {
 
 	t.Parallel()
 
+	// Set excludeFirstSeenTag to false for testing
+	ctx := context.WithValue(context.Background(), util.ExcludeFirstSeenTagKey, false)
+
 	testArn1 := "arn:aws:ecs:us-east-1:123456789012:cluster/cluster1"
 	testArn2 := "arn:aws:ecs:us-east-1:123456789012:cluster/cluster2"
 	testName1 := "cluster1"
@@ -103,14 +106,17 @@ func TestEC2Cluster_GetAll(t *testing.T) {
 	}
 
 	tests := map[string]struct {
+		ctx       context.Context
 		configObj config.ResourceType
 		expected  []string
 	}{
 		"emptyFilter": {
+			ctx:       ctx,
 			configObj: config.ResourceType{},
 			expected:  []string{testArn1, testArn2},
 		},
 		"nameExclusionFilter": {
+			ctx: ctx,
 			configObj: config.ResourceType{
 				ExcludeRule: config.FilterRule{
 					NamesRegExp: []config.Expression{{
@@ -120,6 +126,7 @@ func TestEC2Cluster_GetAll(t *testing.T) {
 			expected: []string{testArn2},
 		},
 		"timeAfterExclusionFilter": {
+			ctx: ctx,
 			configObj: config.ResourceType{
 				ExcludeRule: config.FilterRule{
 					TimeAfter: aws.Time(now.Add(-1 * time.Hour)),
@@ -129,7 +136,7 @@ func TestEC2Cluster_GetAll(t *testing.T) {
 	}
 	for name, tc := range tests {
 		t.Run(name, func(t *testing.T) {
-			names, err := ec.getAll(context.Background(), config.Config{
+			names, err := ec.getAll(tc.ctx, config.Config{
 				ECSCluster: tc.configObj,
 			})
 			require.NoError(t, err)
diff --git a/aws/resources/eip_test.go b/aws/resources/eip_test.go
index 226f95b..6d5ba7a 100644
--- a/aws/resources/eip_test.go
+++ b/aws/resources/eip_test.go
@@ -32,6 +32,9 @@ func TestEIPAddress_GetAll(t *testing.T) {
 
 	t.Parallel()
 
+	// Set excludeFirstSeenTag to false for testing
+	ctx := context.WithValue(context.Background(), util.ExcludeFirstSeenTagKey, false)
+
 	now := time.Now()
 	testName1 := "test-eip1"
 	testAllocId1 := "alloc1"
@@ -73,14 +76,17 @@ func TestEIPAddress_GetAll(t *testing.T) {
 	}
 
 	tests := map[string]struct {
+		ctx       context.Context
 		configObj config.ResourceType
 		expected  []string
 	}{
 		"emptyFilter": {
+			ctx:       ctx,
 			configObj: config.ResourceType{},
 			expected:  []string{testAllocId1, testAllocId2},
 		},
 		"nameExclusionFilter": {
+			ctx: ctx,
 			configObj: config.ResourceType{
 				ExcludeRule: config.FilterRule{
 					NamesRegExp: []config.Expression{{
@@ -90,6 +96,7 @@ func TestEIPAddress_GetAll(t *testing.T) {
 			expected: []string{testAllocId2},
 		},
 		"timeAfterExclusionFilter": {
+			ctx: ctx,
 			configObj: config.ResourceType{
 				ExcludeRule: config.FilterRule{
 					TimeAfter: aws.Time(now.Add(-1 * time.Hour)),
@@ -99,7 +106,7 @@ func TestEIPAddress_GetAll(t *testing.T) {
 	}
 	for name, tc := range tests {
 		t.Run(name, func(t *testing.T) {
-			names, err := ea.getAll(context.Background(), config.Config{
+			names, err := ea.getAll(tc.ctx, config.Config{
 				ElasticIP: tc.configObj,
 			})
 			require.NoError(t, err)
diff --git a/aws/resources/network_firewall_policy_test.go b/aws/resources/network_firewall_policy_test.go
index 8858760..17d8004 100644
--- a/aws/resources/network_firewall_policy_test.go
+++ b/aws/resources/network_firewall_policy_test.go
@@ -56,6 +56,7 @@ func TestNetworkFirewallPolicy_GetAll(t *testing.T) {
 		testId2   = "test-network-firewall-id2"
 		testName1 = "test-network-firewall-1"
 		testName2 = "test-network-firewall-2"
+		ctx       = context.WithValue(context.Background(), util.ExcludeFirstSeenTagKey, false)
 	)
 
 	nfw := NetworkFirewallPolicy{
@@ -134,7 +135,7 @@ func TestNetworkFirewallPolicy_GetAll(t *testing.T) {
 	}
 	for name, tc := range tests {
 		t.Run(name, func(t *testing.T) {
-			names, err := nfw.getAll(context.Background(), config.Config{
+			names, err := nfw.getAll(ctx, config.Config{
 				NetworkFirewallPolicy: tc.configObj,
 			})
 			require.NoError(t, err)
diff --git a/aws/resources/network_firewall_rule_group_test.go b/aws/resources/network_firewall_rule_group_test.go
index 3c46ac2..2f378d6 100644
--- a/aws/resources/network_firewall_rule_group_test.go
+++ b/aws/resources/network_firewall_rule_group_test.go
@@ -56,6 +56,7 @@ func TestNetworkFirewallRuleGroup_GetAll(t *testing.T) {
 		testId2   = "test-network-firewall-id2"
 		testName1 = "test-network-firewall-1"
 		testName2 = "test-network-firewall-2"
+		ctx       = context.WithValue(context.Background(), util.ExcludeFirstSeenTagKey, false)
 	)
 
 	nfw := NetworkFirewallRuleGroup{
@@ -135,7 +136,7 @@ func TestNetworkFirewallRuleGroup_GetAll(t *testing.T) {
 	}
 	for name, tc := range tests {
 		t.Run(name, func(t *testing.T) {
-			names, err := nfw.getAll(context.Background(), config.Config{
+			names, err := nfw.getAll(ctx, config.Config{
 				NetworkFirewallRuleGroup: tc.configObj,
 			})
 			require.NoError(t, err)
diff --git a/aws/resources/network_firewall_test.go b/aws/resources/network_firewall_test.go
index 015c9b8..c76d673 100644
--- a/aws/resources/network_firewall_test.go
+++ b/aws/resources/network_firewall_test.go
@@ -56,6 +56,7 @@ func TestNetworkFirewall_GetAll(t *testing.T) {
 		testId2   = "test-network-firewall-id2"
 		testName1 = "test-network-firewall-1"
 		testName2 = "test-network-firewall-2"
+		ctx       = context.WithValue(context.Background(), util.ExcludeFirstSeenTagKey, false)
 	)
 
 	nfw := NetworkFirewall{
@@ -134,7 +135,7 @@ func TestNetworkFirewall_GetAll(t *testing.T) {
 	}
 	for name, tc := range tests {
 		t.Run(name, func(t *testing.T) {
-			names, err := nfw.getAll(context.Background(), config.Config{
+			names, err := nfw.getAll(ctx, config.Config{
 				NetworkFirewall: tc.configObj,
 			})
 			require.NoError(t, err)
diff --git a/aws/resources/network_firewall_tls_config_test.go b/aws/resources/network_firewall_tls_config_test.go
index c961998..0138449 100644
--- a/aws/resources/network_firewall_tls_config_test.go
+++ b/aws/resources/network_firewall_tls_config_test.go
@@ -56,6 +56,7 @@ func TestNetworkFirewallTLSConfig_GetAll(t *testing.T) {
 		testId2   = "test-network-firewall-id2"
 		testName1 = "test-network-firewall-1"
 		testName2 = "test-network-firewall-2"
+		ctx       = context.WithValue(context.Background(), util.ExcludeFirstSeenTagKey, false)
 	)
 
 	nfw := NetworkFirewallTLSConfig{
@@ -134,7 +135,7 @@ func TestNetworkFirewallTLSConfig_GetAll(t *testing.T) {
 	}
 	for name, tc := range tests {
 		t.Run(name, func(t *testing.T) {
-			names, err := nfw.getAll(context.Background(), config.Config{
+			names, err := nfw.getAll(ctx, config.Config{
 				NetworkFirewallTLSConfig: tc.configObj,
 			})
 			require.NoError(t, err)
diff --git a/aws/resources/opensearch_test.go b/aws/resources/opensearch_test.go
index cab5324..cb55faf 100644
--- a/aws/resources/opensearch_test.go
+++ b/aws/resources/opensearch_test.go
@@ -42,6 +42,9 @@ func TestOpenSearch_GetAll(t *testing.T) {
 
 	t.Parallel()
 
+	// Set excludeFirstSeenTag to false for testing
+	ctx := context.WithValue(context.Background(), util.ExcludeFirstSeenTagKey, false)
+
 	testName1 := "test-domain1"
 	testName2 := "test-domain2"
 	now := time.Now()
@@ -85,14 +88,17 @@ func TestOpenSearch_GetAll(t *testing.T) {
 	}
 
 	tests := map[string]struct {
+		ctx       context.Context
 		configObj config.ResourceType
 		expected  []string
 	}{
 		"emptyFilter": {
+			ctx:       ctx,
 			configObj: config.ResourceType{},
 			expected:  []string{testName1, testName2},
 		},
 		"nameExclusionFilter": {
+			ctx: ctx,
 			configObj: config.ResourceType{
 				ExcludeRule: config.FilterRule{
 					NamesRegExp: []config.Expression{{
@@ -102,6 +108,7 @@ func TestOpenSearch_GetAll(t *testing.T) {
 			expected: []string{testName2},
 		},
 		"timeAfterExclusionFilter": {
+			ctx: ctx,
 			configObj: config.ResourceType{
 				ExcludeRule: config.FilterRule{
 					TimeAfter: aws.Time(now.Add(-1 * time.Hour)),
@@ -111,7 +118,7 @@ func TestOpenSearch_GetAll(t *testing.T) {
 	}
 	for name, tc := range tests {
 		t.Run(name, func(t *testing.T) {
-			names, err := osd.getAll(context.Background(), config.Config{
+			names, err := osd.getAll(tc.ctx, config.Config{
 				OpenSearchDomain: tc.configObj,
 			})
 			require.NoError(t, err)
diff --git a/aws/resources/security_group_test.go b/aws/resources/security_group_test.go
index aa912c2..7755064 100644
--- a/aws/resources/security_group_test.go
+++ b/aws/resources/security_group_test.go
@@ -44,6 +44,9 @@ func TestSecurityGroup_GetAll(t *testing.T) {
 		now       = time.Now()
 	)
 
+	// Set excludeFirstSeenTag to false for testing
+	ctx := context.WithValue(context.Background(), util.ExcludeFirstSeenTagKey, false)
+
 	sg := SecurityGroup{
 		BaseAwsResource: BaseAwsResource{
 			Nukables: map[string]error{
@@ -88,14 +91,17 @@ func TestSecurityGroup_GetAll(t *testing.T) {
 	}
 
 	tests := map[string]struct {
+		ctx       context.Context
 		configObj config.EC2ResourceType
 		expected  []string
 	}{
 		"emptyFilter": {
+			ctx:ctx,
 			configObj: config.EC2ResourceType{},
 			expected:  []string{testId1, testId2},
 		},
 		"nameExclusionFilter": {
+			ctx:ctx,
 			configObj: config.EC2ResourceType{
 				ResourceType: config.ResourceType{
 					ExcludeRule: config.FilterRule{
@@ -107,6 +113,7 @@ func TestSecurityGroup_GetAll(t *testing.T) {
 			expected: []string{testId2},
 		},
 		"timeAfterExclusionFilter": {
+			ctx:ctx,
 			configObj: config.EC2ResourceType{
 				ResourceType: config.ResourceType{
 					ExcludeRule: config.FilterRule{
@@ -120,7 +127,7 @@ func TestSecurityGroup_GetAll(t *testing.T) {
 	for name, tc := range tests {
 		t.Run(name, func(t *testing.T) {
 			t.Parallel()
-			names, err := sg.getAll(context.Background(), config.Config{
+			names, err := sg.getAll(tc.ctx, config.Config{
 				SecurityGroup: tc.configObj,
 			})
 			require.NoError(t, err)
diff --git a/aws/resources/sns_test.go b/aws/resources/sns_test.go
index c441beb..9ec54f0 100644
--- a/aws/resources/sns_test.go
+++ b/aws/resources/sns_test.go
@@ -2,11 +2,14 @@ package resources
 
 import (
 	"context"
-	"github.com/aws/aws-sdk-go/service/sns/snsiface"
 	"regexp"
 	"testing"
 	"time"
 
+	"github.com/aws/aws-sdk-go/service/sns/snsiface"
+
+	"github.com/gruntwork-io/cloud-nuke/util"
+
 	"github.com/aws/aws-sdk-go/aws"
 	awsgo "github.com/aws/aws-sdk-go/aws"
 	"github.com/aws/aws-sdk-go/service/sns"
@@ -41,6 +44,9 @@ func TestSNS_GetAll(t *testing.T) {
 
 	t.Parallel()
 
+	// Set excludeFirstSeenTag to false for testing
+	ctx := context.WithValue(context.Background(), util.ExcludeFirstSeenTagKey, false)
+
 	testTopic1 := "arn:aws:sns:us-east-1:123456789012:MyTopic1"
 	testTopic2 := "arn:aws:sns:us-east-1:123456789012:MyTopic2"
 	now := time.Now()
@@ -70,14 +76,17 @@ func TestSNS_GetAll(t *testing.T) {
 	}
 
 	tests := map[string]struct {
+		ctx       context.Context
 		configObj config.ResourceType
 		expected  []string
 	}{
 		"emptyFilter": {
+			ctx:       ctx,
 			configObj: config.ResourceType{},
 			expected:  []string{testTopic1, testTopic2},
 		},
 		"nameExclusionFilter": {
+			ctx: ctx,
 			configObj: config.ResourceType{
 				ExcludeRule: config.FilterRule{
 					NamesRegExp: []config.Expression{{
@@ -87,6 +96,7 @@ func TestSNS_GetAll(t *testing.T) {
 			expected: []string{testTopic2},
 		},
 		"timeAfterExclusionFilter": {
+			ctx: ctx,
 			configObj: config.ResourceType{
 				ExcludeRule: config.FilterRule{
 					TimeAfter: aws.Time(now.Add(-1 * time.Hour)),
@@ -96,7 +106,7 @@ func TestSNS_GetAll(t *testing.T) {
 	}
 	for name, tc := range tests {
 		t.Run(name, func(t *testing.T) {
-			names, err := s.getAll(context.Background(), config.Config{
+			names, err := s.getAll(tc.ctx, config.Config{
 				SNS: tc.configObj,
 			})
 			require.NoError(t, err)