@@ -19,6 +19,23 @@ import (
1919 log "github.com/sirupsen/logrus"
2020)
2121
22+ // GlobalServices is the set of AWS services that operate globally
23+ // and should only be processed in the "global" pseudo-region.
24+ // The keys are the service IDs as returned by middleware.GetServiceID(ctx).
25+ var GlobalServices = map [string ]struct {}{
26+ "CloudFront" : {},
27+ "IAM" : {},
28+ "Route 53" : {},
29+ "WAF" : {}, // WAF Classic (global)
30+ "CloudFront KeyValueStore" : {},
31+ }
32+
33+ // IsGlobalService returns true if the service should only run in global region
34+ func IsGlobalService (service string ) bool {
35+ _ , ok := GlobalServices [service ]
36+ return ok
37+ }
38+
2239func (c * Credentials ) NewConfig (ctx context.Context , region , serviceType string ) (* aws.Config , error ) {
2340 log .Debugf ("creating new config in %s for %s" , region , serviceType )
2441
@@ -73,6 +90,10 @@ func (c *Credentials) NewConfig(ctx context.Context, region, serviceType string)
7390 cfgCopy .APIOptions = append (cfgCopy .APIOptions , func (stack * middleware.Stack ) error {
7491 return stack .Initialize .Add (SkipGlobal {}, middleware .After )
7592 })
93+ } else {
94+ cfgCopy .APIOptions = append (cfgCopy .APIOptions , func (stack * middleware.Stack ) error {
95+ return stack .Initialize .Add (SkipRegionalForGlobalService {}, middleware .After )
96+ })
7697 }
7798 cfg = & cfgCopy
7899 }
@@ -153,29 +174,9 @@ func (c *Credentials) rootConfig(ctx context.Context) (*aws.Config, error) {
153174 return c .cfg , nil
154175}
155176
156- // SkipGlobal returns ErrSkipRequest when operating in the global
157- // pseudo-region.
158- //
159- // FUTURE: define mechanism for allowing specific resources, such as those in
160- // IAM, to override this skip.
161- //
162- // The simplest way to do this would be to remove this middleware through
163- // functional options on relevant operations. e.g.:
164- //
165- // func isGlobalResource(o *iam.Options) {
166- // o.APIOptions = append(o.APIOptions, func(stack *middleware.Stack) error {
167- // stack.Initialize.Remove(config.SkipGlobal{}.ID())
168- // })
169- // }
170- //
171- // // per-operation
172- // out, err := svc.ListRoles(context.Background(), nil, isGlobalResource)
173- // // on a client, if you know you're only operating in the context of global resources
174- // svc := iam.NewFromConfig(cfg, isGlobalResource)
175- //
176- // You could also define some kind of "is global resource" Context flag, which
177- // SkipGlobal could react to. That may be preferrable to having SkipGlobal be
178- // exported from this package.
177+ // SkipGlobal skips requests for non-global services when operating in the
178+ // global pseudo-region. Global services (CloudFront, IAM, Route 53, etc.)
179+ // are allowed through, while regional services are skipped.
179180type SkipGlobal struct {}
180181
181182func (SkipGlobal ) ID () string {
@@ -187,7 +188,41 @@ func (SkipGlobal) HandleInitialize(
187188) (
188189 out middleware.InitializeOutput , md middleware.Metadata , err error ,
189190) {
190- return out , md , liberrors .ErrSkipRequest (fmt .Sprintf ("skip global: '%s'" , middleware .GetServiceID (ctx )))
191+ service := middleware .GetServiceID (ctx )
192+
193+ if IsGlobalService (service ) {
194+ // Global service in global region - allow
195+ return next .HandleInitialize (ctx , in )
196+ }
197+
198+ // Non-global service in global region - skip
199+ return out , md , liberrors .ErrSkipRequest (
200+ fmt .Sprintf ("service '%s' is not global, but the session is" , service ))
201+ }
202+
203+ // SkipRegionalForGlobalService skips requests for global-only services
204+ // when operating in a non-global region context. This ensures global
205+ // services like CloudFront and IAM are only processed once in the
206+ // "global" pseudo-region rather than in every regional scan.
207+ type SkipRegionalForGlobalService struct {}
208+
209+ func (SkipRegionalForGlobalService ) ID () string {
210+ return "aws-nuke::skipRegionalForGlobalService"
211+ }
212+
213+ func (SkipRegionalForGlobalService ) HandleInitialize (
214+ ctx context.Context , in middleware.InitializeInput , next middleware.InitializeHandler ,
215+ ) (
216+ out middleware.InitializeOutput , md middleware.Metadata , err error ,
217+ ) {
218+ service := middleware .GetServiceID (ctx )
219+
220+ if IsGlobalService (service ) {
221+ return out , md , liberrors .ErrSkipRequest (
222+ fmt .Sprintf ("service '%s' is global, but the session is not" , service ))
223+ }
224+
225+ return next .HandleInitialize (ctx , in )
191226}
192227
193228type traceRequest struct {}
0 commit comments