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