diff --git a/packages/@aws-cdk/aws-apigateway/lib/api-key.ts b/packages/@aws-cdk/aws-apigateway/lib/api-key.ts index ad3523064e5de..e1c90d781731e 100644 --- a/packages/@aws-cdk/aws-apigateway/lib/api-key.ts +++ b/packages/@aws-cdk/aws-apigateway/lib/api-key.ts @@ -1,4 +1,4 @@ -import { Construct, IResource as IResourceBase, Resource } from '@aws-cdk/cdk'; +import { Construct, IResource as IResourceBase, PhysicalName, Resource } from '@aws-cdk/cdk'; import { CfnApiKey } from './apigateway.generated'; import { ResourceOptions } from "./resource"; import { RestApi } from './restapi'; @@ -70,7 +70,7 @@ export interface ApiKeyProps extends ResourceOptions { * @link http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-apigateway-apikey.html#cfn-apigateway-apikey-name * @default automically generated name */ - readonly name?: string; + readonly apiKeyName?: PhysicalName; } /** @@ -83,14 +83,16 @@ export class ApiKey extends Resource implements IApiKey { public readonly keyId: string; constructor(scope: Construct, id: string, props: ApiKeyProps = { }) { - super(scope, id); + super(scope, id, { + physicalName: props.apiKeyName, + }); const resource = new CfnApiKey(this, 'Resource', { customerId: props.customerId, description: props.description, enabled: props.enabled || true, generateDistinctId: props.generateDistinctId, - name: props.name, + name: this.physicalName.value, stageKeys: this.renderStageKeys(props.resources) }); diff --git a/packages/@aws-cdk/aws-apigateway/lib/restapi.ts b/packages/@aws-cdk/aws-apigateway/lib/restapi.ts index b946e5a753ce8..77446d59c8610 100644 --- a/packages/@aws-cdk/aws-apigateway/lib/restapi.ts +++ b/packages/@aws-cdk/aws-apigateway/lib/restapi.ts @@ -1,5 +1,5 @@ import iam = require('@aws-cdk/aws-iam'); -import { CfnOutput, Construct, IResource as IResourceBase, Resource, Stack } from '@aws-cdk/cdk'; +import { CfnOutput, Construct, IResource as IResourceBase, PhysicalName, Resource, Stack } from '@aws-cdk/cdk'; import { ApiKey, IApiKey } from './api-key'; import { CfnAccount, CfnRestApi } from './apigateway.generated'; import { Deployment } from './deployment'; @@ -64,7 +64,7 @@ export interface RestApiProps extends ResourceOptions { * * @default - ID of the RestApi construct. */ - readonly restApiName?: string; + readonly restApiName?: PhysicalName; /** * Custom header parameters for the request. @@ -198,10 +198,12 @@ export class RestApi extends Resource implements IRestApi { private _latestDeployment: Deployment | undefined; constructor(scope: Construct, id: string, props: RestApiProps = { }) { - super(scope, id); + super(scope, id, { + physicalName: props.restApiName || PhysicalName.of(id), + }); const resource = new CfnRestApi(this, 'Resource', { - name: props.restApiName || id, + name: this.physicalName.value!, description: props.description, policy: props.policy, failOnWarnings: props.failOnWarnings, diff --git a/packages/@aws-cdk/aws-apigateway/lib/vpc-link.ts b/packages/@aws-cdk/aws-apigateway/lib/vpc-link.ts index 82394a2daa07b..b850a8b612d49 100644 --- a/packages/@aws-cdk/aws-apigateway/lib/vpc-link.ts +++ b/packages/@aws-cdk/aws-apigateway/lib/vpc-link.ts @@ -1,5 +1,5 @@ import elbv2 = require('@aws-cdk/aws-elasticloadbalancingv2'); -import { Construct, Lazy, Resource } from '@aws-cdk/cdk'; +import { Construct, Lazy, PhysicalName, Resource } from '@aws-cdk/cdk'; import { CfnVpcLink } from './apigateway.generated'; /** @@ -10,7 +10,7 @@ export interface VpcLinkProps { * The name used to label and identify the VPC link. * @default - automatically generated name */ - readonly vpcLinkName?: string; + readonly vpcLinkName?: PhysicalName; /** * The description of the VPC link. @@ -41,10 +41,13 @@ export class VpcLink extends Resource { private readonly targets = new Array(); constructor(scope: Construct, id: string, props: VpcLinkProps = {}) { - super(scope, id); + super(scope, id, { + physicalName: props.vpcLinkName || + PhysicalName.of(Lazy.stringValue({ produce: () => this.node.uniqueId })), + }); const cfnResource = new CfnVpcLink(this, 'Resource', { - name: props.vpcLinkName || this.node.uniqueId, + name: this.physicalName.value!, description: props.description, targetArns: Lazy.listValue({ produce: () => this.renderTargets() }) }); diff --git a/packages/@aws-cdk/aws-apigateway/package.json b/packages/@aws-cdk/aws-apigateway/package.json index 1d33f41476d9b..67d99d6a13101 100644 --- a/packages/@aws-cdk/aws-apigateway/package.json +++ b/packages/@aws-cdk/aws-apigateway/package.json @@ -93,8 +93,15 @@ "exclude": [ "from-method:@aws-cdk/aws-apigateway.Resource", "from-method:@aws-cdk/aws-apigateway.ApiKey", - "ref-via-interface:@aws-cdk/aws-apigateway.ApiKeyProps.resources" + "ref-via-interface:@aws-cdk/aws-apigateway.ApiKeyProps.resources", + "props-physical-name:@aws-cdk/aws-apigateway.DeploymentProps", + "props-physical-name:@aws-cdk/aws-apigateway.MethodProps", + "props-physical-name:@aws-cdk/aws-apigateway.ProxyResourceProps", + "props-physical-name:@aws-cdk/aws-apigateway.ResourceProps", + "props-physical-name:@aws-cdk/aws-apigateway.StageProps", + "props-physical-name:@aws-cdk/aws-apigateway.UsagePlanProps", + "props-physical-name:@aws-cdk/aws-apigateway.LambdaRestApiProps" ] }, "stability": "experimental" -} \ No newline at end of file +} diff --git a/packages/@aws-cdk/aws-apigateway/test/test.lambda-api.ts b/packages/@aws-cdk/aws-apigateway/test/test.lambda-api.ts index dfceb4b38c259..902a4112f5cc8 100644 --- a/packages/@aws-cdk/aws-apigateway/test/test.lambda-api.ts +++ b/packages/@aws-cdk/aws-apigateway/test/test.lambda-api.ts @@ -81,7 +81,7 @@ export = { runtime: lambda.Runtime.Nodejs810, }); const alias = new lambda.Alias(stack, 'alias', { - aliasName: 'my-alias', + aliasName: cdk.PhysicalName.of('my-alias'), version: new lambda.Version(stack, 'version', { lambda: handler }) diff --git a/packages/@aws-cdk/aws-apigateway/test/test.restapi.ts b/packages/@aws-cdk/aws-apigateway/test/test.restapi.ts index 146a51bb82274..c3fccb0313578 100644 --- a/packages/@aws-cdk/aws-apigateway/test/test.restapi.ts +++ b/packages/@aws-cdk/aws-apigateway/test/test.restapi.ts @@ -141,7 +141,7 @@ export = { const api = new apigateway.RestApi(stack, 'restapi', { deploy: false, cloudWatchRole: false, - restApiName: 'my-rest-api' + restApiName: cdk.PhysicalName.of('my-rest-api'), }); api.root.addMethod('GET'); @@ -176,7 +176,7 @@ export = { const api = new apigateway.RestApi(stack, 'restapi', { deploy: false, cloudWatchRole: false, - restApiName: 'my-rest-api' + restApiName: cdk.PhysicalName.of('my-rest-api'), }); // WHEN diff --git a/packages/@aws-cdk/aws-apigateway/test/test.vpc-link.ts b/packages/@aws-cdk/aws-apigateway/test/test.vpc-link.ts index bfd68d16edfff..cb50623ced091 100644 --- a/packages/@aws-cdk/aws-apigateway/test/test.vpc-link.ts +++ b/packages/@aws-cdk/aws-apigateway/test/test.vpc-link.ts @@ -16,7 +16,7 @@ export = { // WHEN new apigateway.VpcLink(stack, 'VpcLink', { - vpcLinkName: 'MyLink', + vpcLinkName: cdk.PhysicalName.of('MyLink'), targets: [nlb] }); @@ -71,4 +71,4 @@ export = { test.throws(() => app.synth(), /No targets added to vpc link/); test.done(); } -}; \ No newline at end of file +}; diff --git a/packages/@aws-cdk/aws-applicationautoscaling/package.json b/packages/@aws-cdk/aws-applicationautoscaling/package.json index 9170a96686dea..968ae55e01516 100644 --- a/packages/@aws-cdk/aws-applicationautoscaling/package.json +++ b/packages/@aws-cdk/aws-applicationautoscaling/package.json @@ -85,5 +85,10 @@ "engines": { "node": ">= 8.10.0" }, + "awslint": { + "exclude": [ + "props-physical-name:@aws-cdk/aws-applicationautoscaling.ScalableTargetProps" + ] + }, "stability": "experimental" -} \ No newline at end of file +} diff --git a/packages/@aws-cdk/aws-autoscaling/lib/lifecycle-hook.ts b/packages/@aws-cdk/aws-autoscaling/lib/lifecycle-hook.ts index 9ab2cf235237b..175742dbc96ff 100644 --- a/packages/@aws-cdk/aws-autoscaling/lib/lifecycle-hook.ts +++ b/packages/@aws-cdk/aws-autoscaling/lib/lifecycle-hook.ts @@ -1,5 +1,5 @@ import iam = require('@aws-cdk/aws-iam'); -import { Construct, IResource, Resource } from '@aws-cdk/cdk'; +import { Construct, IResource, PhysicalName, Resource } from '@aws-cdk/cdk'; import { IAutoScalingGroup } from './auto-scaling-group'; import { CfnLifecycleHook } from './autoscaling.generated'; import { ILifecycleHookTarget } from './lifecycle-hook-target'; @@ -13,7 +13,7 @@ export interface BasicLifecycleHookProps { * * @default - Automatically generated name. */ - readonly lifecycleHookName?: string; + readonly lifecycleHookName?: PhysicalName; /** * The action the Auto Scaling group takes when the lifecycle hook timeout elapses or if an unexpected failure occurs. @@ -92,7 +92,9 @@ export class LifecycleHook extends Resource implements ILifecycleHook { public readonly lifecycleHookName: string; constructor(scope: Construct, id: string, props: LifecycleHookProps) { - super(scope, id); + super(scope, id, { + physicalName: props.lifecycleHookName, + }); this.role = props.role || new iam.Role(this, 'Role', { assumedBy: new iam.ServicePrincipal('autoscaling.amazonaws.com') @@ -104,7 +106,7 @@ export class LifecycleHook extends Resource implements ILifecycleHook { autoScalingGroupName: props.autoScalingGroup.autoScalingGroupName, defaultResult: props.defaultResult, heartbeatTimeout: props.heartbeatTimeoutSec, - lifecycleHookName: props.lifecycleHookName, + lifecycleHookName: this.physicalName.value, lifecycleTransition: props.lifecycleTransition, notificationMetadata: props.notificationMetadata, notificationTargetArn: targetProps.notificationTargetArn, diff --git a/packages/@aws-cdk/aws-autoscaling/package.json b/packages/@aws-cdk/aws-autoscaling/package.json index f068541fcf937..6ce3804c227e3 100644 --- a/packages/@aws-cdk/aws-autoscaling/package.json +++ b/packages/@aws-cdk/aws-autoscaling/package.json @@ -97,8 +97,10 @@ "exclude": [ "import-props-interface:@aws-cdk/aws-autoscaling.AutoScalingGroupImportProps", "resource-interface-extends-construct:@aws-cdk/aws-autoscaling.IAutoScalingGroup", - "export:@aws-cdk/aws-autoscaling.IAutoScalingGroup" + "export:@aws-cdk/aws-autoscaling.IAutoScalingGroup", + "props-physical-name:@aws-cdk/aws-autoscaling.AutoScalingGroupProps", + "props-physical-name:@aws-cdk/aws-autoscaling.ScheduledActionProps" ] }, "stability": "experimental" -} \ No newline at end of file +} diff --git a/packages/@aws-cdk/aws-certificatemanager/package.json b/packages/@aws-cdk/aws-certificatemanager/package.json index d336d1022e0a8..02ada6dca27d8 100644 --- a/packages/@aws-cdk/aws-certificatemanager/package.json +++ b/packages/@aws-cdk/aws-certificatemanager/package.json @@ -86,5 +86,11 @@ "engines": { "node": ">= 8.10.0" }, + "awslint": { + "exclude": [ + "props-physical-name:@aws-cdk/aws-certificatemanager.CertificateProps", + "props-physical-name:@aws-cdk/aws-certificatemanager.DnsValidatedCertificateProps" + ] + }, "stability": "experimental" -} \ No newline at end of file +} diff --git a/packages/@aws-cdk/aws-cloudformation/package.json b/packages/@aws-cdk/aws-cloudformation/package.json index 629b8aa756f81..0232a60e9931b 100644 --- a/packages/@aws-cdk/aws-cloudformation/package.json +++ b/packages/@aws-cdk/aws-cloudformation/package.json @@ -101,8 +101,9 @@ "exclude": [ "construct-ctor:@aws-cdk/aws-cloudformation.PipelineCloudFormationAction.", "construct-ctor:@aws-cdk/aws-cloudformation.PipelineCloudFormationDeployAction.", - "construct-ctor-props-optional:@aws-cdk/aws-cloudformation.AwsCustomResource" + "construct-ctor-props-optional:@aws-cdk/aws-cloudformation.AwsCustomResource", + "props-physical-name:@aws-cdk/aws-cloudformation.CustomResourceProps" ] }, "stability": "experimental" -} \ No newline at end of file +} diff --git a/packages/@aws-cdk/aws-cloudtrail/lib/index.ts b/packages/@aws-cdk/aws-cloudtrail/lib/index.ts index 7ca64ae6d0ff7..d0cc3b6b013d4 100644 --- a/packages/@aws-cdk/aws-cloudtrail/lib/index.ts +++ b/packages/@aws-cdk/aws-cloudtrail/lib/index.ts @@ -3,7 +3,7 @@ import iam = require('@aws-cdk/aws-iam'); import kms = require('@aws-cdk/aws-kms'); import logs = require('@aws-cdk/aws-logs'); import s3 = require('@aws-cdk/aws-s3'); -import { Construct, Resource, Stack } from '@aws-cdk/cdk'; +import { Construct, PhysicalName, Resource, ResourceIdentifiers, Stack } from '@aws-cdk/cdk'; import { CfnTrail } from './cloudtrail.generated'; // AWS::CloudTrail CloudFormation Resources: @@ -86,7 +86,7 @@ export interface TrailProps { * * @default - AWS CloudFormation generated name. */ - readonly trailName?: string; + readonly trailName?: PhysicalName; /** An Amazon S3 object key prefix that precedes the name of all log files. * @@ -125,7 +125,9 @@ export class Trail extends Resource { private eventSelectors: EventSelector[] = []; constructor(scope: Construct, id: string, props: TrailProps = {}) { - super(scope, id); + super(scope, id, { + physicalName: props.trailName, + }); const s3bucket = new s3.Bucket(this, 'S3', {encryption: s3.BucketEncryption.Unencrypted}); const cloudTrailPrincipal = new iam.ServicePrincipal("cloudtrail.amazonaws.com"); @@ -173,7 +175,7 @@ export class Trail extends Resource { enableLogFileValidation: props.enableFileValidation == null ? true : props.enableFileValidation, isMultiRegionTrail: props.isMultiRegionTrail == null ? true : props.isMultiRegionTrail, includeGlobalServiceEvents: props.includeGlobalServiceEvents == null ? true : props.includeGlobalServiceEvents, - trailName: props.trailName, + trailName: this.physicalName.value, kmsKeyId: props.kmsKey && props.kmsKey.keyArn, s3BucketName: s3bucket.bucketName, s3KeyPrefix: props.s3KeyPrefix, @@ -183,7 +185,16 @@ export class Trail extends Resource { eventSelectors: this.eventSelectors }); - this.trailArn = trail.attrArn; + const resourceIdentifiers = new ResourceIdentifiers(this, { + arn: trail.attrArn, + name: trail.trailName || '', + arnComponents: { + service: 'cloudtrail', + resource: 'trail', + resourceName: this.physicalName.value, + }, + }); + this.trailArn = resourceIdentifiers.arn; this.trailSnsTopicArn = trail.attrSnsTopicArn; const s3BucketPolicy = s3bucket.node.findChild("Policy").node.findChild("Resource") as s3.CfnBucketPolicy; diff --git a/packages/@aws-cdk/aws-cloudwatch/lib/alarm.ts b/packages/@aws-cdk/aws-cloudwatch/lib/alarm.ts index b065867a8408e..d29c9783f5b83 100644 --- a/packages/@aws-cdk/aws-cloudwatch/lib/alarm.ts +++ b/packages/@aws-cdk/aws-cloudwatch/lib/alarm.ts @@ -1,4 +1,4 @@ -import { Construct, IResource, Lazy, Resource, Stack } from '@aws-cdk/cdk'; +import { Construct, IResource, Lazy, Resource, ResourceIdentifiers, Stack } from '@aws-cdk/cdk'; import { IAlarmAction } from './alarm-action'; import { CfnAlarm } from './cloudwatch.generated'; import { HorizontalAnnotation } from './graph'; @@ -115,7 +115,9 @@ export class Alarm extends Resource implements IAlarm { private readonly annotation: HorizontalAnnotation; constructor(scope: Construct, id: string, props: AlarmProps) { - super(scope, id); + super(scope, id, { + physicalName: props.alarmName, + }); const comparisonOperator = props.comparisonOperator || ComparisonOperator.GreaterThanOrEqualToThreshold; @@ -124,7 +126,7 @@ export class Alarm extends Resource implements IAlarm { const alarm = new CfnAlarm(this, 'Resource', { // Meta alarmDescription: props.alarmDescription, - alarmName: props.alarmName, + alarmName: this.physicalName.value, // Evaluation comparisonOperator, @@ -149,8 +151,19 @@ export class Alarm extends Resource implements IAlarm { }) }); - this.alarmArn = alarm.attrArn; - this.alarmName = alarm.refAsString; + const resourceIdentifiers = new ResourceIdentifiers(this, { + arn: alarm.attrArn, + name: alarm.refAsString, + arnComponents: { + service: 'cloudwatch', + resource: 'alarm', + resourceName: this.physicalName.value, + sep: ':', + }, + }); + this.alarmArn = resourceIdentifiers.arn; + this.alarmName = resourceIdentifiers.name; + this.metric = props.metric; this.annotation = { // tslint:disable-next-line:max-line-length @@ -239,4 +252,4 @@ function dropUndef(x: T): T { } } return ret; -} \ No newline at end of file +} diff --git a/packages/@aws-cdk/aws-cloudwatch/lib/dashboard.ts b/packages/@aws-cdk/aws-cloudwatch/lib/dashboard.ts index 45bb76981c49f..6c7501138c983 100644 --- a/packages/@aws-cdk/aws-cloudwatch/lib/dashboard.ts +++ b/packages/@aws-cdk/aws-cloudwatch/lib/dashboard.ts @@ -1,4 +1,4 @@ -import { Construct, Lazy, Resource, Stack } from "@aws-cdk/cdk"; +import { Construct, Lazy, PhysicalName, Resource, Stack } from "@aws-cdk/cdk"; import { CfnDashboard } from './cloudwatch.generated'; import { Column, Row } from "./layout"; import { IWidget } from "./widget"; @@ -14,7 +14,7 @@ export interface DashboardProps { * * @default Automatically generated name */ - readonly dashboardName?: string; + readonly dashboardName?: PhysicalName; /** * The start of the time range to use for each widget on the dashboard. @@ -63,10 +63,12 @@ export class Dashboard extends Resource { private readonly rows: IWidget[] = []; constructor(scope: Construct, id: string, props: DashboardProps = {}) { - super(scope, id); + super(scope, id, { + physicalName: props.dashboardName, + }); new CfnDashboard(this, 'Resource', { - dashboardName: props.dashboardName, + dashboardName: this.physicalName.value, dashboardBody: Lazy.stringValue({ produce: () => { const column = new Column(...this.rows); column.position(0, 0); diff --git a/packages/@aws-cdk/aws-cloudwatch/lib/metric.ts b/packages/@aws-cdk/aws-cloudwatch/lib/metric.ts index 2c0384ddf9d16..3f1f6bf26cf6c 100644 --- a/packages/@aws-cdk/aws-cloudwatch/lib/metric.ts +++ b/packages/@aws-cdk/aws-cloudwatch/lib/metric.ts @@ -260,7 +260,7 @@ export interface CreateAlarmOptions { * * @default Automatically generated name */ - readonly alarmName?: string; + readonly alarmName?: cdk.PhysicalName; /** * Description for the alarm diff --git a/packages/@aws-cdk/aws-cloudwatch/test/integ.alarm-and-dashboard.ts b/packages/@aws-cdk/aws-cloudwatch/test/integ.alarm-and-dashboard.ts index a1dbacc5af0f9..dff1aff02ac7c 100644 --- a/packages/@aws-cdk/aws-cloudwatch/test/integ.alarm-and-dashboard.ts +++ b/packages/@aws-cdk/aws-cloudwatch/test/integ.alarm-and-dashboard.ts @@ -27,7 +27,7 @@ const alarm = metric.createAlarm(stack, 'Alarm', { }); const dashboard = new cloudwatch.Dashboard(stack, 'Dash', { - dashboardName: 'MyCustomDashboardName', + dashboardName: cdk.PhysicalName.of('MyCustomDashboardName'), start: '-9H', end: '2018-12-17T06:00:00.000Z', periodOverride: PeriodOverride.Inherit diff --git a/packages/@aws-cdk/aws-cloudwatch/test/test.dashboard.ts b/packages/@aws-cdk/aws-cloudwatch/test/test.dashboard.ts index 73fc6fa55288e..dcbd27b5db341 100644 --- a/packages/@aws-cdk/aws-cloudwatch/test/test.dashboard.ts +++ b/packages/@aws-cdk/aws-cloudwatch/test/test.dashboard.ts @@ -1,5 +1,5 @@ import { expect, haveResource, isSuperObject } from '@aws-cdk/assert'; -import { App, Stack } from '@aws-cdk/cdk'; +import { App, PhysicalName, Stack } from '@aws-cdk/cdk'; import { Test } from 'nodeunit'; import { Dashboard, GraphWidget, PeriodOverride, TextWidget } from '../lib'; @@ -127,7 +127,7 @@ export = { // WHEN new Dashboard(stack, 'MyDashboard', { - dashboardName: 'MyCustomDashboardName' + dashboardName: PhysicalName.of('MyCustomDashboardName'), }); // THEN diff --git a/packages/@aws-cdk/aws-codebuild/lib/project.ts b/packages/@aws-cdk/aws-codebuild/lib/project.ts index 5ca69b746309a..98cc6bb6dbbe6 100644 --- a/packages/@aws-cdk/aws-codebuild/lib/project.ts +++ b/packages/@aws-cdk/aws-codebuild/lib/project.ts @@ -883,7 +883,7 @@ export class Project extends ProjectBase { })); const policy = new iam.Policy(this, 'PolicyDocument', { - policyName: 'CodeBuildEC2Policy', + policyName: PhysicalName.of('CodeBuildEC2Policy'), statements: [ new iam.PolicyStatement({ resources: ['*'], diff --git a/packages/@aws-cdk/aws-codebuild/package.json b/packages/@aws-cdk/aws-codebuild/package.json index 43eb77c48043a..0160f7f4370a7 100644 --- a/packages/@aws-cdk/aws-codebuild/package.json +++ b/packages/@aws-cdk/aws-codebuild/package.json @@ -114,4 +114,4 @@ ] }, "stability": "experimental" -} \ No newline at end of file +} diff --git a/packages/@aws-cdk/aws-codebuild/test/integ.project-vpc.ts b/packages/@aws-cdk/aws-codebuild/test/integ.project-vpc.ts index dd53529eb9748..c5dd286d07514 100644 --- a/packages/@aws-cdk/aws-codebuild/test/integ.project-vpc.ts +++ b/packages/@aws-cdk/aws-codebuild/test/integ.project-vpc.ts @@ -13,7 +13,7 @@ const vpc = new ec2.Vpc(stack, 'MyVPC', { const securityGroup = new ec2.SecurityGroup(stack, 'SecurityGroup1', { allowAllOutbound: true, description: 'Example', - groupName: 'Bob', + groupName: cdk.PhysicalName.of('Bob'), vpc, }); new codebuild.Project(stack, 'MyProject', { diff --git a/packages/@aws-cdk/aws-codebuild/test/test.codebuild.ts b/packages/@aws-cdk/aws-codebuild/test/test.codebuild.ts index a9ff68a1cc876..dae52caec15ea 100644 --- a/packages/@aws-cdk/aws-codebuild/test/test.codebuild.ts +++ b/packages/@aws-cdk/aws-codebuild/test/test.codebuild.ts @@ -141,7 +141,9 @@ export = { 'with CodeCommit source'(test: Test) { const stack = new cdk.Stack(); - const repo = new codecommit.Repository(stack, 'MyRepo', { repositoryName: 'hello-cdk' }); + const repo = new codecommit.Repository(stack, 'MyRepo', { + repositoryName: cdk.PhysicalName.of('hello-cdk'), + }); const source = codebuild.Source.codeCommit({ repository: repo, cloneDepth: 2 }); @@ -628,7 +630,7 @@ export = { const bucket = new s3.Bucket(stack, 'MyBucket'); const vpc = new ec2.Vpc(stack, 'MyVPC'); const securityGroup = new ec2.SecurityGroup(stack, 'SecurityGroup1', { - groupName: 'Bob', + groupName: cdk.PhysicalName.of('Bob'), vpc, allowAllOutbound: true, description: 'Example', @@ -675,7 +677,7 @@ export = { const bucket = new s3.Bucket(stack, 'MyBucket'); const vpc = new ec2.Vpc(stack, 'MyVPC'); const securityGroup = new ec2.SecurityGroup(stack, 'SecurityGroup1', { - groupName: 'Bob', + groupName: cdk.PhysicalName.of('Bob'), vpc, allowAllOutbound: true, description: 'Example', @@ -697,7 +699,7 @@ export = { const bucket = new s3.Bucket(stack, 'MyBucket'); const vpc = new ec2.Vpc(stack, 'MyVPC'); const securityGroup = new ec2.SecurityGroup(stack, 'SecurityGroup1', { - groupName: 'Bob', + groupName: cdk.PhysicalName.of('Bob'), vpc, allowAllOutbound: true, description: 'Example', @@ -1207,7 +1209,9 @@ export = { shouldPassValidation: boolean } - const repo = new codecommit.Repository(stack, 'MyRepo', { repositoryName: 'hello-cdk' }); + const repo = new codecommit.Repository(stack, 'MyRepo', { + repositoryName: cdk.PhysicalName.of('hello-cdk'), + }); const bucket = new s3.Bucket(stack, 'MyBucket'); const cases: BadgeValidationTestCase[] = [ diff --git a/packages/@aws-cdk/aws-codecommit/lib/repository.ts b/packages/@aws-cdk/aws-codecommit/lib/repository.ts index cbd32c91bb936..105071fb90c22 100644 --- a/packages/@aws-cdk/aws-codecommit/lib/repository.ts +++ b/packages/@aws-cdk/aws-codecommit/lib/repository.ts @@ -1,5 +1,5 @@ import events = require('@aws-cdk/aws-events'); -import { Construct, IConstruct, IResource, Resource, Stack } from '@aws-cdk/cdk'; +import { Construct, IConstruct, IResource, PhysicalName, Resource, ResourceIdentifiers, Stack } from '@aws-cdk/cdk'; import { CfnRepository } from './codecommit.generated'; export interface IRepository extends IResource { @@ -212,7 +212,7 @@ export interface RepositoryProps { /** * Name of the repository. This property is required for all repositories. */ - readonly repositoryName: string; + readonly repositoryName: PhysicalName; /** * A description of the repository. Use the description to identify the @@ -270,21 +270,33 @@ export class Repository extends RepositoryBase { }); } + public readonly repositoryArn: string; + public readonly repositoryName: string; private readonly repository: CfnRepository; private readonly triggers = new Array(); constructor(scope: Construct, id: string, props: RepositoryProps) { - super(scope, id); + super(scope, id, { + physicalName: props.repositoryName, + }); this.repository = new CfnRepository(this, 'Resource', { - repositoryName: props.repositoryName, + repositoryName: this.physicalName.value || '', repositoryDescription: props.description, triggers: this.triggers }); - } - public get repositoryArn() { - return this.repository.attrArn; + const resourceIdentifiers = new ResourceIdentifiers(this, { + arn: this.repository.attrArn, + name: this.repository.attrName, + arnComponents: { + service: 'codecommit', + resource: this.physicalName.value || '', + }, + }); + + this.repositoryArn = resourceIdentifiers.arn; + this.repositoryName = resourceIdentifiers.name; } public get repositoryCloneUrlHttp() { @@ -295,10 +307,6 @@ export class Repository extends RepositoryBase { return this.repository.attrCloneUrlSsh; } - public get repositoryName() { - return this.repository.attrName; - } - /** * Create a trigger to notify another service to run actions on repository events. * @param arn Arn of the resource that repository events will notify diff --git a/packages/@aws-cdk/aws-codecommit/test/integ.codecommit-events.ts b/packages/@aws-cdk/aws-codecommit/test/integ.codecommit-events.ts index 9e576c0f6a36a..19695fc7052d5 100644 --- a/packages/@aws-cdk/aws-codecommit/test/integ.codecommit-events.ts +++ b/packages/@aws-cdk/aws-codecommit/test/integ.codecommit-events.ts @@ -5,7 +5,9 @@ import codecommit = require('../lib'); const app = new cdk.App(); const stack = new cdk.Stack(app, 'aws-cdk-codecommit-events'); -const repo = new codecommit.Repository(stack, 'Repo', { repositoryName: 'aws-cdk-codecommit-events' }); +const repo = new codecommit.Repository(stack, 'Repo', { + repositoryName: cdk.PhysicalName.of('aws-cdk-codecommit-events'), +}); const topic = new sns.Topic(stack, 'MyTopic'); // we can't use @aws-cdk/aws-events-targets.SnsTopic here because it will diff --git a/packages/@aws-cdk/aws-codecommit/test/test.codecommit.ts b/packages/@aws-cdk/aws-codecommit/test/test.codecommit.ts index abd3fa373c48d..a621e0359dd62 100644 --- a/packages/@aws-cdk/aws-codecommit/test/test.codecommit.ts +++ b/packages/@aws-cdk/aws-codecommit/test/test.codecommit.ts @@ -1,5 +1,5 @@ import { expect } from '@aws-cdk/assert'; -import { Stack } from '@aws-cdk/cdk'; +import { PhysicalName, Stack } from '@aws-cdk/cdk'; import { Test } from 'nodeunit'; import { Repository, RepositoryProps } from '../lib'; @@ -9,7 +9,7 @@ export = { const stack = new Stack(); const props: RepositoryProps = { - repositoryName: 'MyRepository' + repositoryName: PhysicalName.of('MyRepository'), }; const snsArn = 'arn:aws:sns:*:123456789012:my_topic'; @@ -42,8 +42,9 @@ export = { 'fails when triggers have duplicate names'(test: Test) { const stack = new Stack(); - const props = { repositoryName: 'MyRepository' }; - const myRepository = new Repository(stack, 'MyRepository', props).notify('myTrigger'); + const myRepository = new Repository(stack, 'MyRepository', { + repositoryName: PhysicalName.of('MyRepository'), + }).notify('myTrigger'); test.throws(() => myRepository.notify('myTrigger')); diff --git a/packages/@aws-cdk/aws-codedeploy/lib/lambda/application.ts b/packages/@aws-cdk/aws-codedeploy/lib/lambda/application.ts index a0a795858f209..5c24d1ea562d0 100644 --- a/packages/@aws-cdk/aws-codedeploy/lib/lambda/application.ts +++ b/packages/@aws-cdk/aws-codedeploy/lib/lambda/application.ts @@ -1,4 +1,4 @@ -import { Construct, IResource, Resource } from '@aws-cdk/cdk'; +import { Construct, IResource, PhysicalName, Resource, ResourceIdentifiers } from '@aws-cdk/cdk'; import { CfnApplication } from "../codedeploy.generated"; import { arnForApplication } from "../utils"; @@ -29,7 +29,7 @@ export interface LambdaApplicationProps { * * @default an auto-generated name will be used */ - readonly applicationName?: string; + readonly applicationName?: PhysicalName; } /** @@ -60,14 +60,26 @@ export class LambdaApplication extends Resource implements ILambdaApplication { public readonly applicationName: string; constructor(scope: Construct, id: string, props: LambdaApplicationProps = {}) { - super(scope, id); + super(scope, id, { + physicalName: props.applicationName, + }); const resource = new CfnApplication(this, 'Resource', { - applicationName: props.applicationName, + applicationName: this.physicalName.value, computePlatform: 'Lambda' }); - this.applicationName = resource.refAsString; - this.applicationArn = arnForApplication(this.applicationName); + const resourceIdentifiers = new ResourceIdentifiers(this, { + arn: arnForApplication(resource.refAsString), + name: resource.refAsString, + arnComponents: { + service: 'codedeploy', + resource: 'application', + resourceName: this.physicalName.value, + sep: ':', + }, + }); + this.applicationName = resourceIdentifiers.name; + this.applicationArn = resourceIdentifiers.arn; } } diff --git a/packages/@aws-cdk/aws-codedeploy/lib/lambda/deployment-group.ts b/packages/@aws-cdk/aws-codedeploy/lib/lambda/deployment-group.ts index fc3cfad6f4179..e4d1350283625 100644 --- a/packages/@aws-cdk/aws-codedeploy/lib/lambda/deployment-group.ts +++ b/packages/@aws-cdk/aws-codedeploy/lib/lambda/deployment-group.ts @@ -47,7 +47,7 @@ export interface LambdaDeploymentGroupProps { * * @default - An auto-generated name will be used. */ - readonly deploymentGroupName?: string; + readonly deploymentGroupName?: cdk.PhysicalName; /** * The Deployment Configuration this Deployment Group uses. @@ -142,7 +142,9 @@ export class LambdaDeploymentGroup extends cdk.Resource implements ILambdaDeploy private postHook?: lambda.IFunction; constructor(scope: cdk.Construct, id: string, props: LambdaDeploymentGroupProps) { - super(scope, id); + super(scope, id, { + physicalName: props.deploymentGroupName, + }); this.application = props.application || new LambdaApplication(this, 'Application'); this.alarms = props.alarms || []; @@ -156,7 +158,7 @@ export class LambdaDeploymentGroup extends cdk.Resource implements ILambdaDeploy const resource = new CfnDeploymentGroup(this, 'Resource', { applicationName: this.application.applicationName, serviceRoleArn: this.role.roleArn, - deploymentGroupName: props.deploymentGroupName, + deploymentGroupName: this.physicalName.value, deploymentConfigName: (props.deploymentConfig || LambdaDeploymentConfig.AllAtOnce).deploymentConfigName, deploymentStyle: { deploymentType: 'BLUE_GREEN', @@ -166,8 +168,18 @@ export class LambdaDeploymentGroup extends cdk.Resource implements ILambdaDeploy autoRollbackConfiguration: cdk.Lazy.anyValue({ produce: () => renderAutoRollbackConfiguration(this.alarms, props.autoRollback) }), }); - this.deploymentGroupName = resource.refAsString; - this.deploymentGroupArn = arnForDeploymentGroup(this.application.applicationName, this.deploymentGroupName); + const resourceIdentifiers = new cdk.ResourceIdentifiers(this, { + arn: arnForDeploymentGroup(this.application.applicationName, resource.refAsString), + name: resource.refAsString, + arnComponents: { + service: 'codedeploy', + resource: 'deploymentgroup', + resourceName: `${this.application.physicalName.value}/${this.physicalName.value}`, + sep: ':', + }, + }); + this.deploymentGroupName = resourceIdentifiers.name; + this.deploymentGroupArn = resourceIdentifiers.arn; if (props.preHook) { this.addPreHook(props.preHook); diff --git a/packages/@aws-cdk/aws-codedeploy/lib/server/application.ts b/packages/@aws-cdk/aws-codedeploy/lib/server/application.ts index 14c4beb231898..e6ba379b3fe8a 100644 --- a/packages/@aws-cdk/aws-codedeploy/lib/server/application.ts +++ b/packages/@aws-cdk/aws-codedeploy/lib/server/application.ts @@ -1,4 +1,4 @@ -import { Construct, IResource, Resource } from '@aws-cdk/cdk'; +import { Construct, IResource, PhysicalName, Resource, ResourceIdentifiers } from '@aws-cdk/cdk'; import { CfnApplication } from '../codedeploy.generated'; import { arnForApplication } from '../utils'; @@ -29,7 +29,7 @@ export interface ServerApplicationProps { * * @default an auto-generated name will be used */ - readonly applicationName?: string; + readonly applicationName?: PhysicalName; } /** @@ -61,14 +61,26 @@ export class ServerApplication extends Resource implements IServerApplication { public readonly applicationName: string; constructor(scope: Construct, id: string, props: ServerApplicationProps = {}) { - super(scope, id); + super(scope, id, { + physicalName: props.applicationName, + }); const resource = new CfnApplication(this, 'Resource', { - applicationName: props.applicationName, + applicationName: this.physicalName.value, computePlatform: 'Server', }); - this.applicationName = resource.refAsString; - this.applicationArn = arnForApplication(this.applicationName); + const resourceIdentifiers = new ResourceIdentifiers(this, { + arn: arnForApplication(resource.refAsString), + name: resource.refAsString, + arnComponents: { + service: 'codedeploy', + resource: 'application', + resourceName: this.physicalName.value, + sep: ':', + }, + }); + this.applicationName = resourceIdentifiers.name; + this.applicationArn = resourceIdentifiers.arn; } } diff --git a/packages/@aws-cdk/aws-codedeploy/lib/server/deployment-config.ts b/packages/@aws-cdk/aws-codedeploy/lib/server/deployment-config.ts index 96fd860d7a644..9bbab3a996f81 100644 --- a/packages/@aws-cdk/aws-codedeploy/lib/server/deployment-config.ts +++ b/packages/@aws-cdk/aws-codedeploy/lib/server/deployment-config.ts @@ -65,7 +65,7 @@ export interface ServerDeploymentConfigProps { * * @default a name will be auto-generated */ - readonly deploymentConfigName?: string; + readonly deploymentConfigName?: cdk.PhysicalName; /** * Minimum number of healthy hosts. @@ -106,10 +106,12 @@ export class ServerDeploymentConfig extends cdk.Resource implements IServerDeplo public readonly deploymentConfigArn: string; constructor(scope: cdk.Construct, id: string, props: ServerDeploymentConfigProps) { - super(scope, id); + super(scope, id, { + physicalName: props.deploymentConfigName, + }); const resource = new CfnDeploymentConfig(this, 'Resource', { - deploymentConfigName: props.deploymentConfigName, + deploymentConfigName: this.physicalName.value, minimumHealthyHosts: props.minimumHealthyHosts._json, }); diff --git a/packages/@aws-cdk/aws-codedeploy/lib/server/deployment-group.ts b/packages/@aws-cdk/aws-codedeploy/lib/server/deployment-group.ts index 77d5831cef0b9..9d64e256d8aa6 100644 --- a/packages/@aws-cdk/aws-codedeploy/lib/server/deployment-group.ts +++ b/packages/@aws-cdk/aws-codedeploy/lib/server/deployment-group.ts @@ -4,7 +4,7 @@ import ec2 = require('@aws-cdk/aws-ec2'); import iam = require('@aws-cdk/aws-iam'); import s3 = require('@aws-cdk/aws-s3'); import cdk = require('@aws-cdk/cdk'); -import { Stack } from '@aws-cdk/cdk'; +import { PhysicalName, Stack } from '@aws-cdk/cdk'; import { CfnDeploymentGroup } from '../codedeploy.generated'; import { AutoRollbackConfig } from '../rollback-config'; import { arnForDeploymentGroup, renderAlarmConfiguration, renderAutoRollbackConfiguration } from '../utils'; @@ -73,8 +73,8 @@ abstract class ServerDeploymentGroupBase extends cdk.Resource implements IServer public readonly deploymentConfig: IServerDeploymentConfig; public abstract readonly autoScalingGroups?: autoscaling.AutoScalingGroup[]; - constructor(scope: cdk.Construct, id: string, deploymentConfig?: IServerDeploymentConfig) { - super(scope, id); + constructor(scope: cdk.Construct, id: string, deploymentConfig?: IServerDeploymentConfig, props?: cdk.ResourceProps) { + super(scope, id, props); this.deploymentConfig = deploymentConfig || ServerDeploymentConfig.OneAtATime; } } @@ -153,7 +153,7 @@ export interface ServerDeploymentGroupProps { * * @default - An auto-generated name will be used. */ - readonly deploymentGroupName?: string; + readonly deploymentGroupName?: PhysicalName; /** * The EC2/on-premise Deployment Configuration to use for this Deployment Group. @@ -266,7 +266,9 @@ export class ServerDeploymentGroup extends ServerDeploymentGroupBase { private readonly alarms: cloudwatch.IAlarm[]; constructor(scope: cdk.Construct, id: string, props: ServerDeploymentGroupProps = {}) { - super(scope, id, props.deploymentConfig); + super(scope, id, props.deploymentConfig, { + physicalName: props.deploymentGroupName, + }); this.application = props.application || new ServerApplication(this, 'Application'); @@ -286,7 +288,7 @@ export class ServerDeploymentGroup extends ServerDeploymentGroupBase { const resource = new CfnDeploymentGroup(this, 'Resource', { applicationName: this.application.applicationName, - deploymentGroupName: props.deploymentGroupName, + deploymentGroupName: this.physicalName.value, serviceRoleArn: this.role.roleArn, deploymentConfigName: props.deploymentConfig && props.deploymentConfig.deploymentConfigName, @@ -303,8 +305,18 @@ export class ServerDeploymentGroup extends ServerDeploymentGroupBase { autoRollbackConfiguration: cdk.Lazy.anyValue({ produce: () => renderAutoRollbackConfiguration(this.alarms, props.autoRollback) }), }); - this.deploymentGroupName = resource.refAsString; - this.deploymentGroupArn = arnForDeploymentGroup(this.application.applicationName, this.deploymentGroupName); + const resourceIdentifiers = new cdk.ResourceIdentifiers(this, { + arn: arnForDeploymentGroup(this.application.applicationName, resource.refAsString), + name: resource.refAsString, + arnComponents: { + service: 'codedeploy', + resource: 'deploymentgroup', + resourceName: `${this.application.physicalName.value}/${this.physicalName.value}`, + sep: ':', + }, + }); + this.deploymentGroupName = resourceIdentifiers.name; + this.deploymentGroupArn = resourceIdentifiers.arn; } /** diff --git a/packages/@aws-cdk/aws-codedeploy/package.json b/packages/@aws-cdk/aws-codedeploy/package.json index 9db042158339e..7eddb78888b8f 100644 --- a/packages/@aws-cdk/aws-codedeploy/package.json +++ b/packages/@aws-cdk/aws-codedeploy/package.json @@ -104,4 +104,4 @@ ] }, "stability": "experimental" -} \ No newline at end of file +} diff --git a/packages/@aws-cdk/aws-codedeploy/test/lambda/integ.deployment-group.ts b/packages/@aws-cdk/aws-codedeploy/test/lambda/integ.deployment-group.ts index e5ba28c096c85..8fef7529b6018 100644 --- a/packages/@aws-cdk/aws-codedeploy/test/lambda/integ.deployment-group.ts +++ b/packages/@aws-cdk/aws-codedeploy/test/lambda/integ.deployment-group.ts @@ -15,7 +15,7 @@ const handler = new lambda.Function(stack, `Handler`, { }); const version = handler.addVersion('1'); const blueGreenAlias = new lambda.Alias(stack, `Alias`, { - aliasName: `alias`, + aliasName: cdk.PhysicalName.of('alias'), version }); diff --git a/packages/@aws-cdk/aws-codedeploy/test/lambda/test.application.ts b/packages/@aws-cdk/aws-codedeploy/test/lambda/test.application.ts index d81aa9b3e76e2..573b389b81627 100644 --- a/packages/@aws-cdk/aws-codedeploy/test/lambda/test.application.ts +++ b/packages/@aws-cdk/aws-codedeploy/test/lambda/test.application.ts @@ -16,7 +16,7 @@ export = { "can be created with explicit name"(test: Test) { const stack = new cdk.Stack(); new codedeploy.LambdaApplication(stack, 'MyApp', { - applicationName: 'my-name' + applicationName: cdk.PhysicalName.of('my-name'), }); expect(stack).to(haveResource('AWS::CodeDeploy::Application', { ApplicationName: 'my-name', diff --git a/packages/@aws-cdk/aws-codedeploy/test/lambda/test.deployment-group.ts b/packages/@aws-cdk/aws-codedeploy/test/lambda/test.deployment-group.ts index 9911dfd4193ca..61932e0e50027 100644 --- a/packages/@aws-cdk/aws-codedeploy/test/lambda/test.deployment-group.ts +++ b/packages/@aws-cdk/aws-codedeploy/test/lambda/test.deployment-group.ts @@ -16,7 +16,7 @@ function mockFunction(stack: cdk.Stack, id: string) { } function mockAlias(stack: cdk.Stack) { return new lambda.Alias(stack, 'Alias', { - aliasName: 'my-alias', + aliasName: cdk.PhysicalName.of('my-alias'), version: new lambda.Version(stack, 'Version', { lambda: mockFunction(stack, 'Function') }) @@ -119,7 +119,7 @@ export = { application, alias, deploymentConfig: LambdaDeploymentConfig.AllAtOnce, - deploymentGroupName: 'test' + deploymentGroupName: cdk.PhysicalName.of('test'), }); expect(stack).to(haveResourceLike('AWS::CodeDeploy::DeploymentGroup', { diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/cloudformation/test.cloudformation-pipeline-actions.ts b/packages/@aws-cdk/aws-codepipeline-actions/test/cloudformation/test.cloudformation-pipeline-actions.ts index e9a19f3a8216b..376e42bdec238 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/test/cloudformation/test.cloudformation-pipeline-actions.ts +++ b/packages/@aws-cdk/aws-codepipeline-actions/test/cloudformation/test.cloudformation-pipeline-actions.ts @@ -21,7 +21,9 @@ export = { }); /** Source! */ - const repo = new Repository(stack, 'MyVeryImportantRepo', { repositoryName: 'my-very-important-repo' }); + const repo = new Repository(stack, 'MyVeryImportantRepo', { + repositoryName: cdk.PhysicalName.of('my-very-important-repo'), + }); const sourceOutput = new codepipeline.Artifact('SourceArtifact'); const source = new cpactions.CodeCommitSourceAction({ @@ -430,7 +432,9 @@ class TestFixture extends cdk.Stack { this.pipeline = new codepipeline.Pipeline(this, 'Pipeline'); this.sourceStage = this.pipeline.addStage({ stageName: 'Source' }); this.deployStage = this.pipeline.addStage({ stageName: 'Deploy' }); - this.repo = new Repository(this, 'MyVeryImportantRepo', { repositoryName: 'my-very-important-repo' }); + this.repo = new Repository(this, 'MyVeryImportantRepo', { + repositoryName: cdk.PhysicalName.of('my-very-important-repo'), + }); this.sourceOutput = new codepipeline.Artifact('SourceArtifact'); const source = new cpactions.CodeCommitSourceAction({ actionName: 'Source', diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/integ.cfn-template-from-repo.lit.ts b/packages/@aws-cdk/aws-codepipeline-actions/test/integ.cfn-template-from-repo.lit.ts index d0062f82a0eba..38ec644d1d204 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/test/integ.cfn-template-from-repo.lit.ts +++ b/packages/@aws-cdk/aws-codepipeline-actions/test/integ.cfn-template-from-repo.lit.ts @@ -9,7 +9,7 @@ const stack = new cdk.Stack(app, 'aws-cdk-codepipeline-cloudformation'); /// !show // Source stage: read from repository const repo = new codecommit.Repository(stack, 'TemplateRepo', { - repositoryName: 'template-repo' + repositoryName: cdk.PhysicalName.of('template-repo'), }); const sourceOutput = new codepipeline.Artifact('SourceArtifact'); const source = new cpactions.CodeCommitSourceAction({ diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/integ.lambda-deployed-through-codepipeline.lit.ts b/packages/@aws-cdk/aws-codepipeline-actions/test/integ.lambda-deployed-through-codepipeline.lit.ts index a4add3b48f41f..865965b35f556 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/test/integ.lambda-deployed-through-codepipeline.lit.ts +++ b/packages/@aws-cdk/aws-codepipeline-actions/test/integ.lambda-deployed-through-codepipeline.lit.ts @@ -25,13 +25,17 @@ const pipeline = new codepipeline.Pipeline(pipelineStack, 'Pipeline'); // and the source code of the Lambda Function, if they're separate const cdkSourceOutput = new codepipeline.Artifact(); const cdkSourceAction = new codepipeline_actions.CodeCommitSourceAction({ - repository: new codecommit.Repository(pipelineStack, 'CdkCodeRepo', { repositoryName: 'CdkCodeRepo' }), + repository: new codecommit.Repository(pipelineStack, 'CdkCodeRepo', { + repositoryName: cdk.PhysicalName.of('CdkCodeRepo'), + }), actionName: 'CdkCode_Source', output: cdkSourceOutput, }); const lambdaSourceOutput = new codepipeline.Artifact(); const lambdaSourceAction = new codepipeline_actions.CodeCommitSourceAction({ - repository: new codecommit.Repository(pipelineStack, 'LambdaCodeRepo', { repositoryName: 'LambdaCodeRepo' }), + repository: new codecommit.Repository(pipelineStack, 'LambdaCodeRepo', { + repositoryName: cdk.PhysicalName.of('LambdaCodeRepo'), + }), actionName: 'LambdaCode_Source', output: lambdaSourceOutput, }); diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-code-build-multiple-inputs-outputs.ts b/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-code-build-multiple-inputs-outputs.ts index c14abced15c11..95abfe3dad53e 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-code-build-multiple-inputs-outputs.ts +++ b/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-code-build-multiple-inputs-outputs.ts @@ -10,7 +10,7 @@ const app = new cdk.App(); const stack = new cdk.Stack(app, 'aws-cdk-codepipeline-codebuild-multiple-inputs-outputs'); const repository = new codecommit.Repository(stack, 'MyRepo', { - repositoryName: 'MyIntegTestTempRepo', + repositoryName: cdk.PhysicalName.of('MyIntegTestTempRepo'), }); const bucket = new s3.Bucket(stack, 'MyBucket', { versioned: true, diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-code-commit-build.ts b/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-code-commit-build.ts index 42e64c80c0688..08507ddebd8d8 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-code-commit-build.ts +++ b/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-code-commit-build.ts @@ -9,7 +9,7 @@ const app = new cdk.App(); const stack = new cdk.Stack(app, 'aws-cdk-codepipeline-codecommit-codebuild'); const repository = new codecommit.Repository(stack, 'MyRepo', { - repositoryName: 'my-repo', + repositoryName: cdk.PhysicalName.of('my-repo'), }); const sourceOutput = new codepipeline.Artifact('SourceArtifact'); const sourceAction = new cpactions.CodeCommitSourceAction({ diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-code-commit.ts b/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-code-commit.ts index 54063d0f83c82..1a76865f59db5 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-code-commit.ts +++ b/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-code-commit.ts @@ -7,7 +7,9 @@ const app = new cdk.App(); const stack = new cdk.Stack(app, 'aws-cdk-codepipeline-codecommit'); -const repo = new codecommit.Repository(stack, 'MyRepo', { repositoryName: 'my-repo' }); +const repo = new codecommit.Repository(stack, 'MyRepo', { + repositoryName: cdk.PhysicalName.of('my-repo'), +}); new codepipeline.Pipeline(stack, 'Pipeline', { stages: [ diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-code-deploy.ts b/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-code-deploy.ts index 61d9c479374c6..162bf49f700a5 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-code-deploy.ts +++ b/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-code-deploy.ts @@ -9,7 +9,7 @@ const app = new cdk.App(); const stack = new cdk.Stack(app, 'aws-cdk-codepipeline-codedeploy'); const application = new codedeploy.ServerApplication(stack, 'CodeDeployApplication', { - applicationName: 'IntegTestDeployApp', + applicationName: cdk.PhysicalName.of('IntegTestDeployApp'), }); const deploymentConfig = new codedeploy.ServerDeploymentConfig(stack, 'CustomDeployConfig', { @@ -18,7 +18,7 @@ const deploymentConfig = new codedeploy.ServerDeploymentConfig(stack, 'CustomDep const deploymentGroup = new codedeploy.ServerDeploymentGroup(stack, 'CodeDeployGroup', { application, - deploymentGroupName: 'IntegTestDeploymentGroup', + deploymentGroupName: cdk.PhysicalName.of('IntegTestDeploymentGroup'), deploymentConfig, }); diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-events.ts b/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-events.ts index 6f5838b0aaac2..d34b69a6f18f8 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-events.ts +++ b/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-events.ts @@ -16,7 +16,7 @@ const stack = new cdk.Stack(app, 'aws-cdk-pipeline-event-target'); const pipeline = new codepipeline.Pipeline(stack, 'MyPipeline'); const repository = new codecommit.Repository(stack, 'CodeCommitRepo', { - repositoryName: 'foo' + repositoryName: cdk.PhysicalName.of('foo'), }); const project = new codebuild.PipelineProject(stack, 'BuildProject'); diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/test.pipeline.ts b/packages/@aws-cdk/aws-codepipeline-actions/test/test.pipeline.ts index b4534a812c748..3e206e9261754 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/test/test.pipeline.ts +++ b/packages/@aws-cdk/aws-codepipeline-actions/test/test.pipeline.ts @@ -18,7 +18,7 @@ export = { const stack = new Stack(); const repository = new codecommit.Repository(stack, 'MyRepo', { - repositoryName: 'my-repo', + repositoryName: PhysicalName.of('my-repo'), }); const pipeline = new codepipeline.Pipeline(stack, 'Pipeline'); @@ -54,7 +54,7 @@ export = { const stack = new Stack(undefined, 'StackName'); new codepipeline.Pipeline(stack, 'Pipeline', { - pipelineName: Aws.stackName, + pipelineName: PhysicalName.of(Aws.stackName), }); expect(stack, true).to(haveResourceLike('AWS::CodePipeline::Pipeline', { @@ -870,6 +870,6 @@ function stageForTesting(stack: Stack): codepipeline.IStage { function repositoryForTesting(stack: Stack): codecommit.Repository { return new codecommit.Repository(stack, 'Repository', { - repositoryName: 'Repository' + repositoryName: PhysicalName.of('Repository'), }); } diff --git a/packages/@aws-cdk/aws-codepipeline/lib/pipeline.ts b/packages/@aws-cdk/aws-codepipeline/lib/pipeline.ts index 0a469927fb308..981df5c203b0e 100644 --- a/packages/@aws-cdk/aws-codepipeline/lib/pipeline.ts +++ b/packages/@aws-cdk/aws-codepipeline/lib/pipeline.ts @@ -2,7 +2,7 @@ import events = require('@aws-cdk/aws-events'); import iam = require('@aws-cdk/aws-iam'); import kms = require('@aws-cdk/aws-kms'); import s3 = require('@aws-cdk/aws-s3'); -import { App, Construct, Lazy, PhysicalName, RemovalPolicy, Resource, Stack, Token } from '@aws-cdk/cdk'; +import { App, Construct, Lazy, PhysicalName, RemovalPolicy, Resource, ResourceIdentifiers, Stack, Token } from '@aws-cdk/cdk'; import { Action, IPipeline, IStage } from "./action"; import { CfnPipeline } from './codepipeline.generated'; import { Stage } from './stage'; @@ -78,7 +78,7 @@ export interface PipelineProps { * * @default - AWS CloudFormation generates an ID and uses that for the pipeline name. */ - readonly pipelineName?: string; + readonly pipelineName?: PhysicalName; /** * A map of region to S3 bucket name used for cross-region CodePipeline. @@ -219,9 +219,11 @@ export class Pipeline extends PipelineBase { private readonly _crossRegionScaffoldStacks: { [region: string]: CrossRegionScaffoldStack } = {}; constructor(scope: Construct, id: string, props: PipelineProps = {}) { - super(scope, id); + super(scope, id, { + physicalName: props.pipelineName, + }); - validateName('Pipeline', props.pipelineName); + validateName('Pipeline', this.physicalName.value); // If a bucket has been provided, use it - otherwise, create a bucket. let propsBucket = props.artifactBucket; @@ -247,7 +249,7 @@ export class Pipeline extends PipelineBase { stages: Lazy.anyValue({ produce: () => this.renderStages() }), roleArn: this.role.roleArn, restartExecutionOnUpdate: props && props.restartExecutionOnUpdate, - name: props && props.pipelineName, + name: this.physicalName.value, }); // this will produce a DependsOn for both the role and the policy resources. @@ -255,7 +257,15 @@ export class Pipeline extends PipelineBase { this.artifactBucket.grantReadWrite(this.role); - this.pipelineName = codePipeline.refAsString; + const resourceIdentifiers = new ResourceIdentifiers(this, { + arn: '', + name: codePipeline.refAsString, + arnComponents: { + service: 'codepipeline', + resource: this.physicalName.value || '', + }, + }); + this.pipelineName = resourceIdentifiers.name; this.pipelineVersion = codePipeline.attrVersion; this.crossRegionReplicationBuckets = props.crossRegionReplicationBuckets || {}; this.artifactStores = {}; diff --git a/packages/@aws-cdk/aws-cognito/lib/user-pool-client.ts b/packages/@aws-cdk/aws-cognito/lib/user-pool-client.ts index 4d60cd828b4c5..a7e21cabe4aab 100644 --- a/packages/@aws-cdk/aws-cognito/lib/user-pool-client.ts +++ b/packages/@aws-cdk/aws-cognito/lib/user-pool-client.ts @@ -1,4 +1,4 @@ -import { Construct, Resource } from '@aws-cdk/cdk'; +import { Construct, PhysicalName, Resource } from '@aws-cdk/cdk'; import { CfnUserPoolClient } from './cognito.generated'; import { IUserPool } from './user-pool'; @@ -27,7 +27,7 @@ export interface UserPoolClientProps { * Name of the application client * @default cloudformation generated name */ - readonly clientName?: string; + readonly clientName?: PhysicalName; /** * The UserPool resource this client will have access to @@ -67,10 +67,12 @@ export class UserPoolClient extends Resource { public readonly userPoolClientClientSecret: string; constructor(scope: Construct, id: string, props: UserPoolClientProps) { - super(scope, id); + super(scope, id, { + physicalName: props.clientName, + }); const resource = new CfnUserPoolClient(this, 'Resource', { - clientName: props.clientName, + clientName: this.physicalName.value, generateSecret: props.generateSecret, userPoolId: props.userPool.userPoolId, explicitAuthFlows: props.enabledAuthFlows diff --git a/packages/@aws-cdk/aws-cognito/lib/user-pool.ts b/packages/@aws-cdk/aws-cognito/lib/user-pool.ts index a02db19683011..719553f9bcfa1 100644 --- a/packages/@aws-cdk/aws-cognito/lib/user-pool.ts +++ b/packages/@aws-cdk/aws-cognito/lib/user-pool.ts @@ -1,6 +1,6 @@ import iam = require('@aws-cdk/aws-iam'); import lambda = require('@aws-cdk/aws-lambda'); -import { Construct, IResource, Lazy, Resource } from '@aws-cdk/cdk'; +import { Construct, IResource, Lazy, PhysicalName, Resource, ResourceIdentifiers } from '@aws-cdk/cdk'; import { CfnUserPool } from './cognito.generated'; /** @@ -204,9 +204,9 @@ export interface UserPoolProps { /** * Name of the user pool * - * @default - Unique ID. + * @default - automatically generated name by CloudFormation at deploy time */ - readonly poolName?: string; + readonly poolName?: PhysicalName; /** * Method used for user registration & sign in. @@ -335,7 +335,9 @@ export class UserPool extends Resource implements IUserPool { private triggers: CfnUserPool.LambdaConfigProperty = { }; constructor(scope: Construct, id: string, props: UserPoolProps = {}) { - super(scope, id); + super(scope, id, { + physicalName: props.poolName, + }); let aliasAttributes: UserPoolAttribute[] | undefined; let usernameAttributes: UserPoolAttribute[] | undefined; @@ -389,14 +391,25 @@ export class UserPool extends Resource implements IUserPool { } const userPool = new CfnUserPool(this, 'Resource', { - userPoolName: props.poolName || this.node.uniqueId, + userPoolName: this.physicalName.value, usernameAttributes, aliasAttributes, autoVerifiedAttributes: props.autoVerifiedAttributes, lambdaConfig: Lazy.anyValue({ produce: () => this.triggers }) }); - this.userPoolId = userPool.refAsString; - this.userPoolArn = userPool.attrArn; + + const resourceIdentifiers = new ResourceIdentifiers(this, { + arn: userPool.attrArn, + name: userPool.refAsString, + arnComponents: { + service: 'cognito', + resource: 'userpool', + resourceName: this.physicalName.value, + }, + }); + this.userPoolId = resourceIdentifiers.name; + this.userPoolArn = resourceIdentifiers.arn; + this.userPoolProviderName = userPool.attrProviderName; this.userPoolProviderUrl = userPool.attrProviderUrl; } diff --git a/packages/@aws-cdk/aws-cognito/package.json b/packages/@aws-cdk/aws-cognito/package.json index 6478bf48ba0b0..baab13d1eb229 100644 --- a/packages/@aws-cdk/aws-cognito/package.json +++ b/packages/@aws-cdk/aws-cognito/package.json @@ -89,4 +89,4 @@ ] }, "stability": "experimental" -} \ No newline at end of file +} diff --git a/packages/@aws-cdk/aws-cognito/test/test.user-pool.ts b/packages/@aws-cdk/aws-cognito/test/test.user-pool.ts index d91c9543e133b..5a8697992f4c1 100644 --- a/packages/@aws-cdk/aws-cognito/test/test.user-pool.ts +++ b/packages/@aws-cdk/aws-cognito/test/test.user-pool.ts @@ -11,7 +11,7 @@ export = { // WHEN new cognito.UserPool(stack, 'Pool', { - poolName: 'myPool' + poolName: cdk.PhysicalName.of('myPool'), }); // THEN @@ -181,4 +181,4 @@ export = { test.throws(() => toThrow(), /'autoVerifiedAttributes' can only include EMAIL or PHONE_NUMBER/); test.done(); } -}; \ No newline at end of file +}; diff --git a/packages/@aws-cdk/aws-config/lib/rule.ts b/packages/@aws-cdk/aws-config/lib/rule.ts index e41ea24a889a6..1558b823043f3 100644 --- a/packages/@aws-cdk/aws-config/lib/rule.ts +++ b/packages/@aws-cdk/aws-config/lib/rule.ts @@ -1,7 +1,7 @@ import events = require('@aws-cdk/aws-events'); import iam = require('@aws-cdk/aws-iam'); import lambda = require('@aws-cdk/aws-lambda'); -import { Construct, IResource, Lazy, Resource } from '@aws-cdk/cdk'; +import { Construct, IResource, Lazy, PhysicalName, Resource } from '@aws-cdk/cdk'; import { CfnConfigRule } from './config.generated'; /** @@ -193,7 +193,7 @@ export interface RuleProps { * * @default a CloudFormation generated name */ - readonly name?: string; + readonly ruleName?: PhysicalName; /** * A description about this AWS Config rule. @@ -248,10 +248,12 @@ export class ManagedRule extends RuleNew { public readonly configRuleComplianceType: string; constructor(scope: Construct, id: string, props: ManagedRuleProps) { - super(scope, id); + super(scope, id, { + physicalName: props.ruleName, + }); const rule = new CfnConfigRule(this, 'Resource', { - configRuleName: props.name, + configRuleName: this.physicalName.value, description: props.description, inputParameters: props.inputParameters, maximumExecutionFrequency: props.maximumExecutionFrequency, @@ -313,7 +315,9 @@ export class CustomRule extends RuleNew { public readonly configRuleComplianceType: string; constructor(scope: Construct, id: string, props: CustomRuleProps) { - super(scope, id); + super(scope, id, { + physicalName: props.ruleName, + }); if (!props.configurationChanges && !props.periodic) { throw new Error('At least one of `configurationChanges` or `periodic` must be set to true.'); @@ -354,7 +358,7 @@ export class CustomRule extends RuleNew { this.node.addDependency(props.lambdaFunction); const rule = new CfnConfigRule(this, 'Resource', { - configRuleName: props.name, + configRuleName: this.physicalName.value, description: props.description, inputParameters: props.inputParameters, maximumExecutionFrequency: props.maximumExecutionFrequency, diff --git a/packages/@aws-cdk/aws-config/package.json b/packages/@aws-cdk/aws-config/package.json index 0bfe4e1306488..b17d8f5d23335 100644 --- a/packages/@aws-cdk/aws-config/package.json +++ b/packages/@aws-cdk/aws-config/package.json @@ -89,4 +89,4 @@ "node": ">= 8.10.0" }, "stability": "experimental" -} \ No newline at end of file +} diff --git a/packages/@aws-cdk/aws-config/test/test.rule.ts b/packages/@aws-cdk/aws-config/test/test.rule.ts index 6434e6b899f49..f311a40d6fcde 100644 --- a/packages/@aws-cdk/aws-config/test/test.rule.ts +++ b/packages/@aws-cdk/aws-config/test/test.rule.ts @@ -18,7 +18,7 @@ export = { key: 'value' }, maximumExecutionFrequency: config.MaximumExecutionFrequency.THREE_HOURS, - name: 'cool rule' + ruleName: cdk.PhysicalName.of('cool rule'), }); // THEN @@ -56,7 +56,7 @@ export = { }, lambdaFunction: fn, maximumExecutionFrequency: config.MaximumExecutionFrequency.SIX_HOURS, - name: 'cool rule', + ruleName: cdk.PhysicalName.of('cool rule'), periodic: true }); diff --git a/packages/@aws-cdk/aws-dynamodb-global/lib/aws-dynamodb-global.ts b/packages/@aws-cdk/aws-dynamodb-global/lib/aws-dynamodb-global.ts index 6a865ded03cc8..9c87eaf3d4272 100644 --- a/packages/@aws-cdk/aws-dynamodb-global/lib/aws-dynamodb-global.ts +++ b/packages/@aws-cdk/aws-dynamodb-global/lib/aws-dynamodb-global.ts @@ -11,7 +11,7 @@ export interface GlobalTableProps extends cdk.StackProps, dynamodb.TableOptions * Name of the DynamoDB table to use across all regional tables. * This is required for global tables. */ - readonly tableName: string; + readonly tableName: cdk.PhysicalName; /** * Array of environments to create DynamoDB tables in. diff --git a/packages/@aws-cdk/aws-dynamodb-global/lib/global-table-coordinator.ts b/packages/@aws-cdk/aws-dynamodb-global/lib/global-table-coordinator.ts index abcb1b2e0f1c4..532b49c60ee79 100644 --- a/packages/@aws-cdk/aws-dynamodb-global/lib/global-table-coordinator.ts +++ b/packages/@aws-cdk/aws-dynamodb-global/lib/global-table-coordinator.ts @@ -28,7 +28,7 @@ export class GlobalTableCoordinator extends cdk.Stack { properties: { regions: props.regions, resourceType: "Custom::DynamoGlobalTableCoordinator", - tableName: props.tableName, + tableName: props.tableName.value, }, }); } diff --git a/packages/@aws-cdk/aws-dynamodb-global/test/integ.dynamodb.global.ts b/packages/@aws-cdk/aws-dynamodb-global/test/integ.dynamodb.global.ts index 29dbbd0693823..7a90f338fb277 100644 --- a/packages/@aws-cdk/aws-dynamodb-global/test/integ.dynamodb.global.ts +++ b/packages/@aws-cdk/aws-dynamodb-global/test/integ.dynamodb.global.ts @@ -1,12 +1,12 @@ /// !cdk-integ * import { AttributeType } from '@aws-cdk/aws-dynamodb'; -import { App } from '@aws-cdk/cdk'; +import { App, PhysicalName } from '@aws-cdk/cdk'; import { GlobalTable } from '../lib'; const app = new App(); new GlobalTable(app, 'globdynamodbinteg', { partitionKey: { name: 'hashKey', type: AttributeType.String }, - tableName: 'integrationtest', + tableName: PhysicalName.of('integrationtest'), regions: [ "us-east-1", "us-east-2", "us-west-2" ] }); app.synth(); diff --git a/packages/@aws-cdk/aws-dynamodb-global/test/test.dynamodb.global.ts b/packages/@aws-cdk/aws-dynamodb-global/test/test.dynamodb.global.ts index 56ea1876f3442..08ece23a033eb 100644 --- a/packages/@aws-cdk/aws-dynamodb-global/test/test.dynamodb.global.ts +++ b/packages/@aws-cdk/aws-dynamodb-global/test/test.dynamodb.global.ts @@ -1,7 +1,7 @@ import { expect, haveResource } from '@aws-cdk/assert'; import { Attribute, AttributeType, StreamViewType } from '@aws-cdk/aws-dynamodb'; import { Table } from '@aws-cdk/aws-dynamodb'; -import { Stack } from '@aws-cdk/cdk'; +import { PhysicalName, Stack } from '@aws-cdk/cdk'; import * as assert from 'assert'; import { Test } from 'nodeunit'; import { @@ -20,7 +20,7 @@ const TABLE_PARTITION_KEY: Attribute = { name: 'hashKey', type: AttributeType.St const STACK_PROPS: GlobalTableProps = { partitionKey: TABLE_PARTITION_KEY, - tableName: TABLE_NAME, + tableName: PhysicalName.of(TABLE_NAME), regions: [ 'us-east-1', 'us-east-2', 'us-west-2' ] }; @@ -60,7 +60,7 @@ export = { expect(customResourceStack).to(haveResource('AWS::CloudFormation::CustomResource', { Regions: STACK_PROPS.regions, ResourceType: "Custom::DynamoGlobalTableCoordinator", - TableName: STACK_PROPS.tableName + TableName: TABLE_NAME, })); test.done(); }, @@ -70,7 +70,7 @@ export = { const stack = new Stack(); try { new GlobalTable(stack, CONSTRUCT_NAME, { - tableName: TABLE_NAME, + tableName: PhysicalName.of(TABLE_NAME), stream: StreamViewType.KeysOnly, partitionKey: TABLE_PARTITION_KEY, regions: [ 'us-east-1', 'us-east-2', 'us-west-2' ] @@ -89,7 +89,7 @@ export = { 'global dynamo should only allow NEW_AND_OLD_IMAGES'(test: Test) { const stack = new Stack(); const regTables = new GlobalTable(stack, CONSTRUCT_NAME, { - tableName: TABLE_NAME, + tableName: PhysicalName.of(TABLE_NAME), partitionKey: TABLE_PARTITION_KEY, regions: [ 'us-east-1', 'us-east-2', 'us-west-2' ] }); diff --git a/packages/@aws-cdk/aws-dynamodb/lib/table.ts b/packages/@aws-cdk/aws-dynamodb/lib/table.ts index 0b91f28c26cdc..18c6d586e0848 100644 --- a/packages/@aws-cdk/aws-dynamodb/lib/table.ts +++ b/packages/@aws-cdk/aws-dynamodb/lib/table.ts @@ -1,6 +1,6 @@ import appscaling = require('@aws-cdk/aws-applicationautoscaling'); import iam = require('@aws-cdk/aws-iam'); -import { Aws, Construct, Lazy, Resource, Stack } from '@aws-cdk/cdk'; +import { Aws, Construct, Lazy, PhysicalName, Resource, ResourceIdentifiers, Stack } from '@aws-cdk/cdk'; import { CfnTable } from './dynamodb.generated'; import { EnableScalingProps, IScalableTableAttribute } from './scalable-attribute-api'; import { ScalableTableAttribute } from './scalable-table-attribute'; @@ -114,7 +114,7 @@ export interface TableProps extends TableOptions { * Enforces a particular physical table name. * @default */ - readonly tableName?: string; + readonly tableName?: PhysicalName; } export interface SecondaryIndexProps { @@ -224,13 +224,15 @@ export class Table extends Resource { private readonly scalingRole: iam.IRole; constructor(scope: Construct, id: string, props: TableProps) { - super(scope, id); + super(scope, id, { + physicalName: props.tableName, + }); this.billingMode = props.billingMode || BillingMode.Provisioned; this.validateProvisioning(props); this.table = new CfnTable(this, 'Resource', { - tableName: props.tableName, + tableName: this.physicalName.value, keySchema: this.keySchema, attributeDefinitions: this.attributeDefinitions, globalSecondaryIndexes: Lazy.anyValue({ produce: () => this.globalSecondaryIndexes }, { omitEmptyArray: true }), @@ -248,8 +250,18 @@ export class Table extends Resource { if (props.tableName) { this.node.addMetadata('aws:cdk:hasPhysicalName', props.tableName); } - this.tableArn = this.table.attrArn; - this.tableName = this.table.refAsString; + const resourceIdentifiers = new ResourceIdentifiers(this, { + arn: this.table.attrArn, + name: this.table.refAsString, + arnComponents: { + service: 'dynamodb', + resource: 'table', + resourceName: this.physicalName.value, + }, + }); + this.tableArn = resourceIdentifiers.arn; + this.tableName = resourceIdentifiers.name; + this.tableStreamArn = this.table.attrStreamArn; this.scalingRole = this.makeScalingRole(); diff --git a/packages/@aws-cdk/aws-dynamodb/test/test.dynamodb.ts b/packages/@aws-cdk/aws-dynamodb/test/test.dynamodb.ts index 2faf49cd60a63..3771062f01ebe 100644 --- a/packages/@aws-cdk/aws-dynamodb/test/test.dynamodb.ts +++ b/packages/@aws-cdk/aws-dynamodb/test/test.dynamodb.ts @@ -1,7 +1,7 @@ import { expect, haveResource } from '@aws-cdk/assert'; import appscaling = require('@aws-cdk/aws-applicationautoscaling'); import iam = require('@aws-cdk/aws-iam'); -import { ConstructNode, Stack, Tag } from '@aws-cdk/cdk'; +import { ConstructNode, PhysicalName, Stack, Tag } from '@aws-cdk/cdk'; import { Test } from 'nodeunit'; import { Attribute, @@ -219,7 +219,7 @@ export = { const stack = new Stack(); new Table(stack, CONSTRUCT_NAME, { - tableName: TABLE_NAME, + tableName: PhysicalName.of(TABLE_NAME), readCapacity: 42, writeCapacity: 1337, stream: StreamViewType.NewAndOldImages, @@ -249,7 +249,7 @@ export = { const stack = new Stack(); new Table(stack, CONSTRUCT_NAME, { - tableName: TABLE_NAME, + tableName: PhysicalName.of(TABLE_NAME), readCapacity: 42, writeCapacity: 1337, stream: StreamViewType.NewImage, @@ -279,7 +279,7 @@ export = { const stack = new Stack(); new Table(stack, CONSTRUCT_NAME, { - tableName: TABLE_NAME, + tableName: PhysicalName.of(TABLE_NAME), readCapacity: 42, writeCapacity: 1337, stream: StreamViewType.OldImage, @@ -309,7 +309,7 @@ export = { 'when specifying every property'(test: Test) { const stack = new Stack(); const table = new Table(stack, CONSTRUCT_NAME, { - tableName: TABLE_NAME, + tableName: PhysicalName.of(TABLE_NAME), readCapacity: 42, writeCapacity: 1337, pointInTimeRecovery: true, @@ -350,7 +350,7 @@ export = { 'when specifying PAY_PER_REQUEST billing mode'(test: Test) { const stack = new Stack(); new Table(stack, CONSTRUCT_NAME, { - tableName: TABLE_NAME, + tableName: PhysicalName.of(TABLE_NAME), billingMode: BillingMode.PayPerRequest, partitionKey: TABLE_PARTITION_KEY }); @@ -373,19 +373,19 @@ export = { 'error when specifying read or write capacity with a PAY_PER_REQUEST billing mode'(test: Test) { const stack = new Stack(); test.throws(() => new Table(stack, CONSTRUCT_NAME, { - tableName: TABLE_NAME, + tableName: PhysicalName.of(TABLE_NAME), billingMode: BillingMode.PayPerRequest, partitionKey: TABLE_PARTITION_KEY, readCapacity: 1 })); test.throws(() => new Table(stack, CONSTRUCT_NAME, { - tableName: TABLE_NAME, + tableName: PhysicalName.of(TABLE_NAME), billingMode: BillingMode.PayPerRequest, partitionKey: TABLE_PARTITION_KEY, writeCapacity: 1 })); test.throws(() => new Table(stack, CONSTRUCT_NAME, { - tableName: TABLE_NAME, + tableName: PhysicalName.of(TABLE_NAME), billingMode: BillingMode.PayPerRequest, partitionKey: TABLE_PARTITION_KEY, readCapacity: 1, diff --git a/packages/@aws-cdk/aws-ec2/lib/security-group.ts b/packages/@aws-cdk/aws-ec2/lib/security-group.ts index 8bd0948c75d0e..6ea32fdbc5a42 100644 --- a/packages/@aws-cdk/aws-ec2/lib/security-group.ts +++ b/packages/@aws-cdk/aws-ec2/lib/security-group.ts @@ -1,4 +1,4 @@ -import { Construct, IResource, Lazy, Resource, Stack } from '@aws-cdk/cdk'; +import { Construct, IResource, Lazy, PhysicalName, Resource, ResourceProps, Stack } from '@aws-cdk/cdk'; import { Connections, IConnectable } from './connections'; import { CfnSecurityGroup, CfnSecurityGroupEgress, CfnSecurityGroupIngress } from './ec2.generated'; import { IPortRange, ISecurityGroupRule } from './security-group-rule'; @@ -57,8 +57,8 @@ abstract class SecurityGroupBase extends Resource implements ISecurityGroup { */ public readonly defaultPortRange?: IPortRange; - constructor(scope: Construct, id: string) { - super(scope, id); + constructor(scope: Construct, id: string, props?: ResourceProps) { + super(scope, id, props); Object.defineProperty(this, SECURITY_GROUP_SYMBOL, { value: true }); } @@ -192,7 +192,7 @@ export interface SecurityGroupProps { * @default If you don't specify a GroupName, AWS CloudFormation generates a * unique physical ID and uses that ID for the group name. */ - readonly groupName?: string; + readonly groupName?: PhysicalName; /** * A description of the security group. @@ -266,14 +266,16 @@ export class SecurityGroup extends SecurityGroupBase { private readonly allowAllOutbound: boolean; constructor(scope: Construct, id: string, props: SecurityGroupProps) { - super(scope, id); + super(scope, id, { + physicalName: props.groupName + }); const groupDescription = props.description || this.node.path; this.allowAllOutbound = props.allowAllOutbound !== false; this.securityGroup = new CfnSecurityGroup(this, 'Resource', { - groupName: props.groupName, + groupName: this.physicalName.value, groupDescription, securityGroupIngress: Lazy.anyValue({ produce: () => this.directIngressRules }), securityGroupEgress: Lazy.anyValue({ produce: () => this.directEgressRules }), diff --git a/packages/@aws-cdk/aws-ec2/package.json b/packages/@aws-cdk/aws-ec2/package.json index b3095ad095b97..cb662e1da44ac 100644 --- a/packages/@aws-cdk/aws-ec2/package.json +++ b/packages/@aws-cdk/aws-ec2/package.json @@ -89,8 +89,14 @@ }, "awslint": { "exclude": [ - "resource-attribute:@aws-cdk/aws-ec2.ISecurityGroup.securityGroupVpcId" + "resource-attribute:@aws-cdk/aws-ec2.ISecurityGroup.securityGroupVpcId", + "props-physical-name:@aws-cdk/aws-ec2.GatewayVpcEndpointProps", + "props-physical-name:@aws-cdk/aws-ec2.PrivateSubnetProps", + "props-physical-name:@aws-cdk/aws-ec2.PublicSubnetProps", + "props-physical-name:@aws-cdk/aws-ec2.SubnetProps", + "props-physical-name:@aws-cdk/aws-ec2.VpcProps", + "props-physical-name:@aws-cdk/aws-ec2.InterfaceVpcEndpointProps" ] }, "stability": "experimental" -} \ No newline at end of file +} diff --git a/packages/@aws-cdk/aws-ecr/lib/repository.ts b/packages/@aws-cdk/aws-ecr/lib/repository.ts index 08739c8a0eb02..924046582c9b8 100644 --- a/packages/@aws-cdk/aws-ecr/lib/repository.ts +++ b/packages/@aws-cdk/aws-ecr/lib/repository.ts @@ -1,6 +1,6 @@ import events = require('@aws-cdk/aws-events'); import iam = require('@aws-cdk/aws-iam'); -import { Construct, IConstruct, IResource, Lazy, RemovalPolicy, Resource, Stack, Token } from '@aws-cdk/cdk'; +import { Construct, IConstruct, IResource, Lazy, PhysicalName, RemovalPolicy, Resource, ResourceIdentifiers, Stack, Token } from '@aws-cdk/cdk'; import { CfnRepository } from './ecr.generated'; import { CountType, LifecycleRule, TagStatus } from './lifecycle'; @@ -230,7 +230,7 @@ export interface RepositoryProps { * * @default Automatically generated name. */ - readonly repositoryName?: string; + readonly repositoryName?: PhysicalName; /** * Life cycle rules to apply to this registry @@ -335,10 +335,12 @@ export class Repository extends RepositoryBase { private policyDocument?: iam.PolicyDocument; constructor(scope: Construct, id: string, props: RepositoryProps = {}) { - super(scope, id); + super(scope, id, { + physicalName: props.repositoryName, + }); const resource = new CfnRepository(this, 'Resource', { - repositoryName: props.repositoryName, + repositoryName: this.physicalName.value, // It says "Text", but they actually mean "Object". repositoryPolicyText: Lazy.anyValue({ produce: () => this.policyDocument }), lifecyclePolicy: Lazy.anyValue({ produce: () => this.renderLifecyclePolicy() }), @@ -351,8 +353,17 @@ export class Repository extends RepositoryBase { props.lifecycleRules.forEach(this.addLifecycleRule.bind(this)); } - this.repositoryName = resource.refAsString; - this.repositoryArn = resource.attrArn; + const resourceIdentifiers = new ResourceIdentifiers(this, { + arn: resource.attrArn, + name: resource.refAsString, + arnComponents: { + service: 'ecr', + resource: 'repository', + resourceName: this.physicalName.value, + }, + }); + this.repositoryName = resourceIdentifiers.name; + this.repositoryArn = resourceIdentifiers.arn; } public addToResourcePolicy(statement: iam.PolicyStatement) { diff --git a/packages/@aws-cdk/aws-ecs-patterns/lib/fargate/load-balanced-fargate-service.ts b/packages/@aws-cdk/aws-ecs-patterns/lib/fargate/load-balanced-fargate-service.ts index 9343f00fcb237..c26cfa180b5f9 100644 --- a/packages/@aws-cdk/aws-ecs-patterns/lib/fargate/load-balanced-fargate-service.ts +++ b/packages/@aws-cdk/aws-ecs-patterns/lib/fargate/load-balanced-fargate-service.ts @@ -68,9 +68,9 @@ export interface LoadBalancedFargateServiceProps extends LoadBalancedServiceBase /** * Override value for the service name * - * @default - No value + * @default CloudFormation-generated name */ - readonly serviceName?: string; + readonly serviceName?: cdk.PhysicalName; } /** @@ -110,7 +110,7 @@ export class LoadBalancedFargateService extends LoadBalancedServiceBase { desiredCount: props.desiredCount || 1, taskDefinition, assignPublicIp, - serviceName: props.serviceName || undefined + serviceName: props.serviceName, }); this.service = service; diff --git a/packages/@aws-cdk/aws-ecs-patterns/test/ec2/test.queue-processing-ecs-service.ts b/packages/@aws-cdk/aws-ecs-patterns/test/ec2/test.queue-processing-ecs-service.ts index c8337c1866a76..09a05dfe8e80f 100644 --- a/packages/@aws-cdk/aws-ecs-patterns/test/ec2/test.queue-processing-ecs-service.ts +++ b/packages/@aws-cdk/aws-ecs-patterns/test/ec2/test.queue-processing-ecs-service.ts @@ -71,7 +71,9 @@ export = { const vpc = new ec2.Vpc(stack, 'VPC'); const cluster = new ecs.Cluster(stack, 'Cluster', { vpc }); cluster.addCapacity('DefaultAutoScalingGroup', { instanceType: new ec2.InstanceType('t2.micro') }); - const queue = new sqs.Queue(stack, 'ecs-test-queue', { queueName: 'ecs-test-sqs-queue'}); + const queue = new sqs.Queue(stack, 'ecs-test-queue', { + queueName: cdk.PhysicalName.of('ecs-test-sqs-queue'), + }); // WHEN new ecsPatterns.QueueProcessingEc2Service(stack, 'Service', { @@ -134,4 +136,4 @@ export = { test.done(); } -}; \ No newline at end of file +}; diff --git a/packages/@aws-cdk/aws-ecs-patterns/test/fargate/test.load-balanced-fargate-service.ts b/packages/@aws-cdk/aws-ecs-patterns/test/fargate/test.load-balanced-fargate-service.ts index 327103c71b0e5..3224d06c61fab 100644 --- a/packages/@aws-cdk/aws-ecs-patterns/test/fargate/test.load-balanced-fargate-service.ts +++ b/packages/@aws-cdk/aws-ecs-patterns/test/fargate/test.load-balanced-fargate-service.ts @@ -155,7 +155,7 @@ export = { cluster, loadBalancerType: ecsPatterns.LoadBalancerType.Network, image: ecs.ContainerImage.fromRegistry("/aws/aws-example-app"), - serviceName: 'bob' + serviceName: cdk.PhysicalName.of('bob'), }); // THEN const serviceTaskDefinition = SynthUtils.synthesize(stack).template.Resources.Service9571FDD8; @@ -181,4 +181,4 @@ export = { test.equal(serviceTaskDefinition.Properties.ServiceName, undefined); test.done(); } -}; \ No newline at end of file +}; diff --git a/packages/@aws-cdk/aws-ecs-patterns/test/fargate/test.queue-processing-fargate-service.ts b/packages/@aws-cdk/aws-ecs-patterns/test/fargate/test.queue-processing-fargate-service.ts index dbc80913ac6fd..6d79f0003906c 100644 --- a/packages/@aws-cdk/aws-ecs-patterns/test/fargate/test.queue-processing-fargate-service.ts +++ b/packages/@aws-cdk/aws-ecs-patterns/test/fargate/test.queue-processing-fargate-service.ts @@ -69,7 +69,9 @@ export = { const vpc = new ec2.Vpc(stack, 'VPC'); const cluster = new ecs.Cluster(stack, 'Cluster', { vpc }); cluster.addCapacity('DefaultAutoScalingGroup', { instanceType: new ec2.InstanceType('t2.micro') }); - const queue = new sqs.Queue(stack, 'fargate-test-queue', { queueName: 'fargate-test-sqs-queue'}); + const queue = new sqs.Queue(stack, 'fargate-test-queue', { + queueName: cdk.PhysicalName.of('fargate-test-sqs-queue'), + }); // WHEN new ecsPatterns.QueueProcessingFargateService(stack, 'Service', { diff --git a/packages/@aws-cdk/aws-ecs/lib/base/base-service.ts b/packages/@aws-cdk/aws-ecs/lib/base/base-service.ts index a7f22e24836f5..81b90352e94cd 100644 --- a/packages/@aws-cdk/aws-ecs/lib/base/base-service.ts +++ b/packages/@aws-cdk/aws-ecs/lib/base/base-service.ts @@ -4,7 +4,7 @@ import ec2 = require('@aws-cdk/aws-ec2'); import elbv2 = require('@aws-cdk/aws-elasticloadbalancingv2'); import iam = require('@aws-cdk/aws-iam'); import cloudmap = require('@aws-cdk/aws-servicediscovery'); -import { IResource, Lazy, Resource, Stack } from '@aws-cdk/cdk'; +import { IResource, Lazy, PhysicalName, Resource, ResourceIdentifiers, Stack } from '@aws-cdk/cdk'; import cdk = require('@aws-cdk/cdk'); import { NetworkMode, TaskDefinition } from '../base/task-definition'; import { ICluster } from '../cluster'; @@ -41,7 +41,7 @@ export interface BaseServiceProps { * * @default - CloudFormation-generated name. */ - readonly serviceName?: string; + readonly serviceName?: PhysicalName; /** * The maximum number of tasks, specified as a percentage of the Amazon ECS @@ -139,13 +139,15 @@ export abstract class BaseService extends Resource additionalProps: any, clusterName: string, taskDefinition: TaskDefinition) { - super(scope, id); + super(scope, id, { + physicalName: props.serviceName, + }); this.taskDefinition = taskDefinition; this.resource = new CfnService(this, "Service", { desiredCount: props.desiredCount, - serviceName: props.serviceName, + serviceName: this.physicalName.value, loadBalancers: Lazy.anyValue({ produce: () => this.loadBalancers }), deploymentConfiguration: { maximumPercent: props.maximumPercent || 200, @@ -158,15 +160,25 @@ export abstract class BaseService extends Resource ...additionalProps }); - this.serviceArn = this.resource.refAsString; - // This is a workaround for CFN bug that returns the cluster name instead of the service name when long ARN formats // are enabled for the principal in a given region. const longArnEnabled = props.longArnEnabled !== undefined ? props.longArnEnabled : false; - this.serviceName = longArnEnabled - ? cdk.Fn.select(2, cdk.Fn.split('/', this.serviceArn)) + const serviceName = longArnEnabled + ? cdk.Fn.select(2, cdk.Fn.split('/', this.resource.refAsString)) : this.resource.attrName; + const resourceIdentifiers = new ResourceIdentifiers(this, { + arn: this.resource.refAsString, + name: serviceName, + arnComponents: { + service: 'ecs', + resource: 'service', + resourceName: `${props.cluster.physicalName.value}/${this.physicalName.value}`, + }, + }); + this.serviceArn = resourceIdentifiers.arn; + this.serviceName = resourceIdentifiers.name; + this.clusterName = clusterName; this.cluster = props.cluster; diff --git a/packages/@aws-cdk/aws-ecs/lib/cluster.ts b/packages/@aws-cdk/aws-ecs/lib/cluster.ts index d3981b4bc9314..be4a359092f18 100644 --- a/packages/@aws-cdk/aws-ecs/lib/cluster.ts +++ b/packages/@aws-cdk/aws-ecs/lib/cluster.ts @@ -4,7 +4,7 @@ import ec2 = require('@aws-cdk/aws-ec2'); import iam = require('@aws-cdk/aws-iam'); import cloudmap = require('@aws-cdk/aws-servicediscovery'); import ssm = require('@aws-cdk/aws-ssm'); -import { Construct, IResource, Resource, Stack } from '@aws-cdk/cdk'; +import { Construct, IResource, PhysicalName, Resource, ResourceIdentifiers, Stack } from '@aws-cdk/cdk'; import { InstanceDrainHook } from './drain-hook/instance-drain-hook'; import { CfnCluster } from './ecs.generated'; @@ -17,7 +17,7 @@ export interface ClusterProps { * * @default CloudFormation-generated name */ - readonly clusterName?: string; + readonly clusterName?: PhysicalName; /** * The VPC where your ECS instances will be running or your ENIs will be deployed @@ -67,13 +67,27 @@ export class Cluster extends Resource implements ICluster { private _hasEc2Capacity: boolean = false; constructor(scope: Construct, id: string, props: ClusterProps) { - super(scope, id); + super(scope, id, { + physicalName: props.clusterName, + }); - const cluster = new CfnCluster(this, 'Resource', {clusterName: props.clusterName}); + const cluster = new CfnCluster(this, 'Resource', { + clusterName: this.physicalName.value, + }); + + const resourceIdentifiers = new ResourceIdentifiers(this, { + arn: cluster.attrArn, + name: cluster.refAsString, + arnComponents: { + service: 'ecs', + resource: 'cluster', + resourceName: this.physicalName.value, + }, + }); + this.clusterArn = resourceIdentifiers.arn; + this.clusterName = resourceIdentifiers.name; this.vpc = props.vpc; - this.clusterArn = cluster.attrArn; - this.clusterName = cluster.refAsString; } /** diff --git a/packages/@aws-cdk/aws-ecs/package.json b/packages/@aws-cdk/aws-ecs/package.json index 6d3f9c61abaab..cb33a4f76ab96 100644 --- a/packages/@aws-cdk/aws-ecs/package.json +++ b/packages/@aws-cdk/aws-ecs/package.json @@ -125,5 +125,12 @@ "engines": { "node": ">= 8.10.0" }, + "awslint": { + "exclude": [ + "props-physical-name:@aws-cdk/aws-ecs.TaskDefinitionProps", + "props-physical-name:@aws-cdk/aws-ecs.Ec2TaskDefinitionProps", + "props-physical-name:@aws-cdk/aws-ecs.FargateTaskDefinitionProps" + ] + }, "stability": "experimental" -} \ No newline at end of file +} diff --git a/packages/@aws-cdk/aws-eks/lib/cluster.ts b/packages/@aws-cdk/aws-eks/lib/cluster.ts index 17f93dc3b1acd..3b57e9c95e98e 100644 --- a/packages/@aws-cdk/aws-eks/lib/cluster.ts +++ b/packages/@aws-cdk/aws-eks/lib/cluster.ts @@ -2,7 +2,7 @@ import autoscaling = require('@aws-cdk/aws-autoscaling'); import ec2 = require('@aws-cdk/aws-ec2'); import { Subnet } from '@aws-cdk/aws-ec2'; import iam = require('@aws-cdk/aws-iam'); -import { CfnOutput, Construct, IResource, Resource, Tag } from '@aws-cdk/cdk'; +import { CfnOutput, Construct, IResource, PhysicalName, Resource, ResourceIdentifiers, Tag } from '@aws-cdk/cdk'; import { EksOptimizedAmi, nodeTypeForInstanceType } from './ami'; import { CfnCluster } from './eks.generated'; import { maxPodsForInstanceType } from './instance-data'; @@ -110,7 +110,7 @@ export interface ClusterProps { * * @default Automatically generated name */ - readonly clusterName?: string; + readonly clusterName?: PhysicalName; /** * Security Group to use for Control Plane ENIs @@ -199,7 +199,9 @@ export class Cluster extends Resource implements ICluster { * @param props properties in the IClusterProps interface */ constructor(scope: Construct, id: string, props: ClusterProps) { - super(scope, id); + super(scope, id, { + physicalName: props.clusterName, + }); this.vpc = props.vpc; this.version = props.version; @@ -229,7 +231,7 @@ export class Cluster extends Resource implements ICluster { const subnetIds = [...new Set(Array().concat(...placements.map(s => props.vpc.selectSubnets(s).subnetIds)))]; const resource = new CfnCluster(this, 'Resource', { - name: props.clusterName, + name: this.physicalName.value, roleArn: this.role.roleArn, version: props.version, resourcesVpcConfig: { @@ -238,8 +240,18 @@ export class Cluster extends Resource implements ICluster { } }); - this.clusterName = resource.refAsString; - this.clusterArn = resource.attrArn; + const resourceIdentifiers = new ResourceIdentifiers(this, { + arn: resource.attrArn, + name: resource.refAsString, + arnComponents: { + service: 'eks', + resource: 'cluster', + resourceName: this.physicalName.value, + }, + }); + this.clusterName = resourceIdentifiers.name; + this.clusterArn = resourceIdentifiers.arn; + this.clusterEndpoint = resource.attrEndpoint; this.clusterCertificateAuthorityData = resource.attrCertificateAuthorityData; diff --git a/packages/@aws-cdk/aws-elasticloadbalancing/package.json b/packages/@aws-cdk/aws-elasticloadbalancing/package.json index 164e8319ac3b0..6856d8b6740ce 100644 --- a/packages/@aws-cdk/aws-elasticloadbalancing/package.json +++ b/packages/@aws-cdk/aws-elasticloadbalancing/package.json @@ -81,5 +81,10 @@ "engines": { "node": ">= 8.10.0" }, + "awslint": { + "exclude": [ + "props-physical-name:@aws-cdk/aws-elasticloadbalancing.LoadBalancerProps" + ] + }, "stability": "experimental" -} \ No newline at end of file +} diff --git a/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/shared/base-load-balancer.ts b/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/shared/base-load-balancer.ts index df5d192847a25..2d88265554d33 100644 --- a/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/shared/base-load-balancer.ts +++ b/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/shared/base-load-balancer.ts @@ -1,5 +1,5 @@ import ec2 = require('@aws-cdk/aws-ec2'); -import { Construct, IResource, Lazy, Resource } from '@aws-cdk/cdk'; +import { Construct, IResource, Lazy, PhysicalName, Resource } from '@aws-cdk/cdk'; import { CfnLoadBalancer } from '../elasticloadbalancingv2.generated'; import { Attributes, ifUndefined, renderAttributes } from './util'; @@ -12,7 +12,7 @@ export interface BaseLoadBalancerProps { * * @default - Automatically generated name. */ - readonly loadBalancerName?: string; + readonly loadBalancerName?: PhysicalName; /** * The VPC network to place the load balancer in @@ -121,7 +121,9 @@ export abstract class BaseLoadBalancer extends Resource { private readonly attributes: Attributes = {}; constructor(scope: Construct, id: string, baseProps: BaseLoadBalancerProps, additionalProps: any) { - super(scope, id); + super(scope, id, { + physicalName: baseProps.loadBalancerName, + }); const internetFacing = ifUndefined(baseProps.internetFacing, false); @@ -133,7 +135,7 @@ export abstract class BaseLoadBalancer extends Resource { this.vpc = baseProps.vpc; const resource = new CfnLoadBalancer(this, 'Resource', { - name: baseProps.loadBalancerName, + name: this.physicalName.value, subnets: subnetIds, scheme: internetFacing ? 'internet-facing' : 'internal', loadBalancerAttributes: Lazy.anyValue({ produce: () => renderAttributes(this.attributes) }), diff --git a/packages/@aws-cdk/aws-elasticloadbalancingv2/package.json b/packages/@aws-cdk/aws-elasticloadbalancingv2/package.json index 515ed687be3c7..5062453f2ea8a 100644 --- a/packages/@aws-cdk/aws-elasticloadbalancingv2/package.json +++ b/packages/@aws-cdk/aws-elasticloadbalancingv2/package.json @@ -93,8 +93,10 @@ "exclude": [ "construct-ctor:@aws-cdk/aws-elasticloadbalancingv2.BaseListener..params[2]", "construct-ctor:@aws-cdk/aws-elasticloadbalancingv2.BaseLoadBalancer.", - "construct-ctor:@aws-cdk/aws-elasticloadbalancingv2.TargetGroupBase." + "construct-ctor:@aws-cdk/aws-elasticloadbalancingv2.TargetGroupBase.", + "props-physical-name:@aws-cdk/aws-elasticloadbalancingv2.ApplicationListenerProps", + "props-physical-name:@aws-cdk/aws-elasticloadbalancingv2.NetworkListenerProps" ] }, "stability": "experimental" -} \ No newline at end of file +} diff --git a/packages/@aws-cdk/aws-elasticloadbalancingv2/test/alb/test.load-balancer.ts b/packages/@aws-cdk/aws-elasticloadbalancingv2/test/alb/test.load-balancer.ts index 98b536ffd7045..dd54758b40ddf 100644 --- a/packages/@aws-cdk/aws-elasticloadbalancingv2/test/alb/test.load-balancer.ts +++ b/packages/@aws-cdk/aws-elasticloadbalancingv2/test/alb/test.load-balancer.ts @@ -2,7 +2,7 @@ import { expect, haveResource, ResourcePart } from '@aws-cdk/assert'; import ec2 = require('@aws-cdk/aws-ec2'); import s3 = require('@aws-cdk/aws-s3'); import cdk = require('@aws-cdk/cdk'); -import { Stack } from '@aws-cdk/cdk'; +import { PhysicalName, Stack } from '@aws-cdk/cdk'; import { Test } from 'nodeunit'; import elbv2 = require('../../lib'); @@ -252,7 +252,7 @@ export = { // WHEN new elbv2.ApplicationLoadBalancer(stack, 'ALB', { - loadBalancerName: 'myLoadBalancer', + loadBalancerName: PhysicalName.of('myLoadBalancer'), vpc }); diff --git a/packages/@aws-cdk/aws-elasticloadbalancingv2/test/nlb/test.load-balancer.ts b/packages/@aws-cdk/aws-elasticloadbalancingv2/test/nlb/test.load-balancer.ts index b04d7198b98ce..855ddf338e26d 100644 --- a/packages/@aws-cdk/aws-elasticloadbalancingv2/test/nlb/test.load-balancer.ts +++ b/packages/@aws-cdk/aws-elasticloadbalancingv2/test/nlb/test.load-balancer.ts @@ -81,7 +81,7 @@ export = { // WHEN new elbv2.NetworkLoadBalancer(stack, 'ALB', { - loadBalancerName: 'myLoadBalancer', + loadBalancerName: cdk.PhysicalName.of('myLoadBalancer'), vpc }); diff --git a/packages/@aws-cdk/aws-events-targets/test/codebuild/integ.project-events.ts b/packages/@aws-cdk/aws-events-targets/test/codebuild/integ.project-events.ts index a8e6f35f86dcb..54aadb36d71ba 100644 --- a/packages/@aws-cdk/aws-events-targets/test/codebuild/integ.project-events.ts +++ b/packages/@aws-cdk/aws-events-targets/test/codebuild/integ.project-events.ts @@ -12,7 +12,9 @@ const app = new cdk.App(); const stack = new cdk.Stack(app, 'aws-cdk-codebuild-events'); -const repo = new codecommit.Repository(stack, 'MyRepo', { repositoryName: 'aws-cdk-codebuild-events' }); +const repo = new codecommit.Repository(stack, 'MyRepo', { + repositoryName: cdk.PhysicalName.of('aws-cdk-codebuild-events'), +}); const project = new codebuild.Project(stack, 'MyProject', { source: codebuild.Source.codeCommit({ repository: repo }), }); diff --git a/packages/@aws-cdk/aws-events-targets/test/codepipeline/integ.pipeline-event-target.ts b/packages/@aws-cdk/aws-events-targets/test/codepipeline/integ.pipeline-event-target.ts index 32d81c33dc046..b9d5c4be4f03b 100644 --- a/packages/@aws-cdk/aws-events-targets/test/codepipeline/integ.pipeline-event-target.ts +++ b/packages/@aws-cdk/aws-events-targets/test/codepipeline/integ.pipeline-event-target.ts @@ -14,7 +14,7 @@ const app = new cdk.App(); const stack = new cdk.Stack(app, 'pipeline-events'); const repo = new codecommit.Repository(stack, 'Repo', { - repositoryName: 'TestRepository' + repositoryName: cdk.PhysicalName.of('TestRepository'), }); const pipeline = new codepipeline.Pipeline(stack, 'pipelinePipeline22F2A91D'); diff --git a/packages/@aws-cdk/aws-events/lib/on-event-options.ts b/packages/@aws-cdk/aws-events/lib/on-event-options.ts index 047722cb35ab7..4f0205c5ca5a9 100644 --- a/packages/@aws-cdk/aws-events/lib/on-event-options.ts +++ b/packages/@aws-cdk/aws-events/lib/on-event-options.ts @@ -1,3 +1,4 @@ +import { PhysicalName } from "@aws-cdk/cdk"; import { EventPattern } from "./event-pattern"; import { IRuleTarget } from "./target"; @@ -20,7 +21,7 @@ export interface OnEventOptions { * * @default AWS CloudFormation generates a unique physical ID. */ - readonly ruleName?: string; + readonly ruleName?: PhysicalName; /** * Additional restrictions for the event to route to the specified target @@ -33,4 +34,4 @@ export interface OnEventOptions { * http://docs.aws.amazon.com/AmazonCloudWatch/latest/DeveloperGuide/CloudWatchEventsandEventPatterns.html */ readonly eventPattern?: EventPattern; -} \ No newline at end of file +} diff --git a/packages/@aws-cdk/aws-events/lib/rule.ts b/packages/@aws-cdk/aws-events/lib/rule.ts index 8e2b5ef7c6665..2104ef73d2c95 100644 --- a/packages/@aws-cdk/aws-events/lib/rule.ts +++ b/packages/@aws-cdk/aws-events/lib/rule.ts @@ -1,4 +1,4 @@ -import { Construct, Lazy, Resource } from '@aws-cdk/cdk'; +import { Construct, Lazy, PhysicalName, Resource, ResourceIdentifiers } from '@aws-cdk/cdk'; import { EventPattern } from './event-pattern'; import { CfnRule } from './events.generated'; import { IRule } from './rule-ref'; @@ -20,7 +20,7 @@ export interface RuleProps { * @default - AWS CloudFormation generates a unique physical ID and uses that ID * for the rule name. For more information, see Name Type. */ - readonly ruleName?: string; + readonly ruleName?: PhysicalName; /** * Indicates whether the rule is enabled. @@ -91,10 +91,12 @@ export class Rule extends Resource implements IRule { private scheduleExpression?: string; constructor(scope: Construct, id: string, props: RuleProps = { }) { - super(scope, id); + super(scope, id, { + physicalName: props.ruleName, + }); const resource = new CfnRule(this, 'Resource', { - name: props.ruleName, + name: this.physicalName.value, description: props.description, state: props.enabled == null ? 'ENABLED' : (props.enabled ? 'ENABLED' : 'DISABLED'), scheduleExpression: Lazy.stringValue({ produce: () => this.scheduleExpression }), @@ -102,7 +104,16 @@ export class Rule extends Resource implements IRule { targets: Lazy.anyValue({ produce: () => this.renderTargets() }), }); - this.ruleArn = resource.attrArn; + const resourceIdentifiers = new ResourceIdentifiers(this, { + arn: resource.attrArn, + name: resource.name || '', + arnComponents: { + service: 'events', + resource: 'rule', + resourceName: this.physicalName.value, + }, + }); + this.ruleArn = resourceIdentifiers.arn; this.addEventPattern(props.eventPattern); this.scheduleExpression = props.schedule && props.schedule.expressionString; diff --git a/packages/@aws-cdk/aws-events/test/test.rule.ts b/packages/@aws-cdk/aws-events/test/test.rule.ts index 184ad3cb64479..2b6f53cccad9e 100644 --- a/packages/@aws-cdk/aws-events/test/test.rule.ts +++ b/packages/@aws-cdk/aws-events/test/test.rule.ts @@ -37,7 +37,7 @@ export = { // WHEN new Rule(stack, 'MyRule', { - ruleName: 'PhysicalName', + ruleName: cdk.PhysicalName.of('PhysicalName'), schedule: Schedule.rate(10, TimeUnit.Minute), }); diff --git a/packages/@aws-cdk/aws-glue/lib/database.ts b/packages/@aws-cdk/aws-glue/lib/database.ts index 2773a9ba271ca..f5a6dca67087a 100644 --- a/packages/@aws-cdk/aws-glue/lib/database.ts +++ b/packages/@aws-cdk/aws-glue/lib/database.ts @@ -1,5 +1,5 @@ import s3 = require('@aws-cdk/aws-s3'); -import { Construct, IResource, Resource, Stack } from '@aws-cdk/cdk'; +import { Construct, IResource, PhysicalName, Resource, ResourceIdentifiers, Stack } from '@aws-cdk/cdk'; import { CfnDatabase } from './glue.generated'; export interface IDatabase extends IResource { @@ -32,7 +32,7 @@ export interface DatabaseProps { /** * The name of the database. */ - readonly databaseName: string; + readonly databaseName: PhysicalName; /** * The location of the database (for example, an HDFS path). @@ -86,31 +86,43 @@ export class Database extends Resource implements IDatabase { public readonly locationUri: string; constructor(scope: Construct, id: string, props: DatabaseProps) { - super(scope, id); + super(scope, id, { + physicalName: props.databaseName, + }); if (props.locationUri) { this.locationUri = props.locationUri; } else { const bucket = new s3.Bucket(this, 'Bucket'); - this.locationUri = `s3://${bucket.bucketName}/${props.databaseName}`; + this.locationUri = `s3://${bucket.bucketName}/${props.databaseName.value}`; } this.catalogId = Stack.of(this).account; const resource = new CfnDatabase(this, 'Resource', { catalogId: this.catalogId, databaseInput: { - name: props.databaseName, + name: this.physicalName.value, locationUri: this.locationUri } }); // see https://docs.aws.amazon.com/glue/latest/dg/glue-specifying-resource-arns.html#data-catalog-resource-arns - this.databaseName = resource.refAsString; - this.databaseArn = Stack.of(this).formatArn({ - service: 'glue', - resource: 'database', - resourceName: this.databaseName + const resourceIdentifiers = new ResourceIdentifiers(this, { + arn: Stack.of(this).formatArn({ + service: 'glue', + resource: 'database', + resourceName: resource.refAsString, + }), + name: resource.refAsString, + arnComponents: { + service: 'glue', + resource: 'database', + resourceName: this.physicalName.value, + }, }); + this.databaseName = resourceIdentifiers.name; + this.databaseArn = resourceIdentifiers.arn; + // catalogId is implicitly the accountId, which is why we don't pass the catalogId here this.catalogArn = Stack.of(this).formatArn({ service: 'glue', diff --git a/packages/@aws-cdk/aws-glue/lib/table.ts b/packages/@aws-cdk/aws-glue/lib/table.ts index 77e261f980eb9..e9f877bf7593a 100644 --- a/packages/@aws-cdk/aws-glue/lib/table.ts +++ b/packages/@aws-cdk/aws-glue/lib/table.ts @@ -1,7 +1,7 @@ import iam = require('@aws-cdk/aws-iam'); import kms = require('@aws-cdk/aws-kms'); import s3 = require('@aws-cdk/aws-s3'); -import { Construct, Fn, IResource, Resource, Stack } from '@aws-cdk/cdk'; +import { Construct, Fn, IResource, PhysicalName, Resource, ResourceIdentifiers, Stack } from '@aws-cdk/cdk'; import { DataFormat } from './data-format'; import { IDatabase } from './database'; import { CfnTable } from './glue.generated'; @@ -63,7 +63,7 @@ export interface TableProps { /** * Name of the table. */ - readonly tableName: string; + readonly tableName: PhysicalName; /** * Description of the table. @@ -230,7 +230,9 @@ export class Table extends Resource implements ITable { public readonly partitionKeys?: Column[]; constructor(scope: Construct, id: string, props: TableProps) { - super(scope, id); + super(scope, id, { + physicalName: props.tableName, + }); this.database = props.database; this.dataFormat = props.dataFormat; @@ -252,8 +254,8 @@ export class Table extends Resource implements ITable { databaseName: props.database.databaseName, tableInput: { - name: props.tableName, - description: props.description || `${props.tableName} generated by CDK`, + name: this.physicalName.value, + description: props.description || `${props.tableName.value} generated by CDK`, partitionKeys: renderColumns(props.partitionKeys), @@ -276,12 +278,21 @@ export class Table extends Resource implements ITable { } }); - this.tableName = tableResource.refAsString; - this.tableArn = Stack.of(this).formatArn({ - service: 'glue', - resource: 'table', - resourceName: `${this.database.databaseName}/${this.tableName}` + const resourceIdentifiers = new ResourceIdentifiers(this, { + arn: Stack.of(this).formatArn({ + service: 'glue', + resource: 'table', + resourceName: `${this.database.databaseName}/${tableResource.refAsString}` + }), + name: tableResource.refAsString, + arnComponents: { + service: 'glue', + resource: 'table', + resourceName: `${this.database.physicalName.value}/${this.physicalName.value}` + }, }); + this.tableName = resourceIdentifiers.name; + this.tableArn = resourceIdentifiers.arn; } /** diff --git a/packages/@aws-cdk/aws-glue/test/integ.table.ts b/packages/@aws-cdk/aws-glue/test/integ.table.ts index c624aa8ec75cd..f9ab375a7b46d 100644 --- a/packages/@aws-cdk/aws-glue/test/integ.table.ts +++ b/packages/@aws-cdk/aws-glue/test/integ.table.ts @@ -9,12 +9,12 @@ const app = new cdk.App(); const stack = new cdk.Stack(app, 'aws-cdk-glue'); const database = new glue.Database(stack, 'MyDatabase', { - databaseName: 'my_database' + databaseName: cdk.PhysicalName.of('my_database'), }); const ordinaryTable = new glue.Table(stack, 'MyTable', { database, - tableName: 'my_table', + tableName: cdk.PhysicalName.of('my_table'), columns: [{ name: 'col1', type: glue.Schema.string @@ -44,7 +44,7 @@ const ordinaryTable = new glue.Table(stack, 'MyTable', { const encryptedTable = new glue.Table(stack, 'MyEncryptedTable', { database, - tableName: 'my_encrypted_table', + tableName: cdk.PhysicalName.of('my_encrypted_table'), columns: [{ name: 'col1', type: glue.Schema.string diff --git a/packages/@aws-cdk/aws-glue/test/test.database.ts b/packages/@aws-cdk/aws-glue/test/test.database.ts index 71501270ea99b..7fa03609c4056 100644 --- a/packages/@aws-cdk/aws-glue/test/test.database.ts +++ b/packages/@aws-cdk/aws-glue/test/test.database.ts @@ -1,5 +1,5 @@ import { expect } from '@aws-cdk/assert'; -import { Stack } from '@aws-cdk/cdk'; +import { PhysicalName, Stack } from '@aws-cdk/cdk'; import { Test } from 'nodeunit'; import glue = require('../lib'); @@ -8,7 +8,7 @@ export = { const stack = new Stack(); new glue.Database(stack, 'Database', { - databaseName: 'test_database' + databaseName: PhysicalName.of('test_database'), }); expect(stack).toMatch({ @@ -50,7 +50,7 @@ export = { const stack = new Stack(); new glue.Database(stack, 'Database', { - databaseName: 'test_database', + databaseName: PhysicalName.of('test_database'), locationUri: 's3://my-uri/' }); diff --git a/packages/@aws-cdk/aws-glue/test/test.table.ts b/packages/@aws-cdk/aws-glue/test/test.table.ts index a2a73d94bb69c..fe1c7ebcab69d 100644 --- a/packages/@aws-cdk/aws-glue/test/test.table.ts +++ b/packages/@aws-cdk/aws-glue/test/test.table.ts @@ -11,13 +11,13 @@ export = { const app = new cdk.App(); const dbStack = new cdk.Stack(app, 'db'); const database = new glue.Database(dbStack, 'Database', { - databaseName: 'database' + databaseName: cdk.PhysicalName.of('database'), }); const tableStack = new cdk.Stack(app, 'table'); const table = new glue.Table(tableStack, 'Table', { database, - tableName: 'table', + tableName: cdk.PhysicalName.of('table'), columns: [{ name: 'col', type: glue.Schema.string @@ -82,13 +82,13 @@ export = { const app = new cdk.App(); const dbStack = new cdk.Stack(app, 'db'); const database = new glue.Database(dbStack, 'Database', { - databaseName: 'database' + databaseName: cdk.PhysicalName.of('database'), }); const tableStack = new cdk.Stack(app, 'table'); const table = new glue.Table(tableStack, 'Table', { database, - tableName: 'table', + tableName: cdk.PhysicalName.of('table'), columns: [{ name: 'col', type: glue.Schema.string @@ -159,12 +159,12 @@ export = { 'compressed table'(test: Test) { const stack = new cdk.Stack(); const database = new glue.Database(stack, 'Database', { - databaseName: 'database' + databaseName: cdk.PhysicalName.of('database'), }); const table = new glue.Table(stack, 'Table', { database, - tableName: 'table', + tableName: cdk.PhysicalName.of('table'), columns: [{ name: 'col', type: glue.Schema.string @@ -226,12 +226,12 @@ export = { 'SSE-S3'(test: Test) { const stack = new cdk.Stack(); const database = new glue.Database(stack, 'Database', { - databaseName: 'database' + databaseName: cdk.PhysicalName.of('database'), }); const table = new glue.Table(stack, 'Table', { database, - tableName: 'table', + tableName: cdk.PhysicalName.of('table'), columns: [{ name: 'col', type: glue.Schema.string @@ -305,12 +305,12 @@ export = { 'SSE-KMS (implicitly created key)'(test: Test) { const stack = new cdk.Stack(); const database = new glue.Database(stack, 'Database', { - databaseName: 'database' + databaseName: cdk.PhysicalName.of('database'), }); const table = new glue.Table(stack, 'Table', { database, - tableName: 'table', + tableName: cdk.PhysicalName.of('table'), columns: [{ name: 'col', type: glue.Schema.string @@ -434,13 +434,13 @@ export = { 'SSE-KMS (explicitly created key)'(test: Test) { const stack = new cdk.Stack(); const database = new glue.Database(stack, 'Database', { - databaseName: 'database' + databaseName: cdk.PhysicalName.of('database'), }); const encryptionKey = new kms.Key(stack, 'MyKey'); const table = new glue.Table(stack, 'Table', { database, - tableName: 'table', + tableName: cdk.PhysicalName.of('table'), columns: [{ name: 'col', type: glue.Schema.string @@ -565,12 +565,12 @@ export = { 'SSE-KMS_MANAGED'(test: Test) { const stack = new cdk.Stack(); const database = new glue.Database(stack, 'Database', { - databaseName: 'database' + databaseName: cdk.PhysicalName.of('database'), }); const table = new glue.Table(stack, 'Table', { database, - tableName: 'table', + tableName: cdk.PhysicalName.of('table'), columns: [{ name: 'col', type: glue.Schema.string @@ -644,12 +644,12 @@ export = { 'CSE-KMS (implicitly created key)'(test: Test) { const stack = new cdk.Stack(); const database = new glue.Database(stack, 'Database', { - databaseName: 'database' + databaseName: cdk.PhysicalName.of('database'), }); const table = new glue.Table(stack, 'Table', { database, - tableName: 'table', + tableName: cdk.PhysicalName.of('table'), columns: [{ name: 'col', type: glue.Schema.string @@ -755,13 +755,13 @@ export = { 'CSE-KMS (explicitly created key)'(test: Test) { const stack = new cdk.Stack(); const database = new glue.Database(stack, 'Database', { - databaseName: 'database' + databaseName: cdk.PhysicalName.of('database'), }); const encryptionKey = new kms.Key(stack, 'MyKey'); const table = new glue.Table(stack, 'Table', { database, - tableName: 'table', + tableName: cdk.PhysicalName.of('table'), columns: [{ name: 'col', type: glue.Schema.string @@ -868,14 +868,14 @@ export = { 'CSE-KMS (explicitly passed bucket and key)'(test: Test) { const stack = new cdk.Stack(); const database = new glue.Database(stack, 'Database', { - databaseName: 'database' + databaseName: cdk.PhysicalName.of('database'), }); const bucket = new s3.Bucket(stack, 'Bucket'); const encryptionKey = new kms.Key(stack, 'MyKey'); const table = new glue.Table(stack, 'Table', { database, - tableName: 'table', + tableName: cdk.PhysicalName.of('table'), columns: [{ name: 'col', type: glue.Schema.string @@ -987,14 +987,14 @@ export = { const stack = new cdk.Stack(app, 'app'); const bucket = new s3.Bucket(stack, 'ExplicitBucket'); const database = new glue.Database(dbStack, 'Database', { - databaseName: 'database' + databaseName: cdk.PhysicalName.of('database'), }); new glue.Table(stack, 'Table', { database, bucket, s3Prefix: 'prefix/', - tableName: 'table', + tableName: cdk.PhysicalName.of('table'), columns: [{ name: 'col', type: glue.Schema.string @@ -1054,12 +1054,12 @@ export = { const stack = new cdk.Stack(); const user = new iam.User(stack, 'User'); const database = new glue.Database(stack, 'Database', { - databaseName: 'database' + databaseName: cdk.PhysicalName.of('database'), }); const table = new glue.Table(stack, 'Table', { database, - tableName: 'table', + tableName: cdk.PhysicalName.of('table'), columns: [{ name: 'col', type: glue.Schema.string @@ -1160,12 +1160,12 @@ export = { const stack = new cdk.Stack(); const user = new iam.User(stack, 'User'); const database = new glue.Database(stack, 'Database', { - databaseName: 'database' + databaseName: cdk.PhysicalName.of('database'), }); const table = new glue.Table(stack, 'Table', { database, - tableName: 'table', + tableName: cdk.PhysicalName.of('table'), columns: [{ name: 'col', type: glue.Schema.string @@ -1264,12 +1264,12 @@ export = { const stack = new cdk.Stack(); const user = new iam.User(stack, 'User'); const database = new glue.Database(stack, 'Database', { - databaseName: 'database' + databaseName: cdk.PhysicalName.of('database'), }); const table = new glue.Table(stack, 'Table', { database, - tableName: 'table', + tableName: cdk.PhysicalName.of('table'), columns: [{ name: 'col', type: glue.Schema.string @@ -1380,7 +1380,7 @@ export = { test.throws(() => { createTable({ columns: [], - tableName: 'name', + tableName: cdk.PhysicalName.of('name'), }); }, undefined, 'you must specify at least one column for the table'); @@ -1390,7 +1390,7 @@ export = { 'unique column names'(test: Test) { test.throws(() => { createTable({ - tableName: 'name', + tableName: cdk.PhysicalName.of('name'), columns: [{ name: 'col1', type: glue.Schema.string @@ -1406,7 +1406,7 @@ export = { 'unique partition keys'(test: Test) { test.throws(() => createTable({ - tableName: 'name', + tableName: cdk.PhysicalName.of('name'), columns: [{ name: 'col1', type: glue.Schema.string @@ -1425,7 +1425,7 @@ export = { 'column names and partition keys are all unique'(test: Test) { test.throws(() => createTable({ - tableName: 'name', + tableName: cdk.PhysicalName.of('name'), columns: [{ name: 'col1', type: glue.Schema.string @@ -1441,7 +1441,7 @@ export = { 'can not specify an explicit bucket and encryption'(test: Test) { test.throws(() => createTable({ - tableName: 'name', + tableName: cdk.PhysicalName.of('name'), columns: [{ name: 'col1', type: glue.Schema.string @@ -1454,7 +1454,7 @@ export = { 'can explicitly pass bucket if Encryption undefined'(test: Test) { test.doesNotThrow(() => createTable({ - tableName: 'name', + tableName: cdk.PhysicalName.of('name'), columns: [{ name: 'col1', type: glue.Schema.string @@ -1467,7 +1467,7 @@ export = { 'can explicitly pass bucket if Unencrypted'(test: Test) { test.doesNotThrow(() => createTable({ - tableName: 'name', + tableName: cdk.PhysicalName.of('name'), columns: [{ name: 'col1', type: glue.Schema.string @@ -1480,7 +1480,7 @@ export = { 'can explicitly pass bucket if ClientSideKms'(test: Test) { test.doesNotThrow(() => createTable({ - tableName: 'name', + tableName: cdk.PhysicalName.of('name'), columns: [{ name: 'col1', type: glue.Schema.string @@ -1511,7 +1511,7 @@ function createTable(props: Pick this.managedPolicies.map(p => p.managedPolicyArn) }, { omitEmpty: true }), path: props.path, }); - this.groupName = group.refAsString; - this.groupArn = group.attrArn; + const resourceIdentifiers = new ResourceIdentifiers(this, { + arn: group.attrArn, + name: group.refAsString, + arnComponents: { + region: '', // IAM is global in each partition + service: 'iam', + resource: 'group', + resourceName: this.physicalName.value, + }, + }); + this.groupName = resourceIdentifiers.name; + this.groupArn = resourceIdentifiers.arn; } /** diff --git a/packages/@aws-cdk/aws-iam/lib/policy.ts b/packages/@aws-cdk/aws-iam/lib/policy.ts index e5ef29ec99ff4..c38ef82ab6417 100644 --- a/packages/@aws-cdk/aws-iam/lib/policy.ts +++ b/packages/@aws-cdk/aws-iam/lib/policy.ts @@ -1,4 +1,4 @@ -import { Construct, IResource, Lazy, Resource } from '@aws-cdk/cdk'; +import { Construct, IResource, Lazy, PhysicalName, Resource } from '@aws-cdk/cdk'; import { IGroup } from './group'; import { CfnPolicy } from './iam.generated'; import { PolicyDocument } from './policy-document'; @@ -23,7 +23,7 @@ export interface PolicyProps { * @default - Uses the logical ID of the policy resource, which is ensured * to be unique within the stack. */ - readonly policyName?: string; + readonly policyName?: PhysicalName; /** * Users to attach this policy to. @@ -91,20 +91,23 @@ export class Policy extends Resource implements IPolicy { private readonly groups = new Array(); constructor(scope: Construct, id: string, props: PolicyProps = {}) { - super(scope, id); + super(scope, id, { + physicalName: props.policyName || + // generatePolicyName will take the last 128 characters of the logical id since + // policy names are limited to 128. the last 8 chars are a stack-unique hash, so + // that shouod be sufficient to ensure uniqueness within a principal. + PhysicalName.of(Lazy.stringValue({ produce: () => generatePolicyName(resource.logicalId) }).toString()), + }); const resource = new CfnPolicy(this, 'Resource', { policyDocument: this.document, - policyName: Lazy.stringValue({ produce: () => this.policyName }).toString(), + policyName: this.physicalName.value!, // guaranteed to be not null because of above `super` call roles: undefinedIfEmpty(() => this.roles.map(r => r.roleName)), users: undefinedIfEmpty(() => this.users.map(u => u.userName)), groups: undefinedIfEmpty(() => this.groups.map(g => g.groupName)), }); - // generatePolicyName will take the last 128 characters of the logical id since - // policy names are limited to 128. the last 8 chars are a stack-unique hash, so - // that shouod be sufficient to ensure uniqueness within a principal. - this.policyName = props.policyName || generatePolicyName(resource.logicalId); + this.policyName = this.physicalName.value!; if (props.users) { props.users.forEach(u => this.attachToUser(u)); diff --git a/packages/@aws-cdk/aws-iam/lib/user.ts b/packages/@aws-cdk/aws-iam/lib/user.ts index 7725b8269141d..833ee9d81bca0 100644 --- a/packages/@aws-cdk/aws-iam/lib/user.ts +++ b/packages/@aws-cdk/aws-iam/lib/user.ts @@ -1,4 +1,4 @@ -import { Construct, Lazy, Resource, SecretValue } from '@aws-cdk/cdk'; +import { Construct, Lazy, PhysicalName, Resource, ResourceIdentifiers, SecretValue } from '@aws-cdk/cdk'; import { IGroup } from './group'; import { CfnUser } from './iam.generated'; import { IIdentity } from './identity-base'; @@ -55,7 +55,7 @@ export interface UserProps { * * @default Generated by CloudFormation (recommended) */ - readonly userName?: string; + readonly userName?: PhysicalName; /** * The password for the user. This is required so the user can access the @@ -103,18 +103,31 @@ export class User extends Resource implements IIdentity { private defaultPolicy?: Policy; constructor(scope: Construct, id: string, props: UserProps = {}) { - super(scope, id); + super(scope, id, { + physicalName: props.userName, + }); const user = new CfnUser(this, 'Resource', { - userName: props.userName, + userName: this.physicalName.value, groups: undefinedIfEmpty(() => this.groups), managedPolicyArns: Lazy.listValue({ produce: () => this.managedPolicies.map(p => p.managedPolicyArn) }, { omitEmpty: true }), path: props.path, loginProfile: this.parseLoginProfile(props) }); - this.userName = user.refAsString; - this.userArn = user.attrArn; + const resourceIdentifiers = new ResourceIdentifiers(this, { + arn: user.attrArn, + name: user.refAsString, + arnComponents: { + region: '', // IAM is global in each partition + service: 'iam', + resource: 'user', + resourceName: this.physicalName.value, + }, + }); + this.userName = resourceIdentifiers.name; + this.userArn = resourceIdentifiers.arn; + this.policyFragment = new ArnPrincipal(this.userArn).policyFragment; if (props.groups) { diff --git a/packages/@aws-cdk/aws-iam/test/integ.policy.ts b/packages/@aws-cdk/aws-iam/test/integ.policy.ts index 193643b3e166a..e25390daab79b 100644 --- a/packages/@aws-cdk/aws-iam/test/integ.policy.ts +++ b/packages/@aws-cdk/aws-iam/test/integ.policy.ts @@ -1,4 +1,4 @@ -import { App, Stack } from "@aws-cdk/cdk"; +import { App, PhysicalName, Stack } from "@aws-cdk/cdk"; import { Policy, PolicyStatement } from "../lib"; import { User } from "../lib/user"; @@ -8,7 +8,7 @@ const stack = new Stack(app, 'aws-cdk-iam-policy'); const user = new User(stack, 'MyUser'); -const policy = new Policy(stack, 'HelloPolicy', { policyName: 'Default' }); +const policy = new Policy(stack, 'HelloPolicy', { policyName: PhysicalName.of('Default') }); policy.addStatements(new PolicyStatement({ resources: ['*'], actions: ['sqs:SendMessage'] })); policy.attachToUser(user); diff --git a/packages/@aws-cdk/aws-iam/test/integ.role.ts b/packages/@aws-cdk/aws-iam/test/integ.role.ts index b18b429db597f..73487ee8c8d5b 100644 --- a/packages/@aws-cdk/aws-iam/test/integ.role.ts +++ b/packages/@aws-cdk/aws-iam/test/integ.role.ts @@ -1,4 +1,4 @@ -import { App, Stack } from "@aws-cdk/cdk"; +import { App, PhysicalName, Stack } from "@aws-cdk/cdk"; import { AccountRootPrincipal, Policy, PolicyStatement, Role, ServicePrincipal } from "../lib"; const app = new App(); @@ -11,7 +11,7 @@ const role = new Role(stack, 'TestRole', { role.addToPolicy(new PolicyStatement({ resources: ['*'], actions: ['sqs:SendMessage'] })); -const policy = new Policy(stack, 'HelloPolicy', { policyName: 'Default' }); +const policy = new Policy(stack, 'HelloPolicy', { policyName: PhysicalName.of('Default') }); policy.addStatements(new PolicyStatement({ actions: ['ec2:*'], resources: ['*'] })); policy.attachToRole(role); diff --git a/packages/@aws-cdk/aws-iam/test/integ.user.ts b/packages/@aws-cdk/aws-iam/test/integ.user.ts index 6870ac52ea550..3498a9a1f7932 100644 --- a/packages/@aws-cdk/aws-iam/test/integ.user.ts +++ b/packages/@aws-cdk/aws-iam/test/integ.user.ts @@ -1,4 +1,4 @@ -import { App, SecretValue, Stack } from "@aws-cdk/cdk"; +import { App, PhysicalName, SecretValue, Stack } from "@aws-cdk/cdk"; import { User } from "../lib"; const app = new App(); @@ -6,7 +6,7 @@ const app = new App(); const stack = new Stack(app, 'aws-cdk-iam-user'); new User(stack, 'MyUser', { - userName: 'benisrae', + userName: PhysicalName.of('benisrae'), password: SecretValue.plainText('1234'), passwordResetRequired: true }); diff --git a/packages/@aws-cdk/aws-iam/test/test.escape-hatch.ts b/packages/@aws-cdk/aws-iam/test/test.escape-hatch.ts index 942dd55127ed8..908eb61611f3e 100644 --- a/packages/@aws-cdk/aws-iam/test/test.escape-hatch.ts +++ b/packages/@aws-cdk/aws-iam/test/test.escape-hatch.ts @@ -2,7 +2,7 @@ // because we want to verify them end-to-end, as a complement to the unit // tests in the @aws-cdk/cdk module import { expect } from '@aws-cdk/assert'; -import { Stack } from '@aws-cdk/cdk'; +import { PhysicalName, Stack } from '@aws-cdk/cdk'; import { Test } from 'nodeunit'; import iam = require('../lib'); @@ -12,7 +12,7 @@ export = { 'addPropertyOverride should allow overriding supported properties'(test: Test) { const stack = new Stack(); const user = new iam.User(stack, 'user', { - userName: 'MyUserName' + userName: PhysicalName.of('MyUserName'), }); const cfn = user.node.findChild('Resource') as iam.CfnUser; @@ -33,7 +33,7 @@ export = { 'addPropertyOverrides should allow specifying arbitrary properties'(test: Test) { // GIVEN const stack = new Stack(); - const user = new iam.User(stack, 'user', { userName: 'MyUserName' }); + const user = new iam.User(stack, 'user', { userName: PhysicalName.of('MyUserName') }); const cfn = user.node.findChild('Resource') as iam.CfnUser; // WHEN @@ -59,7 +59,7 @@ export = { 'addOverride should allow overriding properties'(test: Test) { // GIVEN const stack = new Stack(); - const user = new iam.User(stack, 'user', { userName: 'MyUserName' }); + const user = new iam.User(stack, 'user', { userName: PhysicalName.of('MyUserName') }); const cfn = user.node.findChild('Resource') as iam.CfnUser; cfn.options.updatePolicy = { useOnlineResharding: true }; @@ -95,4 +95,4 @@ export = { test.done(); } -}; \ No newline at end of file +}; diff --git a/packages/@aws-cdk/aws-iam/test/test.policy.ts b/packages/@aws-cdk/aws-iam/test/test.policy.ts index fd2530c6ab835..7547e6c356e7d 100644 --- a/packages/@aws-cdk/aws-iam/test/test.policy.ts +++ b/packages/@aws-cdk/aws-iam/test/test.policy.ts @@ -1,5 +1,5 @@ import { expect } from '@aws-cdk/assert'; -import { App, Stack } from '@aws-cdk/cdk'; +import { App, PhysicalName, Stack } from '@aws-cdk/cdk'; import { Test } from 'nodeunit'; import { Group, Policy, PolicyStatement, Role, ServicePrincipal, User } from '../lib'; import { generatePolicyName } from '../lib/util'; @@ -18,7 +18,7 @@ export = { const app = new App(); const stack = new Stack(app, 'MyStack'); - const policy = new Policy(stack, 'MyPolicy', { policyName: 'MyPolicyName' }); + const policy = new Policy(stack, 'MyPolicy', { policyName: PhysicalName.of('MyPolicyName') }); policy.addStatements(new PolicyStatement({ resources: ['*'], actions: ['sqs:SendMessage'] })); policy.addStatements(new PolicyStatement({ resources: ['arn'], actions: ['sns:Subscribe'] })); @@ -78,7 +78,7 @@ export = { }); new Policy(stack, 'MyTestPolicy', { - policyName: 'Foo', + policyName: PhysicalName.of('Foo'), users: [ user1 ], groups: [ group1 ], roles: [ role1 ], @@ -141,7 +141,7 @@ export = { const stack = new Stack(app, 'MyStack'); const p = new Policy(stack, 'MyTestPolicy', { - policyName: 'Foo', + policyName: PhysicalName.of('Foo'), }); p.attachToUser(new User(stack, 'User1')); @@ -222,8 +222,8 @@ export = { const stack = new Stack(app, 'MyStack'); // create two policies named Foo and attach them both to the same user/group/role - const p1 = new Policy(stack, 'P1', { policyName: 'Foo' }); - const p2 = new Policy(stack, 'P2', { policyName: 'Foo' }); + const p1 = new Policy(stack, 'P1', { policyName: PhysicalName.of('Foo') }); + const p2 = new Policy(stack, 'P2', { policyName: PhysicalName.of('Foo') }); const p3 = new Policy(stack, 'P3'); // uses logicalID as name const user = new User(stack, 'MyUser'); diff --git a/packages/@aws-cdk/aws-kinesis/lib/stream.ts b/packages/@aws-cdk/aws-kinesis/lib/stream.ts index eedde8e6c2e39..274917f6a9eb8 100644 --- a/packages/@aws-cdk/aws-kinesis/lib/stream.ts +++ b/packages/@aws-cdk/aws-kinesis/lib/stream.ts @@ -1,6 +1,6 @@ import iam = require('@aws-cdk/aws-iam'); import kms = require('@aws-cdk/aws-kms'); -import { Construct, IResource, Resource, Stack } from '@aws-cdk/cdk'; +import { Construct, IResource, PhysicalName, Resource, ResourceIdentifiers, Stack } from '@aws-cdk/cdk'; import { CfnStream } from './kinesis.generated'; export interface IStream extends IResource { @@ -173,7 +173,7 @@ export interface StreamProps { * Enforces a particular physical stream name. * @default */ - readonly streamName?: string; + readonly streamName?: PhysicalName; /** * The number of hours for the data records that are stored in shards to remain accessible. @@ -241,7 +241,9 @@ export class Stream extends StreamBase { private readonly stream: CfnStream; constructor(scope: Construct, id: string, props: StreamProps = {}) { - super(scope, id); + super(scope, id, { + physicalName: props.streamName, + }); const shardCount = props.shardCount || 1; const retentionPeriodHours = props.retentionPeriodHours || 24; @@ -252,13 +254,24 @@ export class Stream extends StreamBase { const { streamEncryption, encryptionKey } = this.parseEncryption(props); this.stream = new CfnStream(this, "Resource", { - name: props.streamName, + name: this.physicalName.value, retentionPeriodHours, shardCount, streamEncryption }); - this.streamArn = this.stream.attrArn; - this.streamName = this.stream.refAsString; + + const resourceIdentifiers = new ResourceIdentifiers(this, { + arn: this.stream.attrArn, + name: this.stream.refAsString, + arnComponents: { + service: 'kinesis', + resource: 'stream', + resourceName: this.physicalName.value, + }, + }); + this.streamArn = resourceIdentifiers.arn; + this.streamName = resourceIdentifiers.name; + this.encryptionKey = encryptionKey; if (props.streamName) { this.node.addMetadata('aws:cdk:hasPhysicalName', props.streamName); } diff --git a/packages/@aws-cdk/aws-kms/package.json b/packages/@aws-cdk/aws-kms/package.json index 6d5efaa5cc6f6..d5f5ce88df197 100644 --- a/packages/@aws-cdk/aws-kms/package.json +++ b/packages/@aws-cdk/aws-kms/package.json @@ -81,5 +81,11 @@ "engines": { "node": ">= 8.10.0" }, + "awslint": { + "exclude": [ + "props-physical-name:@aws-cdk/aws-kms.AliasProps", + "props-physical-name:@aws-cdk/aws-kms.KeyProps" + ] + }, "stability": "experimental" -} \ No newline at end of file +} diff --git a/packages/@aws-cdk/aws-lambda/lib/alias.ts b/packages/@aws-cdk/aws-lambda/lib/alias.ts index 59b02da68b1cb..291ff7609c4f1 100644 --- a/packages/@aws-cdk/aws-lambda/lib/alias.ts +++ b/packages/@aws-cdk/aws-lambda/lib/alias.ts @@ -1,5 +1,5 @@ import cloudwatch = require('@aws-cdk/aws-cloudwatch'); -import { Construct, Stack } from '@aws-cdk/cdk'; +import { Construct, PhysicalName, ResourceIdentifiers, Stack } from '@aws-cdk/cdk'; import { IFunction, QualifiedFunctionBase } from './function-base'; import { IVersion } from './lambda-version'; import { CfnAlias } from './lambda.generated'; @@ -39,7 +39,7 @@ export interface AliasProps { /** * Name of this alias */ - readonly aliasName: string; + readonly aliasName: PhysicalName; /** * Additional versions with individual weights this alias points to @@ -114,25 +114,38 @@ export class Alias extends QualifiedFunctionBase implements IAlias { protected readonly canCreatePermissions: boolean = true; constructor(scope: Construct, id: string, props: AliasProps) { - super(scope, id); + super(scope, id, { + physicalName: props.aliasName, + }); this.lambda = props.version.lambda; - this.aliasName = props.aliasName; + this.aliasName = this.physicalName.value || ''; this.version = props.version; const alias = new CfnAlias(this, 'Resource', { - name: props.aliasName, + name: this.aliasName, description: props.description, functionName: this.version.lambda.functionName, functionVersion: props.version.version, routingConfig: this.determineRoutingConfig(props) }); + const resourceIdentifiers = new ResourceIdentifiers(this, { + arn: alias.refAsString, + name: this.aliasName, + arnComponents: { + service: 'lambda', + resource: 'function', + resourceName: `${this.lambda.physicalName.value}:${this.physicalName.value}`, + sep: ':', + }, + }); + + this.functionArn = resourceIdentifiers.arn; // ARN parsing splits on `:`, so we can only get the function's name from the ARN as resourceName... // And we're parsing it out (instead of using the underlying function directly) in order to have use of it incur // an implicit dependency on the resource. - this.functionName = `${Stack.of(this).parseArn(alias.refAsString, ":").resourceName!}:${props.aliasName}`; - this.functionArn = alias.refAsString; + this.functionName = `${Stack.of(this).parseArn(this.functionArn, ":").resourceName!}:${this.aliasName}`; } public get grantPrincipal() { diff --git a/packages/@aws-cdk/aws-lambda/lib/function.ts b/packages/@aws-cdk/aws-lambda/lib/function.ts index f880d2816f16d..ee981704eae02 100644 --- a/packages/@aws-cdk/aws-lambda/lib/function.ts +++ b/packages/@aws-cdk/aws-lambda/lib/function.ts @@ -3,7 +3,7 @@ import ec2 = require('@aws-cdk/aws-ec2'); import iam = require('@aws-cdk/aws-iam'); import logs = require('@aws-cdk/aws-logs'); import sqs = require('@aws-cdk/aws-sqs'); -import { Construct, Fn, Lazy, Stack, Token } from '@aws-cdk/cdk'; +import { Construct, Fn, Lazy, PhysicalName, ResourceIdentifiers, Stack, Token } from '@aws-cdk/cdk'; import { Code } from './code'; import { IEventSource } from './event-source'; import { FunctionAttributes, FunctionBase, IFunction } from './function-base'; @@ -91,7 +91,7 @@ export interface FunctionProps { * @default - AWS CloudFormation generates a unique physical ID and uses that * ID for the function's name. For more information, see Name Type. */ - readonly functionName?: string; + readonly functionName?: PhysicalName; /** * The amount of memory, in MB, that is allocated to your Lambda function. @@ -385,7 +385,9 @@ export class Function extends FunctionBase { private readonly environment?: { [key: string]: any }; constructor(scope: Construct, id: string, props: FunctionProps) { - super(scope, id); + super(scope, id, { + physicalName: props.functionName, + }); this.environment = props.environment || { }; @@ -417,7 +419,7 @@ export class Function extends FunctionBase { } const resource: CfnFunction = new CfnFunction(this, 'Resource', { - functionName: props.functionName, + functionName: this.physicalName.value, description: props.description, code: Lazy.anyValue({ produce: () => props.code._toJSON(resource) }), layers: Lazy.listValue({ produce: () => this.layers.map(layer => layer.layerVersionArn) }, { omitEmpty: true }), @@ -435,8 +437,18 @@ export class Function extends FunctionBase { resource.node.addDependency(this.role); - this.functionName = resource.refAsString; - this.functionArn = resource.attrArn; + const resourceIdentifiers = new ResourceIdentifiers(this, { + arn: resource.attrArn, + name: resource.refAsString, + arnComponents: { + service: 'lambda', + resource: 'function', + resourceName: this.physicalName.value, + sep: ':', + }, + }); + this.functionName = resourceIdentifiers.name; + this.functionArn = resourceIdentifiers.arn; this.runtime = props.runtime; // allow code to bind to stack. diff --git a/packages/@aws-cdk/aws-lambda/lib/layers.ts b/packages/@aws-cdk/aws-lambda/lib/layers.ts index d83a6eb97bcf9..16f9ab6b82c80 100644 --- a/packages/@aws-cdk/aws-lambda/lib/layers.ts +++ b/packages/@aws-cdk/aws-lambda/lib/layers.ts @@ -1,4 +1,4 @@ -import { Construct, IResource, Lazy, Resource } from '@aws-cdk/cdk'; +import { Construct, IResource, Lazy, PhysicalName, Resource } from '@aws-cdk/cdk'; import { Code } from './code'; import { CfnLayerVersion, CfnLayerVersionPermission } from './lambda.generated'; import { Runtime } from './runtime'; @@ -35,7 +35,7 @@ export interface LayerVersionProps { * * @default - A name will be generated. */ - readonly name?: string; + readonly layerVersionName?: PhysicalName; } export interface ILayerVersion extends IResource { @@ -159,7 +159,10 @@ export class LayerVersion extends LayerVersionBase { public readonly compatibleRuntimes?: Runtime[]; constructor(scope: Construct, id: string, props: LayerVersionProps) { - super(scope, id); + super(scope, id, { + physicalName: props.layerVersionName, + }); + if (props.compatibleRuntimes && props.compatibleRuntimes.length === 0) { throw new Error('Attempted to define a Lambda layer that supports no runtime!'); } @@ -173,7 +176,7 @@ export class LayerVersion extends LayerVersionBase { compatibleRuntimes: props.compatibleRuntimes && props.compatibleRuntimes.map(r => r.name), content: Lazy.anyValue({ produce: () => props.code._toJSON(resource) }), description: props.description, - layerName: props.name, + layerName: this.physicalName.value, licenseInfo: props.license, }); diff --git a/packages/@aws-cdk/aws-lambda/package.json b/packages/@aws-cdk/aws-lambda/package.json index b87cdbd4765d5..af41c768f6f9c 100644 --- a/packages/@aws-cdk/aws-lambda/package.json +++ b/packages/@aws-cdk/aws-lambda/package.json @@ -112,7 +112,9 @@ }, "awslint": { "exclude": [ - "integ-return-type:@aws-cdk/aws-lambda.IEventSource.bind" + "integ-return-type:@aws-cdk/aws-lambda.IEventSource.bind", + "props-physical-name:@aws-cdk/aws-lambda.VersionProps", + "props-physical-name:@aws-cdk/aws-lambda.EventSourceMappingProps" ] }, "stability": "experimental" diff --git a/packages/@aws-cdk/aws-lambda/test/integ.lambda.ts b/packages/@aws-cdk/aws-lambda/test/integ.lambda.ts index 4832356abd86d..4c8d9044ba544 100644 --- a/packages/@aws-cdk/aws-lambda/test/integ.lambda.ts +++ b/packages/@aws-cdk/aws-lambda/test/integ.lambda.ts @@ -20,7 +20,7 @@ fn.addToRolePolicy(new iam.PolicyStatement({ const version = fn.addVersion('1'); const alias = new lambda.Alias(stack, 'Alias', { - aliasName: 'prod', + aliasName: cdk.PhysicalName.of('prod'), version, }); alias.addPermission('AliasPermission', { diff --git a/packages/@aws-cdk/aws-lambda/test/test.alias.ts b/packages/@aws-cdk/aws-lambda/test/test.alias.ts index 14d53123702fb..a6b98916220fe 100644 --- a/packages/@aws-cdk/aws-lambda/test/test.alias.ts +++ b/packages/@aws-cdk/aws-lambda/test/test.alias.ts @@ -1,6 +1,6 @@ import { beASupersetOfTemplate, expect, haveResource, haveResourceLike } from '@aws-cdk/assert'; import cloudwatch = require('@aws-cdk/aws-cloudwatch'); -import { Stack } from '@aws-cdk/cdk'; +import { PhysicalName, Stack } from '@aws-cdk/cdk'; import { Test } from 'nodeunit'; import lambda = require('../lib'); @@ -16,7 +16,7 @@ export = { const version = fn.addVersion('1'); new lambda.Alias(stack, 'Alias', { - aliasName: 'prod', + aliasName: PhysicalName.of('prod'), version, }); @@ -49,7 +49,7 @@ export = { }); new lambda.Alias(stack, 'Alias', { - aliasName: 'latest', + aliasName: PhysicalName.of('latest'), version: fn.latestVersion, }); @@ -74,7 +74,7 @@ export = { const version = fn.addVersion('NewVersion'); new lambda.Alias(stack, 'Alias', { - aliasName: 'prod', + aliasName: PhysicalName.of('prod'), version, }); @@ -103,7 +103,7 @@ export = { const version2 = fn.addVersion('2'); new lambda.Alias(stack, 'Alias', { - aliasName: 'prod', + aliasName: PhysicalName.of('prod'), version: version1, additionalVersions: [{ version: version2, weight: 0.1 }] }); @@ -137,7 +137,7 @@ export = { // WHEN: Individual weight too high test.throws(() => { new lambda.Alias(stack, 'Alias1', { - aliasName: 'prod', version, + aliasName: PhysicalName.of('prod'), version, additionalVersions: [{ version, weight: 5 }] }); }); @@ -145,7 +145,7 @@ export = { // WHEN: Sum too high test.throws(() => { new lambda.Alias(stack, 'Alias2', { - aliasName: 'prod', version, + aliasName: PhysicalName.of('prod'), version, additionalVersions: [{ version, weight: 0.5 }, { version, weight: 0.6 }] }); }); @@ -164,12 +164,12 @@ export = { }); const version = fn.addVersion('1'); - const alias = new lambda.Alias(stack, 'Alias', { aliasName: 'prod', version }); + const alias = new lambda.Alias(stack, 'Alias', { aliasName: PhysicalName.of('prod'), version }); // WHEN new cloudwatch.Alarm(stack, 'Alarm', { metric: alias.metric('Test'), - alarmName: 'Test', + alarmName: PhysicalName.of('Test'), threshold: 1, evaluationPeriods: 1 }); @@ -214,7 +214,7 @@ export = { }); const version = fn.addVersion('1'); - const alias = new lambda.Alias(stack, 'Alias', { aliasName: 'prod', version }); + const alias = new lambda.Alias(stack, 'Alias', { aliasName: PhysicalName.of('prod'), version }); // THEN test.equals(alias.role, fn.role); @@ -233,7 +233,7 @@ export = { }); const version = fn.addVersion('1'); - const alias = new lambda.Alias(stack, 'Alias', { aliasName: 'prod', version }); + const alias = new lambda.Alias(stack, 'Alias', { aliasName: PhysicalName.of('prod'), version }); // WHEN test.deepEqual(stack.resolve(alias.functionName), { diff --git a/packages/@aws-cdk/aws-lambda/test/test.lambda.ts b/packages/@aws-cdk/aws-lambda/test/test.lambda.ts index bc29497c4f59c..b31aba438f817 100644 --- a/packages/@aws-cdk/aws-lambda/test/test.lambda.ts +++ b/packages/@aws-cdk/aws-lambda/test/test.lambda.ts @@ -294,7 +294,7 @@ export = { code: new lambda.InlineCode('foo'), handler: 'index.handler', runtime: lambda.Runtime.Nodejs810, - functionName: 'OneFunctionToRuleThemAll', + functionName: cdk.PhysicalName.of('OneFunctionToRuleThemAll'), deadLetterQueueEnabled: true }); @@ -582,7 +582,7 @@ export = { const stack = new cdk.Stack(); const dlQueue = new sqs.Queue(stack, 'DeadLetterQueue', { - queueName: 'MyLambda_DLQ', + queueName: cdk.PhysicalName.of('MyLambda_DLQ'), retentionPeriodSec: 1209600 }); @@ -691,7 +691,7 @@ export = { const stack = new cdk.Stack(); const dlQueue = new sqs.Queue(stack, 'DeadLetterQueue', { - queueName: 'MyLambda_DLQ', + queueName: cdk.PhysicalName.of('MyLambda_DLQ'), retentionPeriodSec: 1209600 }); @@ -801,7 +801,7 @@ export = { const stack = new cdk.Stack(); const dlQueue = new sqs.Queue(stack, 'DeadLetterQueue', { - queueName: 'MyLambda_DLQ', + queueName: cdk.PhysicalName.of('MyLambda_DLQ'), retentionPeriodSec: 1209600 }); diff --git a/packages/@aws-cdk/aws-logs/lib/cross-account-destination.ts b/packages/@aws-cdk/aws-logs/lib/cross-account-destination.ts index 865ad2ecfe9c1..043ae2e73bf7a 100644 --- a/packages/@aws-cdk/aws-logs/lib/cross-account-destination.ts +++ b/packages/@aws-cdk/aws-logs/lib/cross-account-destination.ts @@ -1,6 +1,6 @@ import iam = require('@aws-cdk/aws-iam'); import cdk = require('@aws-cdk/cdk'); -import { Construct, Lazy, Stack } from '@aws-cdk/cdk'; +import { Construct, Lazy, PhysicalName, ResourceIdentifiers, Stack } from '@aws-cdk/cdk'; import { ILogGroup } from './log-group'; import { CfnDestination } from './logs.generated'; import { ILogSubscriptionDestination, LogSubscriptionDestinationConfig } from './subscription-filter'; @@ -14,7 +14,7 @@ export interface CrossAccountDestinationProps { * * @default Automatically generated */ - readonly destinationName?: string; + readonly destinationName?: PhysicalName; /** * The role to assume that grants permissions to write to 'target'. @@ -66,21 +66,32 @@ export class CrossAccountDestination extends cdk.Resource implements ILogSubscri private readonly resource: CfnDestination; constructor(scope: cdk.Construct, id: string, props: CrossAccountDestinationProps) { - super(scope, id); - - // In the underlying model, the name is not optional, but we make it so anyway. - const destinationName = props.destinationName || Lazy.stringValue({ produce: () => this.generateUniqueName() }); + super(scope, id, { + physicalName: props.destinationName || + // In the underlying model, the name is not optional, but we make it so anyway. + PhysicalName.of(Lazy.stringValue({ produce: () => this.generateUniqueName() })), + }); this.resource = new CfnDestination(this, 'Resource', { - destinationName, + destinationName: this.physicalName.value!, // Must be stringified policy destinationPolicy: this.lazyStringifiedPolicyDocument(), roleArn: props.role.roleArn, targetArn: props.targetArn }); - this.destinationArn = this.resource.attrArn; - this.destinationName = this.resource.refAsString; + const resourceIdentifiers = new ResourceIdentifiers(this, { + arn: this.resource.attrArn, + name: this.resource.refAsString, + arnComponents: { + service: 'logs', + resource: 'destination', + resourceName: this.physicalName.value, + sep: ':', + }, + }); + this.destinationArn = resourceIdentifiers.arn; + this.destinationName = resourceIdentifiers.name; } public addToPolicy(statement: iam.PolicyStatement) { diff --git a/packages/@aws-cdk/aws-logs/lib/log-group.ts b/packages/@aws-cdk/aws-logs/lib/log-group.ts index 025f9d50c0a35..aeed9fa6c956d 100644 --- a/packages/@aws-cdk/aws-logs/lib/log-group.ts +++ b/packages/@aws-cdk/aws-logs/lib/log-group.ts @@ -1,6 +1,6 @@ import cloudwatch = require('@aws-cdk/aws-cloudwatch'); import iam = require('@aws-cdk/aws-iam'); -import { Construct, IResource, RemovalPolicy, Resource, Stack } from '@aws-cdk/cdk'; +import { Construct, IResource, PhysicalName, RemovalPolicy, Resource, ResourceIdentifiers, Stack } from '@aws-cdk/cdk'; import { LogStream } from './log-stream'; import { CfnLogGroup } from './logs.generated'; import { MetricFilter } from './metric-filter'; @@ -271,7 +271,7 @@ export interface LogGroupProps { * * @default Automatically generated */ - readonly logGroupName?: string; + readonly logGroupName?: PhysicalName; /** * How long, in days, the log contents will be retained. @@ -322,7 +322,9 @@ export class LogGroup extends LogGroupBase { public readonly logGroupName: string; constructor(scope: Construct, id: string, props: LogGroupProps = {}) { - super(scope, id); + super(scope, id, { + physicalName: props.logGroupName, + }); let retentionInDays = props.retentionDays; if (retentionInDays === undefined) { retentionInDays = RetentionDays.TwoYears; } @@ -333,14 +335,24 @@ export class LogGroup extends LogGroupBase { } const resource = new CfnLogGroup(this, 'Resource', { - logGroupName: props.logGroupName, + logGroupName: this.physicalName.value, retentionInDays, }); resource.applyRemovalPolicy(props.removalPolicy); - this.logGroupArn = resource.attrArn; - this.logGroupName = resource.refAsString; + const resourceIdentifiers = new ResourceIdentifiers(this, { + arn: resource.attrArn, + name: resource.refAsString, + arnComponents: { + service: 'logs', + resource: 'log-group', + resourceName: this.physicalName.value, + sep: ':', + }, + }); + this.logGroupArn = resourceIdentifiers.arn; + this.logGroupName = resourceIdentifiers.name; } } @@ -355,7 +367,7 @@ export interface StreamOptions { * * @default Automatically generated */ - readonly logStreamName?: string; + readonly logStreamName?: PhysicalName; } /** diff --git a/packages/@aws-cdk/aws-logs/lib/log-stream.ts b/packages/@aws-cdk/aws-logs/lib/log-stream.ts index 4c864f0dc699e..0f58274d9fea1 100644 --- a/packages/@aws-cdk/aws-logs/lib/log-stream.ts +++ b/packages/@aws-cdk/aws-logs/lib/log-stream.ts @@ -1,4 +1,4 @@ -import { Construct, IResource, RemovalPolicy, Resource } from '@aws-cdk/cdk'; +import { Construct, IResource, PhysicalName, RemovalPolicy, Resource, ResourceIdentifiers } from '@aws-cdk/cdk'; import { ILogGroup } from './log-group'; import { CfnLogStream } from './logs.generated'; @@ -26,7 +26,7 @@ export interface LogStreamProps { * * @default Automatically generated */ - readonly logStreamName?: string; + readonly logStreamName?: PhysicalName; /** * Determine what happens when the log stream resource is removed from the @@ -64,15 +64,27 @@ export class LogStream extends Resource implements ILogStream { public readonly logStreamName: string; constructor(scope: Construct, id: string, props: LogStreamProps) { - super(scope, id); + super(scope, id, { + physicalName: props.logStreamName, + }); const resource = new CfnLogStream(this, 'Resource', { logGroupName: props.logGroup.logGroupName, - logStreamName: props.logStreamName + logStreamName: this.physicalName.value, }); resource.applyRemovalPolicy(props.removalPolicy); - this.logStreamName = resource.refAsString; + const resourceIdentifiers = new ResourceIdentifiers(this, { + arn: '', + name: resource.refAsString, + arnComponents: { + service: 'logs', + resource: 'log-group', + resourceName: `log-stream:${this.physicalName.value}`, + sep: ':', + }, + }); + this.logStreamName = resourceIdentifiers.name; } } diff --git a/packages/@aws-cdk/aws-logs/package.json b/packages/@aws-cdk/aws-logs/package.json index 74bff11e1375a..4f09fa9f9925f 100644 --- a/packages/@aws-cdk/aws-logs/package.json +++ b/packages/@aws-cdk/aws-logs/package.json @@ -85,7 +85,9 @@ }, "awslint": { "exclude": [ - "props-no-arn-refs:@aws-cdk/aws-logs.CrossAccountDestinationProps.targetArn" + "props-no-arn-refs:@aws-cdk/aws-logs.CrossAccountDestinationProps.targetArn", + "props-physical-name:@aws-cdk/aws-logs.MetricFilterProps", + "props-physical-name:@aws-cdk/aws-logs.SubscriptionFilterProps" ] }, "stability": "experimental" diff --git a/packages/@aws-cdk/aws-logs/test/test.destination.ts b/packages/@aws-cdk/aws-logs/test/test.destination.ts index 9d42a6f3852c4..2e141217ceb71 100644 --- a/packages/@aws-cdk/aws-logs/test/test.destination.ts +++ b/packages/@aws-cdk/aws-logs/test/test.destination.ts @@ -14,7 +14,7 @@ export = { // WHEN new CrossAccountDestination(stack, 'Dest', { - destinationName: 'MyDestination', + destinationName: cdk.PhysicalName.of('MyDestination'), role, targetArn: 'arn:bogus' }); @@ -37,7 +37,7 @@ export = { }); const dest = new CrossAccountDestination(stack, 'Dest', { - destinationName: 'MyDestination', + destinationName: cdk.PhysicalName.of('MyDestination'), role, targetArn: 'arn:bogus' }); diff --git a/packages/@aws-cdk/aws-rds/package.json b/packages/@aws-cdk/aws-rds/package.json index 86296ad4564d4..4efa048af0618 100644 --- a/packages/@aws-cdk/aws-rds/package.json +++ b/packages/@aws-cdk/aws-rds/package.json @@ -100,8 +100,16 @@ }, "awslint": { "exclude": [ - "construct-base-is-private:@aws-cdk/aws-rds.DatabaseInstanceBase" + "construct-base-is-private:@aws-cdk/aws-rds.DatabaseInstanceBase", + "props-physical-name:@aws-cdk/aws-rds.ParameterGroupProps", + "props-physical-name:@aws-cdk/aws-rds.ClusterParameterGroupProps", + "props-physical-name:@aws-cdk/aws-rds.DatabaseClusterProps", + "props-physical-name:@aws-cdk/aws-rds.DatabaseInstanceProps", + "props-physical-name:@aws-cdk/aws-rds.DatabaseInstanceFromSnapshotProps", + "props-physical-name:@aws-cdk/aws-rds.DatabaseInstanceReadReplicaProps", + "props-physical-name:@aws-cdk/aws-rds.DatabaseSecretProps", + "props-physical-name:@aws-cdk/aws-rds.OptionGroupProps" ] }, "stability": "experimental" -} \ No newline at end of file +} diff --git a/packages/@aws-cdk/aws-route53/package.json b/packages/@aws-cdk/aws-route53/package.json index d9fe4af2f7d7e..e704f96810491 100644 --- a/packages/@aws-cdk/aws-route53/package.json +++ b/packages/@aws-cdk/aws-route53/package.json @@ -89,7 +89,20 @@ "awslint": { "exclude": [ "from-attributes:fromPrivateHostedZoneAttributes", - "from-attributes:fromPublicHostedZoneAttributes" + "from-attributes:fromPublicHostedZoneAttributes", + "props-physical-name:@aws-cdk/aws-route53.ZoneDelegationRecordProps", + "props-physical-name:@aws-cdk/aws-route53.ARecordProps", + "props-physical-name:@aws-cdk/aws-route53.CaaAmazonRecordProps", + "props-physical-name:@aws-cdk/aws-route53.CaaRecordProps", + "props-physical-name:@aws-cdk/aws-route53.CnameRecordProps", + "props-physical-name:@aws-cdk/aws-route53.HostedZoneProps", + "props-physical-name:@aws-cdk/aws-route53.MxRecordProps", + "props-physical-name:@aws-cdk/aws-route53.PrivateHostedZoneProps", + "props-physical-name:@aws-cdk/aws-route53.PublicHostedZoneProps", + "props-physical-name:@aws-cdk/aws-route53.RecordSetProps", + "props-physical-name:@aws-cdk/aws-route53.SrvRecordProps", + "props-physical-name:@aws-cdk/aws-route53.TxtRecordProps", + "props-physical-name:@aws-cdk/aws-route53.AaaaRecordProps" ] }, "stability": "experimental", diff --git a/packages/@aws-cdk/aws-s3/package.json b/packages/@aws-cdk/aws-s3/package.json index 24a51929c5e0d..6d8c11a0da34d 100644 --- a/packages/@aws-cdk/aws-s3/package.json +++ b/packages/@aws-cdk/aws-s3/package.json @@ -85,5 +85,10 @@ "engines": { "node": ">= 8.10.0" }, + "awslint": { + "exclude": [ + "props-physical-name:@aws-cdk/aws-s3.BucketPolicyProps" + ] + }, "stability": "experimental" -} \ No newline at end of file +} diff --git a/packages/@aws-cdk/aws-secretsmanager/lib/secret.ts b/packages/@aws-cdk/aws-secretsmanager/lib/secret.ts index e44a1aa442d9d..8167dc05855bc 100644 --- a/packages/@aws-cdk/aws-secretsmanager/lib/secret.ts +++ b/packages/@aws-cdk/aws-secretsmanager/lib/secret.ts @@ -1,6 +1,6 @@ import iam = require('@aws-cdk/aws-iam'); import kms = require('@aws-cdk/aws-kms'); -import { Construct, IResource, Resource, SecretValue, Stack } from '@aws-cdk/cdk'; +import { Construct, IResource, PhysicalName, Resource, ResourceIdentifiers, SecretValue, Stack } from '@aws-cdk/cdk'; import { RotationSchedule, RotationScheduleOptions } from './rotation-schedule'; import secretsmanager = require('./secretsmanager.generated'); @@ -78,7 +78,7 @@ export interface SecretProps { * * @default - A name is generated by CloudFormation. */ - readonly name?: string; + readonly secretName?: PhysicalName; } /** @@ -173,7 +173,9 @@ export class Secret extends SecretBase { public readonly secretArn: string; constructor(scope: Construct, id: string, props: SecretProps = {}) { - super(scope, id); + super(scope, id, { + physicalName: props.secretName, + }); if (props.generateSecretString && (props.generateSecretString.secretStringTemplate || props.generateSecretString.generateStringKey) && @@ -185,11 +187,22 @@ export class Secret extends SecretBase { description: props.description, kmsKeyId: props.encryptionKey && props.encryptionKey.keyArn, generateSecretString: props.generateSecretString || {}, - name: props.name, + name: this.physicalName.value, + }); + + const resourceIdentifiers = new ResourceIdentifiers(this, { + arn: resource.refAsString, + name: this.physicalName.value || '', + arnComponents: { + service: 'secretsmanager', + resource: 'secret', + resourceName: this.physicalName.value, + sep: ':', + }, }); + this.secretArn = resourceIdentifiers.arn; this.encryptionKey = props.encryptionKey; - this.secretArn = resource.refAsString; } /** diff --git a/packages/@aws-cdk/aws-secretsmanager/package.json b/packages/@aws-cdk/aws-secretsmanager/package.json index e24a685876dcd..37b9ce27b6bd2 100644 --- a/packages/@aws-cdk/aws-secretsmanager/package.json +++ b/packages/@aws-cdk/aws-secretsmanager/package.json @@ -90,8 +90,10 @@ "awslint": { "exclude": [ "from-signature:@aws-cdk/aws-secretsmanager.SecretTargetAttachment.fromSecretTargetAttachmentSecretArn", - "from-attributes:fromSecretTargetAttachmentAttributes" + "from-attributes:fromSecretTargetAttachmentAttributes", + "props-physical-name:@aws-cdk/aws-secretsmanager.RotationScheduleProps", + "props-physical-name:@aws-cdk/aws-secretsmanager.SecretTargetAttachmentProps" ] }, "stability": "experimental" -} \ No newline at end of file +} diff --git a/packages/@aws-cdk/aws-secretsmanager/test/integ.secret.lit.ts b/packages/@aws-cdk/aws-secretsmanager/test/integ.secret.lit.ts index 0dcf85527cb6e..db2ffb20e5b21 100644 --- a/packages/@aws-cdk/aws-secretsmanager/test/integ.secret.lit.ts +++ b/packages/@aws-cdk/aws-secretsmanager/test/integ.secret.lit.ts @@ -26,7 +26,7 @@ class SecretsManagerStack extends cdk.Stack { }); new iam.User(this, 'OtherUser', { - userName: templatedSecret.secretValueFromJson('username').toString(), + userName: cdk.PhysicalName.of(templatedSecret.secretValueFromJson('username').toString()), password: templatedSecret.secretValueFromJson('password') }); /// !hide diff --git a/packages/@aws-cdk/aws-servicediscovery/package.json b/packages/@aws-cdk/aws-servicediscovery/package.json index df1077231b0bb..d476e87d3742f 100644 --- a/packages/@aws-cdk/aws-servicediscovery/package.json +++ b/packages/@aws-cdk/aws-servicediscovery/package.json @@ -85,5 +85,17 @@ "engines": { "node": ">= 8.10.0" }, + "awslint": { + "exclude": [ + "props-physical-name:@aws-cdk/aws-servicediscovery.HttpNamespaceProps", + "props-physical-name:@aws-cdk/aws-servicediscovery.PublicDnsNamespaceProps", + "props-physical-name:@aws-cdk/aws-servicediscovery.PrivateDnsNamespaceProps", + "props-physical-name:@aws-cdk/aws-servicediscovery.AliasTargetInstanceProps", + "props-physical-name:@aws-cdk/aws-servicediscovery.CnameInstanceProps", + "props-physical-name:@aws-cdk/aws-servicediscovery.IpInstanceProps", + "props-physical-name:@aws-cdk/aws-servicediscovery.NonIpInstanceProps", + "props-physical-name:@aws-cdk/aws-servicediscovery.ServiceProps" + ] + }, "stability": "experimental" -} \ No newline at end of file +} diff --git a/packages/@aws-cdk/aws-ses/lib/receipt-filter.ts b/packages/@aws-cdk/aws-ses/lib/receipt-filter.ts index b9ff54e33d22c..83dc2e20c0aab 100644 --- a/packages/@aws-cdk/aws-ses/lib/receipt-filter.ts +++ b/packages/@aws-cdk/aws-ses/lib/receipt-filter.ts @@ -1,4 +1,4 @@ -import { Construct, Resource } from '@aws-cdk/cdk'; +import { Construct, PhysicalName, Resource } from '@aws-cdk/cdk'; import { CfnReceiptFilter } from './ses.generated'; /** @@ -25,7 +25,7 @@ export interface ReceiptFilterProps { * * @default a CloudFormation generated name */ - readonly name?: string; + readonly receiptFilterName?: PhysicalName; /** * The ip address or range to filter. @@ -47,16 +47,18 @@ export interface ReceiptFilterProps { * block all receipt filter. */ export class ReceiptFilter extends Resource { - constructor(scope: Construct, id: string, props?: ReceiptFilterProps) { - super(scope, id); + constructor(scope: Construct, id: string, props: ReceiptFilterProps = {}) { + super(scope, id, { + physicalName: props.receiptFilterName, + }); new CfnReceiptFilter(this, 'Resource', { filter: { ipFilter: { - cidr: (props && props.ip) || '0.0.0.0/0', - policy: (props && props.policy) || ReceiptFilterPolicy.Block + cidr: props.ip || '0.0.0.0/0', + policy: props.policy || ReceiptFilterPolicy.Block, }, - name: props ? props.name : undefined + name: this.physicalName.value, } }); } diff --git a/packages/@aws-cdk/aws-ses/lib/receipt-rule-set.ts b/packages/@aws-cdk/aws-ses/lib/receipt-rule-set.ts index e48a4705bb13a..e3a0927943565 100644 --- a/packages/@aws-cdk/aws-ses/lib/receipt-rule-set.ts +++ b/packages/@aws-cdk/aws-ses/lib/receipt-rule-set.ts @@ -1,4 +1,4 @@ -import { Construct, IResource, Resource } from '@aws-cdk/cdk'; +import { Construct, IResource, PhysicalName, Resource } from '@aws-cdk/cdk'; import { DropSpamReceiptRule, ReceiptRule, ReceiptRuleOptions } from './receipt-rule'; import { CfnReceiptRuleSet } from './ses.generated'; @@ -28,7 +28,7 @@ export interface ReceiptRuleSetProps { * * @default - A CloudFormation generated name. */ - readonly name?: string; + readonly receiptRuleSetName?: PhysicalName; /** * The list of rules to add to this rule set. Rules are added in the same @@ -96,11 +96,13 @@ export class ReceiptRuleSet extends ReceiptRuleSetBase { public readonly receiptRuleSetName: string; - constructor(scope: Construct, id: string, props?: ReceiptRuleSetProps) { - super(scope, id); + constructor(scope: Construct, id: string, props: ReceiptRuleSetProps = {}) { + super(scope, id, { + physicalName: props.receiptRuleSetName, + }); const resource = new CfnReceiptRuleSet(this, 'Resource', { - ruleSetName: props ? props.name : undefined + ruleSetName: this.physicalName.value, }); this.receiptRuleSetName = resource.refAsString; diff --git a/packages/@aws-cdk/aws-ses/lib/receipt-rule.ts b/packages/@aws-cdk/aws-ses/lib/receipt-rule.ts index 5cfc91e7a60d6..4c679c3feeedb 100644 --- a/packages/@aws-cdk/aws-ses/lib/receipt-rule.ts +++ b/packages/@aws-cdk/aws-ses/lib/receipt-rule.ts @@ -1,5 +1,5 @@ import lambda = require('@aws-cdk/aws-lambda'); -import { Construct, IResource, Lazy, Resource } from '@aws-cdk/cdk'; +import { Construct, IResource, Lazy, PhysicalName, Resource } from '@aws-cdk/cdk'; import { IReceiptRuleAction, LambdaInvocationType, ReceiptRuleActionProps, ReceiptRuleLambdaAction } from './receipt-rule-action'; import { IReceiptRuleSet } from './receipt-rule-set'; import { CfnReceiptRule } from './ses.generated'; @@ -62,7 +62,7 @@ export interface ReceiptRuleOptions { * * @default - A CloudFormation generated name. */ - readonly name?: string; + readonly receiptRuleName?: PhysicalName; /** * The recipient domains and email addresses that the receipt rule applies to. @@ -113,14 +113,16 @@ export class ReceiptRule extends Resource implements IReceiptRule { private readonly renderedActions = new Array(); constructor(scope: Construct, id: string, props: ReceiptRuleProps) { - super(scope, id); + super(scope, id, { + physicalName: props.receiptRuleName, + }); const resource = new CfnReceiptRule(this, 'Resource', { after: props.after ? props.after.receiptRuleName : undefined, rule: { actions: Lazy.anyValue({ produce: () => this.getRenderedActions() }), enabled: props.enabled === undefined ? true : props.enabled, - name: props.name, + name: this.physicalName.value, recipients: props.recipients, scanEnabled: props.scanEnabled, tlsPolicy: props.tlsPolicy diff --git a/packages/@aws-cdk/aws-ses/test/integ.receipt.ts b/packages/@aws-cdk/aws-ses/test/integ.receipt.ts index ea05d0a221cf0..42538689eb8b1 100644 --- a/packages/@aws-cdk/aws-ses/test/integ.receipt.ts +++ b/packages/@aws-cdk/aws-ses/test/integ.receipt.ts @@ -47,7 +47,7 @@ const firstRule = ruleSet.addRule('FirstRule', { topic }) ], - name: 'FirstRule', + receiptRuleName: cdk.PhysicalName.of('FirstRule'), recipients: ['cdk-ses-receipt-test@yopmail.com'], scanEnabled: true, tlsPolicy: ses.TlsPolicy.Require, diff --git a/packages/@aws-cdk/aws-ses/test/test.receipt-filter.ts b/packages/@aws-cdk/aws-ses/test/test.receipt-filter.ts index b0d40052a3534..a0ede1b4409f8 100644 --- a/packages/@aws-cdk/aws-ses/test/test.receipt-filter.ts +++ b/packages/@aws-cdk/aws-ses/test/test.receipt-filter.ts @@ -1,5 +1,5 @@ import { expect } from '@aws-cdk/assert'; -import { Stack } from '@aws-cdk/cdk'; +import { PhysicalName, Stack } from '@aws-cdk/cdk'; import { Test } from 'nodeunit'; import { ReceiptFilter, ReceiptFilterPolicy, WhiteListReceiptFilter } from '../lib'; @@ -13,7 +13,7 @@ export = { // WHEN new ReceiptFilter(stack, 'Filter', { ip: '1.2.3.4/16', - name: 'MyFilter', + receiptFilterName: PhysicalName.of('MyFilter'), policy: ReceiptFilterPolicy.Block }); diff --git a/packages/@aws-cdk/aws-ses/test/test.receipt-rule-set.ts b/packages/@aws-cdk/aws-ses/test/test.receipt-rule-set.ts index 56707a519b810..55de235c8f73b 100644 --- a/packages/@aws-cdk/aws-ses/test/test.receipt-rule-set.ts +++ b/packages/@aws-cdk/aws-ses/test/test.receipt-rule-set.ts @@ -1,5 +1,5 @@ import { expect, haveResource } from '@aws-cdk/assert'; -import { Stack } from '@aws-cdk/cdk'; +import { PhysicalName, Stack } from '@aws-cdk/cdk'; import { Test } from 'nodeunit'; import { ReceiptRuleSet } from '../lib'; @@ -12,7 +12,7 @@ export = { // WHEN new ReceiptRuleSet(stack, 'RuleSet', { - name: 'MyRuleSet' + receiptRuleSetName: PhysicalName.of('MyRuleSet'), }); // THEN diff --git a/packages/@aws-cdk/aws-ses/test/test.receipt-rule.ts b/packages/@aws-cdk/aws-ses/test/test.receipt-rule.ts index 38854b1a2d725..77624bad27253 100644 --- a/packages/@aws-cdk/aws-ses/test/test.receipt-rule.ts +++ b/packages/@aws-cdk/aws-ses/test/test.receipt-rule.ts @@ -1,5 +1,5 @@ import { expect } from '@aws-cdk/assert'; -import { Stack } from '@aws-cdk/cdk'; +import { PhysicalName, Stack } from '@aws-cdk/cdk'; import { Test } from 'nodeunit'; import { ReceiptRule, ReceiptRuleSet, TlsPolicy } from '../lib'; @@ -14,11 +14,11 @@ export = { new ReceiptRuleSet(stack, 'RuleSet', { rules: [ { - name: 'FirstRule', + receiptRuleName: PhysicalName.of('FirstRule'), }, { enabled: false, - name: 'SecondRule', + receiptRuleName: PhysicalName.of('SecondRule'), recipients: ['hello@aws.com'], scanEnabled: true, tlsPolicy: TlsPolicy.Require diff --git a/packages/@aws-cdk/aws-sns-subscriptions/test/subs.test.ts b/packages/@aws-cdk/aws-sns-subscriptions/test/subs.test.ts index fd9ae3f04c05e..6eaa4790a4d73 100644 --- a/packages/@aws-cdk/aws-sns-subscriptions/test/subs.test.ts +++ b/packages/@aws-cdk/aws-sns-subscriptions/test/subs.test.ts @@ -2,7 +2,7 @@ import '@aws-cdk/assert/jest'; import lambda = require('@aws-cdk/aws-lambda'); import sns = require('@aws-cdk/aws-sns'); import sqs = require('@aws-cdk/aws-sqs'); -import { Stack } from '@aws-cdk/cdk'; +import { PhysicalName, Stack } from '@aws-cdk/cdk'; import subs = require('../lib'); // tslint:disable:object-literal-key-quotes @@ -13,7 +13,7 @@ let topic: sns.Topic; beforeEach(() => { stack = new Stack(); topic = new sns.Topic(stack, 'MyTopic', { - topicName: 'topicName', + topicName: PhysicalName.of('topicName'), displayName: 'displayName' }); }); diff --git a/packages/@aws-cdk/aws-sns/lib/topic.ts b/packages/@aws-cdk/aws-sns/lib/topic.ts index 6747712cbc042..f8a9e8d23c7c9 100644 --- a/packages/@aws-cdk/aws-sns/lib/topic.ts +++ b/packages/@aws-cdk/aws-sns/lib/topic.ts @@ -1,4 +1,4 @@ -import { Construct, Stack } from '@aws-cdk/cdk'; +import { Construct, PhysicalName, ResourceIdentifiers, Stack } from '@aws-cdk/cdk'; import { CfnTopic } from './sns.generated'; import { ITopic, TopicBase } from './topic-base'; @@ -22,7 +22,7 @@ export interface TopicProps { * * @default Generated name */ - readonly topicName?: string; + readonly topicName?: PhysicalName; } /** @@ -46,14 +46,24 @@ export class Topic extends TopicBase { protected readonly autoCreatePolicy: boolean = true; constructor(scope: Construct, id: string, props: TopicProps = {}) { - super(scope, id); + super(scope, id, { + physicalName: props.topicName, + }); const resource = new CfnTopic(this, 'Resource', { displayName: props.displayName, - topicName: props.topicName + topicName: this.physicalName.value, }); - this.topicArn = resource.refAsString; - this.topicName = resource.attrTopicName; + const resourceIdentifiers = new ResourceIdentifiers(this, { + arn: resource.refAsString, + name: resource.attrTopicName, + arnComponents: { + service: 'sns', + resource: this.physicalName.value || '', + }, + }); + this.topicArn = resourceIdentifiers.arn; + this.topicName = resourceIdentifiers.name; } } diff --git a/packages/@aws-cdk/aws-sns/package.json b/packages/@aws-cdk/aws-sns/package.json index a4dc3120e4f61..3f6da9eed7cc6 100644 --- a/packages/@aws-cdk/aws-sns/package.json +++ b/packages/@aws-cdk/aws-sns/package.json @@ -92,7 +92,9 @@ "awslint": { "exclude": [ "construct-base-is-private:@aws-cdk/aws-sns.TopicBase", - "integ-return-type:@aws-cdk/aws-sns.ITopicSubscription.bind" + "integ-return-type:@aws-cdk/aws-sns.ITopicSubscription.bind", + "props-physical-name:@aws-cdk/aws-sns.SubscriptionProps", + "props-physical-name:@aws-cdk/aws-sns.TopicPolicyProps" ] }, "stability": "experimental" diff --git a/packages/@aws-cdk/aws-sns/test/integ.sns.ts b/packages/@aws-cdk/aws-sns/test/integ.sns.ts index d6aaa9939ce4d..085dbdf335628 100644 --- a/packages/@aws-cdk/aws-sns/test/integ.sns.ts +++ b/packages/@aws-cdk/aws-sns/test/integ.sns.ts @@ -1,4 +1,4 @@ -import { App, Stack, StackProps } from '@aws-cdk/cdk'; +import { App, PhysicalName, Stack, StackProps } from '@aws-cdk/cdk'; import { Topic } from '../lib'; class SNSInteg extends Stack { @@ -6,7 +6,7 @@ class SNSInteg extends Stack { super(scope, id, props); new Topic(this, 'MyTopic', { - topicName: 'fooTopic', + topicName: PhysicalName.of('fooTopic'), displayName: 'fooDisplayName' }); } diff --git a/packages/@aws-cdk/aws-sns/test/test.sns.ts b/packages/@aws-cdk/aws-sns/test/test.sns.ts index 1ba0eb9365373..0c727f0cf6769 100644 --- a/packages/@aws-cdk/aws-sns/test/test.sns.ts +++ b/packages/@aws-cdk/aws-sns/test/test.sns.ts @@ -27,7 +27,7 @@ export = { const stack = new cdk.Stack(); new sns.Topic(stack, 'MyTopic', { - topicName: 'topicName' + topicName: cdk.PhysicalName.of('topicName'), }); expect(stack).toMatch({ @@ -69,7 +69,7 @@ export = { const stack = new cdk.Stack(); new sns.Topic(stack, 'MyTopic', { - topicName: 'topicName', + topicName: cdk.PhysicalName.of('topicName'), displayName: 'displayName' }); diff --git a/packages/@aws-cdk/aws-sqs/lib/queue.ts b/packages/@aws-cdk/aws-sqs/lib/queue.ts index 6aa609c351ab0..9d9fa28f38e17 100644 --- a/packages/@aws-cdk/aws-sqs/lib/queue.ts +++ b/packages/@aws-cdk/aws-sqs/lib/queue.ts @@ -1,5 +1,5 @@ import kms = require('@aws-cdk/aws-kms'); -import { Construct, Stack } from '@aws-cdk/cdk'; +import { Construct, PhysicalName, ResourceIdentifiers, Stack } from '@aws-cdk/cdk'; import { IQueue, QueueAttributes, QueueBase } from './queue-base'; import { CfnQueue } from './sqs.generated'; import { validateProps } from './validate-props'; @@ -15,7 +15,7 @@ export interface QueueProps { * * @default CloudFormation-generated name */ - readonly queueName?: string; + readonly queueName?: PhysicalName; /** * The number of seconds that Amazon SQS retains a message. @@ -230,7 +230,9 @@ export class Queue extends QueueBase { protected readonly autoCreatePolicy = true; constructor(scope: Construct, id: string, props: QueueProps = {}) { - super(scope, id); + super(scope, id, { + physicalName: props.queueName, + }); validateProps(props); @@ -244,7 +246,7 @@ export class Queue extends QueueBase { const { encryptionMasterKey, encryptionProps } = _determineEncryptionProps.call(this); const queue = new CfnQueue(this, 'Resource', { - queueName: props.queueName, + queueName: this.physicalName.value, ...this.determineFifoProps(props), ...encryptionProps, redrivePolicy, @@ -254,9 +256,18 @@ export class Queue extends QueueBase { receiveMessageWaitTimeSeconds: props.receiveMessageWaitTimeSec, visibilityTimeout: props.visibilityTimeoutSec, }); + + const resourceIdentifiers = new ResourceIdentifiers(this, { + arn: queue.attrArn, + name: queue.attrQueueName, + arnComponents: { + service: 'sqs', + resource: this.physicalName.value || '', + }, + }); + this.queueArn = resourceIdentifiers.arn; + this.queueName = resourceIdentifiers.name; this.encryptionMasterKey = encryptionMasterKey; - this.queueArn = queue.attrArn; - this.queueName = queue.attrQueueName; this.queueUrl = queue.refAsString; function _determineEncryptionProps(this: Queue): { encryptionProps: EncryptionProps, encryptionMasterKey?: kms.IKey } { @@ -306,15 +317,16 @@ export class Queue extends QueueBase { private determineFifoProps(props: QueueProps): FifoProps { // Check if any of the signals that we have say that this is a FIFO queue. let fifoQueue = props.fifo; - if (typeof fifoQueue === 'undefined' && typeof props.queueName === 'string' && props.queueName.endsWith('.fifo')) { fifoQueue = true; } + const queueName = props.queueName && props.queueName.value; + if (typeof fifoQueue === 'undefined' && typeof queueName === 'string' && queueName.endsWith('.fifo')) { fifoQueue = true; } if (typeof fifoQueue === 'undefined' && props.contentBasedDeduplication) { fifoQueue = true; } // If we have a name, see that it agrees with the FIFO setting - if (typeof props.queueName === 'string') { - if (fifoQueue && !props.queueName.endsWith('.fifo')) { + if (typeof queueName === 'string') { + if (fifoQueue && !queueName.endsWith('.fifo')) { throw new Error("FIFO queue names must end in '.fifo'"); } - if (!fifoQueue && props.queueName.endsWith('.fifo')) { + if (!fifoQueue && queueName.endsWith('.fifo')) { throw new Error("Non-FIFO queue name may not end in '.fifo'"); } } diff --git a/packages/@aws-cdk/aws-sqs/package.json b/packages/@aws-cdk/aws-sqs/package.json index 67b21080a5d0d..5b0e9ec663950 100644 --- a/packages/@aws-cdk/aws-sqs/package.json +++ b/packages/@aws-cdk/aws-sqs/package.json @@ -89,8 +89,9 @@ }, "awslint": { "exclude": [ - "construct-base-is-private:@aws-cdk/aws-sqs.QueueBase" + "construct-base-is-private:@aws-cdk/aws-sqs.QueueBase", + "props-physical-name:@aws-cdk/aws-sqs.QueuePolicyProps" ] }, "stability": "experimental" -} \ No newline at end of file +} diff --git a/packages/@aws-cdk/aws-ssm/lib/parameter.ts b/packages/@aws-cdk/aws-ssm/lib/parameter.ts index ccd405abef105..f5706cd2d703d 100644 --- a/packages/@aws-cdk/aws-ssm/lib/parameter.ts +++ b/packages/@aws-cdk/aws-ssm/lib/parameter.ts @@ -1,7 +1,8 @@ import iam = require('@aws-cdk/aws-iam'); import { CfnDynamicReference, CfnDynamicReferenceService, CfnParameter, - Construct, ContextProvider, Fn, IResource, Resource, Stack, Token } from '@aws-cdk/cdk'; + Construct, ContextProvider, Fn, IConstruct, IResource, PhysicalName, Resource, ResourceIdentifiers, Stack, Token +} from '@aws-cdk/cdk'; import cxapi = require('@aws-cdk/cx-api'); import ssm = require('./ssm.generated'); @@ -91,7 +92,7 @@ export interface ParameterOptions { * * @default - a name will be generated by CloudFormation */ - readonly parameterName?: string; + readonly parameterName?: PhysicalName; } /** @@ -118,18 +119,10 @@ export interface StringListParameterProps extends ParameterOptions { * Basic features shared across all types of SSM Parameters. */ abstract class ParameterBase extends Resource implements IParameter { + public abstract readonly parameterArn: string; public abstract readonly parameterName: string; public abstract readonly parameterType: string; - public get parameterArn(): string { - return Stack.of(this).formatArn({ - service: 'ssm', - resource: 'parameter', - sep: '', // Sep is empty because this.parameterName starts with a / already! - resourceName: this.parameterName, - }); - } - public grantRead(grantee: iam.IGrantable): iam.Grant { return iam.Grant.addToPrincipal({ grantee, @@ -204,6 +197,7 @@ export class StringParameter extends ParameterBase implements IStringParameter { class Import extends ParameterBase { public readonly parameterName = attrs.parameterName; + public readonly parameterArn = arnForParameterName(this, this.parameterName); public readonly parameterType = STRING_PARAM_TYPE; public readonly stringValue = stringValue; } @@ -219,6 +213,7 @@ export class StringParameter extends ParameterBase implements IStringParameter { class Import extends ParameterBase { public readonly parameterName = attrs.parameterName; + public readonly parameterArn = arnForParameterName(this, this.parameterName); public readonly parameterType = SECURE_STRING_PARAM_TYPE; public readonly stringValue = stringValue; } @@ -273,12 +268,15 @@ export class StringParameter extends ParameterBase implements IStringParameter { return this.fromSecureStringParameterAttributes(stack, id, { parameterName, version }).stringValue; } + public readonly parameterArn: string; public readonly parameterName: string; public readonly parameterType: string; public readonly stringValue: string; constructor(scope: Construct, id: string, props: StringParameterProps) { - super(scope, id); + super(scope, id, { + physicalName: props.parameterName, + }); if (props.allowedPattern) { _assertValidValue(props.stringValue, props.allowedPattern); @@ -287,12 +285,24 @@ export class StringParameter extends ParameterBase implements IStringParameter { const resource = new ssm.CfnParameter(this, 'Resource', { allowedPattern: props.allowedPattern, description: props.description, - name: props.parameterName, + name: this.physicalName.value, type: STRING_PARAM_TYPE, value: props.stringValue, }); - this.parameterName = resource.refAsString; + const resourceIdentifiers = new ResourceIdentifiers(this, { + arn: arnForParameterName(this, resource.refAsString), + name: resource.refAsString, + arnComponents: { + service: 'ssm', + resource: 'parameter', + resourceName: this.physicalName.value, + sep: '', // `sep` is empty because parameterName starts with a / already! + }, + }); + this.parameterArn = resourceIdentifiers.arn; + this.parameterName = resourceIdentifiers.name; + this.parameterType = resource.attrType; this.stringValue = resource.attrValue; } @@ -310,6 +320,7 @@ export class StringListParameter extends ParameterBase implements IStringListPar public static fromStringListParameterName(scope: Construct, id: string, stringListParameterName: string): IStringListParameter { class Import extends ParameterBase { public readonly parameterName = stringListParameterName; + public readonly parameterArn = arnForParameterName(this, this.parameterName); public readonly parameterType = STRINGLIST_PARAM_TYPE; public readonly stringListValue = Fn.split(',', new CfnDynamicReference(CfnDynamicReferenceService.Ssm, stringListParameterName).toString()); } @@ -317,12 +328,15 @@ export class StringListParameter extends ParameterBase implements IStringListPar return new Import(scope, id); } + public readonly parameterArn: string; public readonly parameterName: string; public readonly parameterType: string; public readonly stringListValue: string[]; constructor(scope: Construct, id: string, props: StringListParameterProps) { - super(scope, id); + super(scope, id, { + physicalName: props.parameterName, + }); if (props.stringListValue.find(str => !Token.isUnresolved(str) && str.indexOf(',') !== -1)) { throw new Error('Values of a StringList SSM Parameter cannot contain the \',\' character. Use a string parameter instead.'); @@ -335,12 +349,23 @@ export class StringListParameter extends ParameterBase implements IStringListPar const resource = new ssm.CfnParameter(this, 'Resource', { allowedPattern: props.allowedPattern, description: props.description, - name: props.parameterName, + name: this.physicalName.value, type: STRINGLIST_PARAM_TYPE, value: props.stringListValue.join(','), }); + const resourceIdentifiers = new ResourceIdentifiers(this, { + arn: arnForParameterName(this, resource.refAsString), + name: resource.refAsString, + arnComponents: { + service: 'ssm', + resource: 'parameter', + resourceName: this.physicalName.value, + sep: '', // `sep` is empty because parameterName starts with a / already! + }, + }); + this.parameterArn = resourceIdentifiers.arn; + this.parameterName = resourceIdentifiers.name; - this.parameterName = resource.refAsString; this.parameterType = resource.attrType; this.stringListValue = Fn.split(',', resource.attrValue); } @@ -367,4 +392,13 @@ function _assertValidValue(value: string, allowedPattern: string): void { function makeIdentityForImportedValue(parameterName: string) { return `SsmParameterValue:${parameterName}:C96584B6-F00A-464E-AD19-53AFF4B05118`; -} \ No newline at end of file +} + +function arnForParameterName(scope: IConstruct, parameterName: string): string { + return Stack.of(scope).formatArn({ + service: 'ssm', + resource: 'parameter', + sep: '', // Sep is empty because this.parameterName starts with a / already! + resourceName: parameterName, + }); +} diff --git a/packages/@aws-cdk/aws-ssm/package.json b/packages/@aws-cdk/aws-ssm/package.json index 892048ea63fbd..1ee2da44c6fcb 100644 --- a/packages/@aws-cdk/aws-ssm/package.json +++ b/packages/@aws-cdk/aws-ssm/package.json @@ -89,8 +89,10 @@ "import-props-interface:@aws-cdk/aws-ssm.ParameterImportProps", "resource-attribute:@aws-cdk/aws-ssm.IParameter.parameterValue", "from-attributes:fromStringParameterAttributes", - "from-attributes:fromStringListParameterAttributes" + "from-attributes:fromStringListParameterAttributes", + "props-physical-name:@aws-cdk/aws-ssm.StringListParameterProps", + "props-physical-name:@aws-cdk/aws-ssm.StringParameterProps" ] }, "stability": "experimental" -} \ No newline at end of file +} diff --git a/packages/@aws-cdk/aws-ssm/test/integ.parameter-store-string.lit.ts b/packages/@aws-cdk/aws-ssm/test/integ.parameter-store-string.lit.ts index 9066582ff5513..8fc3ad64294cd 100644 --- a/packages/@aws-cdk/aws-ssm/test/integ.parameter-store-string.lit.ts +++ b/packages/@aws-cdk/aws-ssm/test/integ.parameter-store-string.lit.ts @@ -7,7 +7,7 @@ class CreatingStack extends cdk.Stack { super(scope, id); new ssm.StringParameter(this, 'String', { - parameterName: '/My/Public/Parameter', + parameterName: cdk.PhysicalName.of('/My/Public/Parameter'), stringValue: 'abcdef' }); } diff --git a/packages/@aws-cdk/aws-ssm/test/test.parameter.ts b/packages/@aws-cdk/aws-ssm/test/test.parameter.ts index 95fcfa71fa285..e9ba5d5c4a851 100644 --- a/packages/@aws-cdk/aws-ssm/test/test.parameter.ts +++ b/packages/@aws-cdk/aws-ssm/test/test.parameter.ts @@ -13,7 +13,7 @@ export = { new ssm.StringParameter(stack, 'Parameter', { allowedPattern: '.*', description: 'The value Foo', - parameterName: 'FooParameter', + parameterName: cdk.PhysicalName.of('FooParameter'), stringValue: 'Foo', }); @@ -60,7 +60,7 @@ export = { new ssm.StringListParameter(stack, 'Parameter', { allowedPattern: '(Foo|Bar)', description: 'The values Foo and Bar', - parameterName: 'FooParameter', + parameterName: cdk.PhysicalName.of('FooParameter'), stringListValue: ['Foo', 'Bar'], }); diff --git a/packages/@aws-cdk/aws-stepfunctions/lib/activity.ts b/packages/@aws-cdk/aws-stepfunctions/lib/activity.ts index 3e41384cb734e..b1fea496569d1 100644 --- a/packages/@aws-cdk/aws-stepfunctions/lib/activity.ts +++ b/packages/@aws-cdk/aws-stepfunctions/lib/activity.ts @@ -1,5 +1,5 @@ import cloudwatch = require('@aws-cdk/aws-cloudwatch'); -import { Construct, IResource, Resource, Stack } from '@aws-cdk/cdk'; +import { Construct, IResource, Lazy, PhysicalName, Resource, ResourceIdentifiers, Stack } from '@aws-cdk/cdk'; import { CfnActivity } from './stepfunctions.generated'; export interface ActivityProps { @@ -8,7 +8,7 @@ export interface ActivityProps { * * @default If not supplied, a name is generated */ - readonly activityName?: string; + readonly activityName?: PhysicalName; } /** @@ -52,14 +52,27 @@ export class Activity extends Resource implements IActivity { public readonly activityName: string; constructor(scope: Construct, id: string, props: ActivityProps = {}) { - super(scope, id); + super(scope, id, { + physicalName: props.activityName || + PhysicalName.of(Lazy.stringValue({ produce: () => this.generateName() })), + }); const resource = new CfnActivity(this, 'Resource', { - name: props.activityName || this.generateName() + name: this.physicalName.value! // not null because of above call to `super` }); - this.activityArn = resource.refAsString; - this.activityName = resource.attrName; + const resourceIdentifiers = new ResourceIdentifiers(this, { + arn: resource.refAsString, + name: resource.attrName, + arnComponents: { + service: 'states', + resource: 'activity', + resourceName: this.physicalName.value, + sep: ':', + }, + }); + this.activityArn = resourceIdentifiers.arn; + this.activityName = resourceIdentifiers.name; } /** diff --git a/packages/@aws-cdk/aws-stepfunctions/lib/state-machine.ts b/packages/@aws-cdk/aws-stepfunctions/lib/state-machine.ts index 202e235e49c41..04d0f8029caa0 100644 --- a/packages/@aws-cdk/aws-stepfunctions/lib/state-machine.ts +++ b/packages/@aws-cdk/aws-stepfunctions/lib/state-machine.ts @@ -1,6 +1,6 @@ import cloudwatch = require('@aws-cdk/aws-cloudwatch'); import iam = require('@aws-cdk/aws-iam'); -import { Construct, IResource, Resource, Stack } from '@aws-cdk/cdk'; +import { Construct, IResource, PhysicalName, Resource, ResourceIdentifiers, Stack } from '@aws-cdk/cdk'; import { StateGraph } from './state-graph'; import { CfnStateMachine } from './stepfunctions.generated'; import { IChainable } from './types'; @@ -14,7 +14,7 @@ export interface StateMachineProps { * * @default A name is automatically generated */ - readonly stateMachineName?: string; + readonly stateMachineName?: PhysicalName; /** * Definition for this state machine @@ -87,7 +87,9 @@ export class StateMachine extends StateMachineBase { public readonly stateMachineArn: string; constructor(scope: Construct, id: string, props: StateMachineProps) { - super(scope, id); + super(scope, id, { + physicalName: props.stateMachineName, + }); this.role = props.role || new iam.Role(this, 'Role', { assumedBy: new iam.ServicePrincipal(`states.${Stack.of(this).region}.amazonaws.com`), @@ -97,7 +99,7 @@ export class StateMachine extends StateMachineBase { graph.timeoutSeconds = props.timeoutSec; const resource = new CfnStateMachine(this, 'Resource', { - stateMachineName: props.stateMachineName, + stateMachineName: this.physicalName.value, roleArn: this.role.roleArn, definitionString: Stack.of(this).toJsonString(graph.toGraphJson()), }); @@ -106,8 +108,18 @@ export class StateMachine extends StateMachineBase { this.addToRolePolicy(statement); } - this.stateMachineName = resource.attrName; - this.stateMachineArn = resource.refAsString; + const resourceIdentifiers = new ResourceIdentifiers(this, { + arn: resource.refAsString, + name: resource.attrName, + arnComponents: { + service: 'states', + resource: 'stateMachine', + resourceName: this.physicalName.value, + sep: ':', + }, + }); + this.stateMachineName = resourceIdentifiers.name; + this.stateMachineArn = resourceIdentifiers.arn; } /** diff --git a/packages/decdk/examples/pipeline.json b/packages/decdk/examples/pipeline.json index 8c0c2ee5d315f..f7c99028ba810 100644 --- a/packages/decdk/examples/pipeline.json +++ b/packages/decdk/examples/pipeline.json @@ -4,7 +4,11 @@ "Repo": { "Type": "@aws-cdk/aws-codecommit.Repository", "Properties": { - "repositoryName": "my-first-decdk-repo" + "repositoryName": { + "of": { + "name": "my-first-decdk-repo" + } + } } }, "Key": { diff --git a/tools/awslint/README.md b/tools/awslint/README.md index fc5511dbe593c..585a2b79a86ed 100644 --- a/tools/awslint/README.md +++ b/tools/awslint/README.md @@ -16,19 +16,19 @@ For example: ```console $ cd @aws-cdk/aws-sns -$ awslint -@aws-cdk/aws-sns.ISubscription -- warning: every resource must have a resource interface [resource-interface] -@aws-cdk/aws-sns.ITopicPolicy -- warning: every resource must have a resource interface [resource-interface] +$ npm run awslint +warning: [awslint:resource-interface:@aws-cdk/aws-sns.Subscription] every resource must have a resource interface +warning: [awslint:resource-interface:@aws-cdk/aws-sns.TopicPolicy] every resource must have a resource interface ``` Each diagnostics includes the following elements: ``` -@aws-cdk/aws-sns.ISubscription -- warning: every resource must have a resource interface [resource-interface] -[----------------------------] [------] [-------------------------------------------] [------------------] - ^ ^ ^ ^ - | | | | - scope level message rule +warning: [awslint:resource-interface:@aws-cdk/aws-sns.Subscription] every resource must have a resource interface +[------] [-------------------------] [----------------------------] [-------------------------------------------] + ^ ^ ^ ^ + | | | | + level rule scope message ``` ## Options and Configuration @@ -46,17 +46,17 @@ via an `awslint` key in the module's `package.json`. ## Include/Exclude -The `--include` and `--exclude` options can be used to specify which rules will +The `i`/`--include` and `-x`/`--exclude` options can be used to specify which rules will be evaluated. For example: ```console # evaluate only the "resource-props" and "import" rules in all scopes -$ awslint -i resource-props -i import +$ npm run awslint -- -i resource-props -i import # evaluate only the "import" rule in all scopes besides ones that begin with "@aws-cdk/aws-s3" -$ awslint -i import -x "*:@aws-cdk/aws-s3*" +$ npm run awslint -- -i import -x "*:@aws-cdk/aws-s3*" ``` @@ -88,7 +88,7 @@ in the module's `package.json` file. This is useful to bootstrap the linting pro progressively fix errors. ```console -$ awslint --save +$ npm run awslint -- --save [shows errors] $ cat package.json @@ -102,7 +102,7 @@ $ cat package.json } } -$ awslint +$ npm run awslint [no errors] ``` @@ -118,7 +118,7 @@ If `--save` is specified, `awslint` will always exit with status code 0. To list all linter rules: ```console -$ awslint list +$ npm run awslint list module-name: module name must be @aws-cdk/aws- construct-ctor: signature of all construct constructors should be "scope, id, props" resource-class: every resource must have a resource class (L2) diff --git a/tools/awslint/lib/rules/construct.ts b/tools/awslint/lib/rules/construct.ts index e615c940460ab..2fa245f3df618 100644 --- a/tools/awslint/lib/rules/construct.ts +++ b/tools/awslint/lib/rules/construct.ts @@ -1,6 +1,7 @@ import reflect = require('jsii-reflect'); import { Linter, MethodSignatureParameterExpectation } from '../linter'; import { CoreTypes } from './core-types'; +import { ResourceReflection } from './resource'; export const constructLinter = new Linter(assembly => assembly.classes .filter(t => CoreTypes.isConstructClass(t)) @@ -293,3 +294,29 @@ constructLinter.add({ } } }); + +constructLinter.add({ + code: 'props-physical-name', + message: "Every Resource must have a single physical name construction property, " + + "with a name that is an ending substring of Name", + eval: e => { + if (!e.ctx.propsType) { return; } + if (!e.ctx.hasPropsArgument) { return; } + + // this rule only applies to Resources + if (!CoreTypes.isResourceClass(e.ctx.classType)) { return; } + + const physicalNameProps = e.ctx.propsType.allProperties.filter(p => p.type.toString() === '@aws-cdk/cdk.PhysicalName'); + if (physicalNameProps.length !== 1) { + e.assert(false, `${e.ctx.propsFqn}`); + } else { + const physicalNameProp = physicalNameProps[0]; + + // check the name of the property + const resourceName = new ResourceReflection(e.ctx).cfn.fullname.split('::')[2]; + const capitalizedProp = physicalNameProp.name[0].toUpperCase() + physicalNameProp.name.slice(1); + + e.assert(`${resourceName}Name`.endsWith(capitalizedProp), `${e.ctx.propsFqn}.${physicalNameProp.name}`); + } + }, +});