Repository: gruntwork-io/cloud-nuke
Pull request #769 (2024-10-01 19:58:20)

port SQS & SNS to awsSDKv2
## Description

- Migrate **SQS** & **SNS** to **awsSDKv2**
- add report to track progress off **awsSDKv2** migration

## TODOs

Read the [Gruntwork contribution guidelines](https://gruntwork.notion.site/Gruntwork-Coding-Methodology-02fdcd6e4b004e818553684760bf691e).

- [x] Update the docs.
- [x] Run the relevant tests successfully, including pre-commit checks.
- [x] 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.
- [x] 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)

Updated **SQS** & **SNS** to **AWS SDK v2**.
Added **AWS SDK v2** migration report.

### Migration Guide

n/a




Patch:
diff --git a/README.md b/README.md
index d2aa4a1..8576771 100644
--- a/README.md
+++ b/README.md
@@ -781,6 +781,9 @@ All other resources that get created within VPCs must be cleaned up prior to run
 
 Happy Nuking!!!
 
+## AWS SDKv2 migration
+We are in progress of migrating towards stable AWS SDKv2, progress can be checked here [migration status](./v2_migration_report/output.md).
+
 ## Credentials
 
 ### AWS
diff --git a/aws/resources/sns.go b/aws/resources/sns.go
index 38a3202..6bb1641 100644
--- a/aws/resources/sns.go
+++ b/aws/resources/sns.go
@@ -6,13 +6,13 @@ import (
 	"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/aws/aws-sdk-go-v2/aws"
+	"github.com/aws/aws-sdk-go-v2/service/sns"
+	"github.com/aws/aws-sdk-go-v2/service/sns/types"
 	"github.com/gruntwork-io/cloud-nuke/config"
 	"github.com/gruntwork-io/cloud-nuke/logging"
 	"github.com/gruntwork-io/cloud-nuke/report"
+	"github.com/gruntwork-io/cloud-nuke/util"
 	"github.com/gruntwork-io/go-commons/errors"
 	"github.com/hashicorp/go-multierror"
 )
