Skip to content

AwsCustomResource: Invalid RoleSessionName when PhysicalResourceId is an Arn #23260

@pergardebrink

Description

@pergardebrink

Describe the bug

I am trying to use AwsCustomResource to make an SDK call with AwsSdkCall for DataSync LocationS3 (I cannot use the CfnLocationS3 as I have to assume another role due to DataSync, which is another issue). I will try to explain the issue in a short way:

Steps to reproduce:

  • Setup a AwsCustomResource
  • Add OnCreate and OnDelete handlers for CreateLocationS3 and DeleteLocation respectively
  • Set AssumedRoleArn to a role that can be assumed by the AwsCustomResource
  • Use the PhysicalResourceId.FromLocation("LocationArn") in the OnCreate handler to get the created location arn
  • Use the PhysicalResourceIdReference in the OnDelete handler for the value LocationArn
  • Use the AwsCustomResource in your stack
  • Deploy
  • Remove the AwsCustomResource from the stack so that it will trigger a OnDelete on next deploy
  • Deploy

Expected Behavior

Resource should have be deleted

Current Behavior

The Lambda fails with the following error (from CloudWatch, my accountid redacted):

2022-12-07T09:24:45.424Z	f0e10d09-fb44-4517-8e75-fa88ff160fc0	INFO	Error [CredentialsError]: Missing credentials in config, if using AWS_CONFIG_FILE, set AWS_SDK_LOAD_CONFIG=1
    at Request.extractError (/tmp/node_modules/aws-sdk/lib/protocol/query.js:50:29)
    at Request.callListeners (/tmp/node_modules/aws-sdk/lib/sequential_executor.js:106:20)
    at Request.emit (/tmp/node_modules/aws-sdk/lib/sequential_executor.js:78:10)
    at Request.emit (/tmp/node_modules/aws-sdk/lib/request.js:686:14)
    at Request.transition (/tmp/node_modules/aws-sdk/lib/request.js:22:10)
    at AcceptorStateMachine.runTo (/tmp/node_modules/aws-sdk/lib/state_machine.js:14:12)
    at /tmp/node_modules/aws-sdk/lib/state_machine.js:26:10
    at Request.<anonymous> (/tmp/node_modules/aws-sdk/lib/request.js:38:9)
    at Request.<anonymous> (/tmp/node_modules/aws-sdk/lib/request.js:688:12)
    at Request.callListeners (/tmp/node_modules/aws-sdk/lib/sequential_executor.js:116:18) {
  code: 'CredentialsError',
  time: 2022-12-07T09:24:45.165Z,
  requestId: '3db8bce7-c03c-42ee-b84b-71e987bfcf3d',
  statusCode: 400,
  retryable: false,
  retryDelay: 67.36482039412164,
  originalError: {
    message: 'Could not load credentials from ChainableTemporaryCredentials',
    code: 'CredentialsError',
    time: 2022-12-07T09:24:45.165Z,
    requestId: '3db8bce7-c03c-42ee-b84b-71e987bfcf3d',
    statusCode: 400,
    retryable: false,
    retryDelay: 67.36482039412164,
    originalError: {
      message: "1 validation error detected: Value '1670405084883-arn:aws:datasync:us-east-1:123456789012:location/l' at 'roleSessionName' failed to satisfy constraint: Member must satisfy regular expression pattern: [\\w+=,.@-]*",
      code: 'ValidationError',
      time: 2022-12-07T09:24:45.164Z,
      requestId: '3db8bce7-c03c-42ee-b84b-71e987bfcf3d',
      statusCode: 400,
      retryable: false,
      retryDelay: 67.36482039412164
    }
  }
}

Reproduction Steps

Sample C# code for the custom call

            {
                ResourceType = "Custom::LocationS3",
                OnCreate = new AwsSdkCall
                {
                    AssumedRoleArn = props.BucketAccessRole.RoleArn,
                    Service = "DataSync",
                    Action = "createLocationS3",
                    Parameters = new Dictionary<string, object>
                    {
                        { "S3BucketArn", props.S3BucketArn },
                        { "S3Config", new Dictionary<string, object> {
                            { "BucketAccessRoleArn", props.BucketAccessRole.RoleArn },
                          }
                        },
                        { "Subdirectory", props.Subdirectory },
                    },
                    PhysicalResourceId = PhysicalResourceId.FromResponse("LocationArn")
                },
                OnDelete = new AwsSdkCall
                {
                    AssumedRoleArn = props.BucketAccessRole.RoleArn,
                    Service = "DataSync",
                    Action = "deleteLocation",
                    Parameters = new Dictionary<string, object>
                    {
                        { "LocationArn", new PhysicalResourceIdReference() },
                    },
                },
                Policy = AwsCustomResourcePolicy.FromSdkCalls(new SdkCallsPolicyOptions
                {
                    Resources = AwsCustomResourcePolicy.ANY_RESOURCE
                })
            });

Possible Solution

RoleSessionName should be filtrered for names that are not allowed in the session name here:

RoleSessionName: `${timestamp}-${physicalResourceId}`.substring(0, 64),

Additional Information/Context

I am not sure this is the correct way to use PhysicalResourceId in this scenario. I need to get the LocationArn that was returned from the OnCreate call, but don't know how to do it other than with PhysicalResourceId.FromLocation("LocationArn") ?

CDK CLI Version

2.53.0

Framework Version

No response

Node.js Version

16.3.2

OS

Windows 11

Language

.NET

Language Version

.NET 6.0

Other information

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    @aws-cdk/aws-iamRelated to AWS Identity and Access ManagementbugThis issue is a bug.effort/smallSmall work item – less than a day of effortp1

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions