diff --git a/packages/@aws-cdk/aws-elasticloadbalancingv2/README.md b/packages/@aws-cdk/aws-elasticloadbalancingv2/README.md index 4ef361dd5512c..a32ae8adf49ef 100644 --- a/packages/@aws-cdk/aws-elasticloadbalancingv2/README.md +++ b/packages/@aws-cdk/aws-elasticloadbalancingv2/README.md @@ -25,7 +25,7 @@ and adding Targets to the Listener: ```ts import * as ec2 from '@aws-cdk/aws-ec2'; import * as elbv2 from '@aws-cdk/aws-elasticloadbalancingv2'; -import * as autoscaling from '@aws-cdk/aws-autoscaling'; +import { AutoScalingGroup } from '@aws-cdk/aws-autoscaling'; // ... @@ -51,7 +51,7 @@ const listener = lb.addListener('Listener', { // Create an AutoScaling group and add it as a load balancing // target to the listener. -const asg = new autoscaling.AutoScalingGroup(...); +const asg = new AutoScalingGroup(...); listener.addTargets('ApplicationFleet', { port: 8080, targets: [asg] diff --git a/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/alb/application-listener.ts b/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/alb/application-listener.ts index 7897f5582eb0a..65d6465cb9efe 100644 --- a/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/alb/application-listener.ts +++ b/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/alb/application-listener.ts @@ -571,6 +571,7 @@ class ImportedApplicationListener extends Resource implements IApplicationListen // New rule new ApplicationListenerRule(this, id, { listener: this, + conditions: props.conditions, hostHeader: props.hostHeader, pathPattern: props.pathPattern, pathPatterns: props.pathPatterns, diff --git a/packages/@aws-cdk/aws-elasticloadbalancingv2/test/alb/test.listener.ts b/packages/@aws-cdk/aws-elasticloadbalancingv2/test/alb/test.listener.ts index ed851080fa5b0..aa6345eac07cc 100644 --- a/packages/@aws-cdk/aws-elasticloadbalancingv2/test/alb/test.listener.ts +++ b/packages/@aws-cdk/aws-elasticloadbalancingv2/test/alb/test.listener.ts @@ -464,6 +464,38 @@ export = { test.done(); }, + 'Can call addTargetGroups on imported listener with conditions prop'(test: Test) { + // GIVEN + const stack = new cdk.Stack(); + const vpc = new ec2.Vpc(stack, 'VPC'); + const listener = elbv2.ApplicationListener.fromApplicationListenerAttributes(stack, 'Listener', { + listenerArn: 'ieks', + securityGroupId: 'sg-12345', + }); + const group = new elbv2.ApplicationTargetGroup(stack, 'TargetGroup', { vpc, port: 80 }); + + // WHEN + listener.addTargetGroups('Gruuup', { + priority: 30, + conditions: [elbv2.ListenerCondition.hostHeaders(['example.com'])], + targetGroups: [group], + }); + + // THEN + expect(stack).to(haveResource('AWS::ElasticLoadBalancingV2::ListenerRule', { + ListenerArn: 'ieks', + Priority: 30, + Actions: [ + { + TargetGroupArn: { Ref: 'TargetGroup3D7CD9B8' }, + Type: 'forward', + }, + ], + })); + + test.done(); + }, + 'Can depend on eventual listener via TargetGroup'(test: Test) { // GIVEN const stack = new cdk.Stack(); diff --git a/packages/@aws-cdk/aws-rds/README.md b/packages/@aws-cdk/aws-rds/README.md index 51729d3778386..829e7237f33e6 100644 --- a/packages/@aws-cdk/aws-rds/README.md +++ b/packages/@aws-cdk/aws-rds/README.md @@ -206,6 +206,28 @@ The rotation will start as soon as this user exists. See also [@aws-cdk/aws-secretsmanager](https://github.com/aws/aws-cdk/blob/master/packages/%40aws-cdk/aws-secretsmanager/README.md) for credentials rotation of existing clusters/instances. +### IAM Authentication + +You can also authenticate to a database instance using AWS Identity and Access Management (IAM) database authentication; +See https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/UsingWithRDS.IAMDBAuth.html for more information +and a list of supported versions and limitations. + +The following example shows enabling IAM authentication for a database instance and granting connection access to an IAM role. + +```ts +const instance = new rds.DatabaseInstance(stack, 'Instance', { + engine: rds.DatabaseInstanceEngine.mysql({ version: rds.MysqlEngineVersion.VER_8_0_19 }), + masterUsername: 'admin', + vpc, + iamAuthentication: true, // Optional - will be automatically set if you call grantConnect(). +}); +const role = new Role(stack, 'DBRole', { assumedBy: new AccountPrincipal(stack.account) }); +instance.grantConnect(role); // Grant the role connection access to the DB. +``` + +**Note**: In addition to the setup above, a database user will need to be created to support IAM auth. +See https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/UsingWithRDS.IAMDBAuth.DBAccounts.html for setup instructions. + ### Metrics Database instances expose metrics (`cloudwatch.Metric`): diff --git a/packages/@aws-cdk/aws-rds/lib/cluster.ts b/packages/@aws-cdk/aws-rds/lib/cluster.ts index d52fff47c476e..56075630e6e79 100644 --- a/packages/@aws-cdk/aws-rds/lib/cluster.ts +++ b/packages/@aws-cdk/aws-rds/lib/cluster.ts @@ -84,6 +84,13 @@ export interface DatabaseClusterProps { */ readonly defaultDatabaseName?: string; + /** + * Indicates whether the DB cluster should have deletion protection enabled. + * + * @default false + */ + readonly deletionProtection?: boolean; + /** * Whether to enable storage encryption. * @@ -425,6 +432,7 @@ export class DatabaseCluster extends DatabaseClusterBase { port: props.port ?? clusterEngineBindConfig.port, dbClusterParameterGroupName: clusterParameterGroupConfig?.parameterGroupName, associatedRoles: clusterAssociatedRoles.length > 0 ? clusterAssociatedRoles : undefined, + deletionProtection: props.deletionProtection, // Admin masterUsername: secret ? secret.secretValueFromJson('username').toString() : props.masterUser.username, masterUserPassword: secret diff --git a/packages/@aws-cdk/aws-rds/lib/instance.ts b/packages/@aws-cdk/aws-rds/lib/instance.ts index 0890b26b705d2..dd8c100ca925b 100644 --- a/packages/@aws-cdk/aws-rds/lib/instance.ts +++ b/packages/@aws-cdk/aws-rds/lib/instance.ts @@ -53,6 +53,11 @@ export interface IDatabaseInstance extends IResource, ec2.IConnectable, secretsm */ addProxy(id: string, options: DatabaseProxyOptions): DatabaseProxy; + /** + * Grant the given identity connection access to the database. + */ + grantConnect(grantee: iam.IGrantable): iam.Grant; + /** * Defines a CloudWatch event rule which triggers for instance events. Use * `rule.addEventPattern(pattern)` to specify a filter. @@ -103,6 +108,7 @@ export abstract class DatabaseInstanceBase extends Resource implements IDatabase public readonly dbInstanceEndpointAddress = attrs.instanceEndpointAddress; public readonly dbInstanceEndpointPort = attrs.port.toString(); public readonly instanceEndpoint = new Endpoint(attrs.instanceEndpointAddress, attrs.port); + protected enableIamAuthentication = true; } return new Import(scope, id); @@ -112,6 +118,7 @@ export abstract class DatabaseInstanceBase extends Resource implements IDatabase public abstract readonly dbInstanceEndpointAddress: string; public abstract readonly dbInstanceEndpointPort: string; public abstract readonly instanceEndpoint: Endpoint; + protected abstract enableIamAuthentication?: boolean; /** * Access to network connections. @@ -128,6 +135,19 @@ export abstract class DatabaseInstanceBase extends Resource implements IDatabase }); } + public grantConnect(grantee: iam.IGrantable): iam.Grant { + if (this.enableIamAuthentication === false) { + throw new Error('Cannot grant connect when IAM authentication is disabled'); + } + + this.enableIamAuthentication = true; + return iam.Grant.addToPrincipal({ + grantee, + actions: ['rds-db:connect'], + resourceArns: [this.instanceArn], + }); + } + /** * Defines a CloudWatch event rule which triggers for instance events. Use * `rule.addEventPattern(pattern)` to specify a filter. @@ -494,6 +514,8 @@ abstract class DatabaseInstanceNew extends DatabaseInstanceBase implements IData private readonly cloudwatchLogsRetention?: logs.RetentionDays; private readonly cloudwatchLogsRetentionRole?: iam.IRole; + protected enableIamAuthentication?: boolean; + constructor(scope: Construct, id: string, props: DatabaseInstanceNewProps) { super(scope, id); @@ -532,6 +554,7 @@ abstract class DatabaseInstanceNew extends DatabaseInstanceBase implements IData this.cloudwatchLogsExports = props.cloudwatchLogsExports; this.cloudwatchLogsRetention = props.cloudwatchLogsRetention; this.cloudwatchLogsRetentionRole = props.cloudwatchLogsRetentionRole; + this.enableIamAuthentication = props.iamAuthentication; this.newCfnProps = { autoMinorVersionUpgrade: props.autoMinorVersionUpgrade, @@ -544,7 +567,7 @@ abstract class DatabaseInstanceNew extends DatabaseInstanceBase implements IData deleteAutomatedBackups: props.deleteAutomatedBackups, deletionProtection, enableCloudwatchLogsExports: this.cloudwatchLogsExports, - enableIamDatabaseAuthentication: props.iamAuthentication, + enableIamDatabaseAuthentication: Lazy.anyValue({ produce: () => this.enableIamAuthentication }), enablePerformanceInsights: props.enablePerformanceInsights, iops, monitoringInterval: props.monitoringInterval && props.monitoringInterval.toSeconds(), diff --git a/packages/@aws-cdk/aws-rds/test/test.cluster.ts b/packages/@aws-cdk/aws-rds/test/test.cluster.ts index 105964b73e0c4..4414ed784b393 100644 --- a/packages/@aws-cdk/aws-rds/test/test.cluster.ts +++ b/packages/@aws-cdk/aws-rds/test/test.cluster.ts @@ -1178,6 +1178,33 @@ export = { test.done(); }, + 'can set deletion protection'(test: Test) { + // GIVEN + const stack = testStack(); + const vpc = new ec2.Vpc(stack, 'VPC'); + + // WHEN + new DatabaseCluster(stack, 'Database', { + engine: DatabaseClusterEngine.AURORA, + masterUser: { + username: 'admin', + password: cdk.SecretValue.plainText('tooshort'), + }, + instanceProps: { + instanceType: ec2.InstanceType.of(ec2.InstanceClass.BURSTABLE2, ec2.InstanceSize.SMALL), + vpc, + }, + deletionProtection: true, + }); + + // THEN + expect(stack).to(haveResourceLike('AWS::RDS::DBCluster', { + DeletionProtection: true, + })); + + test.done(); + }, + 'does not throw (but adds a node error) if a (dummy) VPC does not have sufficient subnets'(test: Test) { // GIVEN const stack = testStack(); diff --git a/packages/@aws-cdk/aws-rds/test/test.instance.ts b/packages/@aws-cdk/aws-rds/test/test.instance.ts index e72c83329edd4..3383f4f93cd55 100644 --- a/packages/@aws-cdk/aws-rds/test/test.instance.ts +++ b/packages/@aws-cdk/aws-rds/test/test.instance.ts @@ -1,19 +1,24 @@ -import { ABSENT, countResources, expect, haveResource, ResourcePart } from '@aws-cdk/assert'; +import { ABSENT, countResources, expect, haveResource, ResourcePart, haveResourceLike } from '@aws-cdk/assert'; import * as ec2 from '@aws-cdk/aws-ec2'; import * as targets from '@aws-cdk/aws-events-targets'; -import { ManagedPolicy, Role, ServicePrincipal } from '@aws-cdk/aws-iam'; +import { ManagedPolicy, Role, ServicePrincipal, AccountPrincipal } from '@aws-cdk/aws-iam'; import * as lambda from '@aws-cdk/aws-lambda'; import * as logs from '@aws-cdk/aws-logs'; import * as cdk from '@aws-cdk/core'; import { Test } from 'nodeunit'; import * as rds from '../lib'; +let stack: cdk.Stack; +let vpc: ec2.Vpc; + export = { - 'create a DB instance'(test: Test) { - // GIVEN - const stack = new cdk.Stack(); - const vpc = new ec2.Vpc(stack, 'VPC'); + 'setUp'(cb: () => void) { + stack = new cdk.Stack(); + vpc = new ec2.Vpc(stack, 'VPC'); + cb(); + }, + 'create a DB instance'(test: Test) { // WHEN new rds.DatabaseInstance(stack, 'Instance', { engine: rds.DatabaseInstanceEngine.ORACLE_SE1, @@ -190,10 +195,6 @@ export = { }, 'instance with option and parameter group'(test: Test) { - // GIVEN - const stack = new cdk.Stack(); - const vpc = new ec2.Vpc(stack, 'VPC'); - const optionGroup = new rds.OptionGroup(stack, 'OptionGroup', { engine: rds.DatabaseInstanceEngine.ORACLE_SE1, configurations: [ @@ -237,10 +238,6 @@ export = { }, 'create an instance from snapshot'(test: Test) { - // GIVEN - const stack = new cdk.Stack(); - const vpc = new ec2.Vpc(stack, 'VPC'); - // WHEN new rds.DatabaseInstanceFromSnapshot(stack, 'Instance', { snapshotIdentifier: 'my-snapshot', @@ -257,10 +254,6 @@ export = { }, 'throws when trying to generate a new password from snapshot without username'(test: Test) { - // GIVEN - const stack = new cdk.Stack(); - const vpc = new ec2.Vpc(stack, 'VPC'); - // THEN test.throws(() => new rds.DatabaseInstanceFromSnapshot(stack, 'Instance', { snapshotIdentifier: 'my-snapshot', @@ -274,10 +267,6 @@ export = { }, 'throws when specifying user name without asking to generate a new password'(test: Test) { - // GIVEN - const stack = new cdk.Stack(); - const vpc = new ec2.Vpc(stack, 'VPC'); - // THEN test.throws(() => new rds.DatabaseInstanceFromSnapshot(stack, 'Instance', { snapshotIdentifier: 'my-snapshot', @@ -291,10 +280,6 @@ export = { }, 'throws when password and generate password ar both specified'(test: Test) { - // GIVEN - const stack = new cdk.Stack(); - const vpc = new ec2.Vpc(stack, 'VPC'); - // THEN test.throws(() => new rds.DatabaseInstanceFromSnapshot(stack, 'Instance', { snapshotIdentifier: 'my-snapshot', @@ -309,9 +294,6 @@ export = { }, 'create a read replica in the same region - with the subnet group name'(test: Test) { - // GIVEN - const stack = new cdk.Stack(); - const vpc = new ec2.Vpc(stack, 'VPC'); const sourceInstance = new rds.DatabaseInstance(stack, 'Instance', { engine: rds.DatabaseInstanceEngine.MYSQL, instanceType: ec2.InstanceType.of(ec2.InstanceClass.BURSTABLE2, ec2.InstanceSize.SMALL), @@ -349,9 +331,6 @@ export = { }, 'on event'(test: Test) { - // GIVEN - const stack = new cdk.Stack(); - const vpc = new ec2.Vpc(stack, 'VPC'); const instance = new rds.DatabaseInstance(stack, 'Instance', { engine: rds.DatabaseInstanceEngine.MYSQL, instanceType: ec2.InstanceType.of(ec2.InstanceClass.BURSTABLE2, ec2.InstanceSize.SMALL), @@ -416,9 +395,6 @@ export = { }, 'on event without target'(test: Test) { - // GIVEN - const stack = new cdk.Stack(); - const vpc = new ec2.Vpc(stack, 'VPC'); const instance = new rds.DatabaseInstance(stack, 'Instance', { engine: rds.DatabaseInstanceEngine.MYSQL, instanceType: ec2.InstanceType.of(ec2.InstanceClass.BURSTABLE2, ec2.InstanceSize.SMALL), @@ -467,10 +443,6 @@ export = { }, 'can use metricCPUUtilization'(test: Test) { - // GIVEN - const stack = new cdk.Stack(); - const vpc = new ec2.Vpc(stack, 'VPC'); - // WHEN const instance = new rds.DatabaseInstance(stack, 'Instance', { engine: rds.DatabaseInstanceEngine.MYSQL, @@ -492,10 +464,6 @@ export = { }, 'can resolve endpoint port and socket address'(test: Test) { - // GIVEN - const stack = new cdk.Stack(); - const vpc = new ec2.Vpc(stack, 'VPC'); - // WHEN const instance = new rds.DatabaseInstance(stack, 'Instance', { engine: rds.DatabaseInstanceEngine.MYSQL, @@ -523,10 +491,6 @@ export = { }, 'can deactivate backup'(test: Test) { - // GIVEN - const stack = new cdk.Stack(); - const vpc = new ec2.Vpc(stack, 'VPC'); - // WHEN new rds.DatabaseInstance(stack, 'Instance', { engine: rds.DatabaseInstanceEngine.MYSQL, @@ -545,9 +509,6 @@ export = { }, 'imported instance with imported security group with allowAllOutbound set to false'(test: Test) { - // GIVEN - const stack = new cdk.Stack(); - const instance = rds.DatabaseInstance.fromDatabaseInstanceAttributes(stack, 'Database', { instanceEndpointAddress: 'address', instanceIdentifier: 'identifier', @@ -569,10 +530,6 @@ export = { }, 'create an instance with imported monitoring role'(test: Test) { - // GIVEN - const stack = new cdk.Stack(); - const vpc = new ec2.Vpc(stack, 'VPC'); - const monitoringRole = new Role(stack, 'MonitoringRole', { assumedBy: new ServicePrincipal('monitoring.rds.amazonaws.com'), managedPolicies: [ @@ -602,9 +559,6 @@ export = { }, 'create an instance with an existing security group'(test: Test) { - // GIVEN - const stack = new cdk.Stack(); - const vpc = new ec2.Vpc(stack, 'VPC'); const securityGroup = ec2.SecurityGroup.fromSecurityGroupId(stack, 'SG', 'sg-123456789', { allowAllOutbound: false, }); @@ -644,9 +598,6 @@ export = { }, 'throws when trying to add rotation to an instance without secret'(test: Test) { - // GIVEN - const stack = new cdk.Stack(); - const vpc = new ec2.Vpc(stack, 'VPC'); const instance = new rds.DatabaseInstance(stack, 'Database', { engine: rds.DatabaseInstanceEngine.SQL_SERVER_EE, instanceType: ec2.InstanceType.of(ec2.InstanceClass.BURSTABLE2, ec2.InstanceSize.SMALL), @@ -662,9 +613,6 @@ export = { }, 'throws when trying to add single user rotation multiple times'(test: Test) { - // GIVEN - const stack = new cdk.Stack(); - const vpc = new ec2.Vpc(stack, 'VPC'); const instance = new rds.DatabaseInstance(stack, 'Database', { engine: rds.DatabaseInstanceEngine.SQL_SERVER_EE, instanceType: ec2.InstanceType.of(ec2.InstanceClass.BURSTABLE2, ec2.InstanceSize.SMALL), @@ -682,9 +630,6 @@ export = { }, 'throws when timezone is set for non-sqlserver database engine'(test: Test) { - // GIVEN - const stack = new cdk.Stack(); - const vpc = new ec2.Vpc(stack, 'vpc'); const tzSupportedEngines = [rds.DatabaseInstanceEngine.SQL_SERVER_EE, rds.DatabaseInstanceEngine.SQL_SERVER_EX, rds.DatabaseInstanceEngine.SQL_SERVER_SE, rds.DatabaseInstanceEngine.SQL_SERVER_WEB]; const tzUnsupportedEngines = [rds.DatabaseInstanceEngine.MYSQL, rds.DatabaseInstanceEngine.POSTGRES, @@ -715,10 +660,6 @@ export = { }, 'create an instance from snapshot with maximum allocated storage'(test: Test) { - // GIVEN - const stack = new cdk.Stack(); - const vpc = new ec2.Vpc(stack, 'VPC'); - // WHEN new rds.DatabaseInstanceFromSnapshot(stack, 'Instance', { snapshotIdentifier: 'my-snapshot', @@ -737,10 +678,6 @@ export = { }, 'create a DB instance with maximum allocated storage'(test: Test) { - // GIVEN - const stack = new cdk.Stack(); - const vpc = new ec2.Vpc(stack, 'VPC'); - // WHEN new rds.DatabaseInstance(stack, 'Instance', { engine: rds.DatabaseInstanceEngine.MYSQL, @@ -759,4 +696,65 @@ export = { test.done(); }, + + 'iam authentication - off by default'(test: Test) { + new rds.DatabaseInstance(stack, 'Instance', { + engine: rds.DatabaseInstanceEngine.mysql({ version: rds.MysqlEngineVersion.VER_8_0_19 }), + masterUsername: 'admin', + vpc, + }); + + expect(stack).to(haveResourceLike('AWS::RDS::DBInstance', { + EnableIAMDatabaseAuthentication: ABSENT, + })); + + test.done(); + }, + + 'createGrant - creates IAM policy and enables IAM auth'(test: Test) { + const instance = new rds.DatabaseInstance(stack, 'Instance', { + engine: rds.DatabaseInstanceEngine.mysql({ version: rds.MysqlEngineVersion.VER_8_0_19 }), + masterUsername: 'admin', + vpc, + }); + const role = new Role(stack, 'DBRole', { + assumedBy: new AccountPrincipal(stack.account), + }); + instance.grantConnect(role); + + expect(stack).to(haveResourceLike('AWS::RDS::DBInstance', { + EnableIAMDatabaseAuthentication: true, + })); + expect(stack).to(haveResource('AWS::IAM::Policy', { + PolicyDocument: { + Statement: [{ + Effect: 'Allow', + Action: 'rds-db:connect', + Resource: { + 'Fn::Join': ['', ['arn:', { Ref: 'AWS::Partition' }, ':rds:', { Ref: 'AWS::Region' }, ':', { Ref: 'AWS::AccountId' }, ':db:', { Ref: 'InstanceC1063A87' }]], + }, + }], + Version: '2012-10-17', + }, + })); + + test.done(); + }, + + 'createGrant - throws if IAM auth disabled'(test: Test) { + const instance = new rds.DatabaseInstance(stack, 'Instance', { + engine: rds.DatabaseInstanceEngine.mysql({ version: rds.MysqlEngineVersion.VER_8_0_19 }), + masterUsername: 'admin', + vpc, + iamAuthentication: false, + }); + const role = new Role(stack, 'DBRole', { + assumedBy: new AccountPrincipal(stack.account), + }); + + test.throws(() => { instance.grantConnect(role); }, /Cannot grant connect when IAM authentication is disabled/); + + test.done(); + }, + }; diff --git a/packages/@aws-cdk/aws-s3/README.md b/packages/@aws-cdk/aws-s3/README.md index 01165967f3dbc..335cdfd871f8a 100644 --- a/packages/@aws-cdk/aws-s3/README.md +++ b/packages/@aws-cdk/aws-s3/README.md @@ -135,6 +135,17 @@ const byName = Bucket.fromBucketName(this, 'BucketByName', 'my-bucket'); const byArn = Bucket.fromBucketArn(this, 'BucketByArn', 'arn:aws:s3:::my-bucket'); ``` +The bucket's region defaults to the current stack's region, but can also be explicitly set in cases where one of the bucket's +regional properties needs to contain the correct values. + +```ts +const myCrossRegionBucket = Bucket.fromBucketAttributes(this, 'CrossRegionImport', { + bucketArn: 'arn:aws:s3:::my-bucket', + region: 'us-east-1', +}); +// myCrossRegionBucket.bucketRegionalDomainName === 'my-bucket.s3.us-east-1.amazonaws.com' +``` + ### Bucket Notifications The Amazon S3 notification feature enables you to receive notifications when diff --git a/packages/@aws-cdk/aws-s3/lib/bucket.ts b/packages/@aws-cdk/aws-s3/lib/bucket.ts index d767011ecb5d3..d44f989ba267b 100644 --- a/packages/@aws-cdk/aws-s3/lib/bucket.ts +++ b/packages/@aws-cdk/aws-s3/lib/bucket.ts @@ -1125,7 +1125,7 @@ export class Bucket extends BucketBase { */ public static fromBucketAttributes(scope: Construct, id: string, attrs: BucketAttributes): IBucket { const stack = Stack.of(scope); - const region = stack.region; + const region = attrs.region ?? stack.region; const urlSuffix = stack.urlSuffix; const bucketName = parseBucketName(scope, attrs); diff --git a/packages/@aws-cdk/aws-s3/test/test.bucket.ts b/packages/@aws-cdk/aws-s3/test/test.bucket.ts index 4e2a8bb60e2aa..2c9e13c798e96 100644 --- a/packages/@aws-cdk/aws-s3/test/test.bucket.ts +++ b/packages/@aws-cdk/aws-s3/test/test.bucket.ts @@ -697,6 +697,22 @@ export = { test.done(); }, + + 'import can explicitly set bucket region'(test: Test) { + const stack = new cdk.Stack(undefined, undefined, { + env: { region: 'us-east-1' }, + }); + + const bucket = s3.Bucket.fromBucketAttributes(stack, 'ImportedBucket', { + bucketName: 'myBucket', + region: 'eu-west-1', + }); + + test.equals(bucket.bucketRegionalDomainName, `myBucket.s3.eu-west-1.${stack.urlSuffix}`); + test.equals(bucket.bucketWebsiteDomainName, `myBucket.s3-website-eu-west-1.${stack.urlSuffix}`); + + test.done(); + }, }, 'grantRead'(test: Test) { diff --git a/packages/@aws-cdk/cfnspec/build-tools/spec-diff.ts b/packages/@aws-cdk/cfnspec/build-tools/spec-diff.ts index 889e631eacf27..f4aa0ee7daab3 100644 --- a/packages/@aws-cdk/cfnspec/build-tools/spec-diff.ts +++ b/packages/@aws-cdk/cfnspec/build-tools/spec-diff.ts @@ -139,7 +139,7 @@ async function main() { throw new Error('Unexpected update to a resource type. Expecting only "Properties" to change: ' + propertyType); } - for (const prop of Object.keys(update.Properties)) { + for (const prop of Object.keys(update.Properties ?? {})) { describeChanges(propertyType, prop, update.Properties[prop]).forEach(change => { propertyTypeChanges.push(change); }); diff --git a/packages/@aws-cdk/core/lib/stack-synthesizers/default-synthesizer.ts b/packages/@aws-cdk/core/lib/stack-synthesizers/default-synthesizer.ts index cac58e7461528..e666cec182ade 100644 --- a/packages/@aws-cdk/core/lib/stack-synthesizers/default-synthesizer.ts +++ b/packages/@aws-cdk/core/lib/stack-synthesizers/default-synthesizer.ts @@ -476,7 +476,7 @@ function addBootstrapVersionRule(stack: Stack, requiredVersion: number, qualifie const param = new CfnParameter(stack, 'BootstrapVersion', { type: 'AWS::SSM::Parameter::Value', description: 'Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store.', - default: `/aws-cdk-bootstrap/${qualifier}/version`, + default: `/cdk-bootstrap/${qualifier}/version`, }); // There is no >= check in CloudFormation, so we have to check the number @@ -499,4 +499,4 @@ function range(startIncl: number, endExcl: number) { ret.push(i); } return ret; -} \ No newline at end of file +} diff --git a/packages/@aws-cdk/core/test/stack-synthesis/test.new-style-synthesis.ts b/packages/@aws-cdk/core/test/stack-synthesis/test.new-style-synthesis.ts index 79c852d494247..7d88ce7c0be49 100644 --- a/packages/@aws-cdk/core/test/stack-synthesis/test.new-style-synthesis.ts +++ b/packages/@aws-cdk/core/test/stack-synthesis/test.new-style-synthesis.ts @@ -36,7 +36,7 @@ export = { // THEN -- the S3 url is advertised on the stack artifact const stackArtifact = asm.getStackArtifact('Stack'); - const templateHash = '19e1e8612660f79362e091714ab7b3583961936d762c75be8b8083c3af40850a'; + const templateHash = '040a6374d4c48c0db867f1d4f95c69b12d28e69c3b8a9903a1db1ec651dcf480'; test.equals(stackArtifact.stackTemplateAssetObjectUrl, `s3://cdk-hnb659fds-assets-\${AWS::AccountId}-\${AWS::Region}/${templateHash}`); @@ -70,7 +70,7 @@ export = { // THEN const template = app.synth().getStackByName('Stack').template; test.deepEqual(template?.Parameters?.BootstrapVersion?.Type, 'AWS::SSM::Parameter::Value'); - test.deepEqual(template?.Parameters?.BootstrapVersion?.Default, '/aws-cdk-bootstrap/hnb659fds/version'); + test.deepEqual(template?.Parameters?.BootstrapVersion?.Default, '/cdk-bootstrap/hnb659fds/version'); const assertions = template?.Rules?.CheckBootstrapVersion?.Assertions ?? []; test.deepEqual(assertions.length, 1); @@ -220,4 +220,4 @@ function readAssetManifest(asm: cxapi.CloudAssembly): cxschema.AssetManifest { if (!manifestArtifact) { throw new Error('no asset manifest in assembly'); } return JSON.parse(fs.readFileSync(manifestArtifact.file, { encoding: 'utf-8' })); -} \ No newline at end of file +} diff --git a/packages/@aws-cdk/pipelines/test/integ.pipeline-with-assets.expected.json b/packages/@aws-cdk/pipelines/test/integ.pipeline-with-assets.expected.json index 0f87f89024dbb..bf0e04a7f96b9 100644 --- a/packages/@aws-cdk/pipelines/test/integ.pipeline-with-assets.expected.json +++ b/packages/@aws-cdk/pipelines/test/integ.pipeline-with-assets.expected.json @@ -2,7 +2,7 @@ "Parameters": { "BootstrapVersion": { "Type": "AWS::SSM::Parameter::Value", - "Default": "/aws-cdk-bootstrap/hnb659fds/version", + "Default": "/cdk-bootstrap/hnb659fds/version", "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store." } }, @@ -1694,4 +1694,4 @@ } } } -} \ No newline at end of file +} diff --git a/packages/@aws-cdk/pipelines/test/integ.pipeline.expected.json b/packages/@aws-cdk/pipelines/test/integ.pipeline.expected.json index 336c2494b3f00..d608c7a22b842 100644 --- a/packages/@aws-cdk/pipelines/test/integ.pipeline.expected.json +++ b/packages/@aws-cdk/pipelines/test/integ.pipeline.expected.json @@ -2,7 +2,7 @@ "Parameters": { "BootstrapVersion": { "Type": "AWS::SSM::Parameter::Value", - "Default": "/aws-cdk-bootstrap/hnb659fds/version", + "Default": "/cdk-bootstrap/hnb659fds/version", "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store." } }, @@ -1390,4 +1390,4 @@ } } } -} \ No newline at end of file +} diff --git a/packages/aws-cdk/lib/api/bootstrap/bootstrap-template.yaml b/packages/aws-cdk/lib/api/bootstrap/bootstrap-template.yaml index eb90045569e8b..91a2a606341f4 100644 --- a/packages/aws-cdk/lib/api/bootstrap/bootstrap-template.yaml +++ b/packages/aws-cdk/lib/api/bootstrap/bootstrap-template.yaml @@ -368,7 +368,7 @@ Resources: Properties: Type: String Name: - Fn::Sub: '/aws-cdk-bootstrap/${Qualifier}/version' + Fn::Sub: '/cdk-bootstrap/${Qualifier}/version' Value: 4 Outputs: BucketName: @@ -401,4 +401,4 @@ Outputs: Description: The version of the bootstrap resources that are currently mastered in this stack Value: - Fn::GetAtt: [CdkBootstrapVersion, Value] \ No newline at end of file + Fn::GetAtt: [CdkBootstrapVersion, Value]