@@ -30,7 +30,14 @@ func (s *SNSTopic) getAll(c context.Context, configObj config.Config) ([]*string
 	if err != nil {
 		return nil, errors.WithStackTrace(err)
 	}
-	err = s.Client.ListTopicsPagesWithContext(s.Context, &sns.ListTopicsInput{}, func(page *sns.ListTopicsOutput, lastPage bool) bool {
+
+	paginator := sns.NewListTopicsPaginator(s.Client, &sns.ListTopicsInput{})
+	for paginator.HasMorePages() {
+		page, errGetPage := paginator.NextPage(c)
+		if errGetPage != nil {
+			return nil, errors.WithStackTrace(errGetPage)
+		}
+
 		for _, topic := range page.Topics {
 			if !excludeFirstSeenTag {
 				firstSeenTime, err = s.getFirstSeenTag(*topic.TopicArn)
@@ -61,9 +68,7 @@ func (s *SNSTopic) getAll(c context.Context, configObj config.Config) ([]*string
 				snsTopics = append(snsTopics, topic.TopicArn)
 			}
 		}
-
-		return !lastPage
-	})
+	}
 
 	if err != nil {
 		return nil, errors.WithStackTrace(err)
@@ -72,12 +77,14 @@ func (s *SNSTopic) getAll(c context.Context, configObj config.Config) ([]*string
 	return snsTopics, nil
 }
 
-// getFirstSeenSNSTopicTag will retrive the time that the topic was first seen, otherwise returning nil if the topic has not been
+// getFirstSeenSNSTopicTag will retrieve the time that the topic was first seen, otherwise returning nil if the topic has not been
 // seen before.
 func (s *SNSTopic) getFirstSeenTag(topicArn string) (*time.Time, error) {
-	response, err := s.Client.ListTagsForResource(&sns.ListTagsForResourceInput{
-		ResourceArn: &topicArn,
-	})
+	response, err := s.Client.ListTagsForResource(
+		s.Context,
+		&sns.ListTagsForResourceInput{
+			ResourceArn: &topicArn,
+		})
 	if err != nil {
 		return nil, err
 	}
@@ -99,9 +106,10 @@ func (s *SNSTopic) getFirstSeenTag(topicArn string) (*time.Time, error) {
 // setFirstSeenSNSTopic will append a tag to the SNS Topic that details the first seen time.
 func (s *SNSTopic) setFirstSeenTag(topicArn string, value time.Time) error {
 	_, err := s.Client.TagResource(
+		s.Context,
 		&sns.TagResourceInput{
 			ResourceArn: &topicArn,
-			Tags: []*sns.Tag{
+			Tags: []types.Tag{
 				{
 					Key:   aws.String(util.FirstSeenTagKey),
 					Value: aws.String(util.FormatTimestamp(value)),
@@ -172,9 +180,9 @@ func (s *SNSTopic) deleteAsync(wg *sync.WaitGroup, errChan chan error, topicArn
 		TopicArn: topicArn,
 	}
 
-	logging.Debugf("Deleting SNS Topic (arn=%s) in region: %s", aws.StringValue(topicArn), s.Region)
+	logging.Debugf("Deleting SNS Topic (arn=%s) in region: %s", aws.ToString(topicArn), s.Region)
 
-	_, err := s.Client.DeleteTopicWithContext(s.Context, deleteParam)
+	_, err := s.Client.DeleteTopic(s.Context, deleteParam)
 
 	errChan <- err
 
@@ -187,8 +195,8 @@ func (s *SNSTopic) deleteAsync(wg *sync.WaitGroup, errChan chan error, topicArn
 	report.Record(e)
 
 	if err == nil {
-		logging.Debugf("[OK] Deleted SNS Topic (arn=%s) in region: %s", aws.StringValue(topicArn), s.Region)
+		logging.Debugf("[OK] Deleted SNS Topic (arn=%s) in region: %s", aws.ToString(topicArn), s.Region)
 	} else {
-		logging.Debugf("[Failed] Error deleting SNS Topic (arn=%s) in %s", aws.StringValue(topicArn), s.Region)
+		logging.Debugf("[Failed] Error deleting SNS Topic (arn=%s) in %s", aws.ToString(topicArn), s.Region)
 	}
 }
diff --git a/aws/resources/sns_types.go b/aws/resources/sns_types.go
index 0eb7905..77fced4 100644
--- a/aws/resources/sns_types.go
+++ b/aws/resources/sns_types.go
@@ -3,25 +3,32 @@ package resources
 import (
 	"context"
 
-	awsgo "github.com/aws/aws-sdk-go/aws"
-	"github.com/aws/aws-sdk-go/aws/session"
-	"github.com/aws/aws-sdk-go/service/sns"
-	"github.com/aws/aws-sdk-go/service/sns/snsiface"
+	"github.com/aws/aws-sdk-go-v2/aws"
+	"github.com/aws/aws-sdk-go-v2/service/sns"
 	"github.com/gruntwork-io/cloud-nuke/config"
 	"github.com/gruntwork-io/go-commons/errors"
 )
 
+type SNSTopicAPI interface {
+	DeleteTopic(ctx context.Context, params *sns.DeleteTopicInput, optFns ...func(*sns.Options)) (*sns.DeleteTopicOutput, error)
+	ListTopics(context.Context, *sns.ListTopicsInput, ...func(*sns.Options)) (*sns.ListTopicsOutput, error)
+	ListTagsForResource(ctx context.Context, params *sns.ListTagsForResourceInput, optFns ...func(*sns.Options)) (*sns.ListTagsForResourceOutput, error)
+	TagResource(ctx context.Context, params *sns.TagResourceInput, optFns ...func(*sns.Options)) (*sns.TagResourceOutput, error)
+}
+
 type SNSTopic struct {
 	BaseAwsResource
-	Client snsiface.SNSAPI
+	Client SNSTopicAPI
 	Region string
 	Arns   []string
 }
 
-func (s *SNSTopic) Init(session *session.Session) {
-	s.Client = sns.New(session)
+func (s *SNSTopic) InitV2(cfg aws.Config) {
+	s.Client = sns.NewFromConfig(cfg)
 }
 
+func (s *SNSTopic) IsUsingV2() bool { return true }
+
 func (s *SNSTopic) ResourceName() string {
 	return "snstopic"
 }
@@ -44,12 +51,12 @@ func (s *SNSTopic) GetAndSetIdentifiers(c context.Context, configObj config.Conf
 		return nil, err
 	}
 
-	s.Arns = awsgo.StringValueSlice(identifiers)
+	s.Arns = aws.ToStringSlice(identifiers)
 	return s.Arns, nil
 }
 
 func (s *SNSTopic) Nuke(identifiers []string) error {
-	if err := s.nukeAll(awsgo.StringSlice(identifiers)); err != nil {
+	if err := s.nukeAll(aws.StringSlice(identifiers)); err != nil {
 		return errors.WithStackTrace(err)
 	}
 	return nil
diff --git a/aws/resources/sqs.go b/aws/resources/sqs.go
index 84a56c8..effdb99 100644
--- a/aws/resources/sqs.go
+++ b/aws/resources/sqs.go
@@ -2,12 +2,13 @@ package resources
 
 import (
 	"context"
+	"fmt"
 	"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"
+	"github.com/aws/aws-sdk-go-v2/aws"
+	"github.com/aws/aws-sdk-go-v2/service/sqs"
+	"github.com/aws/aws-sdk-go-v2/service/sqs/types"
 	"github.com/gruntwork-io/cloud-nuke/config"
 	"github.com/gruntwork-io/cloud-nuke/logging"
 	"github.com/gruntwork-io/cloud-nuke/report"
@@ -15,19 +16,20 @@ import (
 )
 
 // Returns a formatted string of SQS Queue URLs
-func (sq *SqsQueue) getAll(c context.Context, configObj config.Config) ([]*string, error) {
-	result := []*string{}
-	paginator := func(output *sqs.ListQueuesOutput, lastPage bool) bool {
-		result = append(result, output.QueueUrls...)
-		return !lastPage
-	}
+func (sq *SqsQueue) getAll(ctx context.Context, configObj config.Config) ([]*string, error) {
+	var result []*string
 
-	param := &sqs.ListQueuesInput{
-		MaxResults: awsgo.Int64(10),
-	}
-	err := sq.Client.ListQueuesPagesWithContext(sq.Context, param, paginator)
-	if err != nil {
-		return nil, errors.WithStackTrace(err)
+	paginator := sqs.NewListQueuesPaginator(sq.Client, &sqs.ListQueuesInput{
+		MaxResults: aws.Int32(10),
+	})
+
+	for paginator.HasMorePages() {
+		page, err := paginator.NextPage(ctx)
+		if err != nil {
+			return nil, errors.WithStackTrace(err)
+		}
+
+		result = append(result, aws.StringSlice(page.QueueUrls)...)
 	}
 
 	var urls []*string
@@ -35,15 +37,18 @@ func (sq *SqsQueue) getAll(c context.Context, configObj config.Config) ([]*strin
 	for _, queue := range result {
 		param := &sqs.GetQueueAttributesInput{
 			QueueUrl:       queue,
-			AttributeNames: awsgo.StringSlice([]string{"CreatedTimestamp"}),
+			AttributeNames: []types.QueueAttributeName{types.QueueAttributeNameCreatedTimestamp},
 		}
-		queueAttributes, err := sq.Client.GetQueueAttributesWithContext(sq.Context, param)
+		queueAttributes, err := sq.Client.GetQueueAttributes(ctx, param)
 		if err != nil {
 			return nil, errors.WithStackTrace(err)
 		}
 
 		// Convert string timestamp to int64 and then to time.Time
-		createdAt := *queueAttributes.Attributes["CreatedTimestamp"]
+		createdAt, ok := queueAttributes.Attributes["CreatedTimestamp"]
+		if !ok {
+			return nil, errors.WithStackTrace(fmt.Errorf("expected to find CreatedTimestamp attribute"))
+		}
 		createdAtInt, err := strconv.ParseInt(createdAt, 10, 64)
 		if err != nil {
 			return nil, errors.WithStackTrace(err)
@@ -76,11 +81,11 @@ func (sq *SqsQueue) nukeAll(urls []*string) error {
 			QueueUrl: url,
 		}
 
-		_, err := sq.Client.DeleteQueueWithContext(sq.Context, params)
+		_, err := sq.Client.DeleteQueue(sq.Context, params)
 
 		// Record status of this resource
 		e := report.Entry{
-			Identifier:   aws.StringValue(url),
+			Identifier:   aws.ToString(url),
 			ResourceType: "SQS Queue",
 			Error:        err,
 		}
diff --git a/aws/resources/sqs_types.go b/aws/resources/sqs_types.go
index 6ca4e62..fd36a61 100644
--- a/aws/resources/sqs_types.go
+++ b/aws/resources/sqs_types.go
@@ -3,26 +3,32 @@ package resources
 import (
 	"context"
 
-	awsgo "github.com/aws/aws-sdk-go/aws"
-	"github.com/aws/aws-sdk-go/aws/session"
-	"github.com/aws/aws-sdk-go/service/sqs"
-	"github.com/aws/aws-sdk-go/service/sqs/sqsiface"
+	"github.com/aws/aws-sdk-go-v2/aws"
+	"github.com/aws/aws-sdk-go-v2/service/sqs"
 	"github.com/gruntwork-io/cloud-nuke/config"
 	"github.com/gruntwork-io/go-commons/errors"
 )
 
+type SqsQueueAPI interface {
+	DeleteQueue(ctx context.Context, params *sqs.DeleteQueueInput, optFns ...func(*sqs.Options)) (*sqs.DeleteQueueOutput, error)
+	GetQueueAttributes(ctx context.Context, params *sqs.GetQueueAttributesInput, optFns ...func(*sqs.Options)) (*sqs.GetQueueAttributesOutput, error)
+	ListQueues(context.Context, *sqs.ListQueuesInput, ...func(*sqs.Options)) (*sqs.ListQueuesOutput, error)
+}
+
 // SqsQueue - represents all sqs queues
 type SqsQueue struct {
 	BaseAwsResource
-	Client    sqsiface.SQSAPI
+	Client    SqsQueueAPI
 	Region    string
 	QueueUrls []string
 }
 
-func (sq *SqsQueue) Init(session *session.Session) {
-	sq.Client = sqs.New(session)
+func (sq *SqsQueue) InitV2(cfg aws.Config) {
+	sq.Client = sqs.NewFromConfig(cfg)
 }
 
+func (sq *SqsQueue) IsUsingV2() bool { return true }
+
 // ResourceName - the simple name of the aws resource
 func (sq *SqsQueue) ResourceName() string {
 	return "sqs"
@@ -33,7 +39,7 @@ func (sq *SqsQueue) MaxBatchSize() int {
 	return 49
 }
 
-// ResourceIdentifiers - The arns of the sqs queues
+// ResourceIdentifiers - The arn's of the sqs queues
 func (sq *SqsQueue) ResourceIdentifiers() []string {
 	return sq.QueueUrls
 }
@@ -48,13 +54,13 @@ func (sq *SqsQueue) GetAndSetIdentifiers(c context.Context, configObj config.Con
 		return nil, err
 	}
 
-	sq.QueueUrls = awsgo.StringValueSlice(identifiers)
+	sq.QueueUrls = aws.ToStringSlice(identifiers)
 	return sq.QueueUrls, nil
 }
 
 // Nuke - nuke 'em all!!!
 func (sq *SqsQueue) Nuke(identifiers []string) error {
-	if err := sq.nukeAll(awsgo.StringSlice(identifiers)); err != nil {
+	if err := sq.nukeAll(aws.StringSlice(identifiers)); err != nil {
 		return errors.WithStackTrace(err)
 	}
 
diff --git a/aws/v2_migration_report.go b/aws/v2_migration_report.go
new file mode 100644
index 0000000..f5c96a2
--- /dev/null
+++ b/aws/v2_migration_report.go
@@ -0,0 +1,9 @@
+package aws
+
+func ReportGetAllRegisterResources() []AwsResource {
+	var resources []AwsResource
+	resources = append(resources, getRegisteredGlobalResources()...)
+	resources = append(resources, getRegisteredRegionalResources()...)
+
+	return resources
+}
diff --git a/go.mod b/go.mod
index d1f83f8..6f740c1 100644
--- a/go.mod
+++ b/go.mod
@@ -4,12 +4,14 @@ go 1.21
 
 require (
 	github.com/aws/aws-sdk-go v1.49.13
-	github.com/aws/aws-sdk-go-v2 v1.30.5
+	github.com/aws/aws-sdk-go-v2 v1.31.0
 	github.com/aws/aws-sdk-go-v2/config v1.27.24
 	github.com/aws/aws-sdk-go-v2/credentials v1.17.24
 	github.com/aws/aws-sdk-go-v2/service/amp v1.27.1
 	github.com/aws/aws-sdk-go-v2/service/eventbridge v1.33.7
 	github.com/aws/aws-sdk-go-v2/service/scheduler v1.10.7
+	github.com/aws/aws-sdk-go-v2/service/sns v1.32.3
+	github.com/aws/aws-sdk-go-v2/service/sqs v1.35.3
 	github.com/charmbracelet/lipgloss v0.6.0
 	github.com/go-errors/errors v1.4.2
 	github.com/gruntwork-io/go-commons v0.17.0
@@ -26,8 +28,8 @@ require (
 	atomicgo.dev/cursor v0.1.1 // indirect
 	atomicgo.dev/keyboard v0.2.8 // indirect
 	github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.9 // indirect
-	github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.17 // indirect
-	github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.17 // indirect
+	github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.18 // indirect
+	github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.18 // indirect
 	github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 // indirect
 	github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.17 // indirect
 	github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.3 // indirect
@@ -35,7 +37,7 @@ require (
 	github.com/aws/aws-sdk-go-v2/service/sso v1.22.1 // indirect
 	github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.2 // indirect
 	github.com/aws/aws-sdk-go-v2/service/sts v1.30.1 // indirect
-	github.com/aws/smithy-go v1.20.4 // indirect
+	github.com/aws/smithy-go v1.21.0 // indirect
 	github.com/containerd/console v1.0.3 // indirect
 	github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
 	github.com/davecgh/go-spew v1.1.1 // indirect
diff --git a/go.sum b/go.sum
index bf06a49..16d1565 100644
--- a/go.sum
+++ b/go.sum
@@ -15,18 +15,18 @@ github.com/MarvinJWendt/testza v0.4.2/go.mod h1:mSdhXiKH8sg/gQehJ63bINcCKp7RtYew
 github.com/atomicgo/cursor v0.0.1/go.mod h1:cBON2QmmrysudxNBFthvMtN32r3jxVRIvzkUiF/RuIk=
 github.com/aws/aws-sdk-go v1.49.13 h1:f4mGztsgnx2dR9r8FQYa9YW/RsKb+N7bgef4UGrOW1Y=
 github.com/aws/aws-sdk-go v1.49.13/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk=
-github.com/aws/aws-sdk-go-v2 v1.30.5 h1:mWSRTwQAb0aLE17dSzztCVJWI9+cRMgqebndjwDyK0g=
-github.com/aws/aws-sdk-go-v2 v1.30.5/go.mod h1:CT+ZPWXbYrci8chcARI3OmI/qgd+f6WtuLOoaIA8PR0=
+github.com/aws/aws-sdk-go-v2 v1.31.0 h1:3V05LbxTSItI5kUqNwhJrrrY1BAXxXt0sN0l72QmG5U=
+github.com/aws/aws-sdk-go-v2 v1.31.0/go.mod h1:ztolYtaEUtdpf9Wftr31CJfLVjOnD/CVRkKOOYgF8hA=
 github.com/aws/aws-sdk-go-v2/config v1.27.24 h1:NM9XicZ5o1CBU/MZaHwFtimRpWx9ohAUAqkG6AqSqPo=
 github.com/aws/aws-sdk-go-v2/config v1.27.24/go.mod h1:aXzi6QJTuQRVVusAO8/NxpdTeTyr/wRcybdDtfUwJSs=
 github.com/aws/aws-sdk-go-v2/credentials v1.17.24 h1:YclAsrnb1/GTQNt2nzv+756Iw4mF8AOzcDfweWwwm/M=
 github.com/aws/aws-sdk-go-v2/credentials v1.17.24/go.mod h1:Hld7tmnAkoBQdTMNYZGzztzKRdA4fCdn9L83LOoigac=
 github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.9 h1:Aznqksmd6Rfv2HQN9cpqIV/lQRMaIpJkLLaJ1ZI76no=
 github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.9/go.mod h1:WQr3MY7AxGNxaqAtsDWn+fBxmd4XvLkzeqQ8P1VM0/w=
-github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.17 h1:pI7Bzt0BJtYA0N/JEC6B8fJ4RBrEMi1LBrkMdFYNSnQ=
-github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.17/go.mod h1:Dh5zzJYMtxfIjYW+/evjQ8uj2OyR/ve2KROHGHlSFqE=
-github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.17 h1:Mqr/V5gvrhA2gvgnF42Zh5iMiQNcOYthFYwCyrnuWlc=
-github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.17/go.mod h1:aLJpZlCmjE+V+KtN1q1uyZkfnUWpQGpbsn89XPKyzfU=
+github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.18 h1:kYQ3H1u0ANr9KEKlGs/jTLrBFPo8P8NaH/w7A01NeeM=
+github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.18/go.mod h1:r506HmK5JDUh9+Mw4CfGJGSSoqIiLCndAuqXuhbv67Y=
+github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.18 h1:Z7IdFUONvTcvS7YuhtVxN99v2cCoHRXOS4mTr0B/pUc=
+github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.18/go.mod h1:DkKMmksZVVyat+Y+r1dEOgJEfUeA7UngIHWeKsi0yNc=
 github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 h1:hT8rVHwugYE2lEfdFE0QWVo81lF7jMrYJVDWI+f+VxU=
 github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0/go.mod h1:8tu/lYfQfFe6IGnaOdrpVgEL2IrrDOf6/m9RQum4NkY=
 github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.17 h1:Roo69qTpfu8OlJ2Tb7pAYVuF0CpuUMB0IYWwYP/4DZM=
@@ -41,14 +41,18 @@ github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.15 h1:I9zMeF10
 github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.15/go.mod h1:9xWJ3Q/S6Ojusz1UIkfycgD1mGirJfLLKqq3LPT7WN8=
 github.com/aws/aws-sdk-go-v2/service/scheduler v1.10.7 h1:EcPyKUUo3neD2tDFKnq3TR3B9s2uZe9eWgxVQgixdzc=
 github.com/aws/aws-sdk-go-v2/service/scheduler v1.10.7/go.mod h1:MlNx35QVGG8TB2x1kOC0TKd9e93+RmFUE8HzYDLDLso=
+github.com/aws/aws-sdk-go-v2/service/sns v1.32.3 h1:LC5JBrEAdJ0SSRLfNcLzOLsfoc3xO/BAsHiUNcQfDI4=
+github.com/aws/aws-sdk-go-v2/service/sns v1.32.3/go.mod h1:ZO606Jfatw51c8q29gHVVCnufg2dq3MnmkNLlTZFrkE=
+github.com/aws/aws-sdk-go-v2/service/sqs v1.35.3 h1:Lcs658WFW235QuUfpAdxd8RCy8Va2VUA7/U9iIrcjcY=
+github.com/aws/aws-sdk-go-v2/service/sqs v1.35.3/go.mod h1:WuGxWQhu2LXoPGA2HBIbotpwhM6T4hAz0Ip/HjdxfJg=
 github.com/aws/aws-sdk-go-v2/service/sso v1.22.1 h1:p1GahKIjyMDZtiKoIn0/jAj/TkMzfzndDv5+zi2Mhgc=
 github.com/aws/aws-sdk-go-v2/service/sso v1.22.1/go.mod h1:/vWdhoIoYA5hYoPZ6fm7Sv4d8701PiG5VKe8/pPJL60=
 github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.2 h1:ORnrOK0C4WmYV/uYt3koHEWBLYsRDwk2Np+eEoyV4Z0=
 github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.2/go.mod h1:xyFHA4zGxgYkdD73VeezHt3vSKEG9EmFnGwoKlP00u4=
 github.com/aws/aws-sdk-go-v2/service/sts v1.30.1 h1:+woJ607dllHJQtsnJLi52ycuqHMwlW+Wqm2Ppsfp4nQ=
 github.com/aws/aws-sdk-go-v2/service/sts v1.30.1/go.mod h1:jiNR3JqT15Dm+QWq2SRgh0x0bCNSRP2L25+CqPNpJlQ=
-github.com/aws/smithy-go v1.20.4 h1:2HK1zBdPgRbjFOHlfeQZfpC4r72MOb9bZkiFwggKO+4=
-github.com/aws/smithy-go v1.20.4/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg=
+github.com/aws/smithy-go v1.21.0 h1:H7L8dtDRk0P1Qm6y0ji7MCYMQObJ5R9CRpyPhRUkLYA=
+github.com/aws/smithy-go v1.21.0/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg=
 github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
 github.com/charmbracelet/lipgloss v0.6.0 h1:1StyZB9vBSOyuZxQUcUwGr17JmojPNm87inij9N3wJY=
 github.com/charmbracelet/lipgloss v0.6.0/go.mod h1:tHh2wr34xcHjC2HCXIlGSG1jaDF0S0atAUvBMP6Ppuk=
diff --git a/v2_migration_report/main.go b/v2_migration_report/main.go
new file mode 100644
index 0000000..8be5e24
--- /dev/null
+++ b/v2_migration_report/main.go
@@ -0,0 +1,74 @@
+//go:generate go run main.go
+package main
+
+import (
+	"fmt"
+	"log"
+	"os"
+	"sort"
+	"strings"
+	"text/tabwriter"
+
+	"github.com/gruntwork-io/cloud-nuke/aws"
+)
+
+func main() {
+	cwd, err := os.Getwd()
+	if err != nil {
+		log.Fatal("failed to get current working directory")
+	}
+
+	file, err := os.Create(cwd + "/output.md")
+	if err != nil {
+		log.Fatal("failed to create output file")
+	}
+	defer file.Close()
+
+	_, _ = fmt.Fprintln(file, "# AWS SDKv2 Migration Progress")
+	_, _ = fmt.Fprintln(file)
+	_, _ = fmt.Fprintln(file, "The table below outlines the progress of the `AWS SDK` migration as detailed in [#745](https://github.com/gruntwork-io/cloud-nuke/issues/745).")
+	_, _ = fmt.Fprintln(file, "run `go generate ./...` to refresh this report.")
+	_, _ = fmt.Fprintln(file)
+	_, _ = fmt.Fprintln(file)
+
+	var (
+		resources       = aws.ReportGetAllRegisterResources()
+		migrationStatus = make(map[string]string)
+	)
+	const checked = ":white_check_mark:"
+
+	for _, item := range resources {
+		status := ""
+		if item.IsUsingV2() {
+			status = checked
+		}
+
+		migrationStatus[item.ResourceName()] = status
+	}
+
+	var (
+		keys      []string
+		keyMaxLen int
+	)
+	for k := range migrationStatus {
+		keys = append(keys, k)
+		if len(k) > keyMaxLen {
+			keyMaxLen = len(k)
+		}
+	}
+	sort.Strings(keys)
+
+	w := tabwriter.NewWriter(file, 0, 0, 0, ' ', 0)
+	_, _ = fmt.Fprintln(w, "| Resource Name\t| Migrated\t|")
+	_, _ = fmt.Fprintf(w, "|%s\t|%s\t|\n", dash(keyMaxLen), dash(len(checked)))
+
+	for _, k := range keys {
+		_, _ = fmt.Fprintf(w, "| %s\t| %s\t|\n", k, migrationStatus[k])
+	}
+
+	_ = w.Flush()
+}
+
+func dash(n int) string {
+	return strings.Repeat("-", n+2)
+}
diff --git a/v2_migration_report/output.md b/v2_migration_report/output.md
new file mode 100644
index 0000000..7b2e245
--- /dev/null
+++ b/v2_migration_report/output.md
@@ -0,0 +1,121 @@
+# AWS SDKv2 Migration Progress
+
+The table below outlines the progress of the `AWS SDK` migration as detailed in [#745](https://github.com/gruntwork-io/cloud-nuke/issues/745).
+run `go generate ./...` to refresh this report.
+
+
+| Resource Name                    | Migrated           |
+|----------------------------------|--------------------|
+| accessanalyzer                   |                    |
+| acm                              |                    |
+| acmpca                           |                    |
+| ami                              |                    |
+| apigateway                       |                    |
+| apigatewayv2                     |                    |
+| app-runner-service               |                    |
+| asg                              |                    |
+| backup-vault                     |                    |
+| cloudtrail                       |                    |
+| cloudwatch-alarm                 |                    |
+| cloudwatch-dashboard             |                    |
+| cloudwatch-loggroup              |                    |
+| codedeploy-application           |                    |
+| config-recorders                 |                    |
+| config-rules                     |                    |
+| data-sync-location               |                    |
+| data-sync-task                   |                    |
+| dynamodb                         |                    |
+| ebs                              |                    |
+| ec2                              |                    |
+| ec2-dedicated-hosts              |                    |
+| ec2-endpoint                     |                    |
+| ec2-keypairs                     |                    |
+| ec2-placement-groups             |                    |
+| ec2-subnet                       |                    |
+| ec2_dhcp_option                  |                    |
+| ecr                              |                    |
+| ecscluster                       |                    |
+| ecsserv                          |                    |
+| efs                              |                    |
+| egress-only-internet-gateway     |                    |
+| eip                              |                    |
+| ekscluster                       |                    |
+| elastic-beanstalk                |                    |
+| elasticache                      |                    |
+| elasticacheParameterGroups       |                    |
+| elasticacheSubnetGroups          |                    |
+| elb                              |                    |
+| elbv2                            |                    |
+| event-bridge                     | :white_check_mark: |
+| event-bridge-archive             | :white_check_mark: |
+| event-bridge-rule                | :white_check_mark: |
+| event-bridge-schedule            | :white_check_mark: |
+| event-bridge-schedule-group      | :white_check_mark: |
+| guardduty                        |                    |
+| iam                              |                    |
+| iam-group                        |                    |
+| iam-policy                       |                    |
+| iam-role                         |                    |
+| iam-service-linked-role          |                    |
+| internet-gateway                 |                    |
+| ipam                             |                    |
+| ipam-byoasn                      |                    |
+| ipam-custom-allocation           |                    |
+| ipam-pool                        |                    |
+| ipam-resource-discovery          |                    |
+| ipam-scope                       |                    |
+| kinesis-firehose                 |                    |
+| kinesis-stream                   |                    |
+| kmscustomerkeys                  |                    |
+| lambda                           |                    |
+| lambda_layer                     |                    |
+| lc                               |                    |
+| lt                               |                    |
+| macie-member                     |                    |
+| managed-prometheus               | :white_check_mark: |
+| msk-cluster                      |                    |
+| nat-gateway                      |                    |
+| network-acl                      |                    |
+| network-firewall                 |                    |
+| network-firewall-policy          |                    |
+| network-firewall-resource-policy |                    |
+| network-firewall-rule-group      |                    |
+| network-firewall-tls-config      |                    |
+| network-interface                |                    |
+| oidcprovider                     |                    |
+| opensearchdomain                 |                    |
+| rds                              |                    |
+| rds-cluster                      |                    |
+| rds-global-cluster               |                    |
+| rds-global-cluster-membership    |                    |
+| rds-parameter-group              |                    |
+| rds-proxy                        |                    |
+| rds-snapshot                     |                    |
+| rds-subnet-group                 |                    |
+| redshift                         |                    |
+| route53-cidr-collection          |                    |
+| route53-hosted-zone              |                    |
+| route53-traffic-policy           |                    |
+| s3                               |                    |
+| s3-ap                            |                    |
+| s3-mrap                          |                    |
+| s3-olap                          |                    |
+| sagemaker-notebook-smni          |                    |
+| secretsmanager                   |                    |
+| security-group                   |                    |
+| security-hub                     |                    |
+| ses-configuration-set            |                    |
+| ses-email-template               |                    |
+| ses-identity                     |                    |
+| ses-receipt-filter               |                    |
+| ses-receipt-rule-set             |                    |
+| snap                             |                    |
+| snstopic                         | :white_check_mark: |
+| sqs                              | :white_check_mark: |
+| transit-gateway                  |                    |
+| transit-gateway-attachment       |                    |
+| transit-gateway-route-table      |                    |
+| vpc                              |                    |
+| vpc-lattice-service              |                    |
+| vpc-lattice-service-network      |                    |
+| vpc-lattice-target-group         |                    |


Test patch:
diff --git a/aws/resources/sns_test.go b/aws/resources/sns_test.go
index 15dda8a..54c3dc5 100644
--- a/aws/resources/sns_test.go
+++ b/aws/resources/sns_test.go
@@ -6,43 +6,42 @@ import (
 	"testing"
 	"time"
 
-	"github.com/aws/aws-sdk-go/aws/request"
-	"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"
+	"github.com/aws/aws-sdk-go-v2/aws"
+	"github.com/aws/aws-sdk-go-v2/service/sns"
+	"github.com/aws/aws-sdk-go-v2/service/sns/types"
 	"github.com/gruntwork-io/cloud-nuke/config"
+	"github.com/gruntwork-io/cloud-nuke/util"
 	"github.com/stretchr/testify/require"
 )
 
-type mockedSNSTopic struct {
-	snsiface.SNSAPI
-	ListTopicsOutput             sns.ListTopicsOutput
-	ListTagsForResourceOutputMap map[string]sns.ListTagsForResourceOutput
-	DeleteTopicOutput            sns.DeleteTopicOutput
+type mockedSNSService struct {
+	SNSTopic
+	DeleteTopicOutput         sns.DeleteTopicOutput
+	TagResourceOutput         sns.TagResourceOutput
+	ListTopicsOutput          sns.ListTopicsOutput
+	ListTagsForResourceOutput map[string]sns.ListTagsForResourceOutput
 }
 
-func (m mockedSNSTopic) ListTopicsPagesWithContext(_ awsgo.Context, _ *sns.ListTopicsInput, fn func(*sns.ListTopicsOutput, bool) bool, _ ...request.Option) error {
-	fn(&m.ListTopicsOutput, true)
-	return nil
+func (m mockedSNSService) ListTopics(context.Context, *sns.ListTopicsInput, ...func(*sns.Options)) (*sns.ListTopicsOutput, error) {
+	return &m.ListTopicsOutput, nil
 }
 
-func (m mockedSNSTopic) ListTagsForResource(input *sns.ListTagsForResourceInput) (*sns.ListTagsForResourceOutput, error) {
-	arn := input.ResourceArn
-	resp := m.ListTagsForResourceOutputMap[*arn]
+func (m mockedSNSService) ListTagsForResource(ctx context.Context, params *sns.ListTagsForResourceInput, optFns ...func(*sns.Options)) (*sns.ListTagsForResourceOutput, error) {
+	arn := params.ResourceArn
+	resp := m.ListTagsForResourceOutput[*arn]
 
 	return &resp, nil
 }
 
-func (m mockedSNSTopic) DeleteTopicWithContext(_ awsgo.Context, _ *sns.DeleteTopicInput, _ ...request.Option) (*sns.DeleteTopicOutput, error) {
+func (m mockedSNSService) TagResource(ctx context.Context, params *sns.TagResourceInput, optFns ...func(*sns.Options)) (*sns.TagResourceOutput, error) {
+	return &m.TagResourceOutput, nil
+}
+
+func (m mockedSNSService) DeleteTopic(ctx context.Context, params *sns.DeleteTopicInput, optFns ...func(*sns.Options)) (*sns.DeleteTopicOutput, error) {
 	return &m.DeleteTopicOutput, nil
 }
 
 func TestSNS_GetAll(t *testing.T) {
-
 	t.Parallel()
 
 	// Set excludeFirstSeenTag to false for testing
@@ -52,24 +51,24 @@ func TestSNS_GetAll(t *testing.T) {
 	testTopic2 := "arn:aws:sns:us-east-1:123456789012:MyTopic2"
 	now := time.Now()
 	s := SNSTopic{
-		Client: mockedSNSTopic{
+		Client: mockedSNSService{
 			ListTopicsOutput: sns.ListTopicsOutput{
-				Topics: []*sns.Topic{
-					{TopicArn: awsgo.String(testTopic1)},
-					{TopicArn: awsgo.String(testTopic2)},
+				Topics: []types.Topic{
+					{TopicArn: aws.String(testTopic1)},
+					{TopicArn: aws.String(testTopic2)},
 				},
 			},
-			ListTagsForResourceOutputMap: map[string]sns.ListTagsForResourceOutput{
+			ListTagsForResourceOutput: map[string]sns.ListTagsForResourceOutput{
 				testTopic1: {
-					Tags: []*sns.Tag{{
-						Key:   awsgo.String(firstSeenTagKey),
-						Value: awsgo.String(now.Format(firstSeenTimeFormat)),
+					Tags: []types.Tag{{
+						Key:   aws.String(firstSeenTagKey),
+						Value: aws.String(now.Format(firstSeenTimeFormat)),
 					}},
 				},
 				testTopic2: {
-					Tags: []*sns.Tag{{
-						Key:   awsgo.String(firstSeenTagKey),
-						Value: awsgo.String(now.Add(1).Format(firstSeenTimeFormat)),
+					Tags: []types.Tag{{
+						Key:   aws.String(firstSeenTagKey),
+						Value: aws.String(now.Add(1).Format(firstSeenTimeFormat)),
 					}},
 				},
 			},
@@ -111,17 +110,16 @@ func TestSNS_GetAll(t *testing.T) {
 				SNS: tc.configObj,
 			})
 			require.NoError(t, err)
-			require.Equal(t, tc.expected, aws.StringValueSlice(names))
+			require.Equal(t, tc.expected, aws.ToStringSlice(names))
 		})
 	}
 }
 
 func TestSNS_NukeAll(t *testing.T) {
-
 	t.Parallel()
 
 	s := SNSTopic{
-		Client: mockedSNSTopic{
+		Client: mockedSNSService{
 			DeleteTopicOutput: sns.DeleteTopicOutput{},
 		},
 	}
diff --git a/aws/resources/sqs_test.go b/aws/resources/sqs_test.go
index 73d9295..46dfee5 100644
--- a/aws/resources/sqs_test.go
+++ b/aws/resources/sqs_test.go
@@ -8,61 +8,56 @@ import (
 	"time"
 
 	"github.com/aws/aws-sdk-go-v2/aws"
-	awsgo "github.com/aws/aws-sdk-go/aws"
-	"github.com/aws/aws-sdk-go/aws/request"
-	"github.com/aws/aws-sdk-go/service/sqs"
-	"github.com/aws/aws-sdk-go/service/sqs/sqsiface"
+	"github.com/aws/aws-sdk-go-v2/service/sqs"
 	"github.com/gruntwork-io/cloud-nuke/config"
 	"github.com/stretchr/testify/require"
 )
 
-type mockedSqsQueue struct {
-	sqsiface.SQSAPI
-	DeleteQueueOutput        sqs.DeleteQueueOutput
+type mockedSQSService struct {
+	SqsQueueAPI
 	GetQueueAttributesOutput map[string]sqs.GetQueueAttributesOutput
 	ListQueuesOutput         sqs.ListQueuesOutput
+	DeleteQueueOutput        sqs.DeleteQueueOutput
 }
 
-func (m mockedSqsQueue) ListQueuesPagesWithContext(_ awsgo.Context, _ *sqs.ListQueuesInput, fn func(*sqs.ListQueuesOutput, bool) bool, _ ...request.Option) error {
-	fn(&m.ListQueuesOutput, true)
-	return nil
-}
-
-func (m mockedSqsQueue) GetQueueAttributesWithContext(_ awsgo.Context, input *sqs.GetQueueAttributesInput, _ ...request.Option) (*sqs.GetQueueAttributesOutput, error) {
-	url := input.QueueUrl
+func (m mockedSQSService) GetQueueAttributes(ctx context.Context, params *sqs.GetQueueAttributesInput, optFns ...func(*sqs.Options)) (*sqs.GetQueueAttributesOutput, error) {
+	url := params.QueueUrl
 	resp := m.GetQueueAttributesOutput[*url]
 
 	return &resp, nil
 }
 
-func (m mockedSqsQueue) DeleteQueueWithContext(_ awsgo.Context, _ *sqs.DeleteQueueInput, _ ...request.Option) (*sqs.DeleteQueueOutput, error) {
+func (m mockedSQSService) ListQueues(context.Context, *sqs.ListQueuesInput, ...func(*sqs.Options)) (*sqs.ListQueuesOutput, error) {
+	return &m.ListQueuesOutput, nil
+}
+
+func (m mockedSQSService) DeleteQueue(ctx context.Context, params *sqs.DeleteQueueInput, optFns ...func(*sqs.Options)) (*sqs.DeleteQueueOutput, error) {
 	return &m.DeleteQueueOutput, nil
 }
 
 func TestSqsQueue_GetAll(t *testing.T) {
-
 	t.Parallel()
 
 	queue1 := "https://sqs.us-east-1.amazonaws.com/123456789012/MyQueue1"
 	queue2 := "https://sqs.us-east-1.amazonaws.com/123456789012/MyQueue2"
 	now := time.Now()
 	sq := SqsQueue{
-		Client: mockedSqsQueue{
+		Client: mockedSQSService{
 			ListQueuesOutput: sqs.ListQueuesOutput{
-				QueueUrls: []*string{
-					awsgo.String(queue1),
-					awsgo.String(queue2),
+				QueueUrls: []string{
+					queue1,
+					queue2,
 				},
 			},
 			GetQueueAttributesOutput: map[string]sqs.GetQueueAttributesOutput{
 				queue1: {
-					Attributes: map[string]*string{
-						"CreatedTimestamp": awsgo.String(strconv.FormatInt(now.Unix(), 10)),
+					Attributes: map[string]string{
+						"CreatedTimestamp": strconv.FormatInt(now.Unix(), 10),
 					},
 				},
 				queue2: {
-					Attributes: map[string]*string{
-						"CreatedTimestamp": awsgo.String(strconv.FormatInt(now.Add(1).Unix(), 10)),
+					Attributes: map[string]string{
+						"CreatedTimestamp": strconv.FormatInt(now.Add(1).Unix(), 10),
 					},
 				},
 			},
@@ -94,23 +89,23 @@ func TestSqsQueue_GetAll(t *testing.T) {
 			expected: []string{},
 		},
 	}
+
 	for name, tc := range tests {
 		t.Run(name, func(t *testing.T) {
 			names, err := sq.getAll(context.Background(), config.Config{
 				SQS: tc.configObj,
 			})
 			require.NoError(t, err)
-			require.Equal(t, tc.expected, awsgo.StringValueSlice(names))
+			require.Equal(t, tc.expected, aws.ToStringSlice(names))
 		})
 	}
 }
 
 func TestSqsQueue_NukeAll(t *testing.T) {
-
 	t.Parallel()
 
 	sq := SqsQueue{
-		Client: mockedSqsQueue{
+		Client: mockedSQSService{
 			DeleteQueueOutput: sqs.DeleteQueueOutput{},
 		},
 	}