diff --git a/packages/aws-cdk/lib/api/evaluate-cloudformation-template.ts b/packages/aws-cdk/lib/api/evaluate-cloudformation-template.ts index 4c82399bdf7cc..f6d51969af5d5 100644 --- a/packages/aws-cdk/lib/api/evaluate-cloudformation-template.ts +++ b/packages/aws-cdk/lib/api/evaluate-cloudformation-template.ts @@ -300,7 +300,12 @@ export class EvaluateCloudFormationTemplate { } private getResourceTypeArnPartOfResource(resource: AWS.CloudFormation.StackResourceSummary): string { - return resource.ResourceType.split('::')[2].toLowerCase(); + const resourceType = resource.ResourceType; + const specialCaseResourceType = RESOURCE_TYPE_SPECIAL_NAMES[resourceType]?.resourceType; + return specialCaseResourceType + ? specialCaseResourceType + // this is the default case + : resourceType.split('::')[2].toLowerCase(); } } @@ -313,12 +318,30 @@ interface ArnParts { readonly resourceName: string; } +/** + * Usually, we deduce the names of the service and the resource type used to format the ARN from the CloudFormation resource type. + * For a CFN type like AWS::Service::ResourceType, the second segment becomes the service name, and the third the resource type + * (after converting both of them to lowercase). + * However, some resource types break this simple convention, and we need to special-case them. + * This map is for storing those cases. + */ +const RESOURCE_TYPE_SPECIAL_NAMES: { [type: string]: { resourceType: string } } = { + 'AWS::Events::EventBus': { + resourceType: 'event-bus', + }, +}; + const RESOURCE_TYPE_ATTRIBUTES_FORMATS: { [type: string]: { [attribute: string]: (parts: ArnParts) => string } } = { 'AWS::IAM::Role': { Arn: iamArnFmt }, 'AWS::IAM::User': { Arn: iamArnFmt }, 'AWS::IAM::Group': { Arn: iamArnFmt }, 'AWS::S3::Bucket': { Arn: s3ArnFmt }, 'AWS::Lambda::Function': { Arn: stdColonResourceArnFmt }, + 'AWS::Events::EventBus': { + Arn: stdSlashResourceArnFmt, + // the name attribute of the EventBus is the same as the Ref + Name: parts => parts.resourceName, + }, }; function iamArnFmt(parts: ArnParts): string { @@ -336,6 +359,11 @@ function stdColonResourceArnFmt(parts: ArnParts): string { return `arn:${parts.partition}:${parts.service}:${parts.region}:${parts.account}:${parts.resourceType}:${parts.resourceName}`; } +function stdSlashResourceArnFmt(parts: ArnParts): string { + // this is a standard format for ARNs like: arn:aws:service:region:account:resourceType/resourceName + return `arn:${parts.partition}:${parts.service}:${parts.region}:${parts.account}:${parts.resourceType}/${parts.resourceName}`; +} + interface Intrinsic { readonly name: string; readonly args: any; diff --git a/packages/aws-cdk/test/api/hotswap/state-machine-hotswap-deployments.test.ts b/packages/aws-cdk/test/api/hotswap/state-machine-hotswap-deployments.test.ts index 94467c5ac123d..1822258187f53 100644 --- a/packages/aws-cdk/test/api/hotswap/state-machine-hotswap-deployments.test.ts +++ b/packages/aws-cdk/test/api/hotswap/state-machine-hotswap-deployments.test.ts @@ -482,3 +482,79 @@ test("will not perform a hotswap deployment if it doesn't know how to handle a s hotswapMockSdkProvider.tryHotswapDeployment(cdkStackArtifact), ).rejects.toThrow("We don't support the 'UnknownAttribute' attribute of the 'AWS::S3::Bucket' resource. This is a CDK limitation. Please report it at https://github.com/aws/aws-cdk/issues/new/choose"); }); + +test('knows how to handle attributes of the AWS::Events::EventBus resource', async () => { + // GIVEN + setup.setCurrentCfnStackTemplate({ + Resources: { + EventBus: { + Type: 'AWS::Events::EventBus', + Properties: { + Name: 'my-event-bus', + }, + }, + Machine: { + Type: 'AWS::StepFunctions::StateMachine', + Properties: { + DefinitionString: { + 'Fn::Join': ['', [ + '{"EventBus1Arn":"', + { 'Fn::GetAtt': ['EventBus', 'Arn'] }, + '","EventBus1Name":"', + { 'Fn::GetAtt': ['EventBus', 'Name'] }, + '","EventBus1Ref":"', + { Ref: 'EventBus' }, + '"}', + ]], + }, + StateMachineName: 'my-machine', + }, + }, + }, + }); + setup.pushStackResourceSummaries( + setup.stackSummaryOf('EventBus', 'AWS::Events::EventBus', 'my-event-bus'), + ); + const cdkStackArtifact = setup.cdkStackArtifactOf({ + template: { + Resources: { + EventBus: { + Type: 'AWS::Events::EventBus', + Properties: { + Name: 'my-event-bus', + }, + }, + Machine: { + Type: 'AWS::StepFunctions::StateMachine', + Properties: { + DefinitionString: { + 'Fn::Join': ['', [ + '{"EventBus2Arn":"', + { 'Fn::GetAtt': ['EventBus', 'Arn'] }, + '","EventBus2Name":"', + { 'Fn::GetAtt': ['EventBus', 'Name'] }, + '","EventBus2Ref":"', + { Ref: 'EventBus' }, + '"}', + ]], + }, + StateMachineName: 'my-machine', + }, + }, + }, + }, + }); + + // THEN + const result = await hotswapMockSdkProvider.tryHotswapDeployment(cdkStackArtifact); + + expect(result).not.toBeUndefined(); + expect(mockUpdateMachineDefinition).toHaveBeenCalledWith({ + stateMachineArn: 'arn:aws:states:here:123456789012:stateMachine:my-machine', + definition: JSON.stringify({ + EventBus2Arn: 'arn:aws:events:here:123456789012:event-bus/my-event-bus', + EventBus2Name: 'my-event-bus', + EventBus2Ref: 'my-event-bus', + }), + }); +});