From def10bffdd9e3af3c14c78abd3614e04eeea2d59 Mon Sep 17 00:00:00 2001 From: AJ Date: Sat, 21 Sep 2024 08:27:39 +0100 Subject: [PATCH] add support for eventBridge schedule & schedule groups. --- README.md | 4 + aws/resource_registry.go | 2 + aws/resources/eventbridge_schedule.go | 84 +++++++++++++ aws/resources/eventbridge_schedule_group.go | 85 ++++++++++++++ .../eventbridge_schedule_group_test.go | 104 +++++++++++++++++ .../eventbridge_schedule_group_types.go | 58 +++++++++ aws/resources/eventbridge_schedule_test.go | 110 ++++++++++++++++++ aws/resources/eventbridge_schedule_types.go | 58 +++++++++ config/config.go | 2 + config/config_test.go | 2 + go.mod | 1 + go.sum | 2 + 12 files changed, 512 insertions(+) create mode 100644 aws/resources/eventbridge_schedule.go create mode 100644 aws/resources/eventbridge_schedule_group.go create mode 100644 aws/resources/eventbridge_schedule_group_test.go create mode 100644 aws/resources/eventbridge_schedule_group_types.go create mode 100644 aws/resources/eventbridge_schedule_test.go create mode 100644 aws/resources/eventbridge_schedule_types.go diff --git a/README.md b/README.md index f20c10f5..d2aa4a11 100644 --- a/README.md +++ b/README.md @@ -45,6 +45,8 @@ Cloud-nuke suppports 🔎 inspecting and 🔥💀 deleting the following AWS res | Event Bridge | Event buses | | Event Bridge | Archive | | Event Bridge | Rule | +| Event Bridge | Schedule | +| Event Bridge | Schedule Group | | Certificate Manager | ACM Private CA | | Direct Connect | Transit Gateways | | Elasticache | Clusters | @@ -599,6 +601,8 @@ of the file that are supported are listed here. | event-bridge | EventBridge | ✅ (Bus Name) | ✅ (Creation Time) | ❌ | ✅ | | event-bridge-archive | EventBridgeArchive | ✅ (Archive Name) | ✅ (Creation Time) | ❌ | ✅ | | event-bridge-rule | EventBridgeRule | ✅ (Bus Rule Name) | ❌ | ❌ | ✅ | +| event-bridge-schedule | EventBridgeSchedule | ✅ (Schedule Name) | ✅ (Creation Time) | ❌ | ✅ | +| event-bridge-schedule-group | EventBridgeScheduleGroup | ✅ (Schedule Group Name) | ✅ (Creation Time) | ❌ | ✅ | | guardduty | GuardDuty | ❌ | ✅ (Created Time) | ❌ | ✅ | | iam-group | IAMGroups | ✅ (Group Name) | ✅ (Creation Time) | ❌ | ✅ | | iam-policy | IAMPolicies | ✅ (Policy Name) | ✅ (Creation Time) | ❌ | ✅ | diff --git a/aws/resource_registry.go b/aws/resource_registry.go index 1699a864..625ba644 100644 --- a/aws/resource_registry.go +++ b/aws/resource_registry.go @@ -64,6 +64,8 @@ func getRegisteredRegionalResources() []AwsResource { &resources.AppRunnerService{}, &resources.BackupVault{}, &resources.ManagedPrometheus{}, + &resources.EventBridgeSchedule{}, + &resources.EventBridgeScheduleGroup{}, &resources.EventBridgeArchive{}, &resources.EventBridgeRule{}, &resources.EventBridge{}, diff --git a/aws/resources/eventbridge_schedule.go b/aws/resources/eventbridge_schedule.go new file mode 100644 index 00000000..bf20640c --- /dev/null +++ b/aws/resources/eventbridge_schedule.go @@ -0,0 +1,84 @@ +package resources + +import ( + "context" + "fmt" + "strings" + + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/scheduler" + "github.com/gruntwork-io/cloud-nuke/config" + "github.com/gruntwork-io/cloud-nuke/logging" + "github.com/gruntwork-io/cloud-nuke/report" + "github.com/gruntwork-io/go-commons/errors" +) + +func (sch *EventBridgeSchedule) nukeAll(identifiers []*string) error { + if len(identifiers) == 0 { + logging.Debugf("[Event Bridge Schedule] No Schedules found in region %s", sch.Region) + return nil + } + + logging.Debugf("[Event Bridge Schedule] Deleting all Schedules in %s", sch.Region) + + var deleted []*string + for _, identifier := range identifiers { + payload := strings.Split(*identifier, "|") + if len(payload) != 2 { + logging.Debugf("[Event Bridge Schedule] Invalid identifier %s", *identifier) + continue + } + + _, err := sch.Client.DeleteSchedule(sch.Context, &scheduler.DeleteScheduleInput{ + GroupName: aws.String(payload[0]), + Name: aws.String(payload[1]), + }) + + if err != nil { + logging.Debugf( + "[Event Bridge Schedule] Error deleting Schedule %s in region %s, err %s", + *identifier, + sch.Region, + err, + ) + } else { + deleted = append(deleted, identifier) + logging.Debugf("[Event Bridge Schedule] Deleted Schedule %s in region %s", *identifier, sch.Region) + } + + e := report.Entry{ + Identifier: aws.ToString(identifier), + ResourceType: sch.ResourceName(), + Error: err, + } + report.Record(e) + } + + logging.Debugf("[OK] %d Event Bridges Schedul(es) deleted in %s", len(deleted), sch.Region) + return nil +} + +func (sch *EventBridgeSchedule) getAll(ctx context.Context, cnfObj config.Config) ([]*string, error) { + var identifiers []*string + + paginator := scheduler.NewListSchedulesPaginator(sch.Client, &scheduler.ListSchedulesInput{}) + for paginator.HasMorePages() { + page, err := paginator.NextPage(ctx) + if err != nil { + logging.Debugf("[Event Bridge Schedule] Failed to list schedules: %s", err) + return nil, errors.WithStackTrace(err) + } + + for _, schedule := range page.Schedules { + id := aws.String(fmt.Sprintf("%s|%s", *schedule.GroupName, *schedule.Name)) + if cnfObj.EventBridgeSchedule.ShouldInclude(config.ResourceValue{ + Name: id, + Time: schedule.CreationDate, + }) { + identifiers = append(identifiers, id) + } + } + } + + return identifiers, nil +} diff --git a/aws/resources/eventbridge_schedule_group.go b/aws/resources/eventbridge_schedule_group.go new file mode 100644 index 00000000..2e4f320a --- /dev/null +++ b/aws/resources/eventbridge_schedule_group.go @@ -0,0 +1,85 @@ +package resources + +import ( + "context" + + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/scheduler" + "github.com/aws/aws-sdk-go-v2/service/scheduler/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/go-commons/errors" +) + +func (sch *EventBridgeScheduleGroup) nukeAll(identifiers []*string) error { + if len(identifiers) == 0 { + logging.Debugf("[Event Bridge Schedule] No Groups found in region %s", sch.Region) + return nil + } + + logging.Debugf("[Event Bridge Schedule] Deleting all Groups in %s", sch.Region) + + var deleted []*string + for _, identifier := range identifiers { + _, err := sch.Client.DeleteScheduleGroup(sch.Context, &scheduler.DeleteScheduleGroupInput{ + Name: identifier, + }) + + if err != nil { + logging.Debugf( + "[Event Bridge Schedule] Error deleting Group %s in region %s, err %s", + *identifier, + sch.Region, + err, + ) + } else { + deleted = append(deleted, identifier) + logging.Debugf("[Event Bridge Schedule] Deleted Group %s in region %s", *identifier, sch.Region) + } + + e := report.Entry{ + Identifier: aws.ToString(identifier), + ResourceType: sch.ResourceName(), + Error: err, + } + report.Record(e) + } + + logging.Debugf("[OK] %d Event Bridges Schedul Groups deleted in %s", len(deleted), sch.Region) + return nil +} + +func (sch *EventBridgeScheduleGroup) getAll(ctx context.Context, cnfObj config.Config) ([]*string, error) { + var identifiers []*string + paginator := scheduler.NewListScheduleGroupsPaginator(sch.Client, &scheduler.ListScheduleGroupsInput{}) + + for paginator.HasMorePages() { + page, err := paginator.NextPage(ctx) + if err != nil { + logging.Debugf("[Event Bridge Schedule] Failed to list schedule groups: %s", err) + return nil, errors.WithStackTrace(err) + } + + for _, group := range page.ScheduleGroups { + if cnfObj.EventBridgeScheduleGroup.ShouldInclude(config.ResourceValue{ + Name: group.Name, + Time: group.CreationDate, + }) { + if *group.Name == "default" { + logging.Debug("[Event Bridge Schedule] skipping default group") + continue + } + + if group.State != types.ScheduleGroupStateActive { + logging.Debugf("[Event Bridge Schedule] skipping group %s, wrong state %s", *group.Name, group.State) + continue + } + + identifiers = append(identifiers, group.Name) + } + } + } + + return identifiers, nil +} diff --git a/aws/resources/eventbridge_schedule_group_test.go b/aws/resources/eventbridge_schedule_group_test.go new file mode 100644 index 00000000..9f0239d9 --- /dev/null +++ b/aws/resources/eventbridge_schedule_group_test.go @@ -0,0 +1,104 @@ +package resources + +import ( + "context" + "regexp" + "testing" + "time" + + "github.com/aws/aws-sdk-go-v2/service/scheduler" + "github.com/aws/aws-sdk-go-v2/service/scheduler/types" + "github.com/aws/aws-sdk-go/aws" + "github.com/gruntwork-io/cloud-nuke/config" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +type mockedEventBridgeScheduleGroupService struct { + EventBridgeScheduleGroupAPI + ListScheduleGroupsOutput scheduler.ListScheduleGroupsOutput + DeleteScheduleGroupOutput scheduler.DeleteScheduleGroupOutput +} + +func (m mockedEventBridgeScheduleGroupService) DeleteScheduleGroup(ctx context.Context, params *scheduler.DeleteScheduleGroupInput, optFns ...func(*scheduler.Options)) (*scheduler.DeleteScheduleGroupOutput, error) { + return &m.DeleteScheduleGroupOutput, nil +} + +func (m mockedEventBridgeScheduleGroupService) ListScheduleGroups(ctx context.Context, params *scheduler.ListScheduleGroupsInput, optFns ...func(*scheduler.Options)) (*scheduler.ListScheduleGroupsOutput, error) { + return &m.ListScheduleGroupsOutput, nil +} + +func Test_EventBridgeScheduleGroup_GetAll(t *testing.T) { + t.Parallel() + + now := time.Now() + + group1 := "test-group-1" + group2 := "test-group-2" + + service := EventBridgeScheduleGroup{Client: mockedEventBridgeScheduleGroupService{ + ListScheduleGroupsOutput: scheduler.ListScheduleGroupsOutput{ + ScheduleGroups: []types.ScheduleGroupSummary{ + { + Name: aws.String(group1), + State: types.ScheduleGroupStateActive, + CreationDate: &now, + }, + { + Name: aws.String(group2), + State: types.ScheduleGroupStateActive, + CreationDate: aws.Time(now.Add(time.Hour)), + }, + }, + }, + }} + + tests := map[string]struct { + configObj config.ResourceType + expected []string + }{ + "emptyFilter": { + configObj: config.ResourceType{}, + expected: []string{group1, group2}, + }, + "nameExclusionFilter": { + configObj: config.ResourceType{ + ExcludeRule: config.FilterRule{ + NamesRegExp: []config.Expression{{ + RE: *regexp.MustCompile(group1), + }}, + }}, + expected: []string{group2}, + }, + "timeAfterExclusionFilter": { + configObj: config.ResourceType{ + ExcludeRule: config.FilterRule{ + TimeAfter: aws.Time(now.Add(-1 * time.Hour)), + }}, + expected: []string{}, + }, + } + + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + buses, err := service.getAll( + context.Background(), + config.Config{EventBridgeScheduleGroup: tc.configObj}, + ) + require.NoError(t, err) + require.Equal(t, tc.expected, aws.StringValueSlice(buses)) + }) + } +} + +func Test_EventBridgeScheduleGroup_NukeAll(t *testing.T) { + t.Parallel() + + groupName := "test-group" + service := EventBridgeScheduleGroup{Client: mockedEventBridgeScheduleGroupService{ + DeleteScheduleGroupOutput: scheduler.DeleteScheduleGroupOutput{}, + }} + + err := service.nukeAll([]*string{&groupName}) + assert.NoError(t, err) +} diff --git a/aws/resources/eventbridge_schedule_group_types.go b/aws/resources/eventbridge_schedule_group_types.go new file mode 100644 index 00000000..337ec752 --- /dev/null +++ b/aws/resources/eventbridge_schedule_group_types.go @@ -0,0 +1,58 @@ +package resources + +import ( + "context" + + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/scheduler" + "github.com/gruntwork-io/cloud-nuke/config" + "github.com/gruntwork-io/go-commons/errors" +) + +type EventBridgeScheduleGroupAPI interface { + DeleteScheduleGroup(ctx context.Context, params *scheduler.DeleteScheduleGroupInput, optFns ...func(*scheduler.Options)) (*scheduler.DeleteScheduleGroupOutput, error) + ListScheduleGroups(ctx context.Context, params *scheduler.ListScheduleGroupsInput, optFns ...func(*scheduler.Options)) (*scheduler.ListScheduleGroupsOutput, error) +} + +type EventBridgeScheduleGroup struct { + BaseAwsResource + Client EventBridgeScheduleGroupAPI + Region string + Groups []string +} + +func (sch *EventBridgeScheduleGroup) GetAndSetResourceConfig(configObj config.Config) config.ResourceType { + return configObj.EventBridgeScheduleGroup +} + +func (sch *EventBridgeScheduleGroup) InitV2(cfg aws.Config) { + sch.Client = scheduler.NewFromConfig(cfg) +} + +func (sch *EventBridgeScheduleGroup) IsUsingV2() bool { return true } + +func (sch *EventBridgeScheduleGroup) ResourceName() string { return "event-bridge-schedule-group" } + +func (sch *EventBridgeScheduleGroup) ResourceIdentifiers() []string { return sch.Groups } + +func (sch *EventBridgeScheduleGroup) MaxBatchSize() int { + return 100 +} + +func (sch *EventBridgeScheduleGroup) Nuke(identifiers []string) error { + if err := sch.nukeAll(aws.StringSlice(identifiers)); err != nil { + return errors.WithStackTrace(err) + } + + return nil +} + +func (sch *EventBridgeScheduleGroup) GetAndSetIdentifiers(ctx context.Context, cnfObj config.Config) ([]string, error) { + identifiers, err := sch.getAll(ctx, cnfObj) + if err != nil { + return nil, err + } + + sch.Groups = aws.ToStringSlice(identifiers) + return sch.Groups, nil +} diff --git a/aws/resources/eventbridge_schedule_test.go b/aws/resources/eventbridge_schedule_test.go new file mode 100644 index 00000000..0901a8b6 --- /dev/null +++ b/aws/resources/eventbridge_schedule_test.go @@ -0,0 +1,110 @@ +package resources + +import ( + "context" + "fmt" + "regexp" + "testing" + "time" + + "github.com/aws/aws-sdk-go-v2/service/scheduler" + "github.com/aws/aws-sdk-go-v2/service/scheduler/types" + "github.com/aws/aws-sdk-go/aws" + "github.com/gruntwork-io/cloud-nuke/config" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +type mockedEventBridgeScheduleService struct { + EventBridgeScheduleAPI + + ListSchedulesOutput scheduler.ListSchedulesOutput + DeleteScheduleOutput scheduler.DeleteScheduleOutput +} + +func (m mockedEventBridgeScheduleService) DeleteSchedule(ctx context.Context, params *scheduler.DeleteScheduleInput, optFns ...func(*scheduler.Options)) (*scheduler.DeleteScheduleOutput, error) { + return &m.DeleteScheduleOutput, nil +} + +func (m mockedEventBridgeScheduleService) ListSchedules(ctx context.Context, params *scheduler.ListSchedulesInput, optFns ...func(*scheduler.Options)) (*scheduler.ListSchedulesOutput, error) { + return &m.ListSchedulesOutput, nil +} + +func Test_EventBridgeSchedule_GetAll(t *testing.T) { + t.Parallel() + + now := time.Now() + + schedule1 := "test-group-1" + schedule2 := "test-group-2" + + group := "test-default" + gSchedule1 := fmt.Sprintf("%s|%s", group, schedule1) + gSchedule2 := fmt.Sprintf("%s|%s", group, schedule2) + + service := EventBridgeSchedule{ + Client: mockedEventBridgeScheduleService{ + ListSchedulesOutput: scheduler.ListSchedulesOutput{ + Schedules: []types.ScheduleSummary{ + { + GroupName: aws.String(group), + Name: aws.String(schedule1), + CreationDate: &now, + }, + { + GroupName: aws.String(group), + Name: aws.String(schedule2), + CreationDate: aws.Time(now.Add(time.Hour)), + }, + }, + }}} + + tests := map[string]struct { + configObj config.ResourceType + expected []string + }{ + "emptyFilter": { + configObj: config.ResourceType{}, + expected: []string{gSchedule1, gSchedule2}, + }, + "nameExclusionFilter": { + configObj: config.ResourceType{ + ExcludeRule: config.FilterRule{ + NamesRegExp: []config.Expression{{ + RE: *regexp.MustCompile(schedule1), + }}, + }}, + expected: []string{gSchedule2}, + }, + "timeAfterExclusionFilter": { + configObj: config.ResourceType{ + ExcludeRule: config.FilterRule{ + TimeAfter: aws.Time(now.Add(-1 * time.Hour)), + }}, + expected: []string{}, + }, + } + + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + buses, err := service.getAll( + context.Background(), + config.Config{EventBridgeSchedule: tc.configObj}, + ) + require.NoError(t, err) + require.Equal(t, tc.expected, aws.StringValueSlice(buses)) + }) + } +} + +func Test_EventBridgeSchedule_NukeAll(t *testing.T) { + t.Parallel() + + scheduleName := "test-schedule" + service := EventBridgeSchedule{Client: mockedEventBridgeScheduleService{ + DeleteScheduleOutput: scheduler.DeleteScheduleOutput{}, + }} + + err := service.nukeAll([]*string{&scheduleName}) + assert.NoError(t, err) +} diff --git a/aws/resources/eventbridge_schedule_types.go b/aws/resources/eventbridge_schedule_types.go new file mode 100644 index 00000000..a1ef31fa --- /dev/null +++ b/aws/resources/eventbridge_schedule_types.go @@ -0,0 +1,58 @@ +package resources + +import ( + "context" + + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/scheduler" + "github.com/gruntwork-io/cloud-nuke/config" + "github.com/gruntwork-io/go-commons/errors" +) + +type EventBridgeScheduleAPI interface { + DeleteSchedule(ctx context.Context, params *scheduler.DeleteScheduleInput, optFns ...func(*scheduler.Options)) (*scheduler.DeleteScheduleOutput, error) + ListSchedules(ctx context.Context, params *scheduler.ListSchedulesInput, optFns ...func(*scheduler.Options)) (*scheduler.ListSchedulesOutput, error) +} + +type EventBridgeSchedule struct { + BaseAwsResource + Client EventBridgeScheduleAPI + Region string + Schedules []string +} + +func (sch *EventBridgeSchedule) GetAndSetResourceConfig(configObj config.Config) config.ResourceType { + return configObj.EventBridgeSchedule +} + +func (sch *EventBridgeSchedule) InitV2(cfg aws.Config) { + sch.Client = scheduler.NewFromConfig(cfg) +} + +func (sch *EventBridgeSchedule) IsUsingV2() bool { return true } + +func (sch *EventBridgeSchedule) ResourceName() string { return "event-bridge-schedule" } + +func (sch *EventBridgeSchedule) ResourceIdentifiers() []string { return sch.Schedules } + +func (sch *EventBridgeSchedule) MaxBatchSize() int { + return 100 +} + +func (sch *EventBridgeSchedule) Nuke(identifiers []string) error { + if err := sch.nukeAll(aws.StringSlice(identifiers)); err != nil { + return errors.WithStackTrace(err) + } + + return nil +} + +func (sch *EventBridgeSchedule) GetAndSetIdentifiers(ctx context.Context, cnfObj config.Config) ([]string, error) { + identifiers, err := sch.getAll(ctx, cnfObj) + if err != nil { + return nil, err + } + + sch.Schedules = aws.ToStringSlice(identifiers) + return sch.Schedules, nil +} diff --git a/config/config.go b/config/config.go index a34374ca..a1aac29d 100644 --- a/config/config.go +++ b/config/config.go @@ -74,6 +74,8 @@ type Config struct { EventBridge ResourceType `yaml:"EventBridge"` EventBridgeArchive ResourceType `yaml:"EventBridgeArchive"` EventBridgeRule ResourceType `yaml:"EventBridgeRule"` + EventBridgeSchedule ResourceType `yaml:"EventBridgeSchedule"` + EventBridgeScheduleGroup ResourceType `yaml:"EventBridgeScheduleGroup"` GuardDuty ResourceType `yaml:"GuardDuty"` IAMGroups ResourceType `yaml:"IAMGroups"` IAMPolicies ResourceType `yaml:"IAMPolicies"` diff --git a/config/config_test.go b/config/config_test.go index 682c93c0..4681f3c1 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -66,6 +66,8 @@ func emptyConfig() *Config { EventBridge: ResourceType{FilterRule{}, FilterRule{}, "", false}, EventBridgeArchive: ResourceType{FilterRule{}, FilterRule{}, "", false}, EventBridgeRule: ResourceType{FilterRule{}, FilterRule{}, "", false}, + EventBridgeSchedule: ResourceType{FilterRule{}, FilterRule{}, "", false}, + EventBridgeScheduleGroup: ResourceType{FilterRule{}, FilterRule{}, "", false}, GuardDuty: ResourceType{FilterRule{}, FilterRule{}, "", false}, IAMGroups: ResourceType{FilterRule{}, FilterRule{}, "", false}, IAMPolicies: ResourceType{FilterRule{}, FilterRule{}, "", false}, diff --git a/go.mod b/go.mod index 386eb5ff..d1f83f80 100644 --- a/go.mod +++ b/go.mod @@ -9,6 +9,7 @@ require ( 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/charmbracelet/lipgloss v0.6.0 github.com/go-errors/errors v1.4.2 github.com/gruntwork-io/go-commons v0.17.0 diff --git a/go.sum b/go.sum index 522d496b..bf06a497 100644 --- a/go.sum +++ b/go.sum @@ -39,6 +39,8 @@ github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.3 h1:dT3MqvG github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.3/go.mod h1:GlAeCkHwugxdHaueRr4nhPuY+WW+gR8UjlcqzPr1SPI= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.15 h1:I9zMeF107l0rJrpnHpjEiiTSCKYAIw8mALiXcPsGBiA= 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/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=