From f0d8127e085fa36b8422e3a9acf2c94c02b678a3 Mon Sep 17 00:00:00 2001 From: Adam Ruka Date: Mon, 17 Jun 2019 00:37:43 -0700 Subject: [PATCH] refactor: physical names in the entire Construct Library (#2878) This PR changes the `xyzName` attribute of all Resources in the Construct Library to be of type PhysicalName, and adds an AWS linter rule that checks ensures conformance. The name of the property can be any ending substring of the base name of the class with the `Name` suffix - for example, if my resource is `AwesomeFoobar`, the name can be `foobarName` or `awesomeFoobarName` (that's because we often have `Specialized1AwesomeFoobar` and `Specialized2AwesomeFoobar` in our library, that share a base prop interface). There were a few interesting cases in the library which I didn't change, as I wasn't sure of the semantics of the resources, or they would require class name changes. Would appreciate some guidance there. These were: * @aws-kms: AliasProps.name * @aws-ssm: ParameterOptions.name * @aws-applicationautoscaling: ScalableTargetProps.resourceId * @aws-route53: CommonHostedZoneProps.zoneName * @aws-route53: RecordSetOptions.recordName * @aws-apigateway: StageProps.stageName * @aws-servicediscovery: BaseNamespaceProps.name * @aws-config: CustomRuleProps.ruleName * @aws-config: ManagedRuleProps.ruleName * @aws-config: AccessKeysRotatedProps.ruleName * @aws-config: CloudFormationStackNotificationCheckProps.ruleName * @aws-config: CloudFormationStackDriftDetectionCheckProps.ruleName * @aws-rds: DatabaseClusterProps.databaseName / clusterIdentifier * @aws-rds: DatabaseInstanceSourceProps.databaseName BREAKING CHANGE: all resourceName attributes have their type changed from string to cdk.PhysicalName. --- .../@aws-cdk/aws-apigateway/lib/restapi.ts | 10 ++- .../@aws-cdk/aws-apigateway/lib/vpc-link.ts | 12 +-- packages/@aws-cdk/aws-apigateway/package.json | 11 ++- .../aws-apigateway/test/test.lambda-api.ts | 2 +- .../aws-apigateway/test/test.restapi.ts | 4 +- .../aws-apigateway/test/test.vpc-link.ts | 4 +- .../aws-applicationautoscaling/package.json | 7 +- .../aws-autoscaling/lib/lifecycle-hook.ts | 10 ++- .../@aws-cdk/aws-autoscaling/package.json | 6 +- .../aws-certificatemanager/package.json | 8 +- .../@aws-cdk/aws-cloudformation/package.json | 5 +- packages/@aws-cdk/aws-cloudtrail/lib/index.ts | 21 ++++-- packages/@aws-cdk/aws-cloudwatch/lib/alarm.ts | 23 ++++-- .../@aws-cdk/aws-cloudwatch/lib/dashboard.ts | 10 ++- .../@aws-cdk/aws-cloudwatch/lib/metric.ts | 2 +- .../test/integ.alarm-and-dashboard.ts | 2 +- .../aws-cloudwatch/test/test.dashboard.ts | 4 +- .../@aws-cdk/aws-codebuild/lib/project.ts | 2 +- packages/@aws-cdk/aws-codebuild/package.json | 2 +- .../aws-codebuild/test/integ.project-vpc.ts | 2 +- .../aws-codebuild/test/test.codebuild.ts | 18 +++-- .../@aws-cdk/aws-codecommit/lib/repository.ts | 30 +++++--- .../test/integ.codecommit-events.ts | 4 +- .../aws-codecommit/test/test.codecommit.ts | 9 ++- .../aws-codedeploy/lib/lambda/application.ts | 24 ++++-- .../lib/lambda/deployment-group.ts | 22 ++++-- .../aws-codedeploy/lib/server/application.ts | 24 ++++-- .../lib/server/deployment-config.ts | 8 +- .../lib/server/deployment-group.ts | 28 +++++-- packages/@aws-cdk/aws-codedeploy/package.json | 2 +- .../test/lambda/integ.deployment-group.ts | 2 +- .../test/lambda/test.application.ts | 2 +- .../test/lambda/test.deployment-group.ts | 4 +- .../test.cloudformation-pipeline-actions.ts | 8 +- .../test/integ.cfn-template-from-repo.lit.ts | 2 +- ...ambda-deployed-through-codepipeline.lit.ts | 8 +- ...line-code-build-multiple-inputs-outputs.ts | 2 +- .../test/integ.pipeline-code-commit-build.ts | 2 +- .../test/integ.pipeline-code-commit.ts | 4 +- .../test/integ.pipeline-code-deploy.ts | 4 +- .../test/integ.pipeline-events.ts | 2 +- .../test/test.pipeline.ts | 6 +- .../@aws-cdk/aws-codepipeline/lib/pipeline.ts | 22 ++++-- .../aws-cognito/lib/user-pool-client.ts | 12 +-- .../@aws-cdk/aws-cognito/lib/user-pool.ts | 27 +++++-- packages/@aws-cdk/aws-cognito/package.json | 2 +- .../aws-cognito/test/test.user-pool.ts | 4 +- packages/@aws-cdk/aws-config/lib/rule.ts | 16 ++-- packages/@aws-cdk/aws-config/package.json | 11 ++- .../@aws-cdk/aws-config/test/test.rule.ts | 4 +- .../lib/aws-dynamodb-global.ts | 2 +- .../lib/global-table-coordinator.ts | 2 +- .../test/integ.dynamodb.global.ts | 4 +- .../test/test.dynamodb.global.ts | 10 +-- packages/@aws-cdk/aws-dynamodb/lib/table.ts | 23 ++++-- .../aws-dynamodb/test/test.dynamodb.ts | 18 ++--- .../@aws-cdk/aws-ec2/lib/security-group.ts | 14 ++-- packages/@aws-cdk/aws-ec2/package.json | 10 ++- packages/@aws-cdk/aws-ecr/lib/repository.ts | 23 ++++-- .../fargate/load-balanced-fargate-service.ts | 6 +- .../ec2/test.queue-processing-ecs-service.ts | 6 +- .../test.load-balanced-fargate-service.ts | 4 +- .../test.queue-processing-fargate-service.ts | 4 +- .../@aws-cdk/aws-ecs/lib/base/base-service.ts | 28 +++++-- packages/@aws-cdk/aws-ecs/lib/cluster.ts | 26 +++++-- packages/@aws-cdk/aws-ecs/package.json | 9 ++- packages/@aws-cdk/aws-eks/lib/cluster.ts | 24 ++++-- .../aws-elasticloadbalancing/package.json | 7 +- .../lib/shared/base-load-balancer.ts | 10 ++- .../aws-elasticloadbalancingv2/package.json | 6 +- .../test/alb/test.load-balancer.ts | 4 +- .../test/nlb/test.load-balancer.ts | 2 +- .../test/codebuild/integ.project-events.ts | 4 +- .../integ.pipeline-event-target.ts | 4 +- .../aws-events/lib/on-event-options.ts | 5 +- packages/@aws-cdk/aws-events/lib/rule.ts | 23 ++++-- .../@aws-cdk/aws-events/test/test.rule.ts | 2 +- packages/@aws-cdk/aws-glue/lib/database.ts | 34 ++++++--- packages/@aws-cdk/aws-glue/lib/table.ts | 31 +++++--- .../@aws-cdk/aws-glue/test/integ.table.ts | 6 +- .../@aws-cdk/aws-glue/test/test.database.ts | 6 +- packages/@aws-cdk/aws-glue/test/test.table.ts | 74 +++++++++---------- packages/@aws-cdk/aws-iam/lib/group.ts | 24 ++++-- packages/@aws-cdk/aws-iam/lib/policy.ts | 19 +++-- packages/@aws-cdk/aws-iam/lib/user.ts | 24 ++++-- packages/@aws-cdk/aws-iam/package.json | 2 +- .../@aws-cdk/aws-iam/test/integ.policy.ts | 4 +- packages/@aws-cdk/aws-iam/test/integ.role.ts | 4 +- packages/@aws-cdk/aws-iam/test/integ.user.ts | 4 +- .../aws-iam/test/test.escape-hatch.ts | 10 +-- packages/@aws-cdk/aws-iam/test/test.policy.ts | 12 +-- packages/@aws-cdk/aws-kinesis/lib/stream.ts | 24 ++++-- packages/@aws-cdk/aws-kms/package.json | 8 +- packages/@aws-cdk/aws-lambda/lib/alias.ts | 26 +++++-- packages/@aws-cdk/aws-lambda/lib/function.ts | 24 ++++-- packages/@aws-cdk/aws-lambda/lib/layers.ts | 11 ++- packages/@aws-cdk/aws-lambda/package.json | 6 +- .../@aws-cdk/aws-lambda/test/integ.lambda.ts | 2 +- .../@aws-cdk/aws-lambda/test/test.alias.ts | 22 +++--- .../@aws-cdk/aws-lambda/test/test.lambda.ts | 8 +- packages/@aws-cdk/aws-logs/lib/log-group.ts | 26 +++++-- packages/@aws-cdk/aws-logs/lib/log-stream.ts | 22 ++++-- packages/@aws-cdk/aws-logs/package.json | 6 +- packages/@aws-cdk/aws-rds/package.json | 12 ++- packages/@aws-cdk/aws-route53/package.json | 17 ++++- packages/@aws-cdk/aws-s3/package.json | 7 +- .../@aws-cdk/aws-secretsmanager/lib/secret.ts | 22 ++++-- .../@aws-cdk/aws-secretsmanager/package.json | 6 +- .../test/integ.secret.lit.ts | 2 +- .../aws-servicediscovery/package.json | 14 +++- .../@aws-cdk/aws-ses/lib/receipt-filter.ts | 16 ++-- .../@aws-cdk/aws-ses/lib/receipt-rule-set.ts | 12 +-- packages/@aws-cdk/aws-ses/lib/receipt-rule.ts | 10 ++- .../@aws-cdk/aws-ses/test/integ.receipt.ts | 2 +- .../aws-ses/test/test.receipt-filter.ts | 4 +- .../aws-ses/test/test.receipt-rule-set.ts | 4 +- .../aws-ses/test/test.receipt-rule.ts | 6 +- .../aws-sns-subscriptions/test/subs.test.ts | 4 +- packages/@aws-cdk/aws-sns/lib/topic.ts | 22 ++++-- packages/@aws-cdk/aws-sns/package.json | 4 +- packages/@aws-cdk/aws-sns/test/integ.sns.ts | 4 +- packages/@aws-cdk/aws-sns/test/test.sns.ts | 4 +- packages/@aws-cdk/aws-sqs/lib/queue.ts | 32 +++++--- packages/@aws-cdk/aws-sqs/package.json | 5 +- packages/@aws-cdk/aws-ssm/package.json | 6 +- .../aws-stepfunctions/lib/activity.ts | 25 +++++-- .../aws-stepfunctions/lib/state-machine.ts | 24 ++++-- packages/decdk/examples/pipeline.json | 6 +- tools/awslint/README.md | 28 +++---- tools/awslint/lib/rules/construct.ts | 28 ++++++- 130 files changed, 1019 insertions(+), 486 deletions(-) diff --git a/packages/@aws-cdk/aws-apigateway/lib/restapi.ts b/packages/@aws-cdk/aws-apigateway/lib/restapi.ts index 502cbeef35c3b..81bf542884c7b 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. @@ -204,10 +204,12 @@ export class RestApi extends Resource implements IRestApi { private readonly methods = new Array(); constructor(scope: Construct, id: string, props: RestApiProps = { }) { - super(scope, id); + super(scope, id, { + physicalName: props.restApiName, + }); const resource = new CfnRestApi(this, 'Resource', { - name: props.restApiName || id, + name: this.physicalName.value || id, 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 00a5fd3ded451..e16d7950defd4 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, Resource } from '@aws-cdk/cdk'; +import { Construct, 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 name?: string; + readonly vpcLinkName?: PhysicalName; /** * The description of the VPC link. @@ -37,14 +37,16 @@ export class VpcLink extends Resource { public readonly vpcLinkId: string; constructor(scope: Construct, id: string, props: VpcLinkProps) { - super(scope, id); + super(scope, id, { + physicalName: props.vpcLinkName, + }); const cfnResource = new CfnVpcLink(this, 'Resource', { - name: props.name || this.node.uniqueId, + name: this.physicalName.value || this.node.uniqueId, description: props.description, targetArns: props.targets.map(nlb => nlb.loadBalancerArn) }); this.vpcLinkId = cfnResource.vpcLinkId; } -} \ No newline at end of file +} diff --git a/packages/@aws-cdk/aws-apigateway/package.json b/packages/@aws-cdk/aws-apigateway/package.json index 48eac7de08339..5c8d24f2f472d 100644 --- a/packages/@aws-cdk/aws-apigateway/package.json +++ b/packages/@aws-cdk/aws-apigateway/package.json @@ -91,8 +91,15 @@ }, "awslint": { "exclude": [ - "from-method:@aws-cdk/aws-apigateway.Resource" + "from-method:@aws-cdk/aws-apigateway.Resource", + "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 7b3b7f462cacc..e524cb0b89f06 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 585dff09030c2..d058a70a73270 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', { - name: 'MyLink', + vpcLinkName: cdk.PhysicalName.of('MyLink'), targets: [nlb] }); @@ -28,4 +28,4 @@ export = { 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 09d0a2b2a73e1..5f53cb15abab6 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 5859f2b969584..548df9c16a35e 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 7bd79d8271a16..9a50eb572e5b8 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 1ddaa27eafe37..ddb7d765706cb 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 09264e012898c..7e0c4462b5e00 100644 --- a/packages/@aws-cdk/aws-cloudformation/package.json +++ b/packages/@aws-cdk/aws-cloudformation/package.json @@ -100,8 +100,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 bb69c1d10b65b..7c177f1e6dade 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 = "cloudtrail.amazonaws.com"; @@ -168,7 +170,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, @@ -178,7 +180,16 @@ export class Trail extends Resource { eventSelectors: this.eventSelectors }); - this.trailArn = trail.trailArn; + const resourceIdentifiers = new ResourceIdentifiers(this, { + arn: trail.trailArn, + name: trail.trailName, + arnComponents: { + service: 'cloudtrail', + resource: 'trail', + resourceName: this.physicalName.value, + }, + }); + this.trailArn = resourceIdentifiers.arn; this.trailSnsTopicArn = trail.trailSnsTopicArn; 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 52097fc45b6e6..6355f8506366a 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'; @@ -114,14 +114,16 @@ 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; const alarm = new CfnAlarm(this, 'Resource', { // Meta alarmDescription: props.alarmDescription, - alarmName: props.alarmName, + alarmName: this.physicalName.value, // Evaluation comparisonOperator, @@ -141,8 +143,19 @@ export class Alarm extends Resource implements IAlarm { ...metricJson(props.metric) }); - this.alarmArn = alarm.alarmArn; - this.alarmName = alarm.alarmName; + const resourceIdentifiers = new ResourceIdentifiers(this, { + arn: alarm.alarmArn, + name: alarm.alarmName, + 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 diff --git a/packages/@aws-cdk/aws-cloudwatch/lib/dashboard.ts b/packages/@aws-cdk/aws-cloudwatch/lib/dashboard.ts index 4d7aae6e3f4b7..4a7612d1a113a 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. @@ -54,10 +54,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 && props.dashboardName, + }); new CfnDashboard(this, 'Resource', { - dashboardName: (props && props.dashboardName) || undefined, + 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 da81e2022beda..49662e40ed6c3 100644 --- a/packages/@aws-cdk/aws-cloudwatch/lib/metric.ts +++ b/packages/@aws-cdk/aws-cloudwatch/lib/metric.ts @@ -327,7 +327,7 @@ export interface MetricAlarmProps { * * @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 8f62afdb4bd9e..d17ff363000dd 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.newAlarm(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 ca2c46f0b018d..4ded3ce1216c5 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 db24bd78f3277..f6520e508ec31 100644 --- a/packages/@aws-cdk/aws-codebuild/lib/project.ts +++ b/packages/@aws-cdk/aws-codebuild/lib/project.ts @@ -745,7 +745,7 @@ export class Project extends ProjectBase { public addToRoleInlinePolicy(statement: iam.PolicyStatement) { if (this.role) { const policy = new iam.Policy(this, 'PolicyDocument', { - policyName: 'CodeBuildEC2Policy', + policyName: PhysicalName.of('CodeBuildEC2Policy'), statements: [statement] }); this.role.attachInlinePolicy(policy); diff --git a/packages/@aws-cdk/aws-codebuild/package.json b/packages/@aws-cdk/aws-codebuild/package.json index c18b6e2a9eb64..2a412f125f1c7 100644 --- a/packages/@aws-cdk/aws-codebuild/package.json +++ b/packages/@aws-cdk/aws-codebuild/package.json @@ -112,4 +112,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 9d118d04ed81b..870a63f9fdefa 100644 --- a/packages/@aws-cdk/aws-codebuild/test/integ.project-vpc.ts +++ b/packages/@aws-cdk/aws-codebuild/test/integ.project-vpc.ts @@ -14,7 +14,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 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 c71fc625ac0b0..af04ffdd32cf5 100644 --- a/packages/@aws-cdk/aws-codebuild/test/test.codebuild.ts +++ b/packages/@aws-cdk/aws-codebuild/test/test.codebuild.ts @@ -142,7 +142,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 = new codebuild.CodeCommitSource({ repository: repo, cloneDepth: 2 }); @@ -629,7 +631,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', @@ -676,7 +678,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', @@ -698,7 +700,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', @@ -998,7 +1000,9 @@ export = { test.throws(() => new codebuild.Project(stack, 'YourProject', { source: new codebuild.CodeCommitSource({ - repository: new codecommit.Repository(stack, 'MyRepo', { repositoryName: 'boo' }) + repository: new codecommit.Repository(stack, 'MyRepo', { + repositoryName: cdk.PhysicalName.of('boo'), + }), }), artifacts: new codebuild.CodePipelineBuildArtifacts() }), /Both source and artifacts must be set to CodePipeline/); @@ -1240,7 +1244,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 1e3d2b7c7ada7..5ad460790160c 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.repositoryArn; + const resourceIdentifiers = new ResourceIdentifiers(this, { + arn: this.repository.repositoryArn, + name: this.repository.repositoryName, + 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.repositoryCloneUrlSsh; } - public get repositoryName() { - return this.repository.repositoryName; - } - /** * 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 ea0d6cc945294..2e5430e89b2f0 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.deploymentGroupName; - this.deploymentGroupArn = arnForDeploymentGroup(this.application.applicationName, this.deploymentGroupName); + const resourceIdentifiers = new cdk.ResourceIdentifiers(this, { + arn: arnForDeploymentGroup(this.application.applicationName, resource.deploymentGroupName), + name: resource.deploymentGroupName, + 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 b5f5f5c7eb02c..3ed1e58ba9ce1 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.deploymentGroupName; - this.deploymentGroupArn = arnForDeploymentGroup(this.application.applicationName, this.deploymentGroupName); + const resourceIdentifiers = new cdk.ResourceIdentifiers(this, { + arn: arnForDeploymentGroup(this.application.applicationName, resource.deploymentGroupName), + name: resource.deploymentGroupName, + 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 5e35c2afc0171..0ff46733a8322 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 fb4a61c53ac73..09472f7e5f2b7 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') }) @@ -108,7 +108,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 f9fcedf104f00..a05b80cd1c865 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({ @@ -434,7 +436,9 @@ class TestFixture extends cdk.Stack { this.pipeline = new codepipeline.Pipeline(this, 'Pipeline'); this.sourceStage = this.pipeline.addStage({ name: 'Source' }); this.deployStage = this.pipeline.addStage({ name: '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 fc9b186042021..4e3bb74dea1dd 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 752bad003e2c1..5fba2f9a227f5 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 697c2a118832a..1a2b5bbd74a09 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 684adf25b73f1..5bed372a204a6 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 33cb7a4326848..f9ca359c24d27 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 7dc68544e5c69..de7894837c5f6 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 2b4ad4118d0f5..1f6ae0ca3a385 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 d66abf0b94a25..c2e665801dcf6 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'); @@ -56,7 +56,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', { @@ -872,6 +872,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 da6d042233552..bcfd985c3bc52 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.pipelineVersion; 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 c8ff0eda0e111..8c0d7b34ab416 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 @@ -80,4 +82,4 @@ export class UserPoolClient extends Resource { this.userPoolClientClientSecret = resource.userPoolClientClientSecret; this.userPoolClientName = resource.userPoolClientName; } -} \ No newline at end of file +} diff --git a/packages/@aws-cdk/aws-cognito/lib/user-pool.ts b/packages/@aws-cdk/aws-cognito/lib/user-pool.ts index dd59ca6d6fea7..3d01625d3d4e0 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.userPoolId; - this.userPoolArn = userPool.userPoolArn; + + const resourceIdentifiers = new ResourceIdentifiers(this, { + arn: userPool.userPoolArn, + name: userPool.userPoolId, + arnComponents: { + service: 'cognito', + resource: 'userpool', + resourceName: this.physicalName.value, + }, + }); + this.userPoolId = resourceIdentifiers.name; + this.userPoolArn = resourceIdentifiers.arn; + this.userPoolProviderName = userPool.userPoolProviderName; this.userPoolProviderUrl = userPool.userPoolProviderUrl; } diff --git a/packages/@aws-cdk/aws-cognito/package.json b/packages/@aws-cdk/aws-cognito/package.json index 4adef98dd8f2a..fd0eb68e81ee1 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 c1d2f39d4083c..e0982433ac6b4 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 f37b7afa74783..6731c1df60b4d 100644 --- a/packages/@aws-cdk/aws-config/package.json +++ b/packages/@aws-cdk/aws-config/package.json @@ -88,5 +88,14 @@ "engines": { "node": ">= 8.10.0" }, + "awslint": { + "exclude": [ + "props-physical-name:@aws-cdk/aws-config.CustomRuleProps.ruleName", + "props-physical-name:@aws-cdk/aws-config.ManagedRuleProps.ruleName", + "props-physical-name:@aws-cdk/aws-config.AccessKeysRotatedProps.ruleName", + "props-physical-name:@aws-cdk/aws-config.CloudFormationStackNotificationCheckProps.ruleName", + "props-physical-name:@aws-cdk/aws-config.CloudFormationStackDriftDetectionCheckProps.ruleName" + ] + }, "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 efa0ba6dbd786..ade2791e15990 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 ddfef88c00ae7..b499b15d2f334 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 a9cd979219a1b..32961667570ae 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), streamSpecification: 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 7a82d95e8c646..3757fe77c023c 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,17 @@ export class Table extends Resource { if (props.tableName) { this.node.addMetadata('aws:cdk:hasPhysicalName', props.tableName); } - this.tableArn = this.table.tableArn; - this.tableName = this.table.tableName; + const resourceIdentifiers = new ResourceIdentifiers(this, { + arn: this.table.tableArn, + name: this.table.tableName, + arnComponents: { + service: 'dynamodb', + resource: 'table', + resourceName: this.physicalName.value, + }, + }); + this.tableArn = resourceIdentifiers.arn; + this.tableName = resourceIdentifiers.name; this.tableStreamArn = this.table.tableStreamArn; 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 b92d3057c17de..6f873839d6549 100644 --- a/packages/@aws-cdk/aws-dynamodb/test/test.dynamodb.ts +++ b/packages/@aws-cdk/aws-dynamodb/test/test.dynamodb.ts @@ -1,6 +1,6 @@ import { expect, haveResource } from '@aws-cdk/assert'; 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, @@ -218,7 +218,7 @@ export = { const stack = new Stack(); new Table(stack, CONSTRUCT_NAME, { - tableName: TABLE_NAME, + tableName: PhysicalName.of(TABLE_NAME), readCapacity: 42, writeCapacity: 1337, streamSpecification: StreamViewType.NewAndOldImages, @@ -248,7 +248,7 @@ export = { const stack = new Stack(); new Table(stack, CONSTRUCT_NAME, { - tableName: TABLE_NAME, + tableName: PhysicalName.of(TABLE_NAME), readCapacity: 42, writeCapacity: 1337, streamSpecification: StreamViewType.NewImage, @@ -278,7 +278,7 @@ export = { const stack = new Stack(); new Table(stack, CONSTRUCT_NAME, { - tableName: TABLE_NAME, + tableName: PhysicalName.of(TABLE_NAME), readCapacity: 42, writeCapacity: 1337, streamSpecification: StreamViewType.OldImage, @@ -308,7 +308,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, pitrEnabled: true, @@ -349,7 +349,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 }); @@ -372,19 +372,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 31f6946d92ea2..e7ebafae4a8e1 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 23d6beb190553..5cd8391c5e282 100644 --- a/packages/@aws-cdk/aws-ec2/package.json +++ b/packages/@aws-cdk/aws-ec2/package.json @@ -87,8 +87,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 9fa3186fedf3a..71194f3cec78b 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, DeletionPolicy, IConstruct, IResource, Lazy, Resource, Stack, Token } from '@aws-cdk/cdk'; +import { Construct, DeletionPolicy, IConstruct, IResource, Lazy, PhysicalName, 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 @@ -338,10 +338,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() }), @@ -356,8 +358,17 @@ export class Repository extends RepositoryBase { props.lifecycleRules.forEach(this.addLifecycleRule.bind(this)); } - this.repositoryName = resource.repositoryName; - this.repositoryArn = resource.repositoryArn; + const resourceIdentifiers = new ResourceIdentifiers(this, { + arn: resource.repositoryArn, + name: resource.repositoryName, + 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 ff2c41da4eaa8..29e20ee401c58 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.serviceArn; - // 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.serviceArn)) : this.resource.serviceName; + const resourceIdentifiers = new ResourceIdentifiers(this, { + arn: this.resource.serviceArn, + 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 b32591a7c958d..797042c15826b 100644 --- a/packages/@aws-cdk/aws-ecs/lib/cluster.ts +++ b/packages/@aws-cdk/aws-ecs/lib/cluster.ts @@ -3,7 +3,7 @@ import cloudwatch = require ('@aws-cdk/aws-cloudwatch'); import ec2 = require('@aws-cdk/aws-ec2'); import iam = require('@aws-cdk/aws-iam'); import cloudmap = require('@aws-cdk/aws-servicediscovery'); -import { Construct, Context, IResource, Resource, Stack } from '@aws-cdk/cdk'; +import { Construct, Context, IResource, PhysicalName, Resource, ResourceIdentifiers, Stack } from '@aws-cdk/cdk'; import { InstanceDrainHook } from './drain-hook/instance-drain-hook'; import { CfnCluster } from './ecs.generated'; @@ -16,7 +16,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 @@ -66,13 +66,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.clusterArn, + name: cluster.clusterName, + arnComponents: { + service: 'ecs', + resource: 'cluster', + resourceName: this.physicalName.value, + }, + }); + this.clusterArn = resourceIdentifiers.arn; + this.clusterName = resourceIdentifiers.name; this.vpc = props.vpc; - this.clusterArn = cluster.clusterArn; - this.clusterName = cluster.clusterName; } /** diff --git a/packages/@aws-cdk/aws-ecs/package.json b/packages/@aws-cdk/aws-ecs/package.json index 46f5919359936..31c2a68498753 100644 --- a/packages/@aws-cdk/aws-ecs/package.json +++ b/packages/@aws-cdk/aws-ecs/package.json @@ -123,5 +123,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 c63c3eab36d1d..7ffbf30ceb2ba 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.clusterName; - this.clusterArn = resource.clusterArn; + const resourceIdentifiers = new ResourceIdentifiers(this, { + arn: resource.clusterArn, + name: resource.clusterName, + arnComponents: { + service: 'eks', + resource: 'cluster', + resourceName: this.physicalName.value, + }, + }); + this.clusterName = resourceIdentifiers.name; + this.clusterArn = resourceIdentifiers.arn; + this.clusterEndpoint = resource.clusterEndpoint; this.clusterCertificateAuthorityData = resource.clusterCertificateAuthorityData; diff --git a/packages/@aws-cdk/aws-elasticloadbalancing/package.json b/packages/@aws-cdk/aws-elasticloadbalancing/package.json index 21d038a5bded1..d693d89988e53 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 d4af6cf547020..10f09b7866a1e 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 94d303f3d5de2..e1782e4b804d9 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 0b607002a2562..0520c3a0b7e5a 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'); @@ -255,7 +255,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 96da83363c6e9..e72843871f9b6 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 @@ -83,7 +83,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 e1b4f6d24ec91..0a680e432e62d 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: new codebuild.CodeCommitSource({ 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 8ba9c871fccf5..0b6d66f8a3963 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, 'Pipeline'); @@ -47,4 +47,4 @@ new events.Rule(stack, 'rule', { targets: [new targets.CodePipeline(pipeline)] }); -app.synth(); \ No newline at end of file +app.synth(); 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 3a3993ac3ae4c..e02c77c2db79e 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'; @@ -19,7 +19,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. @@ -90,10 +90,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 }), @@ -101,7 +103,16 @@ export class Rule extends Resource implements IRule { targets: Lazy.anyValue({ produce: () => this.renderTargets() }), }); - this.ruleArn = resource.ruleArn; + const resourceIdentifiers = new ResourceIdentifiers(this, { + arn: resource.ruleArn, + name: resource.ruleId, + arnComponents: { + service: 'events', + resource: 'rule', + resourceName: this.physicalName.value, + }, + }); + this.ruleArn = resourceIdentifiers.arn; this.addEventPattern(props.eventPattern); this.scheduleExpression = props.scheduleExpression; @@ -231,4 +242,4 @@ export class Rule extends Resource implements IRule { */ function sanitizeId(id: string) { return id.replace(/[^\.\-_A-Za-z0-9]/g, '-'); -} \ No newline at end of file +} diff --git a/packages/@aws-cdk/aws-events/test/test.rule.ts b/packages/@aws-cdk/aws-events/test/test.rule.ts index 5ed5cba222f5b..955b43937c1e5 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'), scheduleExpression: 'rate(10 minutes)' }); diff --git a/packages/@aws-cdk/aws-glue/lib/database.ts b/packages/@aws-cdk/aws-glue/lib/database.ts index 5bc505944f584..dbbcea0b37201 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.databaseName; - 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.databaseName, + }), + name: resource.databaseName, + arnComponents: { + service: 'glue', + resource: 'database', + resourceName: this.physicalName.value, + }, }); + // see https://docs.aws.amazon.com/glue/latest/dg/glue-specifying-resource-arns.html#data-catalog-resource-arns + 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 51d2de06f2fb2..79c3d62f4c3cb 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.tableName; - 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.tableName}` + }), + name: tableResource.tableName, + arnComponents: { + service: 'glue', + resource: 'table', + resourceName: `${this.database.databaseName}/${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), path: props.path, }); - this.groupName = group.groupName; - this.groupArn = group.groupArn; + const resourceIdentifiers = new ResourceIdentifiers(this, { + arn: group.groupArn, + name: group.groupName, + 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 8d3b7f66d7343..920a870352e86 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, PolicyStatement } from './policy-document'; @@ -22,7 +22,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. @@ -90,20 +90,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 759e99854ffbf..6792b22fe70c1 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, Resource, SecretValue } from '@aws-cdk/cdk'; +import { Construct, PhysicalName, Resource, ResourceIdentifiers, SecretValue } from '@aws-cdk/cdk'; import { IGroup } from './group'; import { CfnUser } from './iam.generated'; import { IIdentity } from './identity-base'; @@ -54,7 +54,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 @@ -102,18 +102,30 @@ 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: undefinedIfEmpty(() => this.managedPolicyArns), path: props.path, loginProfile: this.parseLoginProfile(props) }); - this.userName = user.userName; - this.userArn = user.userArn; + const resourceIdentifiers = new ResourceIdentifiers(this, { + arn: user.userArn, + name: user.userName, + 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/package.json b/packages/@aws-cdk/aws-iam/package.json index 3aae4aeee2e81..1a01dccbd2608 100644 --- a/packages/@aws-cdk/aws-iam/package.json +++ b/packages/@aws-cdk/aws-iam/package.json @@ -84,4 +84,4 @@ "node": ">= 8.10.0" }, "stability": "experimental" -} \ No newline at end of file +} diff --git a/packages/@aws-cdk/aws-iam/test/integ.policy.ts b/packages/@aws-cdk/aws-iam/test/integ.policy.ts index 9d23de82c9cdb..828304a1a20f5 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.addStatement(new PolicyStatement().addResource('*').addAction('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 ee548b53d1f1b..8bdf3c031853f 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().addResource('*').addAction('sqs:SendMessage')); -const policy = new Policy(stack, 'HelloPolicy', { policyName: 'Default' }); +const policy = new Policy(stack, 'HelloPolicy', { policyName: PhysicalName.of('Default') }); policy.addStatement(new PolicyStatement().addAction('ec2:*').addResource('*')); 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 bf54cdd94e2d8..fb93765eb47b1 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.addStatement(new PolicyStatement().addResource('*').addAction('sqs:SendMessage')); policy.addStatement(new PolicyStatement().addResource('arn').addAction('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 7de1d50ff6793..81ed581987212 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,23 @@ 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.streamArn; - this.streamName = this.stream.streamId; + + const resourceIdentifiers = new ResourceIdentifiers(this, { + arn: this.stream.streamArn, + name: this.stream.streamId, + 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 3b0936ba60ed1..51168bdbe18e3 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 b46c31dac4277..8c77998399c1f 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 { FunctionBase, IFunction } from './function-base'; import { IVersion } from './lambda-version'; import { CfnAlias } from './lambda.generated'; @@ -25,7 +25,7 @@ export interface AliasProps { /** * Name of this alias */ - readonly aliasName: string; + readonly aliasName: PhysicalName; /** * Additional versions with individual weights this alias points to @@ -81,24 +81,36 @@ export class Alias extends FunctionBase { private readonly underlyingLambda: IFunction; constructor(scope: Construct, id: string, props: AliasProps) { - super(scope, id); + super(scope, id, { + physicalName: props.aliasName, + }); - this.aliasName = props.aliasName; + this.aliasName = this.physicalName.value || ''; this.underlyingLambda = props.version.lambda; const alias = new CfnAlias(this, 'Resource', { - name: props.aliasName, + name: this.aliasName, description: props.description, functionName: this.underlyingLambda.functionName, functionVersion: props.version.version, routingConfig: this.determineRoutingConfig(props) }); + const resourceIdentifiers = new ResourceIdentifiers(this, { + arn: alias.aliasArn, + name: this.aliasName, + arnComponents: { + service: 'lambda', + resource: 'function', + resourceName: `${this.underlyingLambda.functionName}:${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.aliasArn, ":").resourceName!}:${props.aliasName}`; - this.functionArn = alias.aliasArn; + this.functionName = `${Stack.of(this).parseArn(this.functionArn, ":").resourceName!}:${this.aliasName}`; } /** diff --git a/packages/@aws-cdk/aws-lambda/lib/function.ts b/packages/@aws-cdk/aws-lambda/lib/function.ts index d77d1a8f29238..2056f9daff34d 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. @@ -390,7 +390,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 || { }; @@ -422,7 +424,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 }), @@ -440,8 +442,18 @@ export class Function extends FunctionBase { resource.node.addDependency(this.role); - this.functionName = resource.refAsString; - this.functionArn = resource.functionArn; + const resourceIdentifiers = new ResourceIdentifiers(this, { + arn: resource.functionArn, + name: resource.refAsString, + arnComponents: { + region: '', // IAM is global in each partition + service: 'iam', + resource: 'role', + resourceName: this.physicalName.value, + }, + }); + this.functionName = resourceIdentifiers.name; + this.functionArn = resourceIdentifiers.arn; this.handler = props.handler; this.runtime = props.runtime; diff --git a/packages/@aws-cdk/aws-lambda/lib/layers.ts b/packages/@aws-cdk/aws-lambda/lib/layers.ts index 185f4e753c270..9672e84dce15f 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, Stack } from '@aws-cdk/cdk'; +import { Construct, IResource, Lazy, PhysicalName, Resource, Stack } 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 0b7b5b51e4213..a4e5f73f28646 100644 --- a/packages/@aws-cdk/aws-lambda/package.json +++ b/packages/@aws-cdk/aws-lambda/package.json @@ -112,8 +112,10 @@ }, "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" -} \ No newline at end of file +} diff --git a/packages/@aws-cdk/aws-lambda/test/integ.lambda.ts b/packages/@aws-cdk/aws-lambda/test/integ.lambda.ts index c206e6c8177dc..ae3705b547f32 100644 --- a/packages/@aws-cdk/aws-lambda/test/integ.lambda.ts +++ b/packages/@aws-cdk/aws-lambda/test/integ.lambda.ts @@ -17,7 +17,7 @@ fn.addToRolePolicy(new iam.PolicyStatement().addAllResources().addAction('*')); 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 c641ad1e6c970..8b3f5487fb886 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.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 69dfdfab82375..3bc8ca6e4d1a2 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/log-group.ts b/packages/@aws-cdk/aws-logs/lib/log-group.ts index 6881d573ecc0f..ca5e01a7705cc 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 { applyRemovalPolicy, Construct, IResource, RemovalPolicy, Resource, Stack } from '@aws-cdk/cdk'; +import { applyRemovalPolicy, 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'; @@ -277,7 +277,7 @@ export interface LogGroupProps { * * @default Automatically generated */ - readonly logGroupName?: string; + readonly logGroupName?: PhysicalName; /** * How long, in days, the log contents will be retained. @@ -328,7 +328,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; } @@ -339,7 +341,7 @@ export class LogGroup extends LogGroupBase { } const resource = new CfnLogGroup(this, 'Resource', { - logGroupName: props.logGroupName, + logGroupName: this.physicalName.value, retentionInDays, }); @@ -347,8 +349,18 @@ export class LogGroup extends LogGroupBase { applyRemovalPolicy(resource, RemovalPolicy.Orphan); } - this.logGroupArn = resource.logGroupArn; - this.logGroupName = resource.logGroupName; + const resourceIdentifiers = new ResourceIdentifiers(this, { + arn: resource.logGroupArn, + name: resource.logGroupName, + arnComponents: { + service: 'logs', + resource: 'log-group', + resourceName: this.physicalName.value, + sep: ':', + }, + }); + this.logGroupArn = resourceIdentifiers.arn; + this.logGroupName = resourceIdentifiers.name; } } @@ -363,7 +375,7 @@ export interface NewLogStreamProps { * * @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 b807dc2bf814b..796dc83b22e74 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, DeletionPolicy, IResource, Resource } from '@aws-cdk/cdk'; +import { Construct, DeletionPolicy, IResource, PhysicalName, 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; /** * Retain the log stream if the stack or containing construct ceases to exist @@ -63,17 +63,29 @@ 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, }); if (props.retainLogStream !== false) { resource.options.deletionPolicy = DeletionPolicy.Retain; } - this.logStreamName = resource.logStreamName; + const resourceIdentifiers = new ResourceIdentifiers(this, { + arn: '', + name: resource.logStreamName, + 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 660e812aa68bc..27e59300c1806 100644 --- a/packages/@aws-cdk/aws-logs/package.json +++ b/packages/@aws-cdk/aws-logs/package.json @@ -85,8 +85,10 @@ }, "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" -} \ No newline at end of file +} diff --git a/packages/@aws-cdk/aws-rds/package.json b/packages/@aws-cdk/aws-rds/package.json index dcd03de384082..80c0face4e6bd 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 9450dcaf111ae..90c1d9296b687 100644 --- a/packages/@aws-cdk/aws-route53/package.json +++ b/packages/@aws-cdk/aws-route53/package.json @@ -89,8 +89,21 @@ "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" -} \ No newline at end of file +} diff --git a/packages/@aws-cdk/aws-s3/package.json b/packages/@aws-cdk/aws-s3/package.json index 301e1e1d1521c..99abdffc87814 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 f4cb1ad90aa3b..217b4ed76c72c 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,21 @@ 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.secretArn, + 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.secretArn; } /** diff --git a/packages/@aws-cdk/aws-secretsmanager/package.json b/packages/@aws-cdk/aws-secretsmanager/package.json index dffc33156974f..a1c91715b8493 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 b0acc87da93e0..4c6d08e4aa9b4 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.secretJsonValue('username').toString(), + userName: cdk.PhysicalName.of(templatedSecret.secretJsonValue('username').toString()), password: templatedSecret.secretJsonValue('password') }); /// !hide diff --git a/packages/@aws-cdk/aws-servicediscovery/package.json b/packages/@aws-cdk/aws-servicediscovery/package.json index 819b0860ac402..3425c0d104dc1 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 5b2aa4d3875b9..14ac6126b2d07 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.receiptRuleSetName; diff --git a/packages/@aws-cdk/aws-ses/lib/receipt-rule.ts b/packages/@aws-cdk/aws-ses/lib/receipt-rule.ts index 7bed450a1b691..5f0573b333f47 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 2821191fa5c7f..9c134e57f7cd7 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.topicName; + const resourceIdentifiers = new ResourceIdentifiers(this, { + arn: resource.refAsString, + name: resource.topicName, + 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 7b1e40a7ab768..271aa12030a73 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 4447c45d5d2b9..60c03de13cbfb 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 80b6ecba10be5..5b4763f9e9e89 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.queueArn, + name: queue.queueName, + arnComponents: { + service: 'sqs', + resource: this.physicalName.value || '', + }, + }); + this.queueArn = resourceIdentifiers.arn; + this.queueName = resourceIdentifiers.name; this.encryptionMasterKey = encryptionMasterKey; - this.queueArn = queue.queueArn; - this.queueName = queue.queueName; 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 f0a4c7ae14a94..1f318ebed23b2 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/package.json b/packages/@aws-cdk/aws-ssm/package.json index 20a8597a837f7..209a325e05e42 100644 --- a/packages/@aws-cdk/aws-ssm/package.json +++ b/packages/@aws-cdk/aws-ssm/package.json @@ -87,8 +87,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-stepfunctions/lib/activity.ts b/packages/@aws-cdk/aws-stepfunctions/lib/activity.ts index 6e30ba22ab53f..1e0d6d8c10791 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.activityArn; - this.activityName = resource.activityName; + const resourceIdentifiers = new ResourceIdentifiers(this, { + arn: resource.activityArn, + name: resource.activityName, + 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 2341cd8beb1a7..eac7fc3058d2f 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.stateMachineName; - this.stateMachineArn = resource.stateMachineArn; + const resourceIdentifiers = new ResourceIdentifiers(this, { + arn: resource.stateMachineArn, + name: resource.stateMachineName, + 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 a616e4a11d450..8e3c8fde2c5e0 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 7b9cbb53b6255..7f0fae3616b1e 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)) @@ -292,4 +293,29 @@ constructLinter.add({ e.assert(!property.type.isAny, `${e.ctx.propsFqn}.${property.name}`); } } -}); \ No newline at end of file +}); + +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 { + // check the name of the property + const basename = `${new ResourceReflection(e.ctx).basename}Name`.toLowerCase(); + + const physicalNameProp = physicalNameProps[0]; + e.assert(basename.endsWith(physicalNameProp.name.toLowerCase()) && physicalNameProp.name.endsWith('Name'), + `${e.ctx.propsFqn}.${physicalNameProp.name}`); + } + }, +});