From f75782469a5d805f320c3467b3db489a5713ee40 Mon Sep 17 00:00:00 2001 From: bbernays Date: Mon, 11 Jul 2022 07:13:49 -0500 Subject: [PATCH] feat: Partial lambda Function Fetch (#1194) #### Summary Here is an experimental change, where we can populate some data for Lambda Functions as long as the user has access to `lambda:List*` Pros: - As long as user has access to List they can get some data in the table, they will miss information `types.FunctionCodeLocation`, `types.Concurrency` and `Tags` Cons: - Includes information about function that could have been deleted - Continues to try to get details as we cannot be sure if a user doesn't have access to get a specific function or all functions --- --- client/helpers.go | 30 +++++++++++++++++++++++--- resources/services/lambda/functions.go | 12 ++++++++--- 2 files changed, 36 insertions(+), 6 deletions(-) diff --git a/client/helpers.go b/client/helpers.go index 2a0b1fd3e..8352a0831 100644 --- a/client/helpers.go +++ b/client/helpers.go @@ -72,6 +72,14 @@ var notFoundErrorSubstrings = []string{ "WAFNonexistentItemException", } +var accessDeniedErrorStrings = map[string]struct{}{ + "AuthorizationError": {}, + "AccessDenied": {}, + "AccessDeniedException": {}, + "InsufficientPrivilegesException": {}, + "UnauthorizedOperation": {}, +} + func readSupportedServiceRegions() *SupportedServiceRegionsData { f, err := supportedServiceRegionFile.Open(PartitionServiceRegionFile) if err != nil { @@ -176,13 +184,11 @@ func IgnoreAccessDeniedServiceDisabled(err error) bool { return strings.Contains(ae.Error(), "The security token included in the request is invalid") case "AWSOrganizationsNotInUseException": return true - case "AuthorizationError", "AccessDenied", "AccessDeniedException", "InsufficientPrivilegesException", "UnauthorizedOperation": - return true case "OptInRequired", "SubscriptionRequiredException", "InvalidClientTokenId": return true } } - return false + return isAccessDeniedError(err) } func IgnoreCommonErrors(err error) bool { @@ -301,6 +307,24 @@ func isNotFoundError(err error) bool { return false } +// IsAccessDeniedError checks if api error should be classified as a permissions issue +func (c *Client) IsAccessDeniedError(err error) bool { + if isAccessDeniedError(err) { + c.logger.Warn("API returned an Access Denied error, ignoring it and continuing...", "error", err) + return true + } + return false +} + +func isAccessDeniedError(err error) bool { + var ae smithy.APIError + if !errors.As(err, &ae) { + return false + } + _, ok := accessDeniedErrorStrings[ae.ErrorCode()] + return ok +} + func IsInvalidParameterValueError(err error) bool { var apiErr smithy.APIError return errors.As(err, &apiErr) && apiErr.ErrorCode() == "InvalidParameterValue" diff --git a/resources/services/lambda/functions.go b/resources/services/lambda/functions.go index 7e6407999..5151a76e7 100644 --- a/resources/services/lambda/functions.go +++ b/resources/services/lambda/functions.go @@ -1089,6 +1089,7 @@ func Functions() *schema.Table { // ==================================================================================================================== func fetchLambdaFunctions(ctx context.Context, meta schema.ClientMeta, parent *schema.Resource, res chan<- interface{}) error { + var diags diag.Diagnostics var input lambda.ListFunctionsInput c := meta.(*client.Client) svc := c.Services().Lambda @@ -1107,11 +1108,16 @@ func fetchLambdaFunctions(ctx context.Context, meta schema.ClientMeta, parent *s funcResponse, err := svc.GetFunction(ctx, &getFunctionInput, func(options *lambda.Options) { options.Region = c.Region }) + if err != nil { - if c.IsNotFoundError(err) { + if c.IsNotFoundError(err) || c.IsAccessDeniedError(err) { + diags = diags.Add(diag.FromError(err, diag.RESOLVING, diag.WithSeverity(diag.WARNING))) + res <- &lambda.GetFunctionOutput{ + Configuration: &f, + } continue } - return diag.WrapError(err) + return diags.Add(diag.FromError(diag.WrapError(err), diag.RESOLVING, diag.WithSeverity(diag.ERROR))) } res <- funcResponse } @@ -1121,7 +1127,7 @@ func fetchLambdaFunctions(ctx context.Context, meta schema.ClientMeta, parent *s } input.Marker = response.NextMarker } - return nil + return diags } func resolvePolicyCodeSigningConfig(ctx context.Context, meta schema.ClientMeta, resource *schema.Resource) error { r := resource.Item.(*lambda.GetFunctionOutput)