diff --git a/packages/@aws-cdk/aws-servicecatalogappregistry/lib/application.ts b/packages/@aws-cdk/aws-servicecatalogappregistry/lib/application.ts index eb5112e45287e..ee0465eae5d12 100644 --- a/packages/@aws-cdk/aws-servicecatalogappregistry/lib/application.ts +++ b/packages/@aws-cdk/aws-servicecatalogappregistry/lib/application.ts @@ -134,7 +134,6 @@ abstract class ApplicationBase extends cdk.Resource implements IApplication { /** * Associate stack with the application in the stack passed as parameter. * - * If the stack is already associated, it will ignore duplicate request. * A stack can only be associated with one application. */ public associateApplicationWithStack(stack: cdk.Stack): void { @@ -146,7 +145,7 @@ abstract class ApplicationBase extends cdk.Resource implements IApplication { }); this.associatedResources.add(stack.node.addr); - if (stack !== cdk.Stack.of(this) && this.isSameAccount(stack) && !this.isStageScope(stack)) { + if (stack !== cdk.Stack.of(this) && this.isSameAccount(stack) && !this.isStageScope(stack) && !stack.nested) { stack.addDependency(cdk.Stack.of(this)); } } @@ -251,6 +250,11 @@ export class Application extends ApplicationBase { }); } + /** + * Application manager URL for the Application. + * @attribute + */ + public readonly applicationManagerUrl?: cdk.CfnOutput; public readonly applicationArn: string; public readonly applicationId: string; public readonly applicationName?: string; @@ -270,6 +274,11 @@ export class Application extends ApplicationBase { this.applicationId = application.attrId; this.applicationName = props.applicationName; this.nodeAddress = cdk.Names.nodeUniqueId(application.node); + + this.applicationManagerUrl = new cdk.CfnOutput(this, 'ApplicationManagerUrl', { + value: `https://${this.env.region}.console.aws.amazon.com/systems-manager/appmanager/application/AWS_AppRegistry_Application-${this.applicationName}`, + description: `Application manager url for application ${this.applicationName}`, + }); } protected generateUniqueHash(resourceAddress: string): string { diff --git a/packages/@aws-cdk/aws-servicecatalogappregistry/test/application.test.ts b/packages/@aws-cdk/aws-servicecatalogappregistry/test/application.test.ts index dce0d1147ec44..7a14cda920d28 100644 --- a/packages/@aws-cdk/aws-servicecatalogappregistry/test/application.test.ts +++ b/packages/@aws-cdk/aws-servicecatalogappregistry/test/application.test.ts @@ -34,11 +34,13 @@ describe('Application', () => { test('application with explicit description', () => { const description = 'my test application description'; - new appreg.Application(stack, 'MyApplication', { + const application = new appreg.Application(stack, 'MyApplication', { applicationName: 'testApplication', description: description, }); + Template.fromStack(stack).hasOutput('MyApplicationApplicationManagerUrlB79EF34D', {}); + expect(application.applicationManagerUrl?.value).toContain('AWS_AppRegistry_Application-testApplication'); Template.fromStack(stack).hasResourceProperties('AWS::ServiceCatalogAppRegistry::Application', { Description: description, }); @@ -254,7 +256,7 @@ describe('Application', () => { Template.fromStack(stack).hasResourceProperties('AWS::RAM::ResourceShare', { AllowExternalPrincipals: false, - Name: 'RAMSharee6e0e560e6f8', + Name: 'RAMShare5bb637032063', Principals: ['arn:aws:organizations::123456789012:organization/o-70oi5564q1'], ResourceArns: [{ 'Fn::GetAtt': ['MyApplication5C63EC1D', 'Arn'] }], PermissionArns: ['arn:aws:ram::aws:permission/AWSRAMPermissionServiceCatalogAppRegistryApplicationReadOnly'], @@ -268,7 +270,7 @@ describe('Application', () => { Template.fromStack(stack).hasResourceProperties('AWS::RAM::ResourceShare', { AllowExternalPrincipals: false, - Name: 'RAMSharee6e0e560e6f8', + Name: 'RAMShare5bb637032063', Principals: ['123456789012'], ResourceArns: [{ 'Fn::GetAtt': ['MyApplication5C63EC1D', 'Arn'] }], PermissionArns: ['arn:aws:ram::aws:permission/AWSRAMPermissionServiceCatalogAppRegistryApplicationReadOnly'], @@ -284,7 +286,7 @@ describe('Application', () => { Template.fromStack(stack).hasResourceProperties('AWS::RAM::ResourceShare', { AllowExternalPrincipals: false, - Name: 'RAMSharee6e0e560e6f8', + Name: 'RAMShare5bb637032063', Principals: ['arn:aws:iam::123456789012:role/myRole'], ResourceArns: [{ 'Fn::GetAtt': ['MyApplication5C63EC1D', 'Arn'] }], PermissionArns: ['arn:aws:ram::aws:permission/AWSRAMPermissionServiceCatalogAppRegistryApplicationReadOnly'], @@ -300,7 +302,7 @@ describe('Application', () => { Template.fromStack(stack).hasResourceProperties('AWS::RAM::ResourceShare', { AllowExternalPrincipals: false, - Name: 'RAMSharee6e0e560e6f8', + Name: 'RAMShare5bb637032063', Principals: ['arn:aws:iam::123456789012:user/myUser'], ResourceArns: [{ 'Fn::GetAtt': ['MyApplication5C63EC1D', 'Arn'] }], PermissionArns: ['arn:aws:ram::aws:permission/AWSRAMPermissionServiceCatalogAppRegistryApplicationReadOnly'], @@ -315,7 +317,7 @@ describe('Application', () => { Template.fromStack(stack).hasResourceProperties('AWS::RAM::ResourceShare', { AllowExternalPrincipals: false, - Name: 'RAMSharee6e0e560e6f8', + Name: 'RAMShare5bb637032063', Principals: ['arn:aws:organizations::123456789012:organization/o-70oi5564q1'], ResourceArns: [{ 'Fn::GetAtt': ['MyApplication5C63EC1D', 'Arn'] }], PermissionArns: ['arn:aws:ram::aws:permission/AWSRAMPermissionServiceCatalogAppRegistryApplicationReadOnly'], @@ -330,7 +332,7 @@ describe('Application', () => { Template.fromStack(stack).hasResourceProperties('AWS::RAM::ResourceShare', { AllowExternalPrincipals: false, - Name: 'RAMSharee6e0e560e6f8', + Name: 'RAMShare5bb637032063', Principals: ['arn:aws:organizations::123456789012:organization/o-70oi5564q1'], ResourceArns: [{ 'Fn::GetAtt': ['MyApplication5C63EC1D', 'Arn'] }], PermissionArns: ['arn:aws:ram::aws:permission/AWSRAMPermissionServiceCatalogAppRegistryApplicationAllowAssociation'], @@ -446,8 +448,59 @@ describe('Scope based Associations with Application with Cross Region/Account', }); }); +describe('Conditional nested stack Associations with Application within Same Account', () => { + let app: cdk.App; + beforeEach(() => { + app = new cdk.App({ + context: { + '@aws-cdk/core:newStyleStackSynthesis': false, + }, + }); + }); + + test('Associate conditional nested stack with application', () => { + const stack = new MainStack(app, 'cdkApplication'); + const application = new appreg.Application(stack, 'MyApplication', { + applicationName: 'MyApplication', + }); + application.associateApplicationWithStack(stack); + application.associateApplicationWithStack(stack.nestedStack); + Template.fromStack(stack.nestedStack).hasResource('AWS::ServiceCatalogAppRegistry::ResourceAssociation', { + Properties: { + Application: 'MyApplication', + Resource: { Ref: 'AWS::StackId' }, + ResourceType: 'CFN_STACK', + }, + }); + Template.fromStack(stack.nestedStack).hasCondition('ShouldCreateStackCondition', { + 'Fn::Equals': ['us-east-1'], + }); + }); + +}); + + class AppRegistrySampleStack extends cdk.Stack { public constructor(scope: Construct, id: string, props?: cdk.StackProps) { super(scope, id, props); } } + +class MainStack extends cdk.Stack { + public readonly nestedStack: cdk.Stack; + public constructor(parent: cdk.App, id: string, props?: cdk.StackProps) { + super(parent, id, props); + this.nestedStack = new AppRegistryNestedStack(this, 'nested-stack'); + } +} + +class AppRegistryNestedStack extends cdk.NestedStack { + public constructor(scope: Construct, id: string, props?: cdk.NestedStackProps) { + super(scope, id, props); + + const shouldCreateStack = new cdk.CfnCondition(this, 'ShouldCreateStackCondition', { + expression: cdk.Fn.conditionEquals(process.env.CDK_DEFAULT_REGION, 'us-east-1'), + }); + (this.nestedStackResource as cdk.CfnStack).cfnOptions.condition = shouldCreateStack; + } +} diff --git a/packages/@aws-cdk/aws-servicecatalogappregistry/test/integ.application.js.snapshot/cdk.out b/packages/@aws-cdk/aws-servicecatalogappregistry/test/integ.application.js.snapshot/cdk.out index 8ecc185e9dbee..145739f539580 100644 --- a/packages/@aws-cdk/aws-servicecatalogappregistry/test/integ.application.js.snapshot/cdk.out +++ b/packages/@aws-cdk/aws-servicecatalogappregistry/test/integ.application.js.snapshot/cdk.out @@ -1 +1 @@ -{"version":"21.0.0"} \ No newline at end of file +{"version":"22.0.0"} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-servicecatalogappregistry/test/integ.application.js.snapshot/integ-servicecatalogappregistry-application.assets.json b/packages/@aws-cdk/aws-servicecatalogappregistry/test/integ.application.js.snapshot/integ-servicecatalogappregistry-application.assets.json index 7f5db7d1e1328..225ef80716295 100644 --- a/packages/@aws-cdk/aws-servicecatalogappregistry/test/integ.application.js.snapshot/integ-servicecatalogappregistry-application.assets.json +++ b/packages/@aws-cdk/aws-servicecatalogappregistry/test/integ.application.js.snapshot/integ-servicecatalogappregistry-application.assets.json @@ -1,7 +1,7 @@ { - "version": "20.0.0", + "version": "22.0.0", "files": { - "d03aa6239eb3b20f4b72fb3dd44a4082d06d7a5451d0ac3855bd1aa78aecfbe9": { + "806ae543572346400832fe865c1bdfa243ef2c1f07c3ce10aa0bd27ed4613a42": { "source": { "path": "integ-servicecatalogappregistry-application.template.json", "packaging": "file" @@ -9,7 +9,7 @@ "destinations": { "current_account-current_region": { "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", - "objectKey": "d03aa6239eb3b20f4b72fb3dd44a4082d06d7a5451d0ac3855bd1aa78aecfbe9.json", + "objectKey": "806ae543572346400832fe865c1bdfa243ef2c1f07c3ce10aa0bd27ed4613a42.json", "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" } } diff --git a/packages/@aws-cdk/aws-servicecatalogappregistry/test/integ.application.js.snapshot/integ-servicecatalogappregistry-application.template.json b/packages/@aws-cdk/aws-servicecatalogappregistry/test/integ.application.js.snapshot/integ-servicecatalogappregistry-application.template.json index e36d7a3b58794..33f4f2a1912fb 100644 --- a/packages/@aws-cdk/aws-servicecatalogappregistry/test/integ.application.js.snapshot/integ-servicecatalogappregistry-application.template.json +++ b/packages/@aws-cdk/aws-servicecatalogappregistry/test/integ.application.js.snapshot/integ-servicecatalogappregistry-application.template.json @@ -3,7 +3,7 @@ "TestApplication2FBC585F": { "Type": "AWS::ServiceCatalogAppRegistry::Application", "Properties": { - "Name": "myApplicationtest", + "Name": "myCdkApplication", "Description": "my application description" } }, @@ -39,10 +39,10 @@ } } }, - "TestApplicationRAMSharead8ba81b8cdd40199FD1": { + "TestApplicationRAMShare3dc6227daec11BF3E108": { "Type": "AWS::RAM::ResourceShare", "Properties": { - "Name": "RAMSharead8ba81b8cdd", + "Name": "RAMShare3dc6227daec1", "AllowExternalPrincipals": false, "PermissionArns": [ "arn:aws:ram::aws:permission/AWSRAMPermissionServiceCatalogAppRegistryApplicationReadOnly" @@ -84,7 +84,7 @@ "release": "go time" } }, - "Name": "myAttributeGroupTest", + "Name": "myCdkAttributeGroup", "Description": "my attribute group description" } }, @@ -121,6 +121,23 @@ } } }, + "Outputs": { + "TestApplicationApplicationManagerUrlE1058321": { + "Description": "Application manager url for application myCdkApplication", + "Value": { + "Fn::Join": [ + "", + [ + "https://", + { + "Ref": "AWS::Region" + }, + ".console.aws.amazon.com/systems-manager/appmanager/application/AWS_AppRegistry_Application-myCdkApplication" + ] + ] + } + } + }, "Parameters": { "BootstrapVersion": { "Type": "AWS::SSM::Parameter::Value", diff --git a/packages/@aws-cdk/aws-servicecatalogappregistry/test/integ.application.js.snapshot/integ.json b/packages/@aws-cdk/aws-servicecatalogappregistry/test/integ.application.js.snapshot/integ.json index 5178be112c70a..7db84c3e5dfa8 100644 --- a/packages/@aws-cdk/aws-servicecatalogappregistry/test/integ.application.js.snapshot/integ.json +++ b/packages/@aws-cdk/aws-servicecatalogappregistry/test/integ.application.js.snapshot/integ.json @@ -1,5 +1,5 @@ { - "version": "20.0.0", + "version": "22.0.0", "testCases": { "integ.application": { "stacks": [ diff --git a/packages/@aws-cdk/aws-servicecatalogappregistry/test/integ.application.js.snapshot/manifest.json b/packages/@aws-cdk/aws-servicecatalogappregistry/test/integ.application.js.snapshot/manifest.json index d78791dfe49aa..a8cd9a83688d7 100644 --- a/packages/@aws-cdk/aws-servicecatalogappregistry/test/integ.application.js.snapshot/manifest.json +++ b/packages/@aws-cdk/aws-servicecatalogappregistry/test/integ.application.js.snapshot/manifest.json @@ -1,12 +1,6 @@ { - "version": "20.0.0", + "version": "22.0.0", "artifacts": { - "Tree": { - "type": "cdk:tree", - "properties": { - "file": "tree.json" - } - }, "integ-servicecatalogappregistry-application.assets": { "type": "cdk:asset-manifest", "properties": { @@ -23,7 +17,7 @@ "validateOnSynth": false, "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", - "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/d03aa6239eb3b20f4b72fb3dd44a4082d06d7a5451d0ac3855bd1aa78aecfbe9.json", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/806ae543572346400832fe865c1bdfa243ef2c1f07c3ce10aa0bd27ed4613a42.json", "requiresBootstrapStackVersion": 6, "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", "additionalDependencies": [ @@ -45,6 +39,12 @@ "data": "TestApplication2FBC585F" } ], + "/integ-servicecatalogappregistry-application/TestApplication/ApplicationManagerUrl": [ + { + "type": "aws:cdk:logicalId", + "data": "TestApplicationApplicationManagerUrlE1058321" + } + ], "/integ-servicecatalogappregistry-application/TestApplication/ResourceAssociationd232b63e52a8": [ { "type": "aws:cdk:logicalId", @@ -57,10 +57,10 @@ "data": "TestApplicationAttributeGroupAssociation4ba7f5842818B8EE1C6F" } ], - "/integ-servicecatalogappregistry-application/TestApplication/RAMSharead8ba81b8cdd": [ + "/integ-servicecatalogappregistry-application/TestApplication/RAMShare3dc6227daec1": [ { "type": "aws:cdk:logicalId", - "data": "TestApplicationRAMSharead8ba81b8cdd40199FD1" + "data": "TestApplicationRAMShare3dc6227daec11BF3E108" } ], "/integ-servicecatalogappregistry-application/TestAttributeGroup/Resource": [ @@ -89,6 +89,12 @@ ] }, "displayName": "integ-servicecatalogappregistry-application" + }, + "Tree": { + "type": "cdk:tree", + "properties": { + "file": "tree.json" + } } } } \ No newline at end of file diff --git a/packages/@aws-cdk/aws-servicecatalogappregistry/test/integ.application.js.snapshot/tree.json b/packages/@aws-cdk/aws-servicecatalogappregistry/test/integ.application.js.snapshot/tree.json index f54792dcdef5c..da9f344585ed2 100644 --- a/packages/@aws-cdk/aws-servicecatalogappregistry/test/integ.application.js.snapshot/tree.json +++ b/packages/@aws-cdk/aws-servicecatalogappregistry/test/integ.application.js.snapshot/tree.json @@ -4,14 +4,6 @@ "id": "App", "path": "", "children": { - "Tree": { - "id": "Tree", - "path": "Tree", - "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.85" - } - }, "integ-servicecatalogappregistry-application": { "id": "integ-servicecatalogappregistry-application", "path": "integ-servicecatalogappregistry-application", @@ -26,7 +18,7 @@ "attributes": { "aws:cdk:cloudformation:type": "AWS::ServiceCatalogAppRegistry::Application", "aws:cdk:cloudformation:props": { - "name": "myApplicationtest", + "name": "myCdkApplication", "description": "my application description" } }, @@ -35,6 +27,14 @@ "version": "0.0.0" } }, + "ApplicationManagerUrl": { + "id": "ApplicationManagerUrl", + "path": "integ-servicecatalogappregistry-application/TestApplication/ApplicationManagerUrl", + "constructInfo": { + "fqn": "@aws-cdk/core.CfnOutput", + "version": "0.0.0" + } + }, "ResourceAssociationd232b63e52a8": { "id": "ResourceAssociationd232b63e52a8", "path": "integ-servicecatalogappregistry-application/TestApplication/ResourceAssociationd232b63e52a8", @@ -83,13 +83,13 @@ "version": "0.0.0" } }, - "RAMSharead8ba81b8cdd": { - "id": "RAMSharead8ba81b8cdd", - "path": "integ-servicecatalogappregistry-application/TestApplication/RAMSharead8ba81b8cdd", + "RAMShare3dc6227daec1": { + "id": "RAMShare3dc6227daec1", + "path": "integ-servicecatalogappregistry-application/TestApplication/RAMShare3dc6227daec1", "attributes": { "aws:cdk:cloudformation:type": "AWS::RAM::ResourceShare", "aws:cdk:cloudformation:props": { - "name": "RAMSharead8ba81b8cdd", + "name": "RAMShare3dc6227daec1", "allowExternalPrincipals": false, "permissionArns": [ "arn:aws:ram::aws:permission/AWSRAMPermissionServiceCatalogAppRegistryApplicationReadOnly" @@ -149,7 +149,7 @@ "release": "go time" } }, - "name": "myAttributeGroupTest", + "name": "myCdkAttributeGroup", "description": "my attribute group description" } }, @@ -168,6 +168,14 @@ "id": "MyRole", "path": "integ-servicecatalogappregistry-application/MyRole", "children": { + "ImportMyRole": { + "id": "ImportMyRole", + "path": "integ-servicecatalogappregistry-application/MyRole/ImportMyRole", + "constructInfo": { + "fqn": "@aws-cdk/core.Resource", + "version": "0.0.0" + } + }, "Resource": { "id": "Resource", "path": "integ-servicecatalogappregistry-application/MyRole/Resource", @@ -213,17 +221,41 @@ "fqn": "@aws-cdk/aws-iam.Role", "version": "0.0.0" } + }, + "BootstrapVersion": { + "id": "BootstrapVersion", + "path": "integ-servicecatalogappregistry-application/BootstrapVersion", + "constructInfo": { + "fqn": "@aws-cdk/core.CfnParameter", + "version": "0.0.0" + } + }, + "CheckBootstrapVersion": { + "id": "CheckBootstrapVersion", + "path": "integ-servicecatalogappregistry-application/CheckBootstrapVersion", + "constructInfo": { + "fqn": "@aws-cdk/core.CfnRule", + "version": "0.0.0" + } } }, + "constructInfo": { + "fqn": "@aws-cdk/core.Stack", + "version": "0.0.0" + } + }, + "Tree": { + "id": "Tree", + "path": "Tree", "constructInfo": { "fqn": "constructs.Construct", - "version": "10.1.85" + "version": "10.1.168" } } }, "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.85" + "fqn": "@aws-cdk/core.App", + "version": "0.0.0" } } } \ No newline at end of file diff --git a/packages/@aws-cdk/aws-servicecatalogappregistry/test/integ.application.ts b/packages/@aws-cdk/aws-servicecatalogappregistry/test/integ.application.ts index d51cad051252c..30f5e2bb88829 100644 --- a/packages/@aws-cdk/aws-servicecatalogappregistry/test/integ.application.ts +++ b/packages/@aws-cdk/aws-servicecatalogappregistry/test/integ.application.ts @@ -6,12 +6,12 @@ const app = new cdk.App(); const stack = new cdk.Stack(app, 'integ-servicecatalogappregistry-application'); const application = new appreg.Application(stack, 'TestApplication', { - applicationName: 'myApplicationtest', + applicationName: 'myCdkApplication', description: 'my application description', }); const attributeGroup = new appreg.AttributeGroup(stack, 'TestAttributeGroup', { - attributeGroupName: 'myAttributeGroupTest', + attributeGroupName: 'myCdkAttributeGroup', description: 'my attribute group description', attributes: { stage: 'alpha',