From a7f5330ab1ebe654484bc4bd5811dd357d1bb6c6 Mon Sep 17 00:00:00 2001 From: Rizbir Khan Date: Wed, 18 Jan 2023 09:49:23 +0000 Subject: [PATCH 01/25] Adding custom resource, tests --- packages/@aws-cdk/aws-redshift/lib/cluster.ts | 53 + .../aws-redshift/test/cluster.test.ts | 62 + ...efaultTestDeployAssertBEF20992.assets.json | 19 + ...aultTestDeployAssertBEF20992.template.json | 36 + .../index.js | 253 ++++ .../integ.cluster-iamrole.js.snapshot/cdk.out | 1 + .../integ.json | 12 + .../manifest.json | 333 +++++ .../redshift-iamrole-integ.assets.json | 32 + .../redshift-iamrole-integ.template.json | 787 ++++++++++ .../tree.json | 1269 +++++++++++++++++ .../test/integ.cluster-iamrole.ts | 42 + 12 files changed, 2899 insertions(+) create mode 100644 packages/@aws-cdk/aws-redshift/test/integ.cluster-iamrole.js.snapshot/IamRoleIntegDefaultTestDeployAssertBEF20992.assets.json create mode 100644 packages/@aws-cdk/aws-redshift/test/integ.cluster-iamrole.js.snapshot/IamRoleIntegDefaultTestDeployAssertBEF20992.template.json create mode 100644 packages/@aws-cdk/aws-redshift/test/integ.cluster-iamrole.js.snapshot/asset.a268caa53756f51bda8ad5f499be4ed8484a81b314811806fbb66f874837c476/index.js create mode 100644 packages/@aws-cdk/aws-redshift/test/integ.cluster-iamrole.js.snapshot/cdk.out create mode 100644 packages/@aws-cdk/aws-redshift/test/integ.cluster-iamrole.js.snapshot/integ.json create mode 100644 packages/@aws-cdk/aws-redshift/test/integ.cluster-iamrole.js.snapshot/manifest.json create mode 100644 packages/@aws-cdk/aws-redshift/test/integ.cluster-iamrole.js.snapshot/redshift-iamrole-integ.assets.json create mode 100644 packages/@aws-cdk/aws-redshift/test/integ.cluster-iamrole.js.snapshot/redshift-iamrole-integ.template.json create mode 100644 packages/@aws-cdk/aws-redshift/test/integ.cluster-iamrole.js.snapshot/tree.json create mode 100644 packages/@aws-cdk/aws-redshift/test/integ.cluster-iamrole.ts diff --git a/packages/@aws-cdk/aws-redshift/lib/cluster.ts b/packages/@aws-cdk/aws-redshift/lib/cluster.ts index d09c11b19b51a..aeca70387952d 100644 --- a/packages/@aws-cdk/aws-redshift/lib/cluster.ts +++ b/packages/@aws-cdk/aws-redshift/lib/cluster.ts @@ -4,6 +4,7 @@ import * as kms from '@aws-cdk/aws-kms'; import * as s3 from '@aws-cdk/aws-s3'; import * as secretsmanager from '@aws-cdk/aws-secretsmanager'; import { Duration, IResource, RemovalPolicy, Resource, SecretValue, Token } from '@aws-cdk/core'; +import { AwsCustomResource, AwsCustomResourcePolicy, PhysicalResourceId } from '@aws-cdk/custom-resources'; import { Construct } from 'constructs'; import { DatabaseSecret } from './database-secret'; import { Endpoint } from './endpoint'; @@ -662,4 +663,56 @@ export class Cluster extends ClusterBase { throw new Error('Cannot add a parameter to an imported parameter group.'); } } + + /** + * Adds a role to the cluster + * + * @param role the role to add + * [disable-awslint:ref-via-interface] + */ + public addIamRole(role: iam.Role): void { + const clusterRoleList = this.cluster.iamRoles ?? []; + + if (clusterRoleList.includes(role.roleArn)) { + throw new Error('Role is already attached to the cluster'); + } + if (clusterRoleList.length >= 10) { + throw new Error('Maximum number of IAM roles for a cluster is 10'); + } + + // On UPDATE or CREATE define the new list of roles. On DELETE, detech the role from the cluster + const roleCustomResource = new AwsCustomResource(this, `add-role-${role.node.id}`, { + onUpdate: { + service: 'Redshift', + action: 'modifyClusterIamRoles', + parameters: { + ClusterIdentifier: this.cluster.ref, + AddIamRoles: [role.roleArn], + }, + physicalResourceId: PhysicalResourceId.of( + `${role.roleArn}-${this.cluster.ref}`, + ), + }, + onDelete: { + service: 'Redshift', + action: 'modifyClusterIamRoles', + parameters: { + ClusterIdentifier: this.cluster.ref, + RemoveIamRoles: [role.roleArn], + }, + physicalResourceId: PhysicalResourceId.of( + `${role.roleArn}-${this.cluster.ref}`, + ), + }, + policy: AwsCustomResourcePolicy.fromSdkCalls({ + resources: AwsCustomResourcePolicy.ANY_RESOURCE, + }), + resourceType: 'Custom::ModifyClusterIamRoles', + }); + + // eslint-disable-next-line no-console + console.log(roleCustomResource.getResponseField('IamRoles')); + + role.grantPassRole(roleCustomResource.grantPrincipal); + } } diff --git a/packages/@aws-cdk/aws-redshift/test/cluster.test.ts b/packages/@aws-cdk/aws-redshift/test/cluster.test.ts index 49f52e91dadb3..b11f48c24c08a 100644 --- a/packages/@aws-cdk/aws-redshift/test/cluster.test.ts +++ b/packages/@aws-cdk/aws-redshift/test/cluster.test.ts @@ -1,5 +1,6 @@ import { Match, Template } from '@aws-cdk/assertions'; import * as ec2 from '@aws-cdk/aws-ec2'; +import * as iam from '@aws-cdk/aws-iam'; import * as kms from '@aws-cdk/aws-kms'; import * as s3 from '@aws-cdk/aws-s3'; import * as cdk from '@aws-cdk/core'; @@ -613,6 +614,67 @@ test('elastic ip address', () => { }); }); +describe('IAM role', () => { + test('adding a role after cluster declaration creates a custom resource', () => { + // GIVEN + const cluster = new Cluster(stack, 'Redshift', { + masterUser: { + masterUsername: 'admin', + }, + vpc, + }); + + // WHEN + cluster.addIamRole(new iam.Role(stack, 'Role', { + assumedBy: new iam.ServicePrincipal('redshift.amazonaws.com'), + })); + + // THEN + Template.fromStack(stack).hasResource('Custom::RedshiftAddRole', {}); + }); + + test('throws when adding more than 10 roles to cluster', () => { + // GIVEN + const cluster = new Cluster(stack, 'Redshift', { + masterUser: { + masterUsername: 'admin', + }, + vpc, + roles: new Array(10).fill(0).map((_, i) => new iam.Role(stack, `Role${i+1}`, { + assumedBy: new iam.ServicePrincipal('redshift.amazonaws.com'), + })), + }); + + expect(() => + // WHEN + cluster.addIamRole(new iam.Role(stack, 'Role11', { + assumedBy: new iam.ServicePrincipal('redshift.amazonaws.com'), + })), + // THEN + ).toThrow(/Maximum number of IAM roles for a cluster is 10/); + }); + + test('throws when adding role that is already in cluster', () => { + // GIVEN + const role = new iam.Role(stack, 'Role', { + assumedBy: new iam.ServicePrincipal('redshift.amazonaws.com'), + }); + const cluster = new Cluster(stack, 'Redshift', { + masterUser: { + masterUsername: 'admin', + }, + vpc, + roles: [role], + }); + + expect(() => + // WHEN + cluster.addIamRole(role), + // THEN + ).toThrow(/Role is already attached to the cluster/); + }); +}); + function testStack() { const newTestStack = new cdk.Stack(undefined, undefined, { env: { account: '12345', region: 'us-test-1' } }); newTestStack.node.setContext('availability-zones:12345:us-test-1', ['us-test-1a', 'us-test-1b']); diff --git a/packages/@aws-cdk/aws-redshift/test/integ.cluster-iamrole.js.snapshot/IamRoleIntegDefaultTestDeployAssertBEF20992.assets.json b/packages/@aws-cdk/aws-redshift/test/integ.cluster-iamrole.js.snapshot/IamRoleIntegDefaultTestDeployAssertBEF20992.assets.json new file mode 100644 index 0000000000000..903f4ed2bae4c --- /dev/null +++ b/packages/@aws-cdk/aws-redshift/test/integ.cluster-iamrole.js.snapshot/IamRoleIntegDefaultTestDeployAssertBEF20992.assets.json @@ -0,0 +1,19 @@ +{ + "version": "29.0.0", + "files": { + "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22": { + "source": { + "path": "IamRoleIntegDefaultTestDeployAssertBEF20992.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22.json", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + } + }, + "dockerImages": {} +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-redshift/test/integ.cluster-iamrole.js.snapshot/IamRoleIntegDefaultTestDeployAssertBEF20992.template.json b/packages/@aws-cdk/aws-redshift/test/integ.cluster-iamrole.js.snapshot/IamRoleIntegDefaultTestDeployAssertBEF20992.template.json new file mode 100644 index 0000000000000..ad9d0fb73d1dd --- /dev/null +++ b/packages/@aws-cdk/aws-redshift/test/integ.cluster-iamrole.js.snapshot/IamRoleIntegDefaultTestDeployAssertBEF20992.template.json @@ -0,0 +1,36 @@ +{ + "Parameters": { + "BootstrapVersion": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" + } + }, + "Rules": { + "CheckBootstrapVersion": { + "Assertions": [ + { + "Assert": { + "Fn::Not": [ + { + "Fn::Contains": [ + [ + "1", + "2", + "3", + "4", + "5" + ], + { + "Ref": "BootstrapVersion" + } + ] + } + ] + }, + "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." + } + ] + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-redshift/test/integ.cluster-iamrole.js.snapshot/asset.a268caa53756f51bda8ad5f499be4ed8484a81b314811806fbb66f874837c476/index.js b/packages/@aws-cdk/aws-redshift/test/integ.cluster-iamrole.js.snapshot/asset.a268caa53756f51bda8ad5f499be4ed8484a81b314811806fbb66f874837c476/index.js new file mode 100644 index 0000000000000..d913ab9defaa1 --- /dev/null +++ b/packages/@aws-cdk/aws-redshift/test/integ.cluster-iamrole.js.snapshot/asset.a268caa53756f51bda8ad5f499be4ed8484a81b314811806fbb66f874837c476/index.js @@ -0,0 +1,253 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.handler = exports.forceSdkInstallation = exports.flatten = exports.PHYSICAL_RESOURCE_ID_REFERENCE = void 0; +/* eslint-disable no-console */ +const child_process_1 = require("child_process"); +const fs = require("fs"); +const path_1 = require("path"); +/** + * Serialized form of the physical resource id for use in the operation parameters + */ +exports.PHYSICAL_RESOURCE_ID_REFERENCE = 'PHYSICAL:RESOURCEID:'; +/** + * Flattens a nested object + * + * @param object the object to be flattened + * @returns a flat object with path as keys + */ +function flatten(object) { + return Object.assign({}, ...function _flatten(child, path = []) { + return [].concat(...Object.keys(child) + .map(key => { + const childKey = Buffer.isBuffer(child[key]) ? child[key].toString('utf8') : child[key]; + return typeof childKey === 'object' && childKey !== null + ? _flatten(childKey, path.concat([key])) + : ({ [path.concat([key]).join('.')]: childKey }); + })); + }(object)); +} +exports.flatten = flatten; +/** + * Decodes encoded special values (physicalResourceId) + */ +function decodeSpecialValues(object, physicalResourceId) { + return JSON.parse(JSON.stringify(object), (_k, v) => { + switch (v) { + case exports.PHYSICAL_RESOURCE_ID_REFERENCE: + return physicalResourceId; + default: + return v; + } + }); +} +/** + * Filters the keys of an object. + */ +function filterKeys(object, pred) { + return Object.entries(object) + .reduce((acc, [k, v]) => pred(k) + ? { ...acc, [k]: v } + : acc, {}); +} +let latestSdkInstalled = false; +function forceSdkInstallation() { + latestSdkInstalled = false; +} +exports.forceSdkInstallation = forceSdkInstallation; +/** + * Installs latest AWS SDK v2 + */ +function installLatestSdk() { + console.log('Installing latest AWS SDK v2'); + // Both HOME and --prefix are needed here because /tmp is the only writable location + child_process_1.execSync('HOME=/tmp npm install aws-sdk@2 --production --no-package-lock --no-save --prefix /tmp'); + latestSdkInstalled = true; +} +// no currently patched services +const patchedServices = []; +/** + * Patches the AWS SDK by loading service models in the same manner as the actual SDK + */ +function patchSdk(awsSdk) { + const apiLoader = awsSdk.apiLoader; + patchedServices.forEach(({ serviceName, apiVersions }) => { + const lowerServiceName = serviceName.toLowerCase(); + if (!awsSdk.Service.hasService(lowerServiceName)) { + apiLoader.services[lowerServiceName] = {}; + awsSdk[serviceName] = awsSdk.Service.defineService(lowerServiceName, apiVersions); + } + else { + awsSdk.Service.addVersions(awsSdk[serviceName], apiVersions); + } + apiVersions.forEach(apiVersion => { + Object.defineProperty(apiLoader.services[lowerServiceName], apiVersion, { + get: function get() { + const modelFilePrefix = `aws-sdk-patch/${lowerServiceName}-${apiVersion}`; + const model = JSON.parse(fs.readFileSync(path_1.join(__dirname, `${modelFilePrefix}.service.json`), 'utf-8')); + model.paginators = JSON.parse(fs.readFileSync(path_1.join(__dirname, `${modelFilePrefix}.paginators.json`), 'utf-8')).pagination; + return model; + }, + enumerable: true, + configurable: true, + }); + }); + }); + return awsSdk; +} +/* eslint-disable @typescript-eslint/no-require-imports, import/no-extraneous-dependencies */ +async function handler(event, context) { + try { + let AWS; + if (!latestSdkInstalled && event.ResourceProperties.InstallLatestAwsSdk === 'true') { + try { + installLatestSdk(); + AWS = require('/tmp/node_modules/aws-sdk'); + } + catch (e) { + console.log(`Failed to install latest AWS SDK v2: ${e}`); + AWS = require('aws-sdk'); // Fallback to pre-installed version + } + } + else if (latestSdkInstalled) { + AWS = require('/tmp/node_modules/aws-sdk'); + } + else { + AWS = require('aws-sdk'); + } + try { + AWS = patchSdk(AWS); + } + catch (e) { + console.log(`Failed to patch AWS SDK: ${e}. Proceeding with the installed copy.`); + } + console.log(JSON.stringify({ ...event, ResponseURL: '...' })); + console.log('AWS SDK VERSION: ' + AWS.VERSION); + event.ResourceProperties.Create = decodeCall(event.ResourceProperties.Create); + event.ResourceProperties.Update = decodeCall(event.ResourceProperties.Update); + event.ResourceProperties.Delete = decodeCall(event.ResourceProperties.Delete); + // Default physical resource id + let physicalResourceId; + switch (event.RequestType) { + case 'Create': + physicalResourceId = event.ResourceProperties.Create?.physicalResourceId?.id ?? + event.ResourceProperties.Update?.physicalResourceId?.id ?? + event.ResourceProperties.Delete?.physicalResourceId?.id ?? + event.LogicalResourceId; + break; + case 'Update': + case 'Delete': + physicalResourceId = event.ResourceProperties[event.RequestType]?.physicalResourceId?.id ?? event.PhysicalResourceId; + break; + } + let flatData = {}; + let data = {}; + const call = event.ResourceProperties[event.RequestType]; + if (call) { + let credentials; + if (call.assumedRoleArn) { + const timestamp = (new Date()).getTime(); + const params = { + RoleArn: call.assumedRoleArn, + RoleSessionName: `${timestamp}-${physicalResourceId}`.substring(0, 64), + }; + credentials = new AWS.ChainableTemporaryCredentials({ + params: params, + stsConfig: { stsRegionalEndpoints: 'regional' }, + }); + } + if (!Object.prototype.hasOwnProperty.call(AWS, call.service)) { + throw Error(`Service ${call.service} does not exist in AWS SDK version ${AWS.VERSION}.`); + } + const awsService = new AWS[call.service]({ + apiVersion: call.apiVersion, + credentials: credentials, + region: call.region, + }); + try { + const response = await awsService[call.action](call.parameters && decodeSpecialValues(call.parameters, physicalResourceId)).promise(); + flatData = { + apiVersion: awsService.config.apiVersion, + region: awsService.config.region, + ...flatten(response), + }; + let outputPaths; + if (call.outputPath) { + outputPaths = [call.outputPath]; + } + else if (call.outputPaths) { + outputPaths = call.outputPaths; + } + if (outputPaths) { + data = filterKeys(flatData, startsWithOneOf(outputPaths)); + } + else { + data = flatData; + } + } + catch (e) { + if (!call.ignoreErrorCodesMatching || !new RegExp(call.ignoreErrorCodesMatching).test(e.code)) { + throw e; + } + } + if (call.physicalResourceId?.responsePath) { + physicalResourceId = flatData[call.physicalResourceId.responsePath]; + } + } + await respond('SUCCESS', 'OK', physicalResourceId, data); + } + catch (e) { + console.log(e); + await respond('FAILED', e.message || 'Internal Error', context.logStreamName, {}); + } + function respond(responseStatus, reason, physicalResourceId, data) { + const responseBody = JSON.stringify({ + Status: responseStatus, + Reason: reason, + PhysicalResourceId: physicalResourceId, + StackId: event.StackId, + RequestId: event.RequestId, + LogicalResourceId: event.LogicalResourceId, + NoEcho: false, + Data: data, + }); + console.log('Responding', responseBody); + // eslint-disable-next-line @typescript-eslint/no-require-imports + const parsedUrl = require('url').parse(event.ResponseURL); + const requestOptions = { + hostname: parsedUrl.hostname, + path: parsedUrl.path, + method: 'PUT', + headers: { 'content-type': '', 'content-length': responseBody.length }, + }; + return new Promise((resolve, reject) => { + try { + // eslint-disable-next-line @typescript-eslint/no-require-imports + const request = require('https').request(requestOptions, resolve); + request.on('error', reject); + request.write(responseBody); + request.end(); + } + catch (e) { + reject(e); + } + }); + } +} +exports.handler = handler; +function decodeCall(call) { + if (!call) { + return undefined; + } + return JSON.parse(call); +} +function startsWithOneOf(searchStrings) { + return function (string) { + for (const searchString of searchStrings) { + if (string.startsWith(searchString)) { + return true; + } + } + return false; + }; +} +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJpbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFBQSwrQkFBK0I7QUFDL0IsaURBQXlDO0FBQ3pDLHlCQUF5QjtBQUN6QiwrQkFBNEI7QUFTNUI7O0dBRUc7QUFDVSxRQUFBLDhCQUE4QixHQUFHLHNCQUFzQixDQUFDO0FBRXJFOzs7OztHQUtHO0FBQ0gsU0FBZ0IsT0FBTyxDQUFDLE1BQWM7SUFDcEMsT0FBTyxNQUFNLENBQUMsTUFBTSxDQUNsQixFQUFFLEVBQ0YsR0FBRyxTQUFTLFFBQVEsQ0FBQyxLQUFVLEVBQUUsT0FBaUIsRUFBRTtRQUNsRCxPQUFPLEVBQUUsQ0FBQyxNQUFNLENBQUMsR0FBRyxNQUFNLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQzthQUNuQyxHQUFHLENBQUMsR0FBRyxDQUFDLEVBQUU7WUFDVCxNQUFNLFFBQVEsR0FBRyxNQUFNLENBQUMsUUFBUSxDQUFDLEtBQUssQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxLQUFLLENBQUMsR0FBRyxDQUFDLENBQUMsUUFBUSxDQUFDLE1BQU0sQ0FBQyxDQUFDLENBQUMsQ0FBQyxLQUFLLENBQUMsR0FBRyxDQUFDLENBQUM7WUFDeEYsT0FBTyxPQUFPLFFBQVEsS0FBSyxRQUFRLElBQUksUUFBUSxLQUFLLElBQUk7Z0JBQ3RELENBQUMsQ0FBQyxRQUFRLENBQUMsUUFBUSxFQUFFLElBQUksQ0FBQyxNQUFNLENBQUMsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDO2dCQUN4QyxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxDQUFDLEVBQUUsUUFBUSxFQUFFLENBQUMsQ0FBQztRQUNyRCxDQUFDLENBQUMsQ0FBQyxDQUFDO0lBQ1IsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxDQUNWLENBQUM7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LE9BQU8sR0FBRyxPQUFPLENBQUMsT0FBTyxDQUFDLENBQUMsT0FBTyxDQUFDLGNBQWMsRUFBRSxPQUFPLENBQUMsQ0FBQztnQkFDbEUsT0FBTyxDQUFDLEVBQUUsQ0FBQyxPQUFPLEVBQUUsTUFBTSxDQUFDLENBQUM7Z0JBQzVCLE9BQU8sQ0FBQyxLQUFLLENBQUMsWUFBWSxDQUFDLENBQUM7Z0JBQzVCLE9BQU8sQ0FBQyxHQUFHLEVBQUUsQ0FBQzthQUNmO1lBQUMsT0FBTyxDQUFDLEVBQUU7Z0JBQ1YsTUFBTSxDQUFDLENBQUMsQ0FBQyxDQUFDO2FBQ1g7UUFDSCxDQUFDLENBQUMsQ0FBQztJQUNMLENBQUM7QUFDSCxDQUFDO0FBbEpELDBCQWtKQztBQUVELFNBQVMsVUFBVSxDQUFDLElBQXdCO0lBQzFDLElBQUksQ0FBQyxJQUFJLEVBQUU7UUFBRSxPQUFPLFNBQVMsQ0FBQztLQUFFO0lBQ2hDLE9BQU8sSUFBSSxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsQ0FBQztBQUMxQixDQUFDO0FBRUQsU0FBUyxlQUFlLENBQUMsYUFBdUI7SUFDOUMsT0FBTyxVQUFTLE1BQWM7UUFDNUIsS0FBSyxNQUFNLFlBQVksSUFBSSxhQUFhLEVBQUU7WUFDeEMsSUFBSSxNQUFNLENBQUMsVUFBVSxDQUFDLFlBQVksQ0FBQyxFQUFFO2dCQUNuQyxPQUFPLElBQUksQ0FBQzthQUNiO1NBQ0Y7UUFDRCxPQUFPLEtBQUssQ0FBQztJQUNmLENBQUMsQ0FBQztBQUNKLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvKiBlc2xpbnQtZGlzYWJsZSBuby1jb25zb2xlICovXG5pbXBvcnQgeyBleGVjU3luYyB9IGZyb20gJ2NoaWxkX3Byb2Nlc3MnO1xuaW1wb3J0ICogYXMgZnMgZnJvbSAnZnMnO1xuaW1wb3J0IHsgam9pbiB9IGZyb20gJ3BhdGgnO1xuLy8gaW1wb3J0IHRoZSBBV1NMYW1iZGEgcGFja2FnZSBleHBsaWNpdGx5LFxuLy8gd2hpY2ggaXMgZ2xvYmFsbHkgYXZhaWxhYmxlIGluIHRoZSBMYW1iZGEgcnVudGltZSxcbi8vIGFzIG90aGVyd2lzZSBsaW5raW5nIHRoaXMgcmVwb3NpdG9yeSB3aXRoIGxpbmstYWxsLnNoXG4vLyBmYWlscyBpbiB0aGUgQ0RLIGFwcCBleGVjdXRlZCB3aXRoIHRzLW5vZGVcbi8qIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBpbXBvcnQvbm8tZXh0cmFuZW91cy1kZXBlbmRlbmNpZXMsaW1wb3J0L25vLXVucmVzb2x2ZWQgKi9cbmltcG9ydCAqIGFzIEFXU0xhbWJkYSBmcm9tICdhd3MtbGFtYmRhJztcbmltcG9ydCB7IEF3c1Nka0NhbGwgfSBmcm9tICcuLi9hd3MtY3VzdG9tLXJlc291cmNlJztcblxuLyoqXG4gKiBTZXJpYWxpemVkIGZvcm0gb2YgdGhlIHBoeXNpY2FsIHJlc291cmNlIGlkIGZvciB1c2UgaW4gdGhlIG9wZXJhdGlvbiBwYXJhbWV0ZXJzXG4gKi9cbmV4cG9ydCBjb25zdCBQSFlTSUNBTF9SRVNPVVJDRV9JRF9SRUZFUkVOQ0UgPSAnUEhZU0lDQUw6UkVTT1VSQ0VJRDonO1xuXG4vKipcbiAqIEZsYXR0ZW5zIGEgbmVzdGVkIG9iamVjdFxuICpcbiAqIEBwYXJhbSBvYmplY3QgdGhlIG9iamVjdCB0byBiZSBmbGF0dGVuZWRcbiAqIEByZXR1cm5zIGEgZmxhdCBvYmplY3Qgd2l0aCBwYXRoIGFzIGtleXNcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGZsYXR0ZW4ob2JqZWN0OiBvYmplY3QpOiB7IFtrZXk6IHN0cmluZ106IGFueSB9IHtcbiAgcmV0dXJuIE9iamVjdC5hc3NpZ24oXG4gICAge30sXG4gICAgLi4uZnVuY3Rpb24gX2ZsYXR0ZW4oY2hpbGQ6IGFueSwgcGF0aDogc3RyaW5nW10gPSBbXSk6IGFueSB7XG4gICAgICByZXR1cm4gW10uY29uY2F0KC4uLk9iamVjdC5rZXlzKGNoaWxkKVxuICAgICAgICAubWFwKGtleSA9PiB7XG4gICAgICAgICAgY29uc3QgY2hpbGRLZXkgPSBCdWZmZXIuaXNCdWZmZXIoY2hpbGRba2V5XSkgPyBjaGlsZFtrZXldLnRvU3RyaW5nKCd1dGY4JykgOiBjaGlsZFtrZXldO1xuICAgICAgICAgIHJldHVybiB0eXBlb2YgY2hpbGRLZXkgPT09ICdvYmplY3QnICYmIGNoaWxkS2V5ICE9PSBudWxsXG4gICAgICAgICAgICA/IF9mbGF0dGVuKGNoaWxkS2V5LCBwYXRoLmNvbmNhdChba2V5XSkpXG4gICAgICAgICAgICA6ICh7IFtwYXRoLmNvbmNhdChba2V5XSkuam9pbignLicpXTogY2hpbGRLZXkgfSk7XG4gICAgICAgIH0pKTtcbiAgICB9KG9iamVjdCksXG4gICk7XG59XG5cbi8qKlxuICogRGVjb2RlcyBlbmNvZGVkIHNwZWNpYWwgdmFsdWVzIChwaHlzaWNhbFJlc291cmNlSWQpXG4gKi9cbmZ1bmN0aW9uIGRlY29kZVNwZWNpYWxWYWx1ZXMob2JqZWN0OiBvYmplY3QsIHBoeXNpY2FsUmVzb3VyY2VJZDogc3RyaW5nKSB7XG4gIHJldHVybiBKU09OLnBhcnNlKEpTT04uc3RyaW5naWZ5KG9iamVjdCksIChfaywgdikgPT4ge1xuICAgIHN3aXRjaCAodikge1xuICAgICAgY2FzZSBQSFlTSUNBTF9SRVNPVVJDRV9JRF9SRUZFUkVOQ0U6XG4gICAgICAgIHJldHVybiBwaHlzaWNhbFJlc291cmNlSWQ7XG4gICAgICBkZWZhdWx0OlxuICAgICAgICByZXR1cm4gdjtcbiAgICB9XG4gIH0pO1xufVxuXG4vKipcbiAqIEZpbHRlcnMgdGhlIGtleXMgb2YgYW4gb2JqZWN0LlxuICovXG5mdW5jdGlvbiBmaWx0ZXJLZXlzKG9iamVjdDogb2JqZWN0LCBwcmVkOiAoa2V5OiBzdHJpbmcpID0+IGJvb2xlYW4pIHtcbiAgcmV0dXJuIE9iamVjdC5lbnRyaWVzKG9iamVjdClcbiAgICAucmVkdWNlKFxuICAgICAgKGFjYywgW2ssIHZdKSA9PiBwcmVkKGspXG4gICAgICAgID8geyAuLi5hY2MsIFtrXTogdiB9XG4gICAgICAgIDogYWNjLFxuICAgICAge30sXG4gICAgKTtcbn1cblxubGV0IGxhdGVzdFNka0luc3RhbGxlZCA9IGZhbHNlO1xuXG5leHBvcnQgZnVuY3Rpb24gZm9yY2VTZGtJbnN0YWxsYXRpb24oKSB7XG4gIGxhdGVzdFNka0luc3RhbGxlZCA9IGZhbHNlO1xufVxuXG4vKipcbiAqIEluc3RhbGxzIGxhdGVzdCBBV1MgU0RLIHYyXG4gKi9cbmZ1bmN0aW9uIGluc3RhbGxMYXRlc3RTZGsoKTogdm9pZCB7XG4gIGNvbnNvbGUubG9nKCdJbnN0YWxsaW5nIGxhdGVzdCBBV1MgU0RLIHYyJyk7XG4gIC8vIEJvdGggSE9NRSBhbmQgLS1wcmVmaXggYXJlIG5lZWRlZCBoZXJlIGJlY2F1c2UgL3RtcCBpcyB0aGUgb25seSB3cml0YWJsZSBsb2NhdGlvblxuICBleGVjU3luYygnSE9NRT0vdG1wIG5wbSBpbnN0YWxsIGF3cy1zZGtAMiAtLXByb2R1Y3Rpb24gLS1uby1wYWNrYWdlLWxvY2sgLS1uby1zYXZlIC0tcHJlZml4IC90bXAnKTtcbiAgbGF0ZXN0U2RrSW5zdGFsbGVkID0gdHJ1ZTtcbn1cblxuLy8gbm8gY3VycmVudGx5IHBhdGNoZWQgc2VydmljZXNcbmNvbnN0IHBhdGNoZWRTZXJ2aWNlczogeyBzZXJ2aWNlTmFtZTogc3RyaW5nOyBhcGlWZXJzaW9uczogc3RyaW5nW10gfVtdID0gW107XG4vKipcbiAqIFBhdGNoZXMgdGhlIEFXUyBTREsgYnkgbG9hZGluZyBzZXJ2aWNlIG1vZGVscyBpbiB0aGUgc2FtZSBtYW5uZXIgYXMgdGhlIGFjdHVhbCBTREtcbiAqL1xuZnVuY3Rpb24gcGF0Y2hTZGsoYXdzU2RrOiBhbnkpOiBhbnkge1xuICBjb25zdCBhcGlMb2FkZXIgPSBhd3NTZGsuYXBpTG9hZGVyO1xuICBwYXRjaGVkU2VydmljZXMuZm9yRWFjaCgoeyBzZXJ2aWNlTmFtZSwgYXBpVmVyc2lvbnMgfSkgPT4ge1xuICAgIGNvbnN0IGxvd2VyU2VydmljZU5hbWUgPSBzZXJ2aWNlTmFtZS50b0xvd2VyQ2FzZSgpO1xuICAgIGlmICghYXdzU2RrLlNlcnZpY2UuaGFzU2VydmljZShsb3dlclNlcnZpY2VOYW1lKSkge1xuICAgICAgYXBpTG9hZGVyLnNlcnZpY2VzW2xvd2VyU2VydmljZU5hbWVdID0ge307XG4gICAgICBhd3NTZGtbc2VydmljZU5hbWVdID0gYXdzU2RrLlNlcnZpY2UuZGVmaW5lU2VydmljZShsb3dlclNlcnZpY2VOYW1lLCBhcGlWZXJzaW9ucyk7XG4gICAgfSBlbHNlIHtcbiAgICAgIGF3c1Nkay5TZXJ2aWNlLmFkZFZlcnNpb25zKGF3c1Nka1tzZXJ2aWNlTmFtZV0sIGFwaVZlcnNpb25zKTtcbiAgICB9XG4gICAgYXBpVmVyc2lvbnMuZm9yRWFjaChhcGlWZXJzaW9uID0+IHtcbiAgICAgIE9iamVjdC5kZWZpbmVQcm9wZXJ0eShhcGlMb2FkZXIuc2VydmljZXNbbG93ZXJTZXJ2aWNlTmFtZV0sIGFwaVZlcnNpb24sIHtcbiAgICAgICAgZ2V0OiBmdW5jdGlvbiBnZXQoKSB7XG4gICAgICAgICAgY29uc3QgbW9kZWxGaWxlUHJlZml4ID0gYGF3cy1zZGstcGF0Y2gvJHtsb3dlclNlcnZpY2VOYW1lfS0ke2FwaVZlcnNpb259YDtcbiAgICAgICAgICBjb25zdCBtb2RlbCA9IEpTT04ucGFyc2UoZnMucmVhZEZpbGVTeW5jKGpvaW4oX19kaXJuYW1lLCBgJHttb2RlbEZpbGVQcmVmaXh9LnNlcnZpY2UuanNvbmApLCAndXRmLTgnKSk7XG4gICAgICAgICAgbW9kZWwucGFnaW5hdG9ycyA9IEpTT04ucGFyc2UoZnMucmVhZEZpbGVTeW5jKGpvaW4oX19kaXJuYW1lLCBgJHttb2RlbEZpbGVQcmVmaXh9LnBhZ2luYXRvcnMuanNvbmApLCAndXRmLTgnKSkucGFnaW5hdGlvbjtcbiAgICAgICAgICByZXR1cm4gbW9kZWw7XG4gICAgICAgIH0sXG4gICAgICAgIGVudW1lcmFibGU6IHRydWUsXG4gICAgICAgIGNvbmZpZ3VyYWJsZTogdHJ1ZSxcbiAgICAgIH0pO1xuICAgIH0pO1xuICB9KTtcbiAgcmV0dXJuIGF3c1Nkaztcbn1cblxuLyogZXNsaW50LWRpc2FibGUgQHR5cGVzY3JpcHQtZXNsaW50L25vLXJlcXVpcmUtaW1wb3J0cywgaW1wb3J0L25vLWV4dHJhbmVvdXMtZGVwZW5kZW5jaWVzICovXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gaGFuZGxlcihldmVudDogQVdTTGFtYmRhLkNsb3VkRm9ybWF0aW9uQ3VzdG9tUmVzb3VyY2VFdmVudCwgY29udGV4dDogQVdTTGFtYmRhLkNvbnRleHQpIHtcbiAgdHJ5IHtcbiAgICBsZXQgQVdTOiBhbnk7XG4gICAgaWYgKCFsYXRlc3RTZGtJbnN0YWxsZWQgJiYgZXZlbnQuUmVzb3VyY2VQcm9wZXJ0aWVzLkluc3RhbGxMYXRlc3RBd3NTZGsgPT09ICd0cnVlJykge1xuICAgICAgdHJ5IHtcbiAgICAgICAgaW5zdGFsbExhdGVzdFNkaygpO1xuICAgICAgICBBV1MgPSByZXF1aXJlKCcvdG1wL25vZGVfbW9kdWxlcy9hd3Mtc2RrJyk7XG4gICAgICB9IGNhdGNoIChlKSB7XG4gICAgICAgIGNvbnNvbGUubG9nKGBGYWlsZWQgdG8gaW5zdGFsbCBsYXRlc3QgQVdTIFNESyB2MjogJHtlfWApO1xuICAgICAgICBBV1MgPSByZXF1aXJlKCdhd3Mtc2RrJyk7IC8vIEZhbGxiYWNrIHRvIHByZS1pbnN0YWxsZWQgdmVyc2lvblxuICAgICAgfVxuICAgIH0gZWxzZSBpZiAobGF0ZXN0U2RrSW5zdGFsbGVkKSB7XG4gICAgICBBV1MgPSByZXF1aXJlKCcvdG1wL25vZGVfbW9kdWxlcy9hd3Mtc2RrJyk7XG4gICAgfSBlbHNlIHtcbiAgICAgIEFXUyA9IHJlcXVpcmUoJ2F3cy1zZGsnKTtcbiAgICB9XG4gICAgdHJ5IHtcbiAgICAgIEFXUyA9IHBhdGNoU2RrKEFXUyk7XG4gICAgfSBjYXRjaCAoZSkge1xuICAgICAgY29uc29sZS5sb2coYEZhaWxlZCB0byBwYXRjaCBBV1MgU0RLOiAke2V9LiBQcm9jZWVkaW5nIHdpdGggdGhlIGluc3RhbGxlZCBjb3B5LmApO1xuICAgIH1cblxuICAgIGNvbnNvbGUubG9nKEpTT04uc3RyaW5naWZ5KHsgLi4uZXZlbnQsIFJlc3BvbnNlVVJMOiAnLi4uJyB9KSk7XG4gICAgY29uc29sZS5sb2coJ0FXUyBTREsgVkVSU0lPTjogJyArIEFXUy5WRVJTSU9OKTtcblxuICAgIGV2ZW50LlJlc291cmNlUHJvcGVydGllcy5DcmVhdGUgPSBkZWNvZGVDYWxsKGV2ZW50LlJlc291cmNlUHJvcGVydGllcy5DcmVhdGUpO1xuICAgIGV2ZW50LlJlc291cmNlUHJvcGVydGllcy5VcGRhdGUgPSBkZWNvZGVDYWxsKGV2ZW50LlJlc291cmNlUHJvcGVydGllcy5VcGRhdGUpO1xuICAgIGV2ZW50LlJlc291cmNlUHJvcGVydGllcy5EZWxldGUgPSBkZWNvZGVDYWxsKGV2ZW50LlJlc291cmNlUHJvcGVydGllcy5EZWxldGUpO1xuICAgIC8vIERlZmF1bHQgcGh5c2ljYWwgcmVzb3VyY2UgaWRcbiAgICBsZXQgcGh5c2ljYWxSZXNvdXJjZUlkOiBzdHJpbmc7XG4gICAgc3dpdGNoIChldmVudC5SZXF1ZXN0VHlwZSkge1xuICAgICAgY2FzZSAnQ3JlYXRlJzpcbiAgICAgICAgcGh5c2ljYWxSZXNvdXJjZUlkID0gZXZlbnQuUmVzb3VyY2VQcm9wZXJ0aWVzLkNyZWF0ZT8ucGh5c2ljYWxSZXNvdXJjZUlkPy5pZCA/P1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICBldmVudC5SZXNvdXJjZVByb3BlcnRpZXMuVXBkYXRlPy5waHlzaWNhbFJlc291cmNlSWQ/LmlkID8/XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgIGV2ZW50LlJlc291cmNlUHJvcGVydGllcy5EZWxldGU/LnBoeXNpY2FsUmVzb3VyY2VJZD8uaWQgPz9cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZXZlbnQuTG9naWNhbFJlc291cmNlSWQ7XG4gICAgICAgIGJyZWFrO1xuICAgICAgY2FzZSAnVXBkYXRlJzpcbiAgICAgIGNhc2UgJ0RlbGV0ZSc6XG4gICAgICAgIHBoeXNpY2FsUmVzb3VyY2VJZCA9IGV2ZW50LlJlc291cmNlUHJvcGVydGllc1tldmVudC5SZXF1ZXN0VHlwZV0/LnBoeXNpY2FsUmVzb3VyY2VJZD8uaWQgPz8gZXZlbnQuUGh5c2ljYWxSZXNvdXJjZUlkO1xuICAgICAgICBicmVhaztcbiAgICB9XG5cbiAgICBsZXQgZmxhdERhdGE6IHsgW2tleTogc3RyaW5nXTogc3RyaW5nIH0gPSB7fTtcbiAgICBsZXQgZGF0YTogeyBba2V5OiBzdHJpbmddOiBzdHJpbmcgfSA9IHt9O1xuICAgIGNvbnN0IGNhbGw6IEF3c1Nka0NhbGwgfCB1bmRlZmluZWQgPSBldmVudC5SZXNvdXJjZVByb3BlcnRpZXNbZXZlbnQuUmVxdWVzdFR5cGVdO1xuXG4gICAgaWYgKGNhbGwpIHtcblxuICAgICAgbGV0IGNyZWRlbnRpYWxzO1xuICAgICAgaWYgKGNhbGwuYXNzdW1lZFJvbGVBcm4pIHtcbiAgICAgICAgY29uc3QgdGltZXN0YW1wID0gKG5ldyBEYXRlKCkpLmdldFRpbWUoKTtcblxuICAgICAgICBjb25zdCBwYXJhbXMgPSB7XG4gICAgICAgICAgUm9sZUFybjogY2FsbC5hc3N1bWVkUm9sZUFybixcbiAgICAgICAgICBSb2xlU2Vzc2lvbk5hbWU6IGAke3RpbWVzdGFtcH0tJHtwaHlzaWNhbFJlc291cmNlSWR9YC5zdWJzdHJpbmcoMCwgNjQpLFxuICAgICAgICB9O1xuXG4gICAgICAgIGNyZWRlbnRpYWxzID0gbmV3IEFXUy5DaGFpbmFibGVUZW1wb3JhcnlDcmVkZW50aWFscyh7XG4gICAgICAgICAgcGFyYW1zOiBwYXJhbXMsXG4gICAgICAgICAgc3RzQ29uZmlnOiB7IHN0c1JlZ2lvbmFsRW5kcG9pbnRzOiAncmVnaW9uYWwnIH0sXG4gICAgICAgIH0pO1xuICAgICAgfVxuXG4gICAgICBpZiAoIU9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHkuY2FsbChBV1MsIGNhbGwuc2VydmljZSkpIHtcbiAgICAgICAgdGhyb3cgRXJyb3IoYFNlcnZpY2UgJHtjYWxsLnNlcnZpY2V9IGRvZXMgbm90IGV4aXN0IGluIEFXUyBTREsgdmVyc2lvbiAke0FXUy5WRVJTSU9OfS5gKTtcbiAgICAgIH1cbiAgICAgIGNvbnN0IGF3c1NlcnZpY2UgPSBuZXcgKEFXUyBhcyBhbnkpW2NhbGwuc2VydmljZV0oe1xuICAgICAgICBhcGlWZXJzaW9uOiBjYWxsLmFwaVZlcnNpb24sXG4gICAgICAgIGNyZWRlbnRpYWxzOiBjcmVkZW50aWFscyxcbiAgICAgICAgcmVnaW9uOiBjYWxsLnJlZ2lvbixcbiAgICAgIH0pO1xuXG4gICAgICB0cnkge1xuICAgICAgICBjb25zdCByZXNwb25zZSA9IGF3YWl0IGF3c1NlcnZpY2VbY2FsbC5hY3Rpb25dKFxuICAgICAgICAgIGNhbGwucGFyYW1ldGVycyAmJiBkZWNvZGVTcGVjaWFsVmFsdWVzKGNhbGwucGFyYW1ldGVycywgcGh5c2ljYWxSZXNvdXJjZUlkKSkucHJvbWlzZSgpO1xuICAgICAgICBmbGF0RGF0YSA9IHtcbiAgICAgICAgICBhcGlWZXJzaW9uOiBhd3NTZXJ2aWNlLmNvbmZpZy5hcGlWZXJzaW9uLCAvLyBGb3IgdGVzdCBwdXJwb3NlczogY2hlY2sgaWYgYXBpVmVyc2lvbiB3YXMgY29ycmVjdGx5IHBhc3NlZC5cbiAgICAgICAgICByZWdpb246IGF3c1NlcnZpY2UuY29uZmlnLnJlZ2lvbiwgLy8gRm9yIHRlc3QgcHVycG9zZXM6IGNoZWNrIGlmIHJlZ2lvbiB3YXMgY29ycmVjdGx5IHBhc3NlZC5cbiAgICAgICAgICAuLi5mbGF0dGVuKHJlc3BvbnNlKSxcbiAgICAgICAgfTtcblxuICAgICAgICBsZXQgb3V0cHV0UGF0aHM6IHN0cmluZ1tdIHwgdW5kZWZpbmVkO1xuICAgICAgICBpZiAoY2FsbC5vdXRwdXRQYXRoKSB7XG4gICAgICAgICAgb3V0cHV0UGF0aHMgPSBbY2FsbC5vdXRwdXRQYXRoXTtcbiAgICAgICAgfSBlbHNlIGlmIChjYWxsLm91dHB1dFBhdGhzKSB7XG4gICAgICAgICAgb3V0cHV0UGF0aHMgPSBjYWxsLm91dHB1dFBhdGhzO1xuICAgICAgICB9XG5cbiAgICAgICAgaWYgKG91dHB1dFBhdGhzKSB7XG4gICAgICAgICAgZGF0YSA9IGZpbHRlcktleXMoZmxhdERhdGEsIHN0YXJ0c1dpdGhPbmVPZihvdXRwdXRQYXRocykpO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIGRhdGEgPSBmbGF0RGF0YTtcbiAgICAgICAgfVxuICAgICAgfSBjYXRjaCAoZSkge1xuICAgICAgICBpZiAoIWNhbGwuaWdub3JlRXJyb3JDb2Rlc01hdGNoaW5nIHx8ICFuZXcgUmVnRXhwKGNhbGwuaWdub3JlRXJyb3JDb2Rlc01hdGNoaW5nKS50ZXN0KGUuY29kZSkpIHtcbiAgICAgICAgICB0aHJvdyBlO1xuICAgICAgICB9XG4gICAgICB9XG5cbiAgICAgIGlmIChjYWxsLnBoeXNpY2FsUmVzb3VyY2VJZD8ucmVzcG9uc2VQYXRoKSB7XG4gICAgICAgIHBoeXNpY2FsUmVzb3VyY2VJZCA9IGZsYXREYXRhW2NhbGwucGh5c2ljYWxSZXNvdXJjZUlkLnJlc3BvbnNlUGF0aF07XG4gICAgICB9XG4gICAgfVxuXG4gICAgYXdhaXQgcmVzcG9uZCgnU1VDQ0VTUycsICdPSycsIHBoeXNpY2FsUmVzb3VyY2VJZCwgZGF0YSk7XG4gIH0gY2F0Y2ggKGUpIHtcbiAgICBjb25zb2xlLmxvZyhlKTtcbiAgICBhd2FpdCByZXNwb25kKCdGQUlMRUQnLCBlLm1lc3NhZ2UgfHwgJ0ludGVybmFsIEVycm9yJywgY29udGV4dC5sb2dTdHJlYW1OYW1lLCB7fSk7XG4gIH1cblxuICBmdW5jdGlvbiByZXNwb25kKHJlc3BvbnNlU3RhdHVzOiBzdHJpbmcsIHJlYXNvbjogc3RyaW5nLCBwaHlzaWNhbFJlc291cmNlSWQ6IHN0cmluZywgZGF0YTogYW55KSB7XG4gICAgY29uc3QgcmVzcG9uc2VCb2R5ID0gSlNPTi5zdHJpbmdpZnkoe1xuICAgICAgU3RhdHVzOiByZXNwb25zZVN0YXR1cyxcbiAgICAgIFJlYXNvbjogcmVhc29uLFxuICAgICAgUGh5c2ljYWxSZXNvdXJjZUlkOiBwaHlzaWNhbFJlc291cmNlSWQsXG4gICAgICBTdGFja0lkOiBldmVudC5TdGFja0lkLFxuICAgICAgUmVxdWVzdElkOiBldmVudC5SZXF1ZXN0SWQsXG4gICAgICBMb2dpY2FsUmVzb3VyY2VJZDogZXZlbnQuTG9naWNhbFJlc291cmNlSWQsXG4gICAgICBOb0VjaG86IGZhbHNlLFxuICAgICAgRGF0YTogZGF0YSxcbiAgICB9KTtcblxuICAgIGNvbnNvbGUubG9nKCdSZXNwb25kaW5nJywgcmVzcG9uc2VCb2R5KTtcblxuICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBAdHlwZXNjcmlwdC1lc2xpbnQvbm8tcmVxdWlyZS1pbXBvcnRzXG4gICAgY29uc3QgcGFyc2VkVXJsID0gcmVxdWlyZSgndXJsJykucGFyc2UoZXZlbnQuUmVzcG9uc2VVUkwpO1xuICAgIGNvbnN0IHJlcXVlc3RPcHRpb25zID0ge1xuICAgICAgaG9zdG5hbWU6IHBhcnNlZFVybC5ob3N0bmFtZSxcbiAgICAgIHBhdGg6IHBhcnNlZFVybC5wYXRoLFxuICAgICAgbWV0aG9kOiAnUFVUJyxcbiAgICAgIGhlYWRlcnM6IHsgJ2NvbnRlbnQtdHlwZSc6ICcnLCAnY29udGVudC1sZW5ndGgnOiByZXNwb25zZUJvZHkubGVuZ3RoIH0sXG4gICAgfTtcblxuICAgIHJldHVybiBuZXcgUHJvbWlzZSgocmVzb2x2ZSwgcmVqZWN0KSA9PiB7XG4gICAgICB0cnkge1xuICAgICAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgQHR5cGVzY3JpcHQtZXNsaW50L25vLXJlcXVpcmUtaW1wb3J0c1xuICAgICAgICBjb25zdCByZXF1ZXN0ID0gcmVxdWlyZSgnaHR0cHMnKS5yZXF1ZXN0KHJlcXVlc3RPcHRpb25zLCByZXNvbHZlKTtcbiAgICAgICAgcmVxdWVzdC5vbignZXJyb3InLCByZWplY3QpO1xuICAgICAgICByZXF1ZXN0LndyaXRlKHJlc3BvbnNlQm9keSk7XG4gICAgICAgIHJlcXVlc3QuZW5kKCk7XG4gICAgICB9IGNhdGNoIChlKSB7XG4gICAgICAgIHJlamVjdChlKTtcbiAgICAgIH1cbiAgICB9KTtcbiAgfVxufVxuXG5mdW5jdGlvbiBkZWNvZGVDYWxsKGNhbGw6IHN0cmluZyB8IHVuZGVmaW5lZCkge1xuICBpZiAoIWNhbGwpIHsgcmV0dXJuIHVuZGVmaW5lZDsgfVxuICByZXR1cm4gSlNPTi5wYXJzZShjYWxsKTtcbn1cblxuZnVuY3Rpb24gc3RhcnRzV2l0aE9uZU9mKHNlYXJjaFN0cmluZ3M6IHN0cmluZ1tdKTogKHN0cmluZzogc3RyaW5nKSA9PiBib29sZWFuIHtcbiAgcmV0dXJuIGZ1bmN0aW9uKHN0cmluZzogc3RyaW5nKTogYm9vbGVhbiB7XG4gICAgZm9yIChjb25zdCBzZWFyY2hTdHJpbmcgb2Ygc2VhcmNoU3RyaW5ncykge1xuICAgICAgaWYgKHN0cmluZy5zdGFydHNXaXRoKHNlYXJjaFN0cmluZykpIHtcbiAgICAgICAgcmV0dXJuIHRydWU7XG4gICAgICB9XG4gICAgfVxuICAgIHJldHVybiBmYWxzZTtcbiAgfTtcbn1cbiJdfQ== \ No newline at end of file diff --git a/packages/@aws-cdk/aws-redshift/test/integ.cluster-iamrole.js.snapshot/cdk.out b/packages/@aws-cdk/aws-redshift/test/integ.cluster-iamrole.js.snapshot/cdk.out new file mode 100644 index 0000000000000..d8b441d447f8a --- /dev/null +++ b/packages/@aws-cdk/aws-redshift/test/integ.cluster-iamrole.js.snapshot/cdk.out @@ -0,0 +1 @@ +{"version":"29.0.0"} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-redshift/test/integ.cluster-iamrole.js.snapshot/integ.json b/packages/@aws-cdk/aws-redshift/test/integ.cluster-iamrole.js.snapshot/integ.json new file mode 100644 index 0000000000000..754546fb1b7e4 --- /dev/null +++ b/packages/@aws-cdk/aws-redshift/test/integ.cluster-iamrole.js.snapshot/integ.json @@ -0,0 +1,12 @@ +{ + "version": "29.0.0", + "testCases": { + "IamRoleInteg/DefaultTest": { + "stacks": [ + "redshift-iamrole-integ" + ], + "assertionStack": "IamRoleInteg/DefaultTest/DeployAssert", + "assertionStackName": "IamRoleIntegDefaultTestDeployAssertBEF20992" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-redshift/test/integ.cluster-iamrole.js.snapshot/manifest.json b/packages/@aws-cdk/aws-redshift/test/integ.cluster-iamrole.js.snapshot/manifest.json new file mode 100644 index 0000000000000..681e688e658f8 --- /dev/null +++ b/packages/@aws-cdk/aws-redshift/test/integ.cluster-iamrole.js.snapshot/manifest.json @@ -0,0 +1,333 @@ +{ + "version": "29.0.0", + "artifacts": { + "redshift-iamrole-integ.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "redshift-iamrole-integ.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "redshift-iamrole-integ": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "redshift-iamrole-integ.template.json", + "validateOnSynth": false, + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", + "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/081848ef4160c3161ec9ce1b80c08ed01f44ba9b6ecc751e448f61e1b0cd5c0d.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "redshift-iamrole-integ.assets" + ], + "lookupRole": { + "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}", + "requiresBootstrapStackVersion": 8, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "dependencies": [ + "redshift-iamrole-integ.assets" + ], + "metadata": { + "/redshift-iamrole-integ/VPC/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "VPCB9E5F0B4" + } + ], + "/redshift-iamrole-integ/VPC/PublicSubnet1/Subnet": [ + { + "type": "aws:cdk:logicalId", + "data": "VPCPublicSubnet1SubnetB4246D30" + } + ], + "/redshift-iamrole-integ/VPC/PublicSubnet1/RouteTable": [ + { + "type": "aws:cdk:logicalId", + "data": "VPCPublicSubnet1RouteTableFEE4B781" + } + ], + "/redshift-iamrole-integ/VPC/PublicSubnet1/RouteTableAssociation": [ + { + "type": "aws:cdk:logicalId", + "data": "VPCPublicSubnet1RouteTableAssociation0B0896DC" + } + ], + "/redshift-iamrole-integ/VPC/PublicSubnet1/DefaultRoute": [ + { + "type": "aws:cdk:logicalId", + "data": "VPCPublicSubnet1DefaultRoute91CEF279" + } + ], + "/redshift-iamrole-integ/VPC/PublicSubnet1/EIP": [ + { + "type": "aws:cdk:logicalId", + "data": "VPCPublicSubnet1EIP6AD938E8" + } + ], + "/redshift-iamrole-integ/VPC/PublicSubnet1/NATGateway": [ + { + "type": "aws:cdk:logicalId", + "data": "VPCPublicSubnet1NATGatewayE0556630" + } + ], + "/redshift-iamrole-integ/VPC/PublicSubnet2/Subnet": [ + { + "type": "aws:cdk:logicalId", + "data": "VPCPublicSubnet2Subnet74179F39" + } + ], + "/redshift-iamrole-integ/VPC/PublicSubnet2/RouteTable": [ + { + "type": "aws:cdk:logicalId", + "data": "VPCPublicSubnet2RouteTable6F1A15F1" + } + ], + "/redshift-iamrole-integ/VPC/PublicSubnet2/RouteTableAssociation": [ + { + "type": "aws:cdk:logicalId", + "data": "VPCPublicSubnet2RouteTableAssociation5A808732" + } + ], + "/redshift-iamrole-integ/VPC/PublicSubnet2/DefaultRoute": [ + { + "type": "aws:cdk:logicalId", + "data": "VPCPublicSubnet2DefaultRouteB7481BBA" + } + ], + "/redshift-iamrole-integ/VPC/PublicSubnet2/EIP": [ + { + "type": "aws:cdk:logicalId", + "data": "VPCPublicSubnet2EIP4947BC00" + } + ], + "/redshift-iamrole-integ/VPC/PublicSubnet2/NATGateway": [ + { + "type": "aws:cdk:logicalId", + "data": "VPCPublicSubnet2NATGateway3C070193" + } + ], + "/redshift-iamrole-integ/VPC/PrivateSubnet1/Subnet": [ + { + "type": "aws:cdk:logicalId", + "data": "VPCPrivateSubnet1Subnet8BCA10E0" + } + ], + "/redshift-iamrole-integ/VPC/PrivateSubnet1/RouteTable": [ + { + "type": "aws:cdk:logicalId", + "data": "VPCPrivateSubnet1RouteTableBE8A6027" + } + ], + "/redshift-iamrole-integ/VPC/PrivateSubnet1/RouteTableAssociation": [ + { + "type": "aws:cdk:logicalId", + "data": "VPCPrivateSubnet1RouteTableAssociation347902D1" + } + ], + "/redshift-iamrole-integ/VPC/PrivateSubnet1/DefaultRoute": [ + { + "type": "aws:cdk:logicalId", + "data": "VPCPrivateSubnet1DefaultRouteAE1D6490" + } + ], + "/redshift-iamrole-integ/VPC/PrivateSubnet2/Subnet": [ + { + "type": "aws:cdk:logicalId", + "data": "VPCPrivateSubnet2SubnetCFCDAA7A" + } + ], + "/redshift-iamrole-integ/VPC/PrivateSubnet2/RouteTable": [ + { + "type": "aws:cdk:logicalId", + "data": "VPCPrivateSubnet2RouteTable0A19E10E" + } + ], + "/redshift-iamrole-integ/VPC/PrivateSubnet2/RouteTableAssociation": [ + { + "type": "aws:cdk:logicalId", + "data": "VPCPrivateSubnet2RouteTableAssociation0C73D413" + } + ], + "/redshift-iamrole-integ/VPC/PrivateSubnet2/DefaultRoute": [ + { + "type": "aws:cdk:logicalId", + "data": "VPCPrivateSubnet2DefaultRouteF4F5CFD2" + } + ], + "/redshift-iamrole-integ/VPC/IGW": [ + { + "type": "aws:cdk:logicalId", + "data": "VPCIGWB7E252D3" + } + ], + "/redshift-iamrole-integ/VPC/VPCGW": [ + { + "type": "aws:cdk:logicalId", + "data": "VPCVPCGW99B986DC" + } + ], + "/redshift-iamrole-integ/Role2/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "Role291939BC6" + } + ], + "/redshift-iamrole-integ/Role1/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "Role13A5C70C1" + } + ], + "/redshift-iamrole-integ/Cluster/Subnets/Default": [ + { + "type": "aws:cdk:logicalId", + "data": "ClusterSubnetsDCFA5CB7" + } + ], + "/redshift-iamrole-integ/Cluster/SecurityGroup/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "ClusterSecurityGroup0921994B" + } + ], + "/redshift-iamrole-integ/Cluster/Secret/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "ClusterSecret6368BD0F" + } + ], + "/redshift-iamrole-integ/Cluster/Secret/Attachment/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "ClusterSecretAttachment769E6258" + } + ], + "/redshift-iamrole-integ/Cluster/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "ClusterEB0386A7" + } + ], + "/redshift-iamrole-integ/Cluster/add-role-Role2/Resource/Default": [ + { + "type": "aws:cdk:logicalId", + "data": "ClusteraddroleRole2CCF96C23" + } + ], + "/redshift-iamrole-integ/Cluster/add-role-Role2/CustomResourcePolicy/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "ClusteraddroleRole2CustomResourcePolicyFE1C6F0D" + } + ], + "/redshift-iamrole-integ/AWS679f53fac002430cb0da5b7982bd2287/ServiceRole/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "AWS679f53fac002430cb0da5b7982bd2287ServiceRoleC1EA0FF2" + } + ], + "/redshift-iamrole-integ/AWS679f53fac002430cb0da5b7982bd2287/ServiceRole/DefaultPolicy/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "AWS679f53fac002430cb0da5b7982bd2287ServiceRoleDefaultPolicyD28E1A5E" + } + ], + "/redshift-iamrole-integ/AWS679f53fac002430cb0da5b7982bd2287/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "AWS679f53fac002430cb0da5b7982bd22872D164C4C" + } + ], + "/redshift-iamrole-integ/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/redshift-iamrole-integ/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ], + "ClusteraddroleC521A629": [ + { + "type": "aws:cdk:logicalId", + "data": "ClusteraddroleC521A629", + "trace": [ + "!!DESTRUCTIVE_CHANGES: WILL_DESTROY" + ] + } + ], + "ClusteraddroleCustomResourcePolicyA7EB876A": [ + { + "type": "aws:cdk:logicalId", + "data": "ClusteraddroleCustomResourcePolicyA7EB876A", + "trace": [ + "!!DESTRUCTIVE_CHANGES: WILL_DESTROY" + ] + } + ] + }, + "displayName": "redshift-iamrole-integ" + }, + "IamRoleIntegDefaultTestDeployAssertBEF20992.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "IamRoleIntegDefaultTestDeployAssertBEF20992.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "IamRoleIntegDefaultTestDeployAssertBEF20992": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "IamRoleIntegDefaultTestDeployAssertBEF20992.template.json", + "validateOnSynth": false, + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", + "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "IamRoleIntegDefaultTestDeployAssertBEF20992.assets" + ], + "lookupRole": { + "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}", + "requiresBootstrapStackVersion": 8, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "dependencies": [ + "IamRoleIntegDefaultTestDeployAssertBEF20992.assets" + ], + "metadata": { + "/IamRoleInteg/DefaultTest/DeployAssert/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/IamRoleInteg/DefaultTest/DeployAssert/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "IamRoleInteg/DefaultTest/DeployAssert" + }, + "Tree": { + "type": "cdk:tree", + "properties": { + "file": "tree.json" + } + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-redshift/test/integ.cluster-iamrole.js.snapshot/redshift-iamrole-integ.assets.json b/packages/@aws-cdk/aws-redshift/test/integ.cluster-iamrole.js.snapshot/redshift-iamrole-integ.assets.json new file mode 100644 index 0000000000000..c3881622e6ec8 --- /dev/null +++ b/packages/@aws-cdk/aws-redshift/test/integ.cluster-iamrole.js.snapshot/redshift-iamrole-integ.assets.json @@ -0,0 +1,32 @@ +{ + "version": "29.0.0", + "files": { + "a268caa53756f51bda8ad5f499be4ed8484a81b314811806fbb66f874837c476": { + "source": { + "path": "asset.a268caa53756f51bda8ad5f499be4ed8484a81b314811806fbb66f874837c476", + "packaging": "zip" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "a268caa53756f51bda8ad5f499be4ed8484a81b314811806fbb66f874837c476.zip", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + }, + "081848ef4160c3161ec9ce1b80c08ed01f44ba9b6ecc751e448f61e1b0cd5c0d": { + "source": { + "path": "redshift-iamrole-integ.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "081848ef4160c3161ec9ce1b80c08ed01f44ba9b6ecc751e448f61e1b0cd5c0d.json", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + } + }, + "dockerImages": {} +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-redshift/test/integ.cluster-iamrole.js.snapshot/redshift-iamrole-integ.template.json b/packages/@aws-cdk/aws-redshift/test/integ.cluster-iamrole.js.snapshot/redshift-iamrole-integ.template.json new file mode 100644 index 0000000000000..fdf91d817b7c4 --- /dev/null +++ b/packages/@aws-cdk/aws-redshift/test/integ.cluster-iamrole.js.snapshot/redshift-iamrole-integ.template.json @@ -0,0 +1,787 @@ +{ + "Resources": { + "VPCB9E5F0B4": { + "Type": "AWS::EC2::VPC", + "Properties": { + "CidrBlock": "10.0.0.0/16", + "EnableDnsHostnames": true, + "EnableDnsSupport": true, + "InstanceTenancy": "default", + "Tags": [ + { + "Key": "Name", + "Value": "redshift-iamrole-integ/VPC" + } + ] + } + }, + "VPCPublicSubnet1SubnetB4246D30": { + "Type": "AWS::EC2::Subnet", + "Properties": { + "VpcId": { + "Ref": "VPCB9E5F0B4" + }, + "AvailabilityZone": { + "Fn::Select": [ + 0, + { + "Fn::GetAZs": "" + } + ] + }, + "CidrBlock": "10.0.0.0/18", + "MapPublicIpOnLaunch": true, + "Tags": [ + { + "Key": "aws-cdk:subnet-name", + "Value": "Public" + }, + { + "Key": "aws-cdk:subnet-type", + "Value": "Public" + }, + { + "Key": "Name", + "Value": "redshift-iamrole-integ/VPC/PublicSubnet1" + } + ] + } + }, + "VPCPublicSubnet1RouteTableFEE4B781": { + "Type": "AWS::EC2::RouteTable", + "Properties": { + "VpcId": { + "Ref": "VPCB9E5F0B4" + }, + "Tags": [ + { + "Key": "Name", + "Value": "redshift-iamrole-integ/VPC/PublicSubnet1" + } + ] + } + }, + "VPCPublicSubnet1RouteTableAssociation0B0896DC": { + "Type": "AWS::EC2::SubnetRouteTableAssociation", + "Properties": { + "RouteTableId": { + "Ref": "VPCPublicSubnet1RouteTableFEE4B781" + }, + "SubnetId": { + "Ref": "VPCPublicSubnet1SubnetB4246D30" + } + } + }, + "VPCPublicSubnet1DefaultRoute91CEF279": { + "Type": "AWS::EC2::Route", + "Properties": { + "RouteTableId": { + "Ref": "VPCPublicSubnet1RouteTableFEE4B781" + }, + "DestinationCidrBlock": "0.0.0.0/0", + "GatewayId": { + "Ref": "VPCIGWB7E252D3" + } + }, + "DependsOn": [ + "VPCVPCGW99B986DC" + ] + }, + "VPCPublicSubnet1EIP6AD938E8": { + "Type": "AWS::EC2::EIP", + "Properties": { + "Domain": "vpc", + "Tags": [ + { + "Key": "Name", + "Value": "redshift-iamrole-integ/VPC/PublicSubnet1" + } + ] + } + }, + "VPCPublicSubnet1NATGatewayE0556630": { + "Type": "AWS::EC2::NatGateway", + "Properties": { + "SubnetId": { + "Ref": "VPCPublicSubnet1SubnetB4246D30" + }, + "AllocationId": { + "Fn::GetAtt": [ + "VPCPublicSubnet1EIP6AD938E8", + "AllocationId" + ] + }, + "Tags": [ + { + "Key": "Name", + "Value": "redshift-iamrole-integ/VPC/PublicSubnet1" + } + ] + }, + "DependsOn": [ + "VPCPublicSubnet1DefaultRoute91CEF279", + "VPCPublicSubnet1RouteTableAssociation0B0896DC" + ] + }, + "VPCPublicSubnet2Subnet74179F39": { + "Type": "AWS::EC2::Subnet", + "Properties": { + "VpcId": { + "Ref": "VPCB9E5F0B4" + }, + "AvailabilityZone": { + "Fn::Select": [ + 1, + { + "Fn::GetAZs": "" + } + ] + }, + "CidrBlock": "10.0.64.0/18", + "MapPublicIpOnLaunch": true, + "Tags": [ + { + "Key": "aws-cdk:subnet-name", + "Value": "Public" + }, + { + "Key": "aws-cdk:subnet-type", + "Value": "Public" + }, + { + "Key": "Name", + "Value": "redshift-iamrole-integ/VPC/PublicSubnet2" + } + ] + } + }, + "VPCPublicSubnet2RouteTable6F1A15F1": { + "Type": "AWS::EC2::RouteTable", + "Properties": { + "VpcId": { + "Ref": "VPCB9E5F0B4" + }, + "Tags": [ + { + "Key": "Name", + "Value": "redshift-iamrole-integ/VPC/PublicSubnet2" + } + ] + } + }, + "VPCPublicSubnet2RouteTableAssociation5A808732": { + "Type": "AWS::EC2::SubnetRouteTableAssociation", + "Properties": { + "RouteTableId": { + "Ref": "VPCPublicSubnet2RouteTable6F1A15F1" + }, + "SubnetId": { + "Ref": "VPCPublicSubnet2Subnet74179F39" + } + } + }, + "VPCPublicSubnet2DefaultRouteB7481BBA": { + "Type": "AWS::EC2::Route", + "Properties": { + "RouteTableId": { + "Ref": "VPCPublicSubnet2RouteTable6F1A15F1" + }, + "DestinationCidrBlock": "0.0.0.0/0", + "GatewayId": { + "Ref": "VPCIGWB7E252D3" + } + }, + "DependsOn": [ + "VPCVPCGW99B986DC" + ] + }, + "VPCPublicSubnet2EIP4947BC00": { + "Type": "AWS::EC2::EIP", + "Properties": { + "Domain": "vpc", + "Tags": [ + { + "Key": "Name", + "Value": "redshift-iamrole-integ/VPC/PublicSubnet2" + } + ] + } + }, + "VPCPublicSubnet2NATGateway3C070193": { + "Type": "AWS::EC2::NatGateway", + "Properties": { + "SubnetId": { + "Ref": "VPCPublicSubnet2Subnet74179F39" + }, + "AllocationId": { + "Fn::GetAtt": [ + "VPCPublicSubnet2EIP4947BC00", + "AllocationId" + ] + }, + "Tags": [ + { + "Key": "Name", + "Value": "redshift-iamrole-integ/VPC/PublicSubnet2" + } + ] + }, + "DependsOn": [ + "VPCPublicSubnet2DefaultRouteB7481BBA", + "VPCPublicSubnet2RouteTableAssociation5A808732" + ] + }, + "VPCPrivateSubnet1Subnet8BCA10E0": { + "Type": "AWS::EC2::Subnet", + "Properties": { + "VpcId": { + "Ref": "VPCB9E5F0B4" + }, + "AvailabilityZone": { + "Fn::Select": [ + 0, + { + "Fn::GetAZs": "" + } + ] + }, + "CidrBlock": "10.0.128.0/18", + "MapPublicIpOnLaunch": false, + "Tags": [ + { + "Key": "aws-cdk:subnet-name", + "Value": "Private" + }, + { + "Key": "aws-cdk:subnet-type", + "Value": "Private" + }, + { + "Key": "Name", + "Value": "redshift-iamrole-integ/VPC/PrivateSubnet1" + } + ] + } + }, + "VPCPrivateSubnet1RouteTableBE8A6027": { + "Type": "AWS::EC2::RouteTable", + "Properties": { + "VpcId": { + "Ref": "VPCB9E5F0B4" + }, + "Tags": [ + { + "Key": "Name", + "Value": "redshift-iamrole-integ/VPC/PrivateSubnet1" + } + ] + } + }, + "VPCPrivateSubnet1RouteTableAssociation347902D1": { + "Type": "AWS::EC2::SubnetRouteTableAssociation", + "Properties": { + "RouteTableId": { + "Ref": "VPCPrivateSubnet1RouteTableBE8A6027" + }, + "SubnetId": { + "Ref": "VPCPrivateSubnet1Subnet8BCA10E0" + } + } + }, + "VPCPrivateSubnet1DefaultRouteAE1D6490": { + "Type": "AWS::EC2::Route", + "Properties": { + "RouteTableId": { + "Ref": "VPCPrivateSubnet1RouteTableBE8A6027" + }, + "DestinationCidrBlock": "0.0.0.0/0", + "NatGatewayId": { + "Ref": "VPCPublicSubnet1NATGatewayE0556630" + } + } + }, + "VPCPrivateSubnet2SubnetCFCDAA7A": { + "Type": "AWS::EC2::Subnet", + "Properties": { + "VpcId": { + "Ref": "VPCB9E5F0B4" + }, + "AvailabilityZone": { + "Fn::Select": [ + 1, + { + "Fn::GetAZs": "" + } + ] + }, + "CidrBlock": "10.0.192.0/18", + "MapPublicIpOnLaunch": false, + "Tags": [ + { + "Key": "aws-cdk:subnet-name", + "Value": "Private" + }, + { + "Key": "aws-cdk:subnet-type", + "Value": "Private" + }, + { + "Key": "Name", + "Value": "redshift-iamrole-integ/VPC/PrivateSubnet2" + } + ] + } + }, + "VPCPrivateSubnet2RouteTable0A19E10E": { + "Type": "AWS::EC2::RouteTable", + "Properties": { + "VpcId": { + "Ref": "VPCB9E5F0B4" + }, + "Tags": [ + { + "Key": "Name", + "Value": "redshift-iamrole-integ/VPC/PrivateSubnet2" + } + ] + } + }, + "VPCPrivateSubnet2RouteTableAssociation0C73D413": { + "Type": "AWS::EC2::SubnetRouteTableAssociation", + "Properties": { + "RouteTableId": { + "Ref": "VPCPrivateSubnet2RouteTable0A19E10E" + }, + "SubnetId": { + "Ref": "VPCPrivateSubnet2SubnetCFCDAA7A" + } + } + }, + "VPCPrivateSubnet2DefaultRouteF4F5CFD2": { + "Type": "AWS::EC2::Route", + "Properties": { + "RouteTableId": { + "Ref": "VPCPrivateSubnet2RouteTable0A19E10E" + }, + "DestinationCidrBlock": "0.0.0.0/0", + "NatGatewayId": { + "Ref": "VPCPublicSubnet2NATGateway3C070193" + } + } + }, + "VPCIGWB7E252D3": { + "Type": "AWS::EC2::InternetGateway", + "Properties": { + "Tags": [ + { + "Key": "Name", + "Value": "redshift-iamrole-integ/VPC" + } + ] + } + }, + "VPCVPCGW99B986DC": { + "Type": "AWS::EC2::VPCGatewayAttachment", + "Properties": { + "VpcId": { + "Ref": "VPCB9E5F0B4" + }, + "InternetGatewayId": { + "Ref": "VPCIGWB7E252D3" + } + } + }, + "Role291939BC6": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "redshift.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + } + } + }, + "Role13A5C70C1": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "redshift.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + } + } + }, + "ClusterSubnetsDCFA5CB7": { + "Type": "AWS::Redshift::ClusterSubnetGroup", + "Properties": { + "Description": "Subnets for Cluster Redshift cluster", + "SubnetIds": [ + { + "Ref": "VPCPublicSubnet1SubnetB4246D30" + }, + { + "Ref": "VPCPublicSubnet2Subnet74179F39" + } + ] + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "ClusterSecurityGroup0921994B": { + "Type": "AWS::EC2::SecurityGroup", + "Properties": { + "GroupDescription": "Redshift security group", + "SecurityGroupEgress": [ + { + "CidrIp": "0.0.0.0/0", + "Description": "Allow all outbound traffic by default", + "IpProtocol": "-1" + } + ], + "VpcId": { + "Ref": "VPCB9E5F0B4" + } + } + }, + "ClusterSecret6368BD0F": { + "Type": "AWS::SecretsManager::Secret", + "Properties": { + "GenerateSecretString": { + "ExcludeCharacters": "\"@/\\ '", + "GenerateStringKey": "password", + "PasswordLength": 30, + "SecretStringTemplate": "{\"username\":\"admin\"}" + } + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "ClusterSecretAttachment769E6258": { + "Type": "AWS::SecretsManager::SecretTargetAttachment", + "Properties": { + "SecretId": { + "Ref": "ClusterSecret6368BD0F" + }, + "TargetId": { + "Ref": "ClusterEB0386A7" + }, + "TargetType": "AWS::Redshift::Cluster" + } + }, + "ClusterEB0386A7": { + "Type": "AWS::Redshift::Cluster", + "Properties": { + "ClusterType": "multi-node", + "DBName": "default_db", + "MasterUsername": { + "Fn::Join": [ + "", + [ + "{{resolve:secretsmanager:", + { + "Ref": "ClusterSecret6368BD0F" + }, + ":SecretString:username::}}" + ] + ] + }, + "MasterUserPassword": { + "Fn::Join": [ + "", + [ + "{{resolve:secretsmanager:", + { + "Ref": "ClusterSecret6368BD0F" + }, + ":SecretString:password::}}" + ] + ] + }, + "NodeType": "dc2.large", + "AllowVersionUpgrade": true, + "AutomatedSnapshotRetentionPeriod": 1, + "ClusterSubnetGroupName": { + "Ref": "ClusterSubnetsDCFA5CB7" + }, + "Encrypted": true, + "IamRoles": [ + { + "Fn::GetAtt": [ + "Role13A5C70C1", + "Arn" + ] + } + ], + "NumberOfNodes": 2, + "PubliclyAccessible": false, + "VpcSecurityGroupIds": [ + { + "Fn::GetAtt": [ + "ClusterSecurityGroup0921994B", + "GroupId" + ] + } + ] + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "ClusteraddroleRole2CCF96C23": { + "Type": "Custom::RedshiftAddRole", + "Properties": { + "ServiceToken": { + "Fn::GetAtt": [ + "AWS679f53fac002430cb0da5b7982bd22872D164C4C", + "Arn" + ] + }, + "Create": { + "Fn::Join": [ + "", + [ + "{\"service\":\"Redshift\",\"action\":\"modifyClusterIamRoles\",\"parameters\":{\"ClusterIdentifier\":\"", + { + "Ref": "ClusterEB0386A7" + }, + "\",\"AddIamRoles\":[\"", + { + "Fn::GetAtt": [ + "Role291939BC6", + "Arn" + ] + }, + "\"]},\"physicalResourceId\":{\"id\":\"", + { + "Fn::GetAtt": [ + "Role291939BC6", + "Arn" + ] + }, + "-", + { + "Ref": "ClusterEB0386A7" + }, + "\"}}" + ] + ] + }, + "Update": { + "Fn::Join": [ + "", + [ + "{\"service\":\"Redshift\",\"action\":\"modifyClusterIamRoles\",\"parameters\":{\"ClusterIdentifier\":\"", + { + "Ref": "ClusterEB0386A7" + }, + "\",\"AddIamRoles\":[\"", + { + "Fn::GetAtt": [ + "Role291939BC6", + "Arn" + ] + }, + "\"]},\"physicalResourceId\":{\"id\":\"", + { + "Fn::GetAtt": [ + "Role291939BC6", + "Arn" + ] + }, + "-", + { + "Ref": "ClusterEB0386A7" + }, + "\"}}" + ] + ] + }, + "Delete": { + "Fn::Join": [ + "", + [ + "{\"service\":\"Redshift\",\"action\":\"modifyClusterIamRoles\",\"parameters\":{\"ClusterIdentifier\":\"", + { + "Ref": "ClusterEB0386A7" + }, + "\",\"RemoveIamRoles\":[\"", + { + "Fn::GetAtt": [ + "Role291939BC6", + "Arn" + ] + }, + "\"]},\"physicalResourceId\":{\"id\":\"", + { + "Fn::GetAtt": [ + "Role291939BC6", + "Arn" + ] + }, + "-", + { + "Ref": "ClusterEB0386A7" + }, + "\"}}" + ] + ] + }, + "InstallLatestAwsSdk": true + }, + "DependsOn": [ + "ClusteraddroleRole2CustomResourcePolicyFE1C6F0D" + ], + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "ClusteraddroleRole2CustomResourcePolicyFE1C6F0D": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": "redshift:ModifyClusterIamRoles", + "Effect": "Allow", + "Resource": "*" + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "ClusteraddroleRole2CustomResourcePolicyFE1C6F0D", + "Roles": [ + { + "Ref": "AWS679f53fac002430cb0da5b7982bd2287ServiceRoleC1EA0FF2" + } + ] + } + }, + "AWS679f53fac002430cb0da5b7982bd2287ServiceRoleC1EA0FF2": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + }, + "ManagedPolicyArns": [ + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ] + ] + } + ] + } + }, + "AWS679f53fac002430cb0da5b7982bd2287ServiceRoleDefaultPolicyD28E1A5E": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": "iam:PassRole", + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "Role291939BC6", + "Arn" + ] + } + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "AWS679f53fac002430cb0da5b7982bd2287ServiceRoleDefaultPolicyD28E1A5E", + "Roles": [ + { + "Ref": "AWS679f53fac002430cb0da5b7982bd2287ServiceRoleC1EA0FF2" + } + ] + } + }, + "AWS679f53fac002430cb0da5b7982bd22872D164C4C": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "S3Bucket": { + "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}" + }, + "S3Key": "a268caa53756f51bda8ad5f499be4ed8484a81b314811806fbb66f874837c476.zip" + }, + "Role": { + "Fn::GetAtt": [ + "AWS679f53fac002430cb0da5b7982bd2287ServiceRoleC1EA0FF2", + "Arn" + ] + }, + "Handler": "index.handler", + "Runtime": "nodejs14.x", + "Timeout": 120 + }, + "DependsOn": [ + "AWS679f53fac002430cb0da5b7982bd2287ServiceRoleDefaultPolicyD28E1A5E", + "AWS679f53fac002430cb0da5b7982bd2287ServiceRoleC1EA0FF2" + ] + } + }, + "Parameters": { + "BootstrapVersion": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" + } + }, + "Rules": { + "CheckBootstrapVersion": { + "Assertions": [ + { + "Assert": { + "Fn::Not": [ + { + "Fn::Contains": [ + [ + "1", + "2", + "3", + "4", + "5" + ], + { + "Ref": "BootstrapVersion" + } + ] + } + ] + }, + "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." + } + ] + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-redshift/test/integ.cluster-iamrole.js.snapshot/tree.json b/packages/@aws-cdk/aws-redshift/test/integ.cluster-iamrole.js.snapshot/tree.json new file mode 100644 index 0000000000000..bd76be5d5f0a8 --- /dev/null +++ b/packages/@aws-cdk/aws-redshift/test/integ.cluster-iamrole.js.snapshot/tree.json @@ -0,0 +1,1269 @@ +{ + "version": "tree-0.1", + "tree": { + "id": "App", + "path": "", + "children": { + "redshift-iamrole-integ": { + "id": "redshift-iamrole-integ", + "path": "redshift-iamrole-integ", + "children": { + "VPC": { + "id": "VPC", + "path": "redshift-iamrole-integ/VPC", + "children": { + "Resource": { + "id": "Resource", + "path": "redshift-iamrole-integ/VPC/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::VPC", + "aws:cdk:cloudformation:props": { + "cidrBlock": "10.0.0.0/16", + "enableDnsHostnames": true, + "enableDnsSupport": true, + "instanceTenancy": "default", + "tags": [ + { + "key": "Name", + "value": "redshift-iamrole-integ/VPC" + } + ] + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ec2.CfnVPC", + "version": "0.0.0" + } + }, + "PublicSubnet1": { + "id": "PublicSubnet1", + "path": "redshift-iamrole-integ/VPC/PublicSubnet1", + "children": { + "Subnet": { + "id": "Subnet", + "path": "redshift-iamrole-integ/VPC/PublicSubnet1/Subnet", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::Subnet", + "aws:cdk:cloudformation:props": { + "vpcId": { + "Ref": "VPCB9E5F0B4" + }, + "availabilityZone": { + "Fn::Select": [ + 0, + { + "Fn::GetAZs": "" + } + ] + }, + "cidrBlock": "10.0.0.0/18", + "mapPublicIpOnLaunch": true, + "tags": [ + { + "key": "aws-cdk:subnet-name", + "value": "Public" + }, + { + "key": "aws-cdk:subnet-type", + "value": "Public" + }, + { + "key": "Name", + "value": "redshift-iamrole-integ/VPC/PublicSubnet1" + } + ] + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ec2.CfnSubnet", + "version": "0.0.0" + } + }, + "Acl": { + "id": "Acl", + "path": "redshift-iamrole-integ/VPC/PublicSubnet1/Acl", + "constructInfo": { + "fqn": "@aws-cdk/core.Resource", + "version": "0.0.0" + } + }, + "RouteTable": { + "id": "RouteTable", + "path": "redshift-iamrole-integ/VPC/PublicSubnet1/RouteTable", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::RouteTable", + "aws:cdk:cloudformation:props": { + "vpcId": { + "Ref": "VPCB9E5F0B4" + }, + "tags": [ + { + "key": "Name", + "value": "redshift-iamrole-integ/VPC/PublicSubnet1" + } + ] + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ec2.CfnRouteTable", + "version": "0.0.0" + } + }, + "RouteTableAssociation": { + "id": "RouteTableAssociation", + "path": "redshift-iamrole-integ/VPC/PublicSubnet1/RouteTableAssociation", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::SubnetRouteTableAssociation", + "aws:cdk:cloudformation:props": { + "routeTableId": { + "Ref": "VPCPublicSubnet1RouteTableFEE4B781" + }, + "subnetId": { + "Ref": "VPCPublicSubnet1SubnetB4246D30" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ec2.CfnSubnetRouteTableAssociation", + "version": "0.0.0" + } + }, + "DefaultRoute": { + "id": "DefaultRoute", + "path": "redshift-iamrole-integ/VPC/PublicSubnet1/DefaultRoute", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::Route", + "aws:cdk:cloudformation:props": { + "routeTableId": { + "Ref": "VPCPublicSubnet1RouteTableFEE4B781" + }, + "destinationCidrBlock": "0.0.0.0/0", + "gatewayId": { + "Ref": "VPCIGWB7E252D3" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ec2.CfnRoute", + "version": "0.0.0" + } + }, + "EIP": { + "id": "EIP", + "path": "redshift-iamrole-integ/VPC/PublicSubnet1/EIP", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::EIP", + "aws:cdk:cloudformation:props": { + "domain": "vpc", + "tags": [ + { + "key": "Name", + "value": "redshift-iamrole-integ/VPC/PublicSubnet1" + } + ] + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ec2.CfnEIP", + "version": "0.0.0" + } + }, + "NATGateway": { + "id": "NATGateway", + "path": "redshift-iamrole-integ/VPC/PublicSubnet1/NATGateway", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::NatGateway", + "aws:cdk:cloudformation:props": { + "subnetId": { + "Ref": "VPCPublicSubnet1SubnetB4246D30" + }, + "allocationId": { + "Fn::GetAtt": [ + "VPCPublicSubnet1EIP6AD938E8", + "AllocationId" + ] + }, + "tags": [ + { + "key": "Name", + "value": "redshift-iamrole-integ/VPC/PublicSubnet1" + } + ] + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ec2.CfnNatGateway", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ec2.PublicSubnet", + "version": "0.0.0" + } + }, + "PublicSubnet2": { + "id": "PublicSubnet2", + "path": "redshift-iamrole-integ/VPC/PublicSubnet2", + "children": { + "Subnet": { + "id": "Subnet", + "path": "redshift-iamrole-integ/VPC/PublicSubnet2/Subnet", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::Subnet", + "aws:cdk:cloudformation:props": { + "vpcId": { + "Ref": "VPCB9E5F0B4" + }, + "availabilityZone": { + "Fn::Select": [ + 1, + { + "Fn::GetAZs": "" + } + ] + }, + "cidrBlock": "10.0.64.0/18", + "mapPublicIpOnLaunch": true, + "tags": [ + { + "key": "aws-cdk:subnet-name", + "value": "Public" + }, + { + "key": "aws-cdk:subnet-type", + "value": "Public" + }, + { + "key": "Name", + "value": "redshift-iamrole-integ/VPC/PublicSubnet2" + } + ] + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ec2.CfnSubnet", + "version": "0.0.0" + } + }, + "Acl": { + "id": "Acl", + "path": "redshift-iamrole-integ/VPC/PublicSubnet2/Acl", + "constructInfo": { + "fqn": "@aws-cdk/core.Resource", + "version": "0.0.0" + } + }, + "RouteTable": { + "id": "RouteTable", + "path": "redshift-iamrole-integ/VPC/PublicSubnet2/RouteTable", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::RouteTable", + "aws:cdk:cloudformation:props": { + "vpcId": { + "Ref": "VPCB9E5F0B4" + }, + "tags": [ + { + "key": "Name", + "value": "redshift-iamrole-integ/VPC/PublicSubnet2" + } + ] + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ec2.CfnRouteTable", + "version": "0.0.0" + } + }, + "RouteTableAssociation": { + "id": "RouteTableAssociation", + "path": "redshift-iamrole-integ/VPC/PublicSubnet2/RouteTableAssociation", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::SubnetRouteTableAssociation", + "aws:cdk:cloudformation:props": { + "routeTableId": { + "Ref": "VPCPublicSubnet2RouteTable6F1A15F1" + }, + "subnetId": { + "Ref": "VPCPublicSubnet2Subnet74179F39" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ec2.CfnSubnetRouteTableAssociation", + "version": "0.0.0" + } + }, + "DefaultRoute": { + "id": "DefaultRoute", + "path": "redshift-iamrole-integ/VPC/PublicSubnet2/DefaultRoute", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::Route", + "aws:cdk:cloudformation:props": { + "routeTableId": { + "Ref": "VPCPublicSubnet2RouteTable6F1A15F1" + }, + "destinationCidrBlock": "0.0.0.0/0", + "gatewayId": { + "Ref": "VPCIGWB7E252D3" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ec2.CfnRoute", + "version": "0.0.0" + } + }, + "EIP": { + "id": "EIP", + "path": "redshift-iamrole-integ/VPC/PublicSubnet2/EIP", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::EIP", + "aws:cdk:cloudformation:props": { + "domain": "vpc", + "tags": [ + { + "key": "Name", + "value": "redshift-iamrole-integ/VPC/PublicSubnet2" + } + ] + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ec2.CfnEIP", + "version": "0.0.0" + } + }, + "NATGateway": { + "id": "NATGateway", + "path": "redshift-iamrole-integ/VPC/PublicSubnet2/NATGateway", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::NatGateway", + "aws:cdk:cloudformation:props": { + "subnetId": { + "Ref": "VPCPublicSubnet2Subnet74179F39" + }, + "allocationId": { + "Fn::GetAtt": [ + "VPCPublicSubnet2EIP4947BC00", + "AllocationId" + ] + }, + "tags": [ + { + "key": "Name", + "value": "redshift-iamrole-integ/VPC/PublicSubnet2" + } + ] + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ec2.CfnNatGateway", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ec2.PublicSubnet", + "version": "0.0.0" + } + }, + "PrivateSubnet1": { + "id": "PrivateSubnet1", + "path": "redshift-iamrole-integ/VPC/PrivateSubnet1", + "children": { + "Subnet": { + "id": "Subnet", + "path": "redshift-iamrole-integ/VPC/PrivateSubnet1/Subnet", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::Subnet", + "aws:cdk:cloudformation:props": { + "vpcId": { + "Ref": "VPCB9E5F0B4" + }, + "availabilityZone": { + "Fn::Select": [ + 0, + { + "Fn::GetAZs": "" + } + ] + }, + "cidrBlock": "10.0.128.0/18", + "mapPublicIpOnLaunch": false, + "tags": [ + { + "key": "aws-cdk:subnet-name", + "value": "Private" + }, + { + "key": "aws-cdk:subnet-type", + "value": "Private" + }, + { + "key": "Name", + "value": "redshift-iamrole-integ/VPC/PrivateSubnet1" + } + ] + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ec2.CfnSubnet", + "version": "0.0.0" + } + }, + "Acl": { + "id": "Acl", + "path": "redshift-iamrole-integ/VPC/PrivateSubnet1/Acl", + "constructInfo": { + "fqn": "@aws-cdk/core.Resource", + "version": "0.0.0" + } + }, + "RouteTable": { + "id": "RouteTable", + "path": "redshift-iamrole-integ/VPC/PrivateSubnet1/RouteTable", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::RouteTable", + "aws:cdk:cloudformation:props": { + "vpcId": { + "Ref": "VPCB9E5F0B4" + }, + "tags": [ + { + "key": "Name", + "value": "redshift-iamrole-integ/VPC/PrivateSubnet1" + } + ] + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ec2.CfnRouteTable", + "version": "0.0.0" + } + }, + "RouteTableAssociation": { + "id": "RouteTableAssociation", + "path": "redshift-iamrole-integ/VPC/PrivateSubnet1/RouteTableAssociation", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::SubnetRouteTableAssociation", + "aws:cdk:cloudformation:props": { + "routeTableId": { + "Ref": "VPCPrivateSubnet1RouteTableBE8A6027" + }, + "subnetId": { + "Ref": "VPCPrivateSubnet1Subnet8BCA10E0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ec2.CfnSubnetRouteTableAssociation", + "version": "0.0.0" + } + }, + "DefaultRoute": { + "id": "DefaultRoute", + "path": "redshift-iamrole-integ/VPC/PrivateSubnet1/DefaultRoute", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::Route", + "aws:cdk:cloudformation:props": { + "routeTableId": { + "Ref": "VPCPrivateSubnet1RouteTableBE8A6027" + }, + "destinationCidrBlock": "0.0.0.0/0", + "natGatewayId": { + "Ref": "VPCPublicSubnet1NATGatewayE0556630" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ec2.CfnRoute", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ec2.PrivateSubnet", + "version": "0.0.0" + } + }, + "PrivateSubnet2": { + "id": "PrivateSubnet2", + "path": "redshift-iamrole-integ/VPC/PrivateSubnet2", + "children": { + "Subnet": { + "id": "Subnet", + "path": "redshift-iamrole-integ/VPC/PrivateSubnet2/Subnet", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::Subnet", + "aws:cdk:cloudformation:props": { + "vpcId": { + "Ref": "VPCB9E5F0B4" + }, + "availabilityZone": { + "Fn::Select": [ + 1, + { + "Fn::GetAZs": "" + } + ] + }, + "cidrBlock": "10.0.192.0/18", + "mapPublicIpOnLaunch": false, + "tags": [ + { + "key": "aws-cdk:subnet-name", + "value": "Private" + }, + { + "key": "aws-cdk:subnet-type", + "value": "Private" + }, + { + "key": "Name", + "value": "redshift-iamrole-integ/VPC/PrivateSubnet2" + } + ] + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ec2.CfnSubnet", + "version": "0.0.0" + } + }, + "Acl": { + "id": "Acl", + "path": "redshift-iamrole-integ/VPC/PrivateSubnet2/Acl", + "constructInfo": { + "fqn": "@aws-cdk/core.Resource", + "version": "0.0.0" + } + }, + "RouteTable": { + "id": "RouteTable", + "path": "redshift-iamrole-integ/VPC/PrivateSubnet2/RouteTable", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::RouteTable", + "aws:cdk:cloudformation:props": { + "vpcId": { + "Ref": "VPCB9E5F0B4" + }, + "tags": [ + { + "key": "Name", + "value": "redshift-iamrole-integ/VPC/PrivateSubnet2" + } + ] + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ec2.CfnRouteTable", + "version": "0.0.0" + } + }, + "RouteTableAssociation": { + "id": "RouteTableAssociation", + "path": "redshift-iamrole-integ/VPC/PrivateSubnet2/RouteTableAssociation", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::SubnetRouteTableAssociation", + "aws:cdk:cloudformation:props": { + "routeTableId": { + "Ref": "VPCPrivateSubnet2RouteTable0A19E10E" + }, + "subnetId": { + "Ref": "VPCPrivateSubnet2SubnetCFCDAA7A" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ec2.CfnSubnetRouteTableAssociation", + "version": "0.0.0" + } + }, + "DefaultRoute": { + "id": "DefaultRoute", + "path": "redshift-iamrole-integ/VPC/PrivateSubnet2/DefaultRoute", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::Route", + "aws:cdk:cloudformation:props": { + "routeTableId": { + "Ref": "VPCPrivateSubnet2RouteTable0A19E10E" + }, + "destinationCidrBlock": "0.0.0.0/0", + "natGatewayId": { + "Ref": "VPCPublicSubnet2NATGateway3C070193" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ec2.CfnRoute", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ec2.PrivateSubnet", + "version": "0.0.0" + } + }, + "IGW": { + "id": "IGW", + "path": "redshift-iamrole-integ/VPC/IGW", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::InternetGateway", + "aws:cdk:cloudformation:props": { + "tags": [ + { + "key": "Name", + "value": "redshift-iamrole-integ/VPC" + } + ] + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ec2.CfnInternetGateway", + "version": "0.0.0" + } + }, + "VPCGW": { + "id": "VPCGW", + "path": "redshift-iamrole-integ/VPC/VPCGW", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::VPCGatewayAttachment", + "aws:cdk:cloudformation:props": { + "vpcId": { + "Ref": "VPCB9E5F0B4" + }, + "internetGatewayId": { + "Ref": "VPCIGWB7E252D3" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ec2.CfnVPCGatewayAttachment", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ec2.Vpc", + "version": "0.0.0" + } + }, + "Role2": { + "id": "Role2", + "path": "redshift-iamrole-integ/Role2", + "children": { + "ImportRole2": { + "id": "ImportRole2", + "path": "redshift-iamrole-integ/Role2/ImportRole2", + "constructInfo": { + "fqn": "@aws-cdk/core.Resource", + "version": "0.0.0" + } + }, + "Resource": { + "id": "Resource", + "path": "redshift-iamrole-integ/Role2/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::IAM::Role", + "aws:cdk:cloudformation:props": { + "assumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "redshift.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-iam.CfnRole", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-iam.Role", + "version": "0.0.0" + } + }, + "Role1": { + "id": "Role1", + "path": "redshift-iamrole-integ/Role1", + "children": { + "ImportRole1": { + "id": "ImportRole1", + "path": "redshift-iamrole-integ/Role1/ImportRole1", + "constructInfo": { + "fqn": "@aws-cdk/core.Resource", + "version": "0.0.0" + } + }, + "Resource": { + "id": "Resource", + "path": "redshift-iamrole-integ/Role1/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::IAM::Role", + "aws:cdk:cloudformation:props": { + "assumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "redshift.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-iam.CfnRole", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-iam.Role", + "version": "0.0.0" + } + }, + "Cluster": { + "id": "Cluster", + "path": "redshift-iamrole-integ/Cluster", + "children": { + "Subnets": { + "id": "Subnets", + "path": "redshift-iamrole-integ/Cluster/Subnets", + "children": { + "Default": { + "id": "Default", + "path": "redshift-iamrole-integ/Cluster/Subnets/Default", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::Redshift::ClusterSubnetGroup", + "aws:cdk:cloudformation:props": { + "description": "Subnets for Cluster Redshift cluster", + "subnetIds": [ + { + "Ref": "VPCPublicSubnet1SubnetB4246D30" + }, + { + "Ref": "VPCPublicSubnet2Subnet74179F39" + } + ] + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-redshift.CfnClusterSubnetGroup", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-redshift.ClusterSubnetGroup", + "version": "0.0.0" + } + }, + "SecurityGroup": { + "id": "SecurityGroup", + "path": "redshift-iamrole-integ/Cluster/SecurityGroup", + "children": { + "Resource": { + "id": "Resource", + "path": "redshift-iamrole-integ/Cluster/SecurityGroup/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::SecurityGroup", + "aws:cdk:cloudformation:props": { + "groupDescription": "Redshift security group", + "securityGroupEgress": [ + { + "cidrIp": "0.0.0.0/0", + "description": "Allow all outbound traffic by default", + "ipProtocol": "-1" + } + ], + "vpcId": { + "Ref": "VPCB9E5F0B4" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ec2.CfnSecurityGroup", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ec2.SecurityGroup", + "version": "0.0.0" + } + }, + "Secret": { + "id": "Secret", + "path": "redshift-iamrole-integ/Cluster/Secret", + "children": { + "Resource": { + "id": "Resource", + "path": "redshift-iamrole-integ/Cluster/Secret/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::SecretsManager::Secret", + "aws:cdk:cloudformation:props": { + "generateSecretString": { + "passwordLength": 30, + "secretStringTemplate": "{\"username\":\"admin\"}", + "generateStringKey": "password", + "excludeCharacters": "\"@/\\ '" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-secretsmanager.CfnSecret", + "version": "0.0.0" + } + }, + "Attachment": { + "id": "Attachment", + "path": "redshift-iamrole-integ/Cluster/Secret/Attachment", + "children": { + "Resource": { + "id": "Resource", + "path": "redshift-iamrole-integ/Cluster/Secret/Attachment/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::SecretsManager::SecretTargetAttachment", + "aws:cdk:cloudformation:props": { + "secretId": { + "Ref": "ClusterSecret6368BD0F" + }, + "targetId": { + "Ref": "ClusterEB0386A7" + }, + "targetType": "AWS::Redshift::Cluster" + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-secretsmanager.CfnSecretTargetAttachment", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-secretsmanager.SecretTargetAttachment", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-redshift.DatabaseSecret", + "version": "0.0.0" + } + }, + "Resource": { + "id": "Resource", + "path": "redshift-iamrole-integ/Cluster/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::Redshift::Cluster", + "aws:cdk:cloudformation:props": { + "clusterType": "multi-node", + "dbName": "default_db", + "masterUsername": { + "Fn::Join": [ + "", + [ + "{{resolve:secretsmanager:", + { + "Ref": "ClusterSecret6368BD0F" + }, + ":SecretString:username::}}" + ] + ] + }, + "masterUserPassword": { + "Fn::Join": [ + "", + [ + "{{resolve:secretsmanager:", + { + "Ref": "ClusterSecret6368BD0F" + }, + ":SecretString:password::}}" + ] + ] + }, + "nodeType": "dc2.large", + "allowVersionUpgrade": true, + "automatedSnapshotRetentionPeriod": 1, + "clusterSubnetGroupName": { + "Ref": "ClusterSubnetsDCFA5CB7" + }, + "encrypted": true, + "iamRoles": [ + { + "Fn::GetAtt": [ + "Role13A5C70C1", + "Arn" + ] + } + ], + "numberOfNodes": 2, + "publiclyAccessible": false, + "vpcSecurityGroupIds": [ + { + "Fn::GetAtt": [ + "ClusterSecurityGroup0921994B", + "GroupId" + ] + } + ] + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-redshift.CfnCluster", + "version": "0.0.0" + } + }, + "add-role-Role2": { + "id": "add-role-Role2", + "path": "redshift-iamrole-integ/Cluster/add-role-Role2", + "children": { + "Provider": { + "id": "Provider", + "path": "redshift-iamrole-integ/Cluster/add-role-Role2/Provider", + "constructInfo": { + "fqn": "@aws-cdk/aws-lambda.SingletonFunction", + "version": "0.0.0" + } + }, + "Resource": { + "id": "Resource", + "path": "redshift-iamrole-integ/Cluster/add-role-Role2/Resource", + "children": { + "Default": { + "id": "Default", + "path": "redshift-iamrole-integ/Cluster/add-role-Role2/Resource/Default", + "constructInfo": { + "fqn": "@aws-cdk/core.CfnResource", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/core.CustomResource", + "version": "0.0.0" + } + }, + "CustomResourcePolicy": { + "id": "CustomResourcePolicy", + "path": "redshift-iamrole-integ/Cluster/add-role-Role2/CustomResourcePolicy", + "children": { + "Resource": { + "id": "Resource", + "path": "redshift-iamrole-integ/Cluster/add-role-Role2/CustomResourcePolicy/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::IAM::Policy", + "aws:cdk:cloudformation:props": { + "policyDocument": { + "Statement": [ + { + "Action": "redshift:ModifyClusterIamRoles", + "Effect": "Allow", + "Resource": "*" + } + ], + "Version": "2012-10-17" + }, + "policyName": "ClusteraddroleRole2CustomResourcePolicyFE1C6F0D", + "roles": [ + { + "Ref": "AWS679f53fac002430cb0da5b7982bd2287ServiceRoleC1EA0FF2" + } + ] + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-iam.CfnPolicy", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-iam.Policy", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/custom-resources.AwsCustomResource", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-redshift.Cluster", + "version": "0.0.0" + } + }, + "AWS679f53fac002430cb0da5b7982bd2287": { + "id": "AWS679f53fac002430cb0da5b7982bd2287", + "path": "redshift-iamrole-integ/AWS679f53fac002430cb0da5b7982bd2287", + "children": { + "ServiceRole": { + "id": "ServiceRole", + "path": "redshift-iamrole-integ/AWS679f53fac002430cb0da5b7982bd2287/ServiceRole", + "children": { + "ImportServiceRole": { + "id": "ImportServiceRole", + "path": "redshift-iamrole-integ/AWS679f53fac002430cb0da5b7982bd2287/ServiceRole/ImportServiceRole", + "constructInfo": { + "fqn": "@aws-cdk/core.Resource", + "version": "0.0.0" + } + }, + "Resource": { + "id": "Resource", + "path": "redshift-iamrole-integ/AWS679f53fac002430cb0da5b7982bd2287/ServiceRole/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::IAM::Role", + "aws:cdk:cloudformation:props": { + "assumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + }, + "managedPolicyArns": [ + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ] + ] + } + ] + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-iam.CfnRole", + "version": "0.0.0" + } + }, + "DefaultPolicy": { + "id": "DefaultPolicy", + "path": "redshift-iamrole-integ/AWS679f53fac002430cb0da5b7982bd2287/ServiceRole/DefaultPolicy", + "children": { + "Resource": { + "id": "Resource", + "path": "redshift-iamrole-integ/AWS679f53fac002430cb0da5b7982bd2287/ServiceRole/DefaultPolicy/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::IAM::Policy", + "aws:cdk:cloudformation:props": { + "policyDocument": { + "Statement": [ + { + "Action": "iam:PassRole", + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "Role291939BC6", + "Arn" + ] + } + } + ], + "Version": "2012-10-17" + }, + "policyName": "AWS679f53fac002430cb0da5b7982bd2287ServiceRoleDefaultPolicyD28E1A5E", + "roles": [ + { + "Ref": "AWS679f53fac002430cb0da5b7982bd2287ServiceRoleC1EA0FF2" + } + ] + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-iam.CfnPolicy", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-iam.Policy", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-iam.Role", + "version": "0.0.0" + } + }, + "Code": { + "id": "Code", + "path": "redshift-iamrole-integ/AWS679f53fac002430cb0da5b7982bd2287/Code", + "children": { + "Stage": { + "id": "Stage", + "path": "redshift-iamrole-integ/AWS679f53fac002430cb0da5b7982bd2287/Code/Stage", + "constructInfo": { + "fqn": "@aws-cdk/core.AssetStaging", + "version": "0.0.0" + } + }, + "AssetBucket": { + "id": "AssetBucket", + "path": "redshift-iamrole-integ/AWS679f53fac002430cb0da5b7982bd2287/Code/AssetBucket", + "constructInfo": { + "fqn": "@aws-cdk/aws-s3.BucketBase", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-s3-assets.Asset", + "version": "0.0.0" + } + }, + "Resource": { + "id": "Resource", + "path": "redshift-iamrole-integ/AWS679f53fac002430cb0da5b7982bd2287/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::Lambda::Function", + "aws:cdk:cloudformation:props": { + "code": { + "s3Bucket": { + "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}" + }, + "s3Key": "a268caa53756f51bda8ad5f499be4ed8484a81b314811806fbb66f874837c476.zip" + }, + "role": { + "Fn::GetAtt": [ + "AWS679f53fac002430cb0da5b7982bd2287ServiceRoleC1EA0FF2", + "Arn" + ] + }, + "handler": "index.handler", + "runtime": "nodejs14.x", + "timeout": 120 + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-lambda.CfnFunction", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-lambda.Function", + "version": "0.0.0" + } + }, + "BootstrapVersion": { + "id": "BootstrapVersion", + "path": "redshift-iamrole-integ/BootstrapVersion", + "constructInfo": { + "fqn": "@aws-cdk/core.CfnParameter", + "version": "0.0.0" + } + }, + "CheckBootstrapVersion": { + "id": "CheckBootstrapVersion", + "path": "redshift-iamrole-integ/CheckBootstrapVersion", + "constructInfo": { + "fqn": "@aws-cdk/core.CfnRule", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/core.Stack", + "version": "0.0.0" + } + }, + "IamRoleInteg": { + "id": "IamRoleInteg", + "path": "IamRoleInteg", + "children": { + "DefaultTest": { + "id": "DefaultTest", + "path": "IamRoleInteg/DefaultTest", + "children": { + "Default": { + "id": "Default", + "path": "IamRoleInteg/DefaultTest/Default", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.1.189" + } + }, + "DeployAssert": { + "id": "DeployAssert", + "path": "IamRoleInteg/DefaultTest/DeployAssert", + "children": { + "BootstrapVersion": { + "id": "BootstrapVersion", + "path": "IamRoleInteg/DefaultTest/DeployAssert/BootstrapVersion", + "constructInfo": { + "fqn": "@aws-cdk/core.CfnParameter", + "version": "0.0.0" + } + }, + "CheckBootstrapVersion": { + "id": "CheckBootstrapVersion", + "path": "IamRoleInteg/DefaultTest/DeployAssert/CheckBootstrapVersion", + "constructInfo": { + "fqn": "@aws-cdk/core.CfnRule", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/core.Stack", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests.IntegTestCase", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests.IntegTest", + "version": "0.0.0" + } + }, + "Tree": { + "id": "Tree", + "path": "Tree", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.1.189" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/core.App", + "version": "0.0.0" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-redshift/test/integ.cluster-iamrole.ts b/packages/@aws-cdk/aws-redshift/test/integ.cluster-iamrole.ts new file mode 100644 index 0000000000000..fe87af89ef2cb --- /dev/null +++ b/packages/@aws-cdk/aws-redshift/test/integ.cluster-iamrole.ts @@ -0,0 +1,42 @@ +import * as ec2 from '@aws-cdk/aws-ec2'; +import * as iam from '@aws-cdk/aws-iam'; +import { App, RemovalPolicy, Stack, StackProps } from '@aws-cdk/core'; +import * as integ from '@aws-cdk/integ-tests'; +import { Construct } from 'constructs'; +import * as redshift from '../lib'; + +class RedshiftEnv extends Stack { + constructor(scope: Construct, id: string, props?: StackProps) { + super(scope, id, props); + + const vpc = new ec2.Vpc(this, 'VPC'); + const role = new iam.Role(this, 'Role2', { + assumedBy: new iam.ServicePrincipal('redshift.amazonaws.com'), + }); + + // Adding iam role after cluster creation + const redshiftCluster = new redshift.Cluster(this, 'Cluster', { + vpc: vpc, + vpcSubnets: { + subnetType: ec2.SubnetType.PUBLIC, + }, + masterUser: { + masterUsername: 'admin', + }, + roles: [new iam.Role(this, 'Role1', { + assumedBy: new iam.ServicePrincipal('redshift.amazonaws.com'), + })], + removalPolicy: RemovalPolicy.DESTROY, + }); + + redshiftCluster.addIamRole(role); + } +} + +const app = new App(); + +new integ.IntegTest(app, 'IamRoleInteg', { + testCases: [new RedshiftEnv(app, 'redshift-iamrole-integ')], +}); + +app.synth(); \ No newline at end of file From 39a43bd193a39bb3625863ae555eea5d4216377d Mon Sep 17 00:00:00 2001 From: Rizbir Khan Date: Mon, 23 Jan 2023 15:52:24 +0000 Subject: [PATCH 02/25] modification: using interface instead --- packages/@aws-cdk/aws-redshift/lib/cluster.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/@aws-cdk/aws-redshift/lib/cluster.ts b/packages/@aws-cdk/aws-redshift/lib/cluster.ts index aeca70387952d..45636ade4c6d5 100644 --- a/packages/@aws-cdk/aws-redshift/lib/cluster.ts +++ b/packages/@aws-cdk/aws-redshift/lib/cluster.ts @@ -668,9 +668,8 @@ export class Cluster extends ClusterBase { * Adds a role to the cluster * * @param role the role to add - * [disable-awslint:ref-via-interface] */ - public addIamRole(role: iam.Role): void { + public addIamRole(role: iam.IRole): void { const clusterRoleList = this.cluster.iamRoles ?? []; if (clusterRoleList.includes(role.roleArn)) { From adbfa48a7d9c92b488cd2fdaf9863e255a7c21ba Mon Sep 17 00:00:00 2001 From: Rizbir Khan Date: Mon, 23 Jan 2023 16:08:28 +0000 Subject: [PATCH 03/25] removing console.log --- packages/@aws-cdk/aws-redshift/lib/cluster.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/packages/@aws-cdk/aws-redshift/lib/cluster.ts b/packages/@aws-cdk/aws-redshift/lib/cluster.ts index 45636ade4c6d5..114b9c0039681 100644 --- a/packages/@aws-cdk/aws-redshift/lib/cluster.ts +++ b/packages/@aws-cdk/aws-redshift/lib/cluster.ts @@ -709,9 +709,6 @@ export class Cluster extends ClusterBase { resourceType: 'Custom::ModifyClusterIamRoles', }); - // eslint-disable-next-line no-console - console.log(roleCustomResource.getResponseField('IamRoles')); - role.grantPassRole(roleCustomResource.grantPrincipal); } } From 88c5bb2b1560c24c3a8f4543262fa94a9cdc07a6 Mon Sep 17 00:00:00 2001 From: Rizbir Khan Date: Mon, 23 Jan 2023 16:12:29 +0000 Subject: [PATCH 04/25] modification: changing custom resource type --- packages/@aws-cdk/aws-redshift/test/cluster.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/@aws-cdk/aws-redshift/test/cluster.test.ts b/packages/@aws-cdk/aws-redshift/test/cluster.test.ts index b11f48c24c08a..65c35c5709472 100644 --- a/packages/@aws-cdk/aws-redshift/test/cluster.test.ts +++ b/packages/@aws-cdk/aws-redshift/test/cluster.test.ts @@ -630,7 +630,7 @@ describe('IAM role', () => { })); // THEN - Template.fromStack(stack).hasResource('Custom::RedshiftAddRole', {}); + Template.fromStack(stack).hasResource('Custom::ModifyClusterIamRoles', {}); }); test('throws when adding more than 10 roles to cluster', () => { From fab283d1e2a0c2d37b14e481bc1de67104b9c1ca Mon Sep 17 00:00:00 2001 From: Rizbir Khan Date: Mon, 23 Jan 2023 16:13:55 +0000 Subject: [PATCH 05/25] addition: generating integ test snapshot --- .../manifest.json | 20 +------------------ .../redshift-iamrole-integ.assets.json | 4 ++-- .../redshift-iamrole-integ.template.json | 2 +- 3 files changed, 4 insertions(+), 22 deletions(-) diff --git a/packages/@aws-cdk/aws-redshift/test/integ.cluster-iamrole.js.snapshot/manifest.json b/packages/@aws-cdk/aws-redshift/test/integ.cluster-iamrole.js.snapshot/manifest.json index 681e688e658f8..4985d4561c2e9 100644 --- a/packages/@aws-cdk/aws-redshift/test/integ.cluster-iamrole.js.snapshot/manifest.json +++ b/packages/@aws-cdk/aws-redshift/test/integ.cluster-iamrole.js.snapshot/manifest.json @@ -17,7 +17,7 @@ "validateOnSynth": false, "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", - "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/081848ef4160c3161ec9ce1b80c08ed01f44ba9b6ecc751e448f61e1b0cd5c0d.json", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/6035d2777d8aeb95ba80e06333d6e26f89087f242e2a859011c969eb2293a3c3.json", "requiresBootstrapStackVersion": 6, "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", "additionalDependencies": [ @@ -254,24 +254,6 @@ "type": "aws:cdk:logicalId", "data": "CheckBootstrapVersion" } - ], - "ClusteraddroleC521A629": [ - { - "type": "aws:cdk:logicalId", - "data": "ClusteraddroleC521A629", - "trace": [ - "!!DESTRUCTIVE_CHANGES: WILL_DESTROY" - ] - } - ], - "ClusteraddroleCustomResourcePolicyA7EB876A": [ - { - "type": "aws:cdk:logicalId", - "data": "ClusteraddroleCustomResourcePolicyA7EB876A", - "trace": [ - "!!DESTRUCTIVE_CHANGES: WILL_DESTROY" - ] - } ] }, "displayName": "redshift-iamrole-integ" diff --git a/packages/@aws-cdk/aws-redshift/test/integ.cluster-iamrole.js.snapshot/redshift-iamrole-integ.assets.json b/packages/@aws-cdk/aws-redshift/test/integ.cluster-iamrole.js.snapshot/redshift-iamrole-integ.assets.json index c3881622e6ec8..08c369dbe041e 100644 --- a/packages/@aws-cdk/aws-redshift/test/integ.cluster-iamrole.js.snapshot/redshift-iamrole-integ.assets.json +++ b/packages/@aws-cdk/aws-redshift/test/integ.cluster-iamrole.js.snapshot/redshift-iamrole-integ.assets.json @@ -14,7 +14,7 @@ } } }, - "081848ef4160c3161ec9ce1b80c08ed01f44ba9b6ecc751e448f61e1b0cd5c0d": { + "6035d2777d8aeb95ba80e06333d6e26f89087f242e2a859011c969eb2293a3c3": { "source": { "path": "redshift-iamrole-integ.template.json", "packaging": "file" @@ -22,7 +22,7 @@ "destinations": { "current_account-current_region": { "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", - "objectKey": "081848ef4160c3161ec9ce1b80c08ed01f44ba9b6ecc751e448f61e1b0cd5c0d.json", + "objectKey": "6035d2777d8aeb95ba80e06333d6e26f89087f242e2a859011c969eb2293a3c3.json", "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" } } diff --git a/packages/@aws-cdk/aws-redshift/test/integ.cluster-iamrole.js.snapshot/redshift-iamrole-integ.template.json b/packages/@aws-cdk/aws-redshift/test/integ.cluster-iamrole.js.snapshot/redshift-iamrole-integ.template.json index fdf91d817b7c4..255c6010269c4 100644 --- a/packages/@aws-cdk/aws-redshift/test/integ.cluster-iamrole.js.snapshot/redshift-iamrole-integ.template.json +++ b/packages/@aws-cdk/aws-redshift/test/integ.cluster-iamrole.js.snapshot/redshift-iamrole-integ.template.json @@ -541,7 +541,7 @@ "DeletionPolicy": "Delete" }, "ClusteraddroleRole2CCF96C23": { - "Type": "Custom::RedshiftAddRole", + "Type": "Custom::ModifyClusterIamRoles", "Properties": { "ServiceToken": { "Fn::GetAtt": [ From d6d377e981228e04d2d54eed67d62a85f4f24d0f Mon Sep 17 00:00:00 2001 From: Rizbir Khan Date: Mon, 23 Jan 2023 16:34:29 +0000 Subject: [PATCH 06/25] addition: updating README.md --- packages/@aws-cdk/aws-redshift/README.md | 36 ++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/packages/@aws-cdk/aws-redshift/README.md b/packages/@aws-cdk/aws-redshift/README.md index 056a1fde16276..3973c8eaceac7 100644 --- a/packages/@aws-cdk/aws-redshift/README.md +++ b/packages/@aws-cdk/aws-redshift/README.md @@ -386,3 +386,39 @@ new Cluster(stack, 'Redshift', { ``` If enhanced VPC routing is not enabled, Amazon Redshift routes traffic through the internet, including traffic to other services within the AWS network. + +## IAM roles + +Attaching IAM roles to a Redshift Cluster grants permissions to the Redshift service to perform actions on your behalf. + +```ts +declare const vpc: ec2.Vpc + +const role = new iam.Role(this, 'Role', { + assumedBy: new iam.ServicePrincipal('redshift.amazonaws.com'), +}); +const cluster = new Cluster(this, 'Redshift', { + masterUser: { + masterUsername: 'admin', + }, + vpc, + roles: [role], +}); +``` + +Additional IAM roles can be attached to a cluster using the `addIamRole` method. + +```ts +declare const vpc: ec2.Vpc + +const role = new iam.Role(this, 'Role', { + assumedBy: new iam.ServicePrincipal('redshift.amazonaws.com'), +}); +const cluster = new Cluster(this, 'Redshift', { + masterUser: { + masterUsername: 'admin', + }, + vpc, +}); +cluster.addIamRole(role); +``` \ No newline at end of file From 400fd226461f0a2e434befcf6dc321ad2bdd384e Mon Sep 17 00:00:00 2001 From: Rizbir Khan Date: Tue, 24 Jan 2023 10:49:22 +0000 Subject: [PATCH 07/25] modification: MD047/single-trailing-newline --- packages/@aws-cdk/aws-redshift/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/@aws-cdk/aws-redshift/README.md b/packages/@aws-cdk/aws-redshift/README.md index 3973c8eaceac7..77cc249b4af4c 100644 --- a/packages/@aws-cdk/aws-redshift/README.md +++ b/packages/@aws-cdk/aws-redshift/README.md @@ -421,4 +421,4 @@ const cluster = new Cluster(this, 'Redshift', { vpc, }); cluster.addIamRole(role); -``` \ No newline at end of file +``` From 63cfe78518ebb2281d869ff63c8ac0e339fc7e1e Mon Sep 17 00:00:00 2001 From: Rizbir Khan Date: Tue, 24 Jan 2023 12:05:31 +0000 Subject: [PATCH 08/25] another test --- .../manifest.json | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/packages/@aws-cdk/aws-redshift/test/integ.cluster-iamrole.js.snapshot/manifest.json b/packages/@aws-cdk/aws-redshift/test/integ.cluster-iamrole.js.snapshot/manifest.json index 4985d4561c2e9..8a28ba425757e 100644 --- a/packages/@aws-cdk/aws-redshift/test/integ.cluster-iamrole.js.snapshot/manifest.json +++ b/packages/@aws-cdk/aws-redshift/test/integ.cluster-iamrole.js.snapshot/manifest.json @@ -254,6 +254,33 @@ "type": "aws:cdk:logicalId", "data": "CheckBootstrapVersion" } + ], + "Role1ABCC5F0": [ + { + "type": "aws:cdk:logicalId", + "data": "Role1ABCC5F0", + "trace": [ + "!!DESTRUCTIVE_CHANGES: WILL_DESTROY" + ] + } + ], + "ClusteraddroleRoleAA246548": [ + { + "type": "aws:cdk:logicalId", + "data": "ClusteraddroleRoleAA246548", + "trace": [ + "!!DESTRUCTIVE_CHANGES: WILL_DESTROY" + ] + } + ], + "ClusteraddroleRoleCustomResourcePolicy9F03684E": [ + { + "type": "aws:cdk:logicalId", + "data": "ClusteraddroleRoleCustomResourcePolicy9F03684E", + "trace": [ + "!!DESTRUCTIVE_CHANGES: WILL_DESTROY" + ] + } ] }, "displayName": "redshift-iamrole-integ" From 5fcd84ff446a606c9c6e7f0cfff66510859a4399 Mon Sep 17 00:00:00 2001 From: Rizbir Khan Date: Thu, 26 Jan 2023 12:39:49 +0000 Subject: [PATCH 09/25] diffAssets --- .../integ.json | 1 + .../manifest.json | 45 ++++------------- .../redshift-iamrole-integ.assets.json | 4 +- .../redshift-iamrole-integ.template.json | 28 +++++------ .../tree.json | 50 +++++++++---------- .../test/integ.cluster-iamrole.ts | 5 +- 6 files changed, 54 insertions(+), 79 deletions(-) diff --git a/packages/@aws-cdk/aws-redshift/test/integ.cluster-iamrole.js.snapshot/integ.json b/packages/@aws-cdk/aws-redshift/test/integ.cluster-iamrole.js.snapshot/integ.json index 754546fb1b7e4..578d7f9ce178c 100644 --- a/packages/@aws-cdk/aws-redshift/test/integ.cluster-iamrole.js.snapshot/integ.json +++ b/packages/@aws-cdk/aws-redshift/test/integ.cluster-iamrole.js.snapshot/integ.json @@ -5,6 +5,7 @@ "stacks": [ "redshift-iamrole-integ" ], + "diffAssets": true, "assertionStack": "IamRoleInteg/DefaultTest/DeployAssert", "assertionStackName": "IamRoleIntegDefaultTestDeployAssertBEF20992" } diff --git a/packages/@aws-cdk/aws-redshift/test/integ.cluster-iamrole.js.snapshot/manifest.json b/packages/@aws-cdk/aws-redshift/test/integ.cluster-iamrole.js.snapshot/manifest.json index 8a28ba425757e..75a3f549d4750 100644 --- a/packages/@aws-cdk/aws-redshift/test/integ.cluster-iamrole.js.snapshot/manifest.json +++ b/packages/@aws-cdk/aws-redshift/test/integ.cluster-iamrole.js.snapshot/manifest.json @@ -17,7 +17,7 @@ "validateOnSynth": false, "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", - "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/6035d2777d8aeb95ba80e06333d6e26f89087f242e2a859011c969eb2293a3c3.json", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/62416b81b75b2f3e2aaef03c6ebb06cbc9c9643581cb32402fc66b56d05e79d1.json", "requiresBootstrapStackVersion": 6, "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", "additionalDependencies": [ @@ -171,16 +171,16 @@ "data": "VPCVPCGW99B986DC" } ], - "/redshift-iamrole-integ/Role2/Resource": [ + "/redshift-iamrole-integ/RoleA/Resource": [ { "type": "aws:cdk:logicalId", - "data": "Role291939BC6" + "data": "RoleA3119C8FE" } ], - "/redshift-iamrole-integ/Role1/Resource": [ + "/redshift-iamrole-integ/RoleB/Resource": [ { "type": "aws:cdk:logicalId", - "data": "Role13A5C70C1" + "data": "RoleB318292C8" } ], "/redshift-iamrole-integ/Cluster/Subnets/Default": [ @@ -213,16 +213,16 @@ "data": "ClusterEB0386A7" } ], - "/redshift-iamrole-integ/Cluster/add-role-Role2/Resource/Default": [ + "/redshift-iamrole-integ/Cluster/add-role-RoleA/Resource/Default": [ { "type": "aws:cdk:logicalId", - "data": "ClusteraddroleRole2CCF96C23" + "data": "ClusteraddroleRoleAA2FB3227" } ], - "/redshift-iamrole-integ/Cluster/add-role-Role2/CustomResourcePolicy/Resource": [ + "/redshift-iamrole-integ/Cluster/add-role-RoleA/CustomResourcePolicy/Resource": [ { "type": "aws:cdk:logicalId", - "data": "ClusteraddroleRole2CustomResourcePolicyFE1C6F0D" + "data": "ClusteraddroleRoleACustomResourcePolicyB2DE8E3F" } ], "/redshift-iamrole-integ/AWS679f53fac002430cb0da5b7982bd2287/ServiceRole/Resource": [ @@ -254,33 +254,6 @@ "type": "aws:cdk:logicalId", "data": "CheckBootstrapVersion" } - ], - "Role1ABCC5F0": [ - { - "type": "aws:cdk:logicalId", - "data": "Role1ABCC5F0", - "trace": [ - "!!DESTRUCTIVE_CHANGES: WILL_DESTROY" - ] - } - ], - "ClusteraddroleRoleAA246548": [ - { - "type": "aws:cdk:logicalId", - "data": "ClusteraddroleRoleAA246548", - "trace": [ - "!!DESTRUCTIVE_CHANGES: WILL_DESTROY" - ] - } - ], - "ClusteraddroleRoleCustomResourcePolicy9F03684E": [ - { - "type": "aws:cdk:logicalId", - "data": "ClusteraddroleRoleCustomResourcePolicy9F03684E", - "trace": [ - "!!DESTRUCTIVE_CHANGES: WILL_DESTROY" - ] - } ] }, "displayName": "redshift-iamrole-integ" diff --git a/packages/@aws-cdk/aws-redshift/test/integ.cluster-iamrole.js.snapshot/redshift-iamrole-integ.assets.json b/packages/@aws-cdk/aws-redshift/test/integ.cluster-iamrole.js.snapshot/redshift-iamrole-integ.assets.json index 08c369dbe041e..da257a4deb8f2 100644 --- a/packages/@aws-cdk/aws-redshift/test/integ.cluster-iamrole.js.snapshot/redshift-iamrole-integ.assets.json +++ b/packages/@aws-cdk/aws-redshift/test/integ.cluster-iamrole.js.snapshot/redshift-iamrole-integ.assets.json @@ -14,7 +14,7 @@ } } }, - "6035d2777d8aeb95ba80e06333d6e26f89087f242e2a859011c969eb2293a3c3": { + "62416b81b75b2f3e2aaef03c6ebb06cbc9c9643581cb32402fc66b56d05e79d1": { "source": { "path": "redshift-iamrole-integ.template.json", "packaging": "file" @@ -22,7 +22,7 @@ "destinations": { "current_account-current_region": { "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", - "objectKey": "6035d2777d8aeb95ba80e06333d6e26f89087f242e2a859011c969eb2293a3c3.json", + "objectKey": "62416b81b75b2f3e2aaef03c6ebb06cbc9c9643581cb32402fc66b56d05e79d1.json", "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" } } diff --git a/packages/@aws-cdk/aws-redshift/test/integ.cluster-iamrole.js.snapshot/redshift-iamrole-integ.template.json b/packages/@aws-cdk/aws-redshift/test/integ.cluster-iamrole.js.snapshot/redshift-iamrole-integ.template.json index 255c6010269c4..8f42fdc57e0c0 100644 --- a/packages/@aws-cdk/aws-redshift/test/integ.cluster-iamrole.js.snapshot/redshift-iamrole-integ.template.json +++ b/packages/@aws-cdk/aws-redshift/test/integ.cluster-iamrole.js.snapshot/redshift-iamrole-integ.template.json @@ -391,7 +391,7 @@ } } }, - "Role291939BC6": { + "RoleA3119C8FE": { "Type": "AWS::IAM::Role", "Properties": { "AssumeRolePolicyDocument": { @@ -408,7 +408,7 @@ } } }, - "Role13A5C70C1": { + "RoleB318292C8": { "Type": "AWS::IAM::Role", "Properties": { "AssumeRolePolicyDocument": { @@ -521,7 +521,7 @@ "IamRoles": [ { "Fn::GetAtt": [ - "Role13A5C70C1", + "RoleB318292C8", "Arn" ] } @@ -540,7 +540,7 @@ "UpdateReplacePolicy": "Delete", "DeletionPolicy": "Delete" }, - "ClusteraddroleRole2CCF96C23": { + "ClusteraddroleRoleAA2FB3227": { "Type": "Custom::ModifyClusterIamRoles", "Properties": { "ServiceToken": { @@ -560,14 +560,14 @@ "\",\"AddIamRoles\":[\"", { "Fn::GetAtt": [ - "Role291939BC6", + "RoleA3119C8FE", "Arn" ] }, "\"]},\"physicalResourceId\":{\"id\":\"", { "Fn::GetAtt": [ - "Role291939BC6", + "RoleA3119C8FE", "Arn" ] }, @@ -590,14 +590,14 @@ "\",\"AddIamRoles\":[\"", { "Fn::GetAtt": [ - "Role291939BC6", + "RoleA3119C8FE", "Arn" ] }, "\"]},\"physicalResourceId\":{\"id\":\"", { "Fn::GetAtt": [ - "Role291939BC6", + "RoleA3119C8FE", "Arn" ] }, @@ -620,14 +620,14 @@ "\",\"RemoveIamRoles\":[\"", { "Fn::GetAtt": [ - "Role291939BC6", + "RoleA3119C8FE", "Arn" ] }, "\"]},\"physicalResourceId\":{\"id\":\"", { "Fn::GetAtt": [ - "Role291939BC6", + "RoleA3119C8FE", "Arn" ] }, @@ -642,12 +642,12 @@ "InstallLatestAwsSdk": true }, "DependsOn": [ - "ClusteraddroleRole2CustomResourcePolicyFE1C6F0D" + "ClusteraddroleRoleACustomResourcePolicyB2DE8E3F" ], "UpdateReplacePolicy": "Delete", "DeletionPolicy": "Delete" }, - "ClusteraddroleRole2CustomResourcePolicyFE1C6F0D": { + "ClusteraddroleRoleACustomResourcePolicyB2DE8E3F": { "Type": "AWS::IAM::Policy", "Properties": { "PolicyDocument": { @@ -660,7 +660,7 @@ ], "Version": "2012-10-17" }, - "PolicyName": "ClusteraddroleRole2CustomResourcePolicyFE1C6F0D", + "PolicyName": "ClusteraddroleRoleACustomResourcePolicyB2DE8E3F", "Roles": [ { "Ref": "AWS679f53fac002430cb0da5b7982bd2287ServiceRoleC1EA0FF2" @@ -709,7 +709,7 @@ "Effect": "Allow", "Resource": { "Fn::GetAtt": [ - "Role291939BC6", + "RoleA3119C8FE", "Arn" ] } diff --git a/packages/@aws-cdk/aws-redshift/test/integ.cluster-iamrole.js.snapshot/tree.json b/packages/@aws-cdk/aws-redshift/test/integ.cluster-iamrole.js.snapshot/tree.json index bd76be5d5f0a8..6be2189090cba 100644 --- a/packages/@aws-cdk/aws-redshift/test/integ.cluster-iamrole.js.snapshot/tree.json +++ b/packages/@aws-cdk/aws-redshift/test/integ.cluster-iamrole.js.snapshot/tree.json @@ -651,13 +651,13 @@ "version": "0.0.0" } }, - "Role2": { - "id": "Role2", - "path": "redshift-iamrole-integ/Role2", + "RoleA": { + "id": "RoleA", + "path": "redshift-iamrole-integ/RoleA", "children": { - "ImportRole2": { - "id": "ImportRole2", - "path": "redshift-iamrole-integ/Role2/ImportRole2", + "ImportRoleA": { + "id": "ImportRoleA", + "path": "redshift-iamrole-integ/RoleA/ImportRoleA", "constructInfo": { "fqn": "@aws-cdk/core.Resource", "version": "0.0.0" @@ -665,7 +665,7 @@ }, "Resource": { "id": "Resource", - "path": "redshift-iamrole-integ/Role2/Resource", + "path": "redshift-iamrole-integ/RoleA/Resource", "attributes": { "aws:cdk:cloudformation:type": "AWS::IAM::Role", "aws:cdk:cloudformation:props": { @@ -694,13 +694,13 @@ "version": "0.0.0" } }, - "Role1": { - "id": "Role1", - "path": "redshift-iamrole-integ/Role1", + "RoleB": { + "id": "RoleB", + "path": "redshift-iamrole-integ/RoleB", "children": { - "ImportRole1": { - "id": "ImportRole1", - "path": "redshift-iamrole-integ/Role1/ImportRole1", + "ImportRoleB": { + "id": "ImportRoleB", + "path": "redshift-iamrole-integ/RoleB/ImportRoleB", "constructInfo": { "fqn": "@aws-cdk/core.Resource", "version": "0.0.0" @@ -708,7 +708,7 @@ }, "Resource": { "id": "Resource", - "path": "redshift-iamrole-integ/Role1/Resource", + "path": "redshift-iamrole-integ/RoleB/Resource", "attributes": { "aws:cdk:cloudformation:type": "AWS::IAM::Role", "aws:cdk:cloudformation:props": { @@ -908,7 +908,7 @@ "iamRoles": [ { "Fn::GetAtt": [ - "Role13A5C70C1", + "RoleB318292C8", "Arn" ] } @@ -930,13 +930,13 @@ "version": "0.0.0" } }, - "add-role-Role2": { - "id": "add-role-Role2", - "path": "redshift-iamrole-integ/Cluster/add-role-Role2", + "add-role-RoleA": { + "id": "add-role-RoleA", + "path": "redshift-iamrole-integ/Cluster/add-role-RoleA", "children": { "Provider": { "id": "Provider", - "path": "redshift-iamrole-integ/Cluster/add-role-Role2/Provider", + "path": "redshift-iamrole-integ/Cluster/add-role-RoleA/Provider", "constructInfo": { "fqn": "@aws-cdk/aws-lambda.SingletonFunction", "version": "0.0.0" @@ -944,11 +944,11 @@ }, "Resource": { "id": "Resource", - "path": "redshift-iamrole-integ/Cluster/add-role-Role2/Resource", + "path": "redshift-iamrole-integ/Cluster/add-role-RoleA/Resource", "children": { "Default": { "id": "Default", - "path": "redshift-iamrole-integ/Cluster/add-role-Role2/Resource/Default", + "path": "redshift-iamrole-integ/Cluster/add-role-RoleA/Resource/Default", "constructInfo": { "fqn": "@aws-cdk/core.CfnResource", "version": "0.0.0" @@ -962,11 +962,11 @@ }, "CustomResourcePolicy": { "id": "CustomResourcePolicy", - "path": "redshift-iamrole-integ/Cluster/add-role-Role2/CustomResourcePolicy", + "path": "redshift-iamrole-integ/Cluster/add-role-RoleA/CustomResourcePolicy", "children": { "Resource": { "id": "Resource", - "path": "redshift-iamrole-integ/Cluster/add-role-Role2/CustomResourcePolicy/Resource", + "path": "redshift-iamrole-integ/Cluster/add-role-RoleA/CustomResourcePolicy/Resource", "attributes": { "aws:cdk:cloudformation:type": "AWS::IAM::Policy", "aws:cdk:cloudformation:props": { @@ -980,7 +980,7 @@ ], "Version": "2012-10-17" }, - "policyName": "ClusteraddroleRole2CustomResourcePolicyFE1C6F0D", + "policyName": "ClusteraddroleRoleACustomResourcePolicyB2DE8E3F", "roles": [ { "Ref": "AWS679f53fac002430cb0da5b7982bd2287ServiceRoleC1EA0FF2" @@ -1083,7 +1083,7 @@ "Effect": "Allow", "Resource": { "Fn::GetAtt": [ - "Role291939BC6", + "RoleA3119C8FE", "Arn" ] } diff --git a/packages/@aws-cdk/aws-redshift/test/integ.cluster-iamrole.ts b/packages/@aws-cdk/aws-redshift/test/integ.cluster-iamrole.ts index fe87af89ef2cb..83b6474af370a 100644 --- a/packages/@aws-cdk/aws-redshift/test/integ.cluster-iamrole.ts +++ b/packages/@aws-cdk/aws-redshift/test/integ.cluster-iamrole.ts @@ -10,7 +10,7 @@ class RedshiftEnv extends Stack { super(scope, id, props); const vpc = new ec2.Vpc(this, 'VPC'); - const role = new iam.Role(this, 'Role2', { + const role = new iam.Role(this, 'RoleA', { assumedBy: new iam.ServicePrincipal('redshift.amazonaws.com'), }); @@ -23,7 +23,7 @@ class RedshiftEnv extends Stack { masterUser: { masterUsername: 'admin', }, - roles: [new iam.Role(this, 'Role1', { + roles: [new iam.Role(this, 'RoleB', { assumedBy: new iam.ServicePrincipal('redshift.amazonaws.com'), })], removalPolicy: RemovalPolicy.DESTROY, @@ -37,6 +37,7 @@ const app = new App(); new integ.IntegTest(app, 'IamRoleInteg', { testCases: [new RedshiftEnv(app, 'redshift-iamrole-integ')], + diffAssets: true, }); app.synth(); \ No newline at end of file From 4dd83dc45c9f36780f00c2a59e5d099a1f6c2a5b Mon Sep 17 00:00:00 2001 From: Rizbir Khan Date: Mon, 30 Jan 2023 10:33:41 +0000 Subject: [PATCH 10/25] removing test --- .../test/integ.cluster-iamrole.ts | 43 ------------------- 1 file changed, 43 deletions(-) delete mode 100644 packages/@aws-cdk/aws-redshift/test/integ.cluster-iamrole.ts diff --git a/packages/@aws-cdk/aws-redshift/test/integ.cluster-iamrole.ts b/packages/@aws-cdk/aws-redshift/test/integ.cluster-iamrole.ts deleted file mode 100644 index 83b6474af370a..0000000000000 --- a/packages/@aws-cdk/aws-redshift/test/integ.cluster-iamrole.ts +++ /dev/null @@ -1,43 +0,0 @@ -import * as ec2 from '@aws-cdk/aws-ec2'; -import * as iam from '@aws-cdk/aws-iam'; -import { App, RemovalPolicy, Stack, StackProps } from '@aws-cdk/core'; -import * as integ from '@aws-cdk/integ-tests'; -import { Construct } from 'constructs'; -import * as redshift from '../lib'; - -class RedshiftEnv extends Stack { - constructor(scope: Construct, id: string, props?: StackProps) { - super(scope, id, props); - - const vpc = new ec2.Vpc(this, 'VPC'); - const role = new iam.Role(this, 'RoleA', { - assumedBy: new iam.ServicePrincipal('redshift.amazonaws.com'), - }); - - // Adding iam role after cluster creation - const redshiftCluster = new redshift.Cluster(this, 'Cluster', { - vpc: vpc, - vpcSubnets: { - subnetType: ec2.SubnetType.PUBLIC, - }, - masterUser: { - masterUsername: 'admin', - }, - roles: [new iam.Role(this, 'RoleB', { - assumedBy: new iam.ServicePrincipal('redshift.amazonaws.com'), - })], - removalPolicy: RemovalPolicy.DESTROY, - }); - - redshiftCluster.addIamRole(role); - } -} - -const app = new App(); - -new integ.IntegTest(app, 'IamRoleInteg', { - testCases: [new RedshiftEnv(app, 'redshift-iamrole-integ')], - diffAssets: true, -}); - -app.synth(); \ No newline at end of file From ff9900c50d45050b227744f6fb39d6aa8451c87a Mon Sep 17 00:00:00 2001 From: Rizbir Khan Date: Mon, 30 Jan 2023 10:42:19 +0000 Subject: [PATCH 11/25] testing again --- .../test/integ.cluster-iamrole.ts | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 packages/@aws-cdk/aws-redshift/test/integ.cluster-iamrole.ts diff --git a/packages/@aws-cdk/aws-redshift/test/integ.cluster-iamrole.ts b/packages/@aws-cdk/aws-redshift/test/integ.cluster-iamrole.ts new file mode 100644 index 0000000000000..1e053a1de9599 --- /dev/null +++ b/packages/@aws-cdk/aws-redshift/test/integ.cluster-iamrole.ts @@ -0,0 +1,42 @@ +import * as ec2 from '@aws-cdk/aws-ec2'; +import * as iam from '@aws-cdk/aws-iam'; +import { App, RemovalPolicy, Stack, StackProps } from '@aws-cdk/core'; +import * as integ from '@aws-cdk/integ-tests'; +import { Construct } from 'constructs'; +import * as redshift from '../lib'; + +class RedshiftEnv extends Stack { + constructor(scope: Construct, id: string, props?: StackProps) { + super(scope, id, props); + + const vpc = new ec2.Vpc(this, 'VPC'); + const role = new iam.Role(this, 'RoleA', { + assumedBy: new iam.ServicePrincipal('redshift.amazonaws.com'), + }); + + // Adding iam role after cluster creation + const redshiftCluster = new redshift.Cluster(this, 'Cluster', { + vpc: vpc, + vpcSubnets: { + subnetType: ec2.SubnetType.PUBLIC, + }, + masterUser: { + masterUsername: 'admin', + }, + roles: [new iam.Role(this, 'RoleB', { + assumedBy: new iam.ServicePrincipal('redshift.amazonaws.com'), + })], + removalPolicy: RemovalPolicy.DESTROY, + }); + + redshiftCluster.addIamRole(role); + } +} + +const app = new App(); + +new integ.IntegTest(app, 'IamRoleInteg', { + testCases: [new RedshiftEnv(app, 'redshift-iamrole-integ')], +}); + +app.synth(); \ No newline at end of file From 18d2da9e0651dd233a99f12234449d7cea55b299 Mon Sep 17 00:00:00 2001 From: Rizbir Khan Date: Mon, 30 Jan 2023 10:52:31 +0000 Subject: [PATCH 12/25] line break EOF --- packages/@aws-cdk/aws-redshift/test/integ.cluster-iamrole.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/@aws-cdk/aws-redshift/test/integ.cluster-iamrole.ts b/packages/@aws-cdk/aws-redshift/test/integ.cluster-iamrole.ts index 1e053a1de9599..cb664b04c87fa 100644 --- a/packages/@aws-cdk/aws-redshift/test/integ.cluster-iamrole.ts +++ b/packages/@aws-cdk/aws-redshift/test/integ.cluster-iamrole.ts @@ -39,4 +39,4 @@ new integ.IntegTest(app, 'IamRoleInteg', { testCases: [new RedshiftEnv(app, 'redshift-iamrole-integ')], }); -app.synth(); \ No newline at end of file +app.synth(); From e48cf829781a3bf55425b4a235c1db80e1a7f2df Mon Sep 17 00:00:00 2001 From: Rizbir Khan Date: Mon, 30 Jan 2023 11:31:48 +0000 Subject: [PATCH 13/25] modification: preventing install of latest sdk, uses 2 or 3 version anyways --- packages/@aws-cdk/aws-redshift/lib/cluster.ts | 2 ++ .../test/integ.cluster-iamrole.js.snapshot/integ.json | 1 - .../test/integ.cluster-iamrole.js.snapshot/manifest.json | 2 +- .../redshift-iamrole-integ.assets.json | 4 ++-- .../redshift-iamrole-integ.template.json | 2 +- 5 files changed, 6 insertions(+), 5 deletions(-) diff --git a/packages/@aws-cdk/aws-redshift/lib/cluster.ts b/packages/@aws-cdk/aws-redshift/lib/cluster.ts index 114b9c0039681..15f4fa2b01542 100644 --- a/packages/@aws-cdk/aws-redshift/lib/cluster.ts +++ b/packages/@aws-cdk/aws-redshift/lib/cluster.ts @@ -707,6 +707,8 @@ export class Cluster extends ClusterBase { resources: AwsCustomResourcePolicy.ANY_RESOURCE, }), resourceType: 'Custom::ModifyClusterIamRoles', + // Latest SDK version for AWS Lambda is 2.x/3.x, so we do not need to install a later version + installLatestAwsSdk: false, }); role.grantPassRole(roleCustomResource.grantPrincipal); diff --git a/packages/@aws-cdk/aws-redshift/test/integ.cluster-iamrole.js.snapshot/integ.json b/packages/@aws-cdk/aws-redshift/test/integ.cluster-iamrole.js.snapshot/integ.json index 578d7f9ce178c..754546fb1b7e4 100644 --- a/packages/@aws-cdk/aws-redshift/test/integ.cluster-iamrole.js.snapshot/integ.json +++ b/packages/@aws-cdk/aws-redshift/test/integ.cluster-iamrole.js.snapshot/integ.json @@ -5,7 +5,6 @@ "stacks": [ "redshift-iamrole-integ" ], - "diffAssets": true, "assertionStack": "IamRoleInteg/DefaultTest/DeployAssert", "assertionStackName": "IamRoleIntegDefaultTestDeployAssertBEF20992" } diff --git a/packages/@aws-cdk/aws-redshift/test/integ.cluster-iamrole.js.snapshot/manifest.json b/packages/@aws-cdk/aws-redshift/test/integ.cluster-iamrole.js.snapshot/manifest.json index 75a3f549d4750..39df51f1d9b5c 100644 --- a/packages/@aws-cdk/aws-redshift/test/integ.cluster-iamrole.js.snapshot/manifest.json +++ b/packages/@aws-cdk/aws-redshift/test/integ.cluster-iamrole.js.snapshot/manifest.json @@ -17,7 +17,7 @@ "validateOnSynth": false, "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", - "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/62416b81b75b2f3e2aaef03c6ebb06cbc9c9643581cb32402fc66b56d05e79d1.json", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/0ba06e1076d9b758a143f953e974a5e0407321a5422be5c023862933504f2b62.json", "requiresBootstrapStackVersion": 6, "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", "additionalDependencies": [ diff --git a/packages/@aws-cdk/aws-redshift/test/integ.cluster-iamrole.js.snapshot/redshift-iamrole-integ.assets.json b/packages/@aws-cdk/aws-redshift/test/integ.cluster-iamrole.js.snapshot/redshift-iamrole-integ.assets.json index da257a4deb8f2..29752b2bc0bf5 100644 --- a/packages/@aws-cdk/aws-redshift/test/integ.cluster-iamrole.js.snapshot/redshift-iamrole-integ.assets.json +++ b/packages/@aws-cdk/aws-redshift/test/integ.cluster-iamrole.js.snapshot/redshift-iamrole-integ.assets.json @@ -14,7 +14,7 @@ } } }, - "62416b81b75b2f3e2aaef03c6ebb06cbc9c9643581cb32402fc66b56d05e79d1": { + "0ba06e1076d9b758a143f953e974a5e0407321a5422be5c023862933504f2b62": { "source": { "path": "redshift-iamrole-integ.template.json", "packaging": "file" @@ -22,7 +22,7 @@ "destinations": { "current_account-current_region": { "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", - "objectKey": "62416b81b75b2f3e2aaef03c6ebb06cbc9c9643581cb32402fc66b56d05e79d1.json", + "objectKey": "0ba06e1076d9b758a143f953e974a5e0407321a5422be5c023862933504f2b62.json", "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" } } diff --git a/packages/@aws-cdk/aws-redshift/test/integ.cluster-iamrole.js.snapshot/redshift-iamrole-integ.template.json b/packages/@aws-cdk/aws-redshift/test/integ.cluster-iamrole.js.snapshot/redshift-iamrole-integ.template.json index 8f42fdc57e0c0..988d8eb58512c 100644 --- a/packages/@aws-cdk/aws-redshift/test/integ.cluster-iamrole.js.snapshot/redshift-iamrole-integ.template.json +++ b/packages/@aws-cdk/aws-redshift/test/integ.cluster-iamrole.js.snapshot/redshift-iamrole-integ.template.json @@ -639,7 +639,7 @@ ] ] }, - "InstallLatestAwsSdk": true + "InstallLatestAwsSdk": false }, "DependsOn": [ "ClusteraddroleRoleACustomResourcePolicyB2DE8E3F" From b647db66491edaa4484749f8f38505594c06f288 Mon Sep 17 00:00:00 2001 From: Rizbir Khan Date: Wed, 1 Feb 2023 16:28:29 +0000 Subject: [PATCH 14/25] modification: specifying role arn that is already attached --- packages/@aws-cdk/aws-redshift/lib/cluster.ts | 5 +---- packages/@aws-cdk/aws-redshift/test/cluster.test.ts | 2 +- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/packages/@aws-cdk/aws-redshift/lib/cluster.ts b/packages/@aws-cdk/aws-redshift/lib/cluster.ts index 4faf873091dbe..c1a7004b612e0 100644 --- a/packages/@aws-cdk/aws-redshift/lib/cluster.ts +++ b/packages/@aws-cdk/aws-redshift/lib/cluster.ts @@ -743,10 +743,7 @@ export class Cluster extends ClusterBase { const clusterRoleList = this.cluster.iamRoles ?? []; if (clusterRoleList.includes(role.roleArn)) { - throw new Error('Role is already attached to the cluster'); - } - if (clusterRoleList.length >= 10) { - throw new Error('Maximum number of IAM roles for a cluster is 10'); + throw new Error(`Role '${role.roleArn}' is already attached to the cluster`); } // On UPDATE or CREATE define the new list of roles. On DELETE, detech the role from the cluster diff --git a/packages/@aws-cdk/aws-redshift/test/cluster.test.ts b/packages/@aws-cdk/aws-redshift/test/cluster.test.ts index c8743d5584879..e33748e7ae9c1 100644 --- a/packages/@aws-cdk/aws-redshift/test/cluster.test.ts +++ b/packages/@aws-cdk/aws-redshift/test/cluster.test.ts @@ -705,7 +705,7 @@ describe('IAM role', () => { // WHEN cluster.addIamRole(role), // THEN - ).toThrow(/Role is already attached to the cluster/); + ).toThrow(`Role '${role.roleArn}' is already attached to the cluster`); }); }); From 9de80132fe4fc0e5cf8bbb81fec7a8b342fd32f6 Mon Sep 17 00:00:00 2001 From: Rizbir Khan Date: Wed, 1 Feb 2023 16:30:07 +0000 Subject: [PATCH 15/25] removal: test for max roles. Will let cloudformation take over as roles are region specific --- .../aws-redshift/test/cluster.test.ts | 21 ------------------- 1 file changed, 21 deletions(-) diff --git a/packages/@aws-cdk/aws-redshift/test/cluster.test.ts b/packages/@aws-cdk/aws-redshift/test/cluster.test.ts index e33748e7ae9c1..4b898c568e38a 100644 --- a/packages/@aws-cdk/aws-redshift/test/cluster.test.ts +++ b/packages/@aws-cdk/aws-redshift/test/cluster.test.ts @@ -667,27 +667,6 @@ describe('IAM role', () => { Template.fromStack(stack).hasResource('Custom::ModifyClusterIamRoles', {}); }); - test('throws when adding more than 10 roles to cluster', () => { - // GIVEN - const cluster = new Cluster(stack, 'Redshift', { - masterUser: { - masterUsername: 'admin', - }, - vpc, - roles: new Array(10).fill(0).map((_, i) => new iam.Role(stack, `Role${i+1}`, { - assumedBy: new iam.ServicePrincipal('redshift.amazonaws.com'), - })), - }); - - expect(() => - // WHEN - cluster.addIamRole(new iam.Role(stack, 'Role11', { - assumedBy: new iam.ServicePrincipal('redshift.amazonaws.com'), - })), - // THEN - ).toThrow(/Maximum number of IAM roles for a cluster is 10/); - }); - test('throws when adding role that is already in cluster', () => { // GIVEN const role = new iam.Role(stack, 'Role', { From dd9a42be73e0785d5ad5b66330ff89327aa965a6 Mon Sep 17 00:00:00 2001 From: Rizbir Khan Date: Thu, 2 Feb 2023 15:05:05 +0000 Subject: [PATCH 16/25] modification: lazy evaluation using private readonly constant --- packages/@aws-cdk/aws-redshift/lib/cluster.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/packages/@aws-cdk/aws-redshift/lib/cluster.ts b/packages/@aws-cdk/aws-redshift/lib/cluster.ts index c1a7004b612e0..0b37de9e069c3 100644 --- a/packages/@aws-cdk/aws-redshift/lib/cluster.ts +++ b/packages/@aws-cdk/aws-redshift/lib/cluster.ts @@ -470,6 +470,13 @@ export class Cluster extends ClusterBase { */ protected parameterGroup?: IClusterParameterGroup; + /** + * The ARNs of the roles that will be attached to the cluster. + * + * **NOTE** Please do not access this directly, use the `addIamRole` method instead. + */ + private readonly roleArns: string[]; + constructor(scope: Construct, id: string, props: ClusterProps) { super(scope, id); @@ -478,6 +485,7 @@ export class Cluster extends ClusterBase { subnetType: ec2.SubnetType.PRIVATE_WITH_EGRESS, }; this.parameterGroup = props.parameterGroup; + this.roleArns = props.roles?.map(role => role.roleArn) ?? []; const removalPolicy = props.removalPolicy ?? RemovalPolicy.RETAIN; @@ -557,7 +565,7 @@ export class Cluster extends ClusterBase { nodeType: props.nodeType || NodeType.DC2_LARGE, numberOfNodes: nodeCount, loggingProperties, - iamRoles: props?.roles?.map(role => role.roleArn), + iamRoles: Lazy.list({ produce: () => this.roleArns }, { omitEmpty: true }), dbName: props.defaultDatabaseName || 'default_db', publiclyAccessible: props.publiclyAccessible || false, // Encryption From ac081526b271c2795022124a4b653351fb3bb0fa Mon Sep 17 00:00:00 2001 From: Rizbir Khan Date: Thu, 2 Feb 2023 15:07:52 +0000 Subject: [PATCH 17/25] modification: using roleArns variable --- packages/@aws-cdk/aws-redshift/lib/cluster.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/@aws-cdk/aws-redshift/lib/cluster.ts b/packages/@aws-cdk/aws-redshift/lib/cluster.ts index 0b37de9e069c3..5349e356b5e9a 100644 --- a/packages/@aws-cdk/aws-redshift/lib/cluster.ts +++ b/packages/@aws-cdk/aws-redshift/lib/cluster.ts @@ -696,7 +696,7 @@ export class Cluster extends ClusterBase { */ public addDefaultIamRole(defaultIamRole: iam.IRole): void { // Get list of IAM roles attached to cluster - const clusterRoleList = this.cluster.iamRoles ?? []; + const clusterRoleList = this.roleArns ?? []; // Check to see if default role is included in list of cluster IAM roles var roleAlreadyOnCluster = false; @@ -748,7 +748,7 @@ export class Cluster extends ClusterBase { * @param role the role to add */ public addIamRole(role: iam.IRole): void { - const clusterRoleList = this.cluster.iamRoles ?? []; + const clusterRoleList = this.roleArns; if (clusterRoleList.includes(role.roleArn)) { throw new Error(`Role '${role.roleArn}' is already attached to the cluster`); From 13a38ab54fb5d6d578a8fdf5b4544db4d3f410fb Mon Sep 17 00:00:00 2001 From: Rizbir Khan Date: Thu, 2 Feb 2023 15:08:37 +0000 Subject: [PATCH 18/25] modification: removed custom resource --- packages/@aws-cdk/aws-redshift/lib/cluster.ts | 34 +------------------ 1 file changed, 1 insertion(+), 33 deletions(-) diff --git a/packages/@aws-cdk/aws-redshift/lib/cluster.ts b/packages/@aws-cdk/aws-redshift/lib/cluster.ts index 5349e356b5e9a..02baa74fe5146 100644 --- a/packages/@aws-cdk/aws-redshift/lib/cluster.ts +++ b/packages/@aws-cdk/aws-redshift/lib/cluster.ts @@ -754,38 +754,6 @@ export class Cluster extends ClusterBase { throw new Error(`Role '${role.roleArn}' is already attached to the cluster`); } - // On UPDATE or CREATE define the new list of roles. On DELETE, detech the role from the cluster - const roleCustomResource = new AwsCustomResource(this, `add-role-${role.node.id}`, { - onUpdate: { - service: 'Redshift', - action: 'modifyClusterIamRoles', - parameters: { - ClusterIdentifier: this.cluster.ref, - AddIamRoles: [role.roleArn], - }, - physicalResourceId: PhysicalResourceId.of( - `${role.roleArn}-${this.cluster.ref}`, - ), - }, - onDelete: { - service: 'Redshift', - action: 'modifyClusterIamRoles', - parameters: { - ClusterIdentifier: this.cluster.ref, - RemoveIamRoles: [role.roleArn], - }, - physicalResourceId: PhysicalResourceId.of( - `${role.roleArn}-${this.cluster.ref}`, - ), - }, - policy: AwsCustomResourcePolicy.fromSdkCalls({ - resources: AwsCustomResourcePolicy.ANY_RESOURCE, - }), - resourceType: 'Custom::ModifyClusterIamRoles', - // Latest SDK version for AWS Lambda is 2.x/3.x, so we do not need to install a later version - installLatestAwsSdk: false, - }); - - role.grantPassRole(roleCustomResource.grantPrincipal); + clusterRoleList.push(role.roleArn); } } From 69bd2049cb1c03f13ca97422f1ce5841c653b7a5 Mon Sep 17 00:00:00 2001 From: Rizbir Khan Date: Thu, 2 Feb 2023 15:08:58 +0000 Subject: [PATCH 19/25] addition: importing Lazy module --- packages/@aws-cdk/aws-redshift/lib/cluster.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/@aws-cdk/aws-redshift/lib/cluster.ts b/packages/@aws-cdk/aws-redshift/lib/cluster.ts index 02baa74fe5146..d19eb96bb6986 100644 --- a/packages/@aws-cdk/aws-redshift/lib/cluster.ts +++ b/packages/@aws-cdk/aws-redshift/lib/cluster.ts @@ -3,7 +3,7 @@ import * as iam from '@aws-cdk/aws-iam'; import * as kms from '@aws-cdk/aws-kms'; import * as s3 from '@aws-cdk/aws-s3'; import * as secretsmanager from '@aws-cdk/aws-secretsmanager'; -import { Duration, IResource, RemovalPolicy, Resource, SecretValue, Token } from '@aws-cdk/core'; +import { Duration, IResource, Lazy, RemovalPolicy, Resource, SecretValue, Token } from '@aws-cdk/core'; import { AwsCustomResource, AwsCustomResourcePolicy, PhysicalResourceId } from '@aws-cdk/custom-resources'; import { Construct } from 'constructs'; import { DatabaseSecret } from './database-secret'; From 3b18cb40b1e1bfb5a39ebf3dda3e1d0719bb1898 Mon Sep 17 00:00:00 2001 From: Rizbir Khan Date: Thu, 2 Feb 2023 15:10:10 +0000 Subject: [PATCH 20/25] addition, removal addition: adding test for lazy eval, also across stacks removal: removing custom resource test --- .../aws-redshift/test/cluster.test.ts | 65 +++++++++++++++++-- 1 file changed, 61 insertions(+), 4 deletions(-) diff --git a/packages/@aws-cdk/aws-redshift/test/cluster.test.ts b/packages/@aws-cdk/aws-redshift/test/cluster.test.ts index 4b898c568e38a..b05188bc300d9 100644 --- a/packages/@aws-cdk/aws-redshift/test/cluster.test.ts +++ b/packages/@aws-cdk/aws-redshift/test/cluster.test.ts @@ -649,8 +649,34 @@ describe('default IAM role', () => { }); describe('IAM role', () => { - test('adding a role after cluster declaration creates a custom resource', () => { + test('roles can be directly attached to cluster during declaration', () => { // GIVEN + const role = new iam.Role(stack, 'Role', { + assumedBy: new iam.ServicePrincipal('redshift.amazonaws.com'), + }); + new Cluster(stack, 'Redshift', { + masterUser: { + masterUsername: 'admin', + }, + vpc, + roles: [role], + }); + + // THEN + Template.fromStack(stack).hasResource('AWS::Redshift::Cluster', { + Properties: { + IamRoles: Match.arrayEquals([ + { 'Fn::GetAtt': [Match.stringLikeRegexp('Role*'), 'Arn'] }, + ]), + }, + }); + }); + + test('roles can be attached to cluster after declaration', () => { + // GIVEN + const role = new iam.Role(stack, 'Role', { + assumedBy: new iam.ServicePrincipal('redshift.amazonaws.com'), + }); const cluster = new Cluster(stack, 'Redshift', { masterUser: { masterUsername: 'admin', @@ -659,12 +685,43 @@ describe('IAM role', () => { }); // WHEN - cluster.addIamRole(new iam.Role(stack, 'Role', { + cluster.addIamRole(role); + + // THEN + Template.fromStack(stack).hasResource('AWS::Redshift::Cluster', { + Properties: { + IamRoles: Match.arrayEquals([ + { 'Fn::GetAtt': [Match.stringLikeRegexp('Role*'), 'Arn'] }, + ]), + }, + }); + }); + + test('roles can be attached to cluster in another stack', () => { + // GIVEN + const cluster = new Cluster(stack, 'Redshift', { + masterUser: { + masterUsername: 'admin', + }, + vpc, + }); + + const newTestStack = new cdk.Stack(stack, 'NewTestStack', { env: { account: stack.account, region: stack.region } }); + const role = new iam.Role(newTestStack, 'Role', { assumedBy: new iam.ServicePrincipal('redshift.amazonaws.com'), - })); + }); + + // WHEN + cluster.addIamRole(role); // THEN - Template.fromStack(stack).hasResource('Custom::ModifyClusterIamRoles', {}); + Template.fromStack(stack).hasResource('AWS::Redshift::Cluster', { + Properties: { + IamRoles: Match.arrayEquals([ + { 'Fn::ImportValue': Match.stringLikeRegexp('NewTestStack:ExportsOutputFnGetAttRole*') }, + ]), + }, + }); }); test('throws when adding role that is already in cluster', () => { From fa1b4fa246a48b15bea99b1755f92191ff20de93 Mon Sep 17 00:00:00 2001 From: Rizbir Khan Date: Thu, 2 Feb 2023 15:10:25 +0000 Subject: [PATCH 21/25] change in integ test --- .../manifest.json | 14 +- .../redshift-defaultiamrole-integ.assets.json | 4 +- ...edshift-defaultiamrole-integ.template.json | 4 +- .../index.js | 253 ------------------ .../manifest.json | 45 ++-- .../redshift-iamrole-integ.assets.json | 17 +- .../redshift-iamrole-integ.template.json | 215 +-------------- .../tree.json | 250 +---------------- 8 files changed, 51 insertions(+), 751 deletions(-) delete mode 100644 packages/@aws-cdk/aws-redshift/test/integ.cluster-iamrole.js.snapshot/asset.a268caa53756f51bda8ad5f499be4ed8484a81b314811806fbb66f874837c476/index.js diff --git a/packages/@aws-cdk/aws-redshift/test/integ.cluster-defaultiamrole.js.snapshot/manifest.json b/packages/@aws-cdk/aws-redshift/test/integ.cluster-defaultiamrole.js.snapshot/manifest.json index 572dd5a8f51eb..ab8d9386ab0eb 100644 --- a/packages/@aws-cdk/aws-redshift/test/integ.cluster-defaultiamrole.js.snapshot/manifest.json +++ b/packages/@aws-cdk/aws-redshift/test/integ.cluster-defaultiamrole.js.snapshot/manifest.json @@ -17,7 +17,7 @@ "validateOnSynth": false, "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", - "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/fa991089a30fe26fab8225c5ca4145a65cd9c6f1c7ebebae477f130972053ca5.json", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/3b8fac91d54694ce82b7d11fb7e162d77477381e3254fead4dee0509fcf0d575.json", "requiresBootstrapStackVersion": 6, "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", "additionalDependencies": [ @@ -207,12 +207,6 @@ "data": "Cluster192CD0375" } ], - "/redshift-defaultiamrole-integ/Cluster1/default-role": [ - { - "type": "aws:cdk:warning", - "data": "installLatestAwsSdk was not specified, and defaults to true. You probably do not want this. Set the global context flag '@aws-cdk/customresources:installLatestAwsSdkDefault' to false to switch this behavior off project-wide, or set the property explicitly to true if you know you need to call APIs that are not in Lambda's built-in SDK version." - } - ], "/redshift-defaultiamrole-integ/Cluster1/default-role/Resource/Default": [ { "type": "aws:cdk:logicalId", @@ -273,12 +267,6 @@ "data": "Cluster2720FF351" } ], - "/redshift-defaultiamrole-integ/Cluster2/default-role": [ - { - "type": "aws:cdk:warning", - "data": "installLatestAwsSdk was not specified, and defaults to true. You probably do not want this. Set the global context flag '@aws-cdk/customresources:installLatestAwsSdkDefault' to false to switch this behavior off project-wide, or set the property explicitly to true if you know you need to call APIs that are not in Lambda's built-in SDK version." - } - ], "/redshift-defaultiamrole-integ/Cluster2/default-role/Resource/Default": [ { "type": "aws:cdk:logicalId", diff --git a/packages/@aws-cdk/aws-redshift/test/integ.cluster-defaultiamrole.js.snapshot/redshift-defaultiamrole-integ.assets.json b/packages/@aws-cdk/aws-redshift/test/integ.cluster-defaultiamrole.js.snapshot/redshift-defaultiamrole-integ.assets.json index 0470e75b0fae0..05b0c2b60f3e6 100644 --- a/packages/@aws-cdk/aws-redshift/test/integ.cluster-defaultiamrole.js.snapshot/redshift-defaultiamrole-integ.assets.json +++ b/packages/@aws-cdk/aws-redshift/test/integ.cluster-defaultiamrole.js.snapshot/redshift-defaultiamrole-integ.assets.json @@ -14,7 +14,7 @@ } } }, - "fa991089a30fe26fab8225c5ca4145a65cd9c6f1c7ebebae477f130972053ca5": { + "3b8fac91d54694ce82b7d11fb7e162d77477381e3254fead4dee0509fcf0d575": { "source": { "path": "redshift-defaultiamrole-integ.template.json", "packaging": "file" @@ -22,7 +22,7 @@ "destinations": { "current_account-current_region": { "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", - "objectKey": "fa991089a30fe26fab8225c5ca4145a65cd9c6f1c7ebebae477f130972053ca5.json", + "objectKey": "3b8fac91d54694ce82b7d11fb7e162d77477381e3254fead4dee0509fcf0d575.json", "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" } } diff --git a/packages/@aws-cdk/aws-redshift/test/integ.cluster-defaultiamrole.js.snapshot/redshift-defaultiamrole-integ.template.json b/packages/@aws-cdk/aws-redshift/test/integ.cluster-defaultiamrole.js.snapshot/redshift-defaultiamrole-integ.template.json index 12ab1f22a9a01..0ee81c7df1a4e 100644 --- a/packages/@aws-cdk/aws-redshift/test/integ.cluster-defaultiamrole.js.snapshot/redshift-defaultiamrole-integ.template.json +++ b/packages/@aws-cdk/aws-redshift/test/integ.cluster-defaultiamrole.js.snapshot/redshift-defaultiamrole-integ.template.json @@ -615,7 +615,7 @@ ] ] }, - "InstallLatestAwsSdk": "false" + "InstallLatestAwsSdk": true }, "DependsOn": [ "Cluster1defaultroleCustomResourcePolicy6ECBAB35" @@ -932,7 +932,7 @@ ] ] }, - "InstallLatestAwsSdk": "false" + "InstallLatestAwsSdk": true }, "DependsOn": [ "Cluster2defaultroleCustomResourcePolicy042F9AF5" diff --git a/packages/@aws-cdk/aws-redshift/test/integ.cluster-iamrole.js.snapshot/asset.a268caa53756f51bda8ad5f499be4ed8484a81b314811806fbb66f874837c476/index.js b/packages/@aws-cdk/aws-redshift/test/integ.cluster-iamrole.js.snapshot/asset.a268caa53756f51bda8ad5f499be4ed8484a81b314811806fbb66f874837c476/index.js deleted file mode 100644 index d913ab9defaa1..0000000000000 --- a/packages/@aws-cdk/aws-redshift/test/integ.cluster-iamrole.js.snapshot/asset.a268caa53756f51bda8ad5f499be4ed8484a81b314811806fbb66f874837c476/index.js +++ /dev/null @@ -1,253 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.handler = exports.forceSdkInstallation = exports.flatten = exports.PHYSICAL_RESOURCE_ID_REFERENCE = void 0; -/* eslint-disable no-console */ -const child_process_1 = require("child_process"); -const fs = require("fs"); -const path_1 = require("path"); -/** - * Serialized form of the physical resource id for use in the operation parameters - */ -exports.PHYSICAL_RESOURCE_ID_REFERENCE = 'PHYSICAL:RESOURCEID:'; -/** - * Flattens a nested object - * - * @param object the object to be flattened - * @returns a flat object with path as keys - */ -function flatten(object) { - return Object.assign({}, ...function _flatten(child, path = []) { - return [].concat(...Object.keys(child) - .map(key => { - const childKey = Buffer.isBuffer(child[key]) ? child[key].toString('utf8') : child[key]; - return typeof childKey === 'object' && childKey !== null - ? _flatten(childKey, path.concat([key])) - : ({ [path.concat([key]).join('.')]: childKey }); - })); - }(object)); -} -exports.flatten = flatten; -/** - * Decodes encoded special values (physicalResourceId) - */ -function decodeSpecialValues(object, physicalResourceId) { - return JSON.parse(JSON.stringify(object), (_k, v) => { - switch (v) { - case exports.PHYSICAL_RESOURCE_ID_REFERENCE: - return physicalResourceId; - default: - return v; - } - }); -} -/** - * Filters the keys of an object. - */ -function filterKeys(object, pred) { - return Object.entries(object) - .reduce((acc, [k, v]) => pred(k) - ? { ...acc, [k]: v } - : acc, {}); -} -let latestSdkInstalled = false; -function forceSdkInstallation() { - latestSdkInstalled = false; -} -exports.forceSdkInstallation = forceSdkInstallation; -/** - * Installs latest AWS SDK v2 - */ -function installLatestSdk() { - console.log('Installing latest AWS SDK v2'); - // Both HOME and --prefix are needed here because /tmp is the only writable location - child_process_1.execSync('HOME=/tmp npm install aws-sdk@2 --production --no-package-lock --no-save --prefix /tmp'); - latestSdkInstalled = true; -} -// no currently patched services -const patchedServices = []; -/** - * Patches the AWS SDK by loading service models in the same manner as the actual SDK - */ -function patchSdk(awsSdk) { - const apiLoader = awsSdk.apiLoader; - patchedServices.forEach(({ serviceName, apiVersions }) => { - const lowerServiceName = serviceName.toLowerCase(); - if (!awsSdk.Service.hasService(lowerServiceName)) { - apiLoader.services[lowerServiceName] = {}; - awsSdk[serviceName] = awsSdk.Service.defineService(lowerServiceName, apiVersions); - } - else { - awsSdk.Service.addVersions(awsSdk[serviceName], apiVersions); - } - apiVersions.forEach(apiVersion => { - Object.defineProperty(apiLoader.services[lowerServiceName], apiVersion, { - get: function get() { - const modelFilePrefix = `aws-sdk-patch/${lowerServiceName}-${apiVersion}`; - const model = JSON.parse(fs.readFileSync(path_1.join(__dirname, `${modelFilePrefix}.service.json`), 'utf-8')); - model.paginators = JSON.parse(fs.readFileSync(path_1.join(__dirname, `${modelFilePrefix}.paginators.json`), 'utf-8')).pagination; - return model; - }, - enumerable: true, - configurable: true, - }); - }); - }); - return awsSdk; -} -/* eslint-disable @typescript-eslint/no-require-imports, import/no-extraneous-dependencies */ -async function handler(event, context) { - try { - let AWS; - if (!latestSdkInstalled && event.ResourceProperties.InstallLatestAwsSdk === 'true') { - try { - installLatestSdk(); - AWS = require('/tmp/node_modules/aws-sdk'); - } - catch (e) { - console.log(`Failed to install latest AWS SDK v2: ${e}`); - AWS = require('aws-sdk'); // Fallback to pre-installed version - } - } - else if (latestSdkInstalled) { - AWS = require('/tmp/node_modules/aws-sdk'); - } - else { - AWS = require('aws-sdk'); - } - try { - AWS = patchSdk(AWS); - } - catch (e) { - console.log(`Failed to patch AWS SDK: ${e}. Proceeding with the installed copy.`); - } - console.log(JSON.stringify({ ...event, ResponseURL: '...' })); - console.log('AWS SDK VERSION: ' + AWS.VERSION); - event.ResourceProperties.Create = decodeCall(event.ResourceProperties.Create); - event.ResourceProperties.Update = decodeCall(event.ResourceProperties.Update); - event.ResourceProperties.Delete = decodeCall(event.ResourceProperties.Delete); - // Default physical resource id - let physicalResourceId; - switch (event.RequestType) { - case 'Create': - physicalResourceId = event.ResourceProperties.Create?.physicalResourceId?.id ?? - event.ResourceProperties.Update?.physicalResourceId?.id ?? - event.ResourceProperties.Delete?.physicalResourceId?.id ?? - event.LogicalResourceId; - break; - case 'Update': - case 'Delete': - physicalResourceId = event.ResourceProperties[event.RequestType]?.physicalResourceId?.id ?? event.PhysicalResourceId; - break; - } - let flatData = {}; - let data = {}; - const call = event.ResourceProperties[event.RequestType]; - if (call) { - let credentials; - if (call.assumedRoleArn) { - const timestamp = (new Date()).getTime(); - const params = { - RoleArn: call.assumedRoleArn, - RoleSessionName: `${timestamp}-${physicalResourceId}`.substring(0, 64), - }; - credentials = new AWS.ChainableTemporaryCredentials({ - params: params, - stsConfig: { stsRegionalEndpoints: 'regional' }, - }); - } - if (!Object.prototype.hasOwnProperty.call(AWS, call.service)) { - throw Error(`Service ${call.service} does not exist in AWS SDK version ${AWS.VERSION}.`); - } - const awsService = new AWS[call.service]({ - apiVersion: call.apiVersion, - credentials: credentials, - region: call.region, - }); - try { - const response = await awsService[call.action](call.parameters && decodeSpecialValues(call.parameters, physicalResourceId)).promise(); - flatData = { - apiVersion: awsService.config.apiVersion, - region: awsService.config.region, - ...flatten(response), - }; - let outputPaths; - if (call.outputPath) { - outputPaths = [call.outputPath]; - } - else if (call.outputPaths) { - outputPaths = call.outputPaths; - } - if (outputPaths) { - data = filterKeys(flatData, startsWithOneOf(outputPaths)); - } - else { - data = flatData; - } - } - catch (e) { - if (!call.ignoreErrorCodesMatching || !new RegExp(call.ignoreErrorCodesMatching).test(e.code)) { - throw e; - } - } - if (call.physicalResourceId?.responsePath) { - physicalResourceId = flatData[call.physicalResourceId.responsePath]; - } - } - await respond('SUCCESS', 'OK', physicalResourceId, data); - } - catch (e) { - console.log(e); - await respond('FAILED', e.message || 'Internal Error', context.logStreamName, {}); - } - function respond(responseStatus, reason, physicalResourceId, data) { - const responseBody = JSON.stringify({ - Status: responseStatus, - Reason: reason, - PhysicalResourceId: physicalResourceId, - StackId: event.StackId, - RequestId: event.RequestId, - LogicalResourceId: event.LogicalResourceId, - NoEcho: false, - Data: data, - }); - console.log('Responding', responseBody); - // eslint-disable-next-line @typescript-eslint/no-require-imports - const parsedUrl = require('url').parse(event.ResponseURL); - const requestOptions = { - hostname: parsedUrl.hostname, - path: parsedUrl.path, - method: 'PUT', - headers: { 'content-type': '', 'content-length': responseBody.length }, - }; - return new Promise((resolve, reject) => { - try { - // eslint-disable-next-line @typescript-eslint/no-require-imports - const request = require('https').request(requestOptions, resolve); - request.on('error', reject); - request.write(responseBody); - request.end(); - } - catch (e) { - reject(e); - } - }); - } -} -exports.handler = handler; -function decodeCall(call) { - if (!call) { - return undefined; - } - return JSON.parse(call); -} -function startsWithOneOf(searchStrings) { - return function (string) { - for (const searchString of searchStrings) { - if (string.startsWith(searchString)) { - return true; - } - } - return false; - }; -} -//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJpbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFBQSwrQkFBK0I7QUFDL0IsaURBQXlDO0FBQ3pDLHlCQUF5QjtBQUN6QiwrQkFBNEI7QUFTNUI7O0dBRUc7QUFDVSxRQUFBLDhCQUE4QixHQUFHLHNCQUFzQixDQUFDO0FBRXJFOzs7OztHQUtHO0FBQ0gsU0FBZ0IsT0FBTyxDQUFDLE1BQWM7SUFDcEMsT0FBTyxNQUFNLENBQUMsTUFBTSxDQUNsQixFQUFFLEVBQ0YsR0FBRyxTQUFTLFFBQVEsQ0FBQyxLQUFVLEVBQUUsT0FBaUIsRUFBRTtRQUNsRCxPQUFPLEVBQUUsQ0FBQyxNQUFNLENBQUMsR0FBRyxNQUFNLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQzthQUNuQyxHQUFHLENBQUMsR0FBRyxDQUFDLEVBQUU7WUFDVCxNQUFNLFFBQVEsR0FBRyxNQUFNLENBQUMsUUFBUSxDQUFDLEtBQUssQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxLQUFLLENBQUMsR0FBRyxDQUFDLENBQUMsUUFBUSxDQUFDLE1BQU0sQ0FBQyxDQUFDLENBQUMsQ0FBQyxLQUFLLENBQUMsR0FBRyxDQUFDLENBQUM7WUFDeEYsT0FBTyxPQUFPLFFBQVEsS0FBSyxRQUFRLElBQUksUUFBUSxLQUFLLElBQUk7Z0JBQ3RELENBQUMsQ0FBQyxRQUFRLENBQUMsUUFBUSxFQUFFLElBQUksQ0FBQyxNQUFNLENBQUMsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDO2dCQUN4QyxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxDQUFDLEVBQUUsUUFBUSxFQUFFLENBQUMsQ0FBQztRQUNyRCxDQUFDLENBQUMsQ0FBQyxDQUFDO0lBQ1IsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxDQUNWLENBQUM7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LE9BQU8sR0FBRyxPQUFPLENBQUMsT0FBTyxDQUFDLENBQUMsT0FBTyxDQUFDLGNBQWMsRUFBRSxPQUFPLENBQUMsQ0FBQztnQkFDbEUsT0FBTyxDQUFDLEVBQUUsQ0FBQyxPQUFPLEVBQUUsTUFBTSxDQUFDLENBQUM7Z0JBQzVCLE9BQU8sQ0FBQyxLQUFLLENBQUMsWUFBWSxDQUFDLENBQUM7Z0JBQzVCLE9BQU8sQ0FBQyxHQUFHLEVBQUUsQ0FBQzthQUNmO1lBQUMsT0FBTyxDQUFDLEVBQUU7Z0JBQ1YsTUFBTSxDQUFDLENBQUMsQ0FBQyxDQUFDO2FBQ1g7UUFDSCxDQUFDLENBQUMsQ0FBQztJQUNMLENBQUM7QUFDSCxDQUFDO0FBbEpELDBCQWtKQztBQUVELFNBQVMsVUFBVSxDQUFDLElBQXdCO0lBQzFDLElBQUksQ0FBQyxJQUFJLEVBQUU7UUFBRSxPQUFPLFNBQVMsQ0FBQztLQUFFO0lBQ2hDLE9BQU8sSUFBSSxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsQ0FBQztBQUMxQixDQUFDO0FBRUQsU0FBUyxlQUFlLENBQUMsYUFBdUI7SUFDOUMsT0FBTyxVQUFTLE1BQWM7UUFDNUIsS0FBSyxNQUFNLFlBQVksSUFBSSxhQUFhLEVBQUU7WUFDeEMsSUFBSSxNQUFNLENBQUMsVUFBVSxDQUFDLFlBQVksQ0FBQyxFQUFFO2dCQUNuQyxPQUFPLElBQUksQ0FBQzthQUNiO1NBQ0Y7UUFDRCxPQUFPLEtBQUssQ0FBQztJQUNmLENBQUMsQ0FBQztBQUNKLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvKiBlc2xpbnQtZGlzYWJsZSBuby1jb25zb2xlICovXG5pbXBvcnQgeyBleGVjU3luYyB9IGZyb20gJ2NoaWxkX3Byb2Nlc3MnO1xuaW1wb3J0ICogYXMgZnMgZnJvbSAnZnMnO1xuaW1wb3J0IHsgam9pbiB9IGZyb20gJ3BhdGgnO1xuLy8gaW1wb3J0IHRoZSBBV1NMYW1iZGEgcGFja2FnZSBleHBsaWNpdGx5LFxuLy8gd2hpY2ggaXMgZ2xvYmFsbHkgYXZhaWxhYmxlIGluIHRoZSBMYW1iZGEgcnVudGltZSxcbi8vIGFzIG90aGVyd2lzZSBsaW5raW5nIHRoaXMgcmVwb3NpdG9yeSB3aXRoIGxpbmstYWxsLnNoXG4vLyBmYWlscyBpbiB0aGUgQ0RLIGFwcCBleGVjdXRlZCB3aXRoIHRzLW5vZGVcbi8qIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBpbXBvcnQvbm8tZXh0cmFuZW91cy1kZXBlbmRlbmNpZXMsaW1wb3J0L25vLXVucmVzb2x2ZWQgKi9cbmltcG9ydCAqIGFzIEFXU0xhbWJkYSBmcm9tICdhd3MtbGFtYmRhJztcbmltcG9ydCB7IEF3c1Nka0NhbGwgfSBmcm9tICcuLi9hd3MtY3VzdG9tLXJlc291cmNlJztcblxuLyoqXG4gKiBTZXJpYWxpemVkIGZvcm0gb2YgdGhlIHBoeXNpY2FsIHJlc291cmNlIGlkIGZvciB1c2UgaW4gdGhlIG9wZXJhdGlvbiBwYXJhbWV0ZXJzXG4gKi9cbmV4cG9ydCBjb25zdCBQSFlTSUNBTF9SRVNPVVJDRV9JRF9SRUZFUkVOQ0UgPSAnUEhZU0lDQUw6UkVTT1VSQ0VJRDonO1xuXG4vKipcbiAqIEZsYXR0ZW5zIGEgbmVzdGVkIG9iamVjdFxuICpcbiAqIEBwYXJhbSBvYmplY3QgdGhlIG9iamVjdCB0byBiZSBmbGF0dGVuZWRcbiAqIEByZXR1cm5zIGEgZmxhdCBvYmplY3Qgd2l0aCBwYXRoIGFzIGtleXNcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGZsYXR0ZW4ob2JqZWN0OiBvYmplY3QpOiB7IFtrZXk6IHN0cmluZ106IGFueSB9IHtcbiAgcmV0dXJuIE9iamVjdC5hc3NpZ24oXG4gICAge30sXG4gICAgLi4uZnVuY3Rpb24gX2ZsYXR0ZW4oY2hpbGQ6IGFueSwgcGF0aDogc3RyaW5nW10gPSBbXSk6IGFueSB7XG4gICAgICByZXR1cm4gW10uY29uY2F0KC4uLk9iamVjdC5rZXlzKGNoaWxkKVxuICAgICAgICAubWFwKGtleSA9PiB7XG4gICAgICAgICAgY29uc3QgY2hpbGRLZXkgPSBCdWZmZXIuaXNCdWZmZXIoY2hpbGRba2V5XSkgPyBjaGlsZFtrZXldLnRvU3RyaW5nKCd1dGY4JykgOiBjaGlsZFtrZXldO1xuICAgICAgICAgIHJldHVybiB0eXBlb2YgY2hpbGRLZXkgPT09ICdvYmplY3QnICYmIGNoaWxkS2V5ICE9PSBudWxsXG4gICAgICAgICAgICA/IF9mbGF0dGVuKGNoaWxkS2V5LCBwYXRoLmNvbmNhdChba2V5XSkpXG4gICAgICAgICAgICA6ICh7IFtwYXRoLmNvbmNhdChba2V5XSkuam9pbignLicpXTogY2hpbGRLZXkgfSk7XG4gICAgICAgIH0pKTtcbiAgICB9KG9iamVjdCksXG4gICk7XG59XG5cbi8qKlxuICogRGVjb2RlcyBlbmNvZGVkIHNwZWNpYWwgdmFsdWVzIChwaHlzaWNhbFJlc291cmNlSWQpXG4gKi9cbmZ1bmN0aW9uIGRlY29kZVNwZWNpYWxWYWx1ZXMob2JqZWN0OiBvYmplY3QsIHBoeXNpY2FsUmVzb3VyY2VJZDogc3RyaW5nKSB7XG4gIHJldHVybiBKU09OLnBhcnNlKEpTT04uc3RyaW5naWZ5KG9iamVjdCksIChfaywgdikgPT4ge1xuICAgIHN3aXRjaCAodikge1xuICAgICAgY2FzZSBQSFlTSUNBTF9SRVNPVVJDRV9JRF9SRUZFUkVOQ0U6XG4gICAgICAgIHJldHVybiBwaHlzaWNhbFJlc291cmNlSWQ7XG4gICAgICBkZWZhdWx0OlxuICAgICAgICByZXR1cm4gdjtcbiAgICB9XG4gIH0pO1xufVxuXG4vKipcbiAqIEZpbHRlcnMgdGhlIGtleXMgb2YgYW4gb2JqZWN0LlxuICovXG5mdW5jdGlvbiBmaWx0ZXJLZXlzKG9iamVjdDogb2JqZWN0LCBwcmVkOiAoa2V5OiBzdHJpbmcpID0+IGJvb2xlYW4pIHtcbiAgcmV0dXJuIE9iamVjdC5lbnRyaWVzKG9iamVjdClcbiAgICAucmVkdWNlKFxuICAgICAgKGFjYywgW2ssIHZdKSA9PiBwcmVkKGspXG4gICAgICAgID8geyAuLi5hY2MsIFtrXTogdiB9XG4gICAgICAgIDogYWNjLFxuICAgICAge30sXG4gICAgKTtcbn1cblxubGV0IGxhdGVzdFNka0luc3RhbGxlZCA9IGZhbHNlO1xuXG5leHBvcnQgZnVuY3Rpb24gZm9yY2VTZGtJbnN0YWxsYXRpb24oKSB7XG4gIGxhdGVzdFNka0luc3RhbGxlZCA9IGZhbHNlO1xufVxuXG4vKipcbiAqIEluc3RhbGxzIGxhdGVzdCBBV1MgU0RLIHYyXG4gKi9cbmZ1bmN0aW9uIGluc3RhbGxMYXRlc3RTZGsoKTogdm9pZCB7XG4gIGNvbnNvbGUubG9nKCdJbnN0YWxsaW5nIGxhdGVzdCBBV1MgU0RLIHYyJyk7XG4gIC8vIEJvdGggSE9NRSBhbmQgLS1wcmVmaXggYXJlIG5lZWRlZCBoZXJlIGJlY2F1c2UgL3RtcCBpcyB0aGUgb25seSB3cml0YWJsZSBsb2NhdGlvblxuICBleGVjU3luYygnSE9NRT0vdG1wIG5wbSBpbnN0YWxsIGF3cy1zZGtAMiAtLXByb2R1Y3Rpb24gLS1uby1wYWNrYWdlLWxvY2sgLS1uby1zYXZlIC0tcHJlZml4IC90bXAnKTtcbiAgbGF0ZXN0U2RrSW5zdGFsbGVkID0gdHJ1ZTtcbn1cblxuLy8gbm8gY3VycmVudGx5IHBhdGNoZWQgc2VydmljZXNcbmNvbnN0IHBhdGNoZWRTZXJ2aWNlczogeyBzZXJ2aWNlTmFtZTogc3RyaW5nOyBhcGlWZXJzaW9uczogc3RyaW5nW10gfVtdID0gW107XG4vKipcbiAqIFBhdGNoZXMgdGhlIEFXUyBTREsgYnkgbG9hZGluZyBzZXJ2aWNlIG1vZGVscyBpbiB0aGUgc2FtZSBtYW5uZXIgYXMgdGhlIGFjdHVhbCBTREtcbiAqL1xuZnVuY3Rpb24gcGF0Y2hTZGsoYXdzU2RrOiBhbnkpOiBhbnkge1xuICBjb25zdCBhcGlMb2FkZXIgPSBhd3NTZGsuYXBpTG9hZGVyO1xuICBwYXRjaGVkU2VydmljZXMuZm9yRWFjaCgoeyBzZXJ2aWNlTmFtZSwgYXBpVmVyc2lvbnMgfSkgPT4ge1xuICAgIGNvbnN0IGxvd2VyU2VydmljZU5hbWUgPSBzZXJ2aWNlTmFtZS50b0xvd2VyQ2FzZSgpO1xuICAgIGlmICghYXdzU2RrLlNlcnZpY2UuaGFzU2VydmljZShsb3dlclNlcnZpY2VOYW1lKSkge1xuICAgICAgYXBpTG9hZGVyLnNlcnZpY2VzW2xvd2VyU2VydmljZU5hbWVdID0ge307XG4gICAgICBhd3NTZGtbc2VydmljZU5hbWVdID0gYXdzU2RrLlNlcnZpY2UuZGVmaW5lU2VydmljZShsb3dlclNlcnZpY2VOYW1lLCBhcGlWZXJzaW9ucyk7XG4gICAgfSBlbHNlIHtcbiAgICAgIGF3c1Nkay5TZXJ2aWNlLmFkZFZlcnNpb25zKGF3c1Nka1tzZXJ2aWNlTmFtZV0sIGFwaVZlcnNpb25zKTtcbiAgICB9XG4gICAgYXBpVmVyc2lvbnMuZm9yRWFjaChhcGlWZXJzaW9uID0+IHtcbiAgICAgIE9iamVjdC5kZWZpbmVQcm9wZXJ0eShhcGlMb2FkZXIuc2VydmljZXNbbG93ZXJTZXJ2aWNlTmFtZV0sIGFwaVZlcnNpb24sIHtcbiAgICAgICAgZ2V0OiBmdW5jdGlvbiBnZXQoKSB7XG4gICAgICAgICAgY29uc3QgbW9kZWxGaWxlUHJlZml4ID0gYGF3cy1zZGstcGF0Y2gvJHtsb3dlclNlcnZpY2VOYW1lfS0ke2FwaVZlcnNpb259YDtcbiAgICAgICAgICBjb25zdCBtb2RlbCA9IEpTT04ucGFyc2UoZnMucmVhZEZpbGVTeW5jKGpvaW4oX19kaXJuYW1lLCBgJHttb2RlbEZpbGVQcmVmaXh9LnNlcnZpY2UuanNvbmApLCAndXRmLTgnKSk7XG4gICAgICAgICAgbW9kZWwucGFnaW5hdG9ycyA9IEpTT04ucGFyc2UoZnMucmVhZEZpbGVTeW5jKGpvaW4oX19kaXJuYW1lLCBgJHttb2RlbEZpbGVQcmVmaXh9LnBhZ2luYXRvcnMuanNvbmApLCAndXRmLTgnKSkucGFnaW5hdGlvbjtcbiAgICAgICAgICByZXR1cm4gbW9kZWw7XG4gICAgICAgIH0sXG4gICAgICAgIGVudW1lcmFibGU6IHRydWUsXG4gICAgICAgIGNvbmZpZ3VyYWJsZTogdHJ1ZSxcbiAgICAgIH0pO1xuICAgIH0pO1xuICB9KTtcbiAgcmV0dXJuIGF3c1Nkaztcbn1cblxuLyogZXNsaW50LWRpc2FibGUgQHR5cGVzY3JpcHQtZXNsaW50L25vLXJlcXVpcmUtaW1wb3J0cywgaW1wb3J0L25vLWV4dHJhbmVvdXMtZGVwZW5kZW5jaWVzICovXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gaGFuZGxlcihldmVudDogQVdTTGFtYmRhLkNsb3VkRm9ybWF0aW9uQ3VzdG9tUmVzb3VyY2VFdmVudCwgY29udGV4dDogQVdTTGFtYmRhLkNvbnRleHQpIHtcbiAgdHJ5IHtcbiAgICBsZXQgQVdTOiBhbnk7XG4gICAgaWYgKCFsYXRlc3RTZGtJbnN0YWxsZWQgJiYgZXZlbnQuUmVzb3VyY2VQcm9wZXJ0aWVzLkluc3RhbGxMYXRlc3RBd3NTZGsgPT09ICd0cnVlJykge1xuICAgICAgdHJ5IHtcbiAgICAgICAgaW5zdGFsbExhdGVzdFNkaygpO1xuICAgICAgICBBV1MgPSByZXF1aXJlKCcvdG1wL25vZGVfbW9kdWxlcy9hd3Mtc2RrJyk7XG4gICAgICB9IGNhdGNoIChlKSB7XG4gICAgICAgIGNvbnNvbGUubG9nKGBGYWlsZWQgdG8gaW5zdGFsbCBsYXRlc3QgQVdTIFNESyB2MjogJHtlfWApO1xuICAgICAgICBBV1MgPSByZXF1aXJlKCdhd3Mtc2RrJyk7IC8vIEZhbGxiYWNrIHRvIHByZS1pbnN0YWxsZWQgdmVyc2lvblxuICAgICAgfVxuICAgIH0gZWxzZSBpZiAobGF0ZXN0U2RrSW5zdGFsbGVkKSB7XG4gICAgICBBV1MgPSByZXF1aXJlKCcvdG1wL25vZGVfbW9kdWxlcy9hd3Mtc2RrJyk7XG4gICAgfSBlbHNlIHtcbiAgICAgIEFXUyA9IHJlcXVpcmUoJ2F3cy1zZGsnKTtcbiAgICB9XG4gICAgdHJ5IHtcbiAgICAgIEFXUyA9IHBhdGNoU2RrKEFXUyk7XG4gICAgfSBjYXRjaCAoZSkge1xuICAgICAgY29uc29sZS5sb2coYEZhaWxlZCB0byBwYXRjaCBBV1MgU0RLOiAke2V9LiBQcm9jZWVkaW5nIHdpdGggdGhlIGluc3RhbGxlZCBjb3B5LmApO1xuICAgIH1cblxuICAgIGNvbnNvbGUubG9nKEpTT04uc3RyaW5naWZ5KHsgLi4uZXZlbnQsIFJlc3BvbnNlVVJMOiAnLi4uJyB9KSk7XG4gICAgY29uc29sZS5sb2coJ0FXUyBTREsgVkVSU0lPTjogJyArIEFXUy5WRVJTSU9OKTtcblxuICAgIGV2ZW50LlJlc291cmNlUHJvcGVydGllcy5DcmVhdGUgPSBkZWNvZGVDYWxsKGV2ZW50LlJlc291cmNlUHJvcGVydGllcy5DcmVhdGUpO1xuICAgIGV2ZW50LlJlc291cmNlUHJvcGVydGllcy5VcGRhdGUgPSBkZWNvZGVDYWxsKGV2ZW50LlJlc291cmNlUHJvcGVydGllcy5VcGRhdGUpO1xuICAgIGV2ZW50LlJlc291cmNlUHJvcGVydGllcy5EZWxldGUgPSBkZWNvZGVDYWxsKGV2ZW50LlJlc291cmNlUHJvcGVydGllcy5EZWxldGUpO1xuICAgIC8vIERlZmF1bHQgcGh5c2ljYWwgcmVzb3VyY2UgaWRcbiAgICBsZXQgcGh5c2ljYWxSZXNvdXJjZUlkOiBzdHJpbmc7XG4gICAgc3dpdGNoIChldmVudC5SZXF1ZXN0VHlwZSkge1xuICAgICAgY2FzZSAnQ3JlYXRlJzpcbiAgICAgICAgcGh5c2ljYWxSZXNvdXJjZUlkID0gZXZlbnQuUmVzb3VyY2VQcm9wZXJ0aWVzLkNyZWF0ZT8ucGh5c2ljYWxSZXNvdXJjZUlkPy5pZCA/P1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICBldmVudC5SZXNvdXJjZVByb3BlcnRpZXMuVXBkYXRlPy5waHlzaWNhbFJlc291cmNlSWQ/LmlkID8/XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgIGV2ZW50LlJlc291cmNlUHJvcGVydGllcy5EZWxldGU/LnBoeXNpY2FsUmVzb3VyY2VJZD8uaWQgPz9cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZXZlbnQuTG9naWNhbFJlc291cmNlSWQ7XG4gICAgICAgIGJyZWFrO1xuICAgICAgY2FzZSAnVXBkYXRlJzpcbiAgICAgIGNhc2UgJ0RlbGV0ZSc6XG4gICAgICAgIHBoeXNpY2FsUmVzb3VyY2VJZCA9IGV2ZW50LlJlc291cmNlUHJvcGVydGllc1tldmVudC5SZXF1ZXN0VHlwZV0/LnBoeXNpY2FsUmVzb3VyY2VJZD8uaWQgPz8gZXZlbnQuUGh5c2ljYWxSZXNvdXJjZUlkO1xuICAgICAgICBicmVhaztcbiAgICB9XG5cbiAgICBsZXQgZmxhdERhdGE6IHsgW2tleTogc3RyaW5nXTogc3RyaW5nIH0gPSB7fTtcbiAgICBsZXQgZGF0YTogeyBba2V5OiBzdHJpbmddOiBzdHJpbmcgfSA9IHt9O1xuICAgIGNvbnN0IGNhbGw6IEF3c1Nka0NhbGwgfCB1bmRlZmluZWQgPSBldmVudC5SZXNvdXJjZVByb3BlcnRpZXNbZXZlbnQuUmVxdWVzdFR5cGVdO1xuXG4gICAgaWYgKGNhbGwpIHtcblxuICAgICAgbGV0IGNyZWRlbnRpYWxzO1xuICAgICAgaWYgKGNhbGwuYXNzdW1lZFJvbGVBcm4pIHtcbiAgICAgICAgY29uc3QgdGltZXN0YW1wID0gKG5ldyBEYXRlKCkpLmdldFRpbWUoKTtcblxuICAgICAgICBjb25zdCBwYXJhbXMgPSB7XG4gICAgICAgICAgUm9sZUFybjogY2FsbC5hc3N1bWVkUm9sZUFybixcbiAgICAgICAgICBSb2xlU2Vzc2lvbk5hbWU6IGAke3RpbWVzdGFtcH0tJHtwaHlzaWNhbFJlc291cmNlSWR9YC5zdWJzdHJpbmcoMCwgNjQpLFxuICAgICAgICB9O1xuXG4gICAgICAgIGNyZWRlbnRpYWxzID0gbmV3IEFXUy5DaGFpbmFibGVUZW1wb3JhcnlDcmVkZW50aWFscyh7XG4gICAgICAgICAgcGFyYW1zOiBwYXJhbXMsXG4gICAgICAgICAgc3RzQ29uZmlnOiB7IHN0c1JlZ2lvbmFsRW5kcG9pbnRzOiAncmVnaW9uYWwnIH0sXG4gICAgICAgIH0pO1xuICAgICAgfVxuXG4gICAgICBpZiAoIU9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHkuY2FsbChBV1MsIGNhbGwuc2VydmljZSkpIHtcbiAgICAgICAgdGhyb3cgRXJyb3IoYFNlcnZpY2UgJHtjYWxsLnNlcnZpY2V9IGRvZXMgbm90IGV4aXN0IGluIEFXUyBTREsgdmVyc2lvbiAke0FXUy5WRVJTSU9OfS5gKTtcbiAgICAgIH1cbiAgICAgIGNvbnN0IGF3c1NlcnZpY2UgPSBuZXcgKEFXUyBhcyBhbnkpW2NhbGwuc2VydmljZV0oe1xuICAgICAgICBhcGlWZXJzaW9uOiBjYWxsLmFwaVZlcnNpb24sXG4gICAgICAgIGNyZWRlbnRpYWxzOiBjcmVkZW50aWFscyxcbiAgICAgICAgcmVnaW9uOiBjYWxsLnJlZ2lvbixcbiAgICAgIH0pO1xuXG4gICAgICB0cnkge1xuICAgICAgICBjb25zdCByZXNwb25zZSA9IGF3YWl0IGF3c1NlcnZpY2VbY2FsbC5hY3Rpb25dKFxuICAgICAgICAgIGNhbGwucGFyYW1ldGVycyAmJiBkZWNvZGVTcGVjaWFsVmFsdWVzKGNhbGwucGFyYW1ldGVycywgcGh5c2ljYWxSZXNvdXJjZUlkKSkucHJvbWlzZSgpO1xuICAgICAgICBmbGF0RGF0YSA9IHtcbiAgICAgICAgICBhcGlWZXJzaW9uOiBhd3NTZXJ2aWNlLmNvbmZpZy5hcGlWZXJzaW9uLCAvLyBGb3IgdGVzdCBwdXJwb3NlczogY2hlY2sgaWYgYXBpVmVyc2lvbiB3YXMgY29ycmVjdGx5IHBhc3NlZC5cbiAgICAgICAgICByZWdpb246IGF3c1NlcnZpY2UuY29uZmlnLnJlZ2lvbiwgLy8gRm9yIHRlc3QgcHVycG9zZXM6IGNoZWNrIGlmIHJlZ2lvbiB3YXMgY29ycmVjdGx5IHBhc3NlZC5cbiAgICAgICAgICAuLi5mbGF0dGVuKHJlc3BvbnNlKSxcbiAgICAgICAgfTtcblxuICAgICAgICBsZXQgb3V0cHV0UGF0aHM6IHN0cmluZ1tdIHwgdW5kZWZpbmVkO1xuICAgICAgICBpZiAoY2FsbC5vdXRwdXRQYXRoKSB7XG4gICAgICAgICAgb3V0cHV0UGF0aHMgPSBbY2FsbC5vdXRwdXRQYXRoXTtcbiAgICAgICAgfSBlbHNlIGlmIChjYWxsLm91dHB1dFBhdGhzKSB7XG4gICAgICAgICAgb3V0cHV0UGF0aHMgPSBjYWxsLm91dHB1dFBhdGhzO1xuICAgICAgICB9XG5cbiAgICAgICAgaWYgKG91dHB1dFBhdGhzKSB7XG4gICAgICAgICAgZGF0YSA9IGZpbHRlcktleXMoZmxhdERhdGEsIHN0YXJ0c1dpdGhPbmVPZihvdXRwdXRQYXRocykpO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIGRhdGEgPSBmbGF0RGF0YTtcbiAgICAgICAgfVxuICAgICAgfSBjYXRjaCAoZSkge1xuICAgICAgICBpZiAoIWNhbGwuaWdub3JlRXJyb3JDb2Rlc01hdGNoaW5nIHx8ICFuZXcgUmVnRXhwKGNhbGwuaWdub3JlRXJyb3JDb2Rlc01hdGNoaW5nKS50ZXN0KGUuY29kZSkpIHtcbiAgICAgICAgICB0aHJvdyBlO1xuICAgICAgICB9XG4gICAgICB9XG5cbiAgICAgIGlmIChjYWxsLnBoeXNpY2FsUmVzb3VyY2VJZD8ucmVzcG9uc2VQYXRoKSB7XG4gICAgICAgIHBoeXNpY2FsUmVzb3VyY2VJZCA9IGZsYXREYXRhW2NhbGwucGh5c2ljYWxSZXNvdXJjZUlkLnJlc3BvbnNlUGF0aF07XG4gICAgICB9XG4gICAgfVxuXG4gICAgYXdhaXQgcmVzcG9uZCgnU1VDQ0VTUycsICdPSycsIHBoeXNpY2FsUmVzb3VyY2VJZCwgZGF0YSk7XG4gIH0gY2F0Y2ggKGUpIHtcbiAgICBjb25zb2xlLmxvZyhlKTtcbiAgICBhd2FpdCByZXNwb25kKCdGQUlMRUQnLCBlLm1lc3NhZ2UgfHwgJ0ludGVybmFsIEVycm9yJywgY29udGV4dC5sb2dTdHJlYW1OYW1lLCB7fSk7XG4gIH1cblxuICBmdW5jdGlvbiByZXNwb25kKHJlc3BvbnNlU3RhdHVzOiBzdHJpbmcsIHJlYXNvbjogc3RyaW5nLCBwaHlzaWNhbFJlc291cmNlSWQ6IHN0cmluZywgZGF0YTogYW55KSB7XG4gICAgY29uc3QgcmVzcG9uc2VCb2R5ID0gSlNPTi5zdHJpbmdpZnkoe1xuICAgICAgU3RhdHVzOiByZXNwb25zZVN0YXR1cyxcbiAgICAgIFJlYXNvbjogcmVhc29uLFxuICAgICAgUGh5c2ljYWxSZXNvdXJjZUlkOiBwaHlzaWNhbFJlc291cmNlSWQsXG4gICAgICBTdGFja0lkOiBldmVudC5TdGFja0lkLFxuICAgICAgUmVxdWVzdElkOiBldmVudC5SZXF1ZXN0SWQsXG4gICAgICBMb2dpY2FsUmVzb3VyY2VJZDogZXZlbnQuTG9naWNhbFJlc291cmNlSWQsXG4gICAgICBOb0VjaG86IGZhbHNlLFxuICAgICAgRGF0YTogZGF0YSxcbiAgICB9KTtcblxuICAgIGNvbnNvbGUubG9nKCdSZXNwb25kaW5nJywgcmVzcG9uc2VCb2R5KTtcblxuICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBAdHlwZXNjcmlwdC1lc2xpbnQvbm8tcmVxdWlyZS1pbXBvcnRzXG4gICAgY29uc3QgcGFyc2VkVXJsID0gcmVxdWlyZSgndXJsJykucGFyc2UoZXZlbnQuUmVzcG9uc2VVUkwpO1xuICAgIGNvbnN0IHJlcXVlc3RPcHRpb25zID0ge1xuICAgICAgaG9zdG5hbWU6IHBhcnNlZFVybC5ob3N0bmFtZSxcbiAgICAgIHBhdGg6IHBhcnNlZFVybC5wYXRoLFxuICAgICAgbWV0aG9kOiAnUFVUJyxcbiAgICAgIGhlYWRlcnM6IHsgJ2NvbnRlbnQtdHlwZSc6ICcnLCAnY29udGVudC1sZW5ndGgnOiByZXNwb25zZUJvZHkubGVuZ3RoIH0sXG4gICAgfTtcblxuICAgIHJldHVybiBuZXcgUHJvbWlzZSgocmVzb2x2ZSwgcmVqZWN0KSA9PiB7XG4gICAgICB0cnkge1xuICAgICAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgQHR5cGVzY3JpcHQtZXNsaW50L25vLXJlcXVpcmUtaW1wb3J0c1xuICAgICAgICBjb25zdCByZXF1ZXN0ID0gcmVxdWlyZSgnaHR0cHMnKS5yZXF1ZXN0KHJlcXVlc3RPcHRpb25zLCByZXNvbHZlKTtcbiAgICAgICAgcmVxdWVzdC5vbignZXJyb3InLCByZWplY3QpO1xuICAgICAgICByZXF1ZXN0LndyaXRlKHJlc3BvbnNlQm9keSk7XG4gICAgICAgIHJlcXVlc3QuZW5kKCk7XG4gICAgICB9IGNhdGNoIChlKSB7XG4gICAgICAgIHJlamVjdChlKTtcbiAgICAgIH1cbiAgICB9KTtcbiAgfVxufVxuXG5mdW5jdGlvbiBkZWNvZGVDYWxsKGNhbGw6IHN0cmluZyB8IHVuZGVmaW5lZCkge1xuICBpZiAoIWNhbGwpIHsgcmV0dXJuIHVuZGVmaW5lZDsgfVxuICByZXR1cm4gSlNPTi5wYXJzZShjYWxsKTtcbn1cblxuZnVuY3Rpb24gc3RhcnRzV2l0aE9uZU9mKHNlYXJjaFN0cmluZ3M6IHN0cmluZ1tdKTogKHN0cmluZzogc3RyaW5nKSA9PiBib29sZWFuIHtcbiAgcmV0dXJuIGZ1bmN0aW9uKHN0cmluZzogc3RyaW5nKTogYm9vbGVhbiB7XG4gICAgZm9yIChjb25zdCBzZWFyY2hTdHJpbmcgb2Ygc2VhcmNoU3RyaW5ncykge1xuICAgICAgaWYgKHN0cmluZy5zdGFydHNXaXRoKHNlYXJjaFN0cmluZykpIHtcbiAgICAgICAgcmV0dXJuIHRydWU7XG4gICAgICB9XG4gICAgfVxuICAgIHJldHVybiBmYWxzZTtcbiAgfTtcbn1cbiJdfQ== \ No newline at end of file diff --git a/packages/@aws-cdk/aws-redshift/test/integ.cluster-iamrole.js.snapshot/manifest.json b/packages/@aws-cdk/aws-redshift/test/integ.cluster-iamrole.js.snapshot/manifest.json index 39df51f1d9b5c..91316c878a02a 100644 --- a/packages/@aws-cdk/aws-redshift/test/integ.cluster-iamrole.js.snapshot/manifest.json +++ b/packages/@aws-cdk/aws-redshift/test/integ.cluster-iamrole.js.snapshot/manifest.json @@ -17,7 +17,7 @@ "validateOnSynth": false, "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", - "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/0ba06e1076d9b758a143f953e974a5e0407321a5422be5c023862933504f2b62.json", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/85e1ecadf2c1e47c7bf3425bf186e3d1e0b6fe7a4c4d5b8956acb337d4fd9c22.json", "requiresBootstrapStackVersion": 6, "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", "additionalDependencies": [ @@ -213,46 +213,61 @@ "data": "ClusterEB0386A7" } ], - "/redshift-iamrole-integ/Cluster/add-role-RoleA/Resource/Default": [ + "/redshift-iamrole-integ/BootstrapVersion": [ { "type": "aws:cdk:logicalId", - "data": "ClusteraddroleRoleAA2FB3227" + "data": "BootstrapVersion" } ], - "/redshift-iamrole-integ/Cluster/add-role-RoleA/CustomResourcePolicy/Resource": [ + "/redshift-iamrole-integ/CheckBootstrapVersion": [ { "type": "aws:cdk:logicalId", - "data": "ClusteraddroleRoleACustomResourcePolicyB2DE8E3F" + "data": "CheckBootstrapVersion" } ], - "/redshift-iamrole-integ/AWS679f53fac002430cb0da5b7982bd2287/ServiceRole/Resource": [ + "ClusteraddroleRoleAA2FB3227": [ { "type": "aws:cdk:logicalId", - "data": "AWS679f53fac002430cb0da5b7982bd2287ServiceRoleC1EA0FF2" + "data": "ClusteraddroleRoleAA2FB3227", + "trace": [ + "!!DESTRUCTIVE_CHANGES: WILL_DESTROY" + ] } ], - "/redshift-iamrole-integ/AWS679f53fac002430cb0da5b7982bd2287/ServiceRole/DefaultPolicy/Resource": [ + "ClusteraddroleRoleACustomResourcePolicyB2DE8E3F": [ { "type": "aws:cdk:logicalId", - "data": "AWS679f53fac002430cb0da5b7982bd2287ServiceRoleDefaultPolicyD28E1A5E" + "data": "ClusteraddroleRoleACustomResourcePolicyB2DE8E3F", + "trace": [ + "!!DESTRUCTIVE_CHANGES: WILL_DESTROY" + ] } ], - "/redshift-iamrole-integ/AWS679f53fac002430cb0da5b7982bd2287/Resource": [ + "AWS679f53fac002430cb0da5b7982bd2287ServiceRoleC1EA0FF2": [ { "type": "aws:cdk:logicalId", - "data": "AWS679f53fac002430cb0da5b7982bd22872D164C4C" + "data": "AWS679f53fac002430cb0da5b7982bd2287ServiceRoleC1EA0FF2", + "trace": [ + "!!DESTRUCTIVE_CHANGES: WILL_DESTROY" + ] } ], - "/redshift-iamrole-integ/BootstrapVersion": [ + "AWS679f53fac002430cb0da5b7982bd2287ServiceRoleDefaultPolicyD28E1A5E": [ { "type": "aws:cdk:logicalId", - "data": "BootstrapVersion" + "data": "AWS679f53fac002430cb0da5b7982bd2287ServiceRoleDefaultPolicyD28E1A5E", + "trace": [ + "!!DESTRUCTIVE_CHANGES: WILL_DESTROY" + ] } ], - "/redshift-iamrole-integ/CheckBootstrapVersion": [ + "AWS679f53fac002430cb0da5b7982bd22872D164C4C": [ { "type": "aws:cdk:logicalId", - "data": "CheckBootstrapVersion" + "data": "AWS679f53fac002430cb0da5b7982bd22872D164C4C", + "trace": [ + "!!DESTRUCTIVE_CHANGES: WILL_DESTROY" + ] } ] }, diff --git a/packages/@aws-cdk/aws-redshift/test/integ.cluster-iamrole.js.snapshot/redshift-iamrole-integ.assets.json b/packages/@aws-cdk/aws-redshift/test/integ.cluster-iamrole.js.snapshot/redshift-iamrole-integ.assets.json index 29752b2bc0bf5..fb7c5c6cd1f9f 100644 --- a/packages/@aws-cdk/aws-redshift/test/integ.cluster-iamrole.js.snapshot/redshift-iamrole-integ.assets.json +++ b/packages/@aws-cdk/aws-redshift/test/integ.cluster-iamrole.js.snapshot/redshift-iamrole-integ.assets.json @@ -1,20 +1,7 @@ { "version": "29.0.0", "files": { - "a268caa53756f51bda8ad5f499be4ed8484a81b314811806fbb66f874837c476": { - "source": { - "path": "asset.a268caa53756f51bda8ad5f499be4ed8484a81b314811806fbb66f874837c476", - "packaging": "zip" - }, - "destinations": { - "current_account-current_region": { - "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", - "objectKey": "a268caa53756f51bda8ad5f499be4ed8484a81b314811806fbb66f874837c476.zip", - "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" - } - } - }, - "0ba06e1076d9b758a143f953e974a5e0407321a5422be5c023862933504f2b62": { + "85e1ecadf2c1e47c7bf3425bf186e3d1e0b6fe7a4c4d5b8956acb337d4fd9c22": { "source": { "path": "redshift-iamrole-integ.template.json", "packaging": "file" @@ -22,7 +9,7 @@ "destinations": { "current_account-current_region": { "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", - "objectKey": "0ba06e1076d9b758a143f953e974a5e0407321a5422be5c023862933504f2b62.json", + "objectKey": "85e1ecadf2c1e47c7bf3425bf186e3d1e0b6fe7a4c4d5b8956acb337d4fd9c22.json", "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" } } diff --git a/packages/@aws-cdk/aws-redshift/test/integ.cluster-iamrole.js.snapshot/redshift-iamrole-integ.template.json b/packages/@aws-cdk/aws-redshift/test/integ.cluster-iamrole.js.snapshot/redshift-iamrole-integ.template.json index 988d8eb58512c..e1d24bdabd1dc 100644 --- a/packages/@aws-cdk/aws-redshift/test/integ.cluster-iamrole.js.snapshot/redshift-iamrole-integ.template.json +++ b/packages/@aws-cdk/aws-redshift/test/integ.cluster-iamrole.js.snapshot/redshift-iamrole-integ.template.json @@ -524,6 +524,12 @@ "RoleB318292C8", "Arn" ] + }, + { + "Fn::GetAtt": [ + "RoleA3119C8FE", + "Arn" + ] } ], "NumberOfNodes": 2, @@ -539,215 +545,6 @@ }, "UpdateReplacePolicy": "Delete", "DeletionPolicy": "Delete" - }, - "ClusteraddroleRoleAA2FB3227": { - "Type": "Custom::ModifyClusterIamRoles", - "Properties": { - "ServiceToken": { - "Fn::GetAtt": [ - "AWS679f53fac002430cb0da5b7982bd22872D164C4C", - "Arn" - ] - }, - "Create": { - "Fn::Join": [ - "", - [ - "{\"service\":\"Redshift\",\"action\":\"modifyClusterIamRoles\",\"parameters\":{\"ClusterIdentifier\":\"", - { - "Ref": "ClusterEB0386A7" - }, - "\",\"AddIamRoles\":[\"", - { - "Fn::GetAtt": [ - "RoleA3119C8FE", - "Arn" - ] - }, - "\"]},\"physicalResourceId\":{\"id\":\"", - { - "Fn::GetAtt": [ - "RoleA3119C8FE", - "Arn" - ] - }, - "-", - { - "Ref": "ClusterEB0386A7" - }, - "\"}}" - ] - ] - }, - "Update": { - "Fn::Join": [ - "", - [ - "{\"service\":\"Redshift\",\"action\":\"modifyClusterIamRoles\",\"parameters\":{\"ClusterIdentifier\":\"", - { - "Ref": "ClusterEB0386A7" - }, - "\",\"AddIamRoles\":[\"", - { - "Fn::GetAtt": [ - "RoleA3119C8FE", - "Arn" - ] - }, - "\"]},\"physicalResourceId\":{\"id\":\"", - { - "Fn::GetAtt": [ - "RoleA3119C8FE", - "Arn" - ] - }, - "-", - { - "Ref": "ClusterEB0386A7" - }, - "\"}}" - ] - ] - }, - "Delete": { - "Fn::Join": [ - "", - [ - "{\"service\":\"Redshift\",\"action\":\"modifyClusterIamRoles\",\"parameters\":{\"ClusterIdentifier\":\"", - { - "Ref": "ClusterEB0386A7" - }, - "\",\"RemoveIamRoles\":[\"", - { - "Fn::GetAtt": [ - "RoleA3119C8FE", - "Arn" - ] - }, - "\"]},\"physicalResourceId\":{\"id\":\"", - { - "Fn::GetAtt": [ - "RoleA3119C8FE", - "Arn" - ] - }, - "-", - { - "Ref": "ClusterEB0386A7" - }, - "\"}}" - ] - ] - }, - "InstallLatestAwsSdk": false - }, - "DependsOn": [ - "ClusteraddroleRoleACustomResourcePolicyB2DE8E3F" - ], - "UpdateReplacePolicy": "Delete", - "DeletionPolicy": "Delete" - }, - "ClusteraddroleRoleACustomResourcePolicyB2DE8E3F": { - "Type": "AWS::IAM::Policy", - "Properties": { - "PolicyDocument": { - "Statement": [ - { - "Action": "redshift:ModifyClusterIamRoles", - "Effect": "Allow", - "Resource": "*" - } - ], - "Version": "2012-10-17" - }, - "PolicyName": "ClusteraddroleRoleACustomResourcePolicyB2DE8E3F", - "Roles": [ - { - "Ref": "AWS679f53fac002430cb0da5b7982bd2287ServiceRoleC1EA0FF2" - } - ] - } - }, - "AWS679f53fac002430cb0da5b7982bd2287ServiceRoleC1EA0FF2": { - "Type": "AWS::IAM::Role", - "Properties": { - "AssumeRolePolicyDocument": { - "Statement": [ - { - "Action": "sts:AssumeRole", - "Effect": "Allow", - "Principal": { - "Service": "lambda.amazonaws.com" - } - } - ], - "Version": "2012-10-17" - }, - "ManagedPolicyArns": [ - { - "Fn::Join": [ - "", - [ - "arn:", - { - "Ref": "AWS::Partition" - }, - ":iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" - ] - ] - } - ] - } - }, - "AWS679f53fac002430cb0da5b7982bd2287ServiceRoleDefaultPolicyD28E1A5E": { - "Type": "AWS::IAM::Policy", - "Properties": { - "PolicyDocument": { - "Statement": [ - { - "Action": "iam:PassRole", - "Effect": "Allow", - "Resource": { - "Fn::GetAtt": [ - "RoleA3119C8FE", - "Arn" - ] - } - } - ], - "Version": "2012-10-17" - }, - "PolicyName": "AWS679f53fac002430cb0da5b7982bd2287ServiceRoleDefaultPolicyD28E1A5E", - "Roles": [ - { - "Ref": "AWS679f53fac002430cb0da5b7982bd2287ServiceRoleC1EA0FF2" - } - ] - } - }, - "AWS679f53fac002430cb0da5b7982bd22872D164C4C": { - "Type": "AWS::Lambda::Function", - "Properties": { - "Code": { - "S3Bucket": { - "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}" - }, - "S3Key": "a268caa53756f51bda8ad5f499be4ed8484a81b314811806fbb66f874837c476.zip" - }, - "Role": { - "Fn::GetAtt": [ - "AWS679f53fac002430cb0da5b7982bd2287ServiceRoleC1EA0FF2", - "Arn" - ] - }, - "Handler": "index.handler", - "Runtime": "nodejs14.x", - "Timeout": 120 - }, - "DependsOn": [ - "AWS679f53fac002430cb0da5b7982bd2287ServiceRoleDefaultPolicyD28E1A5E", - "AWS679f53fac002430cb0da5b7982bd2287ServiceRoleC1EA0FF2" - ] } }, "Parameters": { diff --git a/packages/@aws-cdk/aws-redshift/test/integ.cluster-iamrole.js.snapshot/tree.json b/packages/@aws-cdk/aws-redshift/test/integ.cluster-iamrole.js.snapshot/tree.json index 6be2189090cba..4044a5ca3f0c4 100644 --- a/packages/@aws-cdk/aws-redshift/test/integ.cluster-iamrole.js.snapshot/tree.json +++ b/packages/@aws-cdk/aws-redshift/test/integ.cluster-iamrole.js.snapshot/tree.json @@ -911,6 +911,12 @@ "RoleB318292C8", "Arn" ] + }, + { + "Fn::GetAtt": [ + "RoleA3119C8FE", + "Arn" + ] } ], "numberOfNodes": 2, @@ -929,81 +935,6 @@ "fqn": "@aws-cdk/aws-redshift.CfnCluster", "version": "0.0.0" } - }, - "add-role-RoleA": { - "id": "add-role-RoleA", - "path": "redshift-iamrole-integ/Cluster/add-role-RoleA", - "children": { - "Provider": { - "id": "Provider", - "path": "redshift-iamrole-integ/Cluster/add-role-RoleA/Provider", - "constructInfo": { - "fqn": "@aws-cdk/aws-lambda.SingletonFunction", - "version": "0.0.0" - } - }, - "Resource": { - "id": "Resource", - "path": "redshift-iamrole-integ/Cluster/add-role-RoleA/Resource", - "children": { - "Default": { - "id": "Default", - "path": "redshift-iamrole-integ/Cluster/add-role-RoleA/Resource/Default", - "constructInfo": { - "fqn": "@aws-cdk/core.CfnResource", - "version": "0.0.0" - } - } - }, - "constructInfo": { - "fqn": "@aws-cdk/core.CustomResource", - "version": "0.0.0" - } - }, - "CustomResourcePolicy": { - "id": "CustomResourcePolicy", - "path": "redshift-iamrole-integ/Cluster/add-role-RoleA/CustomResourcePolicy", - "children": { - "Resource": { - "id": "Resource", - "path": "redshift-iamrole-integ/Cluster/add-role-RoleA/CustomResourcePolicy/Resource", - "attributes": { - "aws:cdk:cloudformation:type": "AWS::IAM::Policy", - "aws:cdk:cloudformation:props": { - "policyDocument": { - "Statement": [ - { - "Action": "redshift:ModifyClusterIamRoles", - "Effect": "Allow", - "Resource": "*" - } - ], - "Version": "2012-10-17" - }, - "policyName": "ClusteraddroleRoleACustomResourcePolicyB2DE8E3F", - "roles": [ - { - "Ref": "AWS679f53fac002430cb0da5b7982bd2287ServiceRoleC1EA0FF2" - } - ] - } - }, - "constructInfo": { - "fqn": "@aws-cdk/aws-iam.CfnPolicy", - "version": "0.0.0" - } - } - }, - "constructInfo": { - "fqn": "@aws-cdk/aws-iam.Policy", - "version": "0.0.0" - } - } - }, - "constructInfo": { - "fqn": "@aws-cdk/custom-resources.AwsCustomResource", - "version": "0.0.0" - } } }, "constructInfo": { @@ -1011,171 +942,6 @@ "version": "0.0.0" } }, - "AWS679f53fac002430cb0da5b7982bd2287": { - "id": "AWS679f53fac002430cb0da5b7982bd2287", - "path": "redshift-iamrole-integ/AWS679f53fac002430cb0da5b7982bd2287", - "children": { - "ServiceRole": { - "id": "ServiceRole", - "path": "redshift-iamrole-integ/AWS679f53fac002430cb0da5b7982bd2287/ServiceRole", - "children": { - "ImportServiceRole": { - "id": "ImportServiceRole", - "path": "redshift-iamrole-integ/AWS679f53fac002430cb0da5b7982bd2287/ServiceRole/ImportServiceRole", - "constructInfo": { - "fqn": "@aws-cdk/core.Resource", - "version": "0.0.0" - } - }, - "Resource": { - "id": "Resource", - "path": "redshift-iamrole-integ/AWS679f53fac002430cb0da5b7982bd2287/ServiceRole/Resource", - "attributes": { - "aws:cdk:cloudformation:type": "AWS::IAM::Role", - "aws:cdk:cloudformation:props": { - "assumeRolePolicyDocument": { - "Statement": [ - { - "Action": "sts:AssumeRole", - "Effect": "Allow", - "Principal": { - "Service": "lambda.amazonaws.com" - } - } - ], - "Version": "2012-10-17" - }, - "managedPolicyArns": [ - { - "Fn::Join": [ - "", - [ - "arn:", - { - "Ref": "AWS::Partition" - }, - ":iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" - ] - ] - } - ] - } - }, - "constructInfo": { - "fqn": "@aws-cdk/aws-iam.CfnRole", - "version": "0.0.0" - } - }, - "DefaultPolicy": { - "id": "DefaultPolicy", - "path": "redshift-iamrole-integ/AWS679f53fac002430cb0da5b7982bd2287/ServiceRole/DefaultPolicy", - "children": { - "Resource": { - "id": "Resource", - "path": "redshift-iamrole-integ/AWS679f53fac002430cb0da5b7982bd2287/ServiceRole/DefaultPolicy/Resource", - "attributes": { - "aws:cdk:cloudformation:type": "AWS::IAM::Policy", - "aws:cdk:cloudformation:props": { - "policyDocument": { - "Statement": [ - { - "Action": "iam:PassRole", - "Effect": "Allow", - "Resource": { - "Fn::GetAtt": [ - "RoleA3119C8FE", - "Arn" - ] - } - } - ], - "Version": "2012-10-17" - }, - "policyName": "AWS679f53fac002430cb0da5b7982bd2287ServiceRoleDefaultPolicyD28E1A5E", - "roles": [ - { - "Ref": "AWS679f53fac002430cb0da5b7982bd2287ServiceRoleC1EA0FF2" - } - ] - } - }, - "constructInfo": { - "fqn": "@aws-cdk/aws-iam.CfnPolicy", - "version": "0.0.0" - } - } - }, - "constructInfo": { - "fqn": "@aws-cdk/aws-iam.Policy", - "version": "0.0.0" - } - } - }, - "constructInfo": { - "fqn": "@aws-cdk/aws-iam.Role", - "version": "0.0.0" - } - }, - "Code": { - "id": "Code", - "path": "redshift-iamrole-integ/AWS679f53fac002430cb0da5b7982bd2287/Code", - "children": { - "Stage": { - "id": "Stage", - "path": "redshift-iamrole-integ/AWS679f53fac002430cb0da5b7982bd2287/Code/Stage", - "constructInfo": { - "fqn": "@aws-cdk/core.AssetStaging", - "version": "0.0.0" - } - }, - "AssetBucket": { - "id": "AssetBucket", - "path": "redshift-iamrole-integ/AWS679f53fac002430cb0da5b7982bd2287/Code/AssetBucket", - "constructInfo": { - "fqn": "@aws-cdk/aws-s3.BucketBase", - "version": "0.0.0" - } - } - }, - "constructInfo": { - "fqn": "@aws-cdk/aws-s3-assets.Asset", - "version": "0.0.0" - } - }, - "Resource": { - "id": "Resource", - "path": "redshift-iamrole-integ/AWS679f53fac002430cb0da5b7982bd2287/Resource", - "attributes": { - "aws:cdk:cloudformation:type": "AWS::Lambda::Function", - "aws:cdk:cloudformation:props": { - "code": { - "s3Bucket": { - "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}" - }, - "s3Key": "a268caa53756f51bda8ad5f499be4ed8484a81b314811806fbb66f874837c476.zip" - }, - "role": { - "Fn::GetAtt": [ - "AWS679f53fac002430cb0da5b7982bd2287ServiceRoleC1EA0FF2", - "Arn" - ] - }, - "handler": "index.handler", - "runtime": "nodejs14.x", - "timeout": 120 - } - }, - "constructInfo": { - "fqn": "@aws-cdk/aws-lambda.CfnFunction", - "version": "0.0.0" - } - } - }, - "constructInfo": { - "fqn": "@aws-cdk/aws-lambda.Function", - "version": "0.0.0" - } - }, "BootstrapVersion": { "id": "BootstrapVersion", "path": "redshift-iamrole-integ/BootstrapVersion", @@ -1211,7 +977,7 @@ "path": "IamRoleInteg/DefaultTest/Default", "constructInfo": { "fqn": "constructs.Construct", - "version": "10.1.189" + "version": "10.1.209" } }, "DeployAssert": { @@ -1257,7 +1023,7 @@ "path": "Tree", "constructInfo": { "fqn": "constructs.Construct", - "version": "10.1.189" + "version": "10.1.209" } } }, From 83052d3c2c58f9a25c8de01cf2f5a5db39da161a Mon Sep 17 00:00:00 2001 From: Rizbir Khan Date: Thu, 2 Feb 2023 15:43:47 +0000 Subject: [PATCH 22/25] modification: reverting change in installLatestAwsSdk --- packages/@aws-cdk/aws-redshift/lib/cluster.ts | 1 + .../integ.cluster-defaultiamrole.js.snapshot/manifest.json | 2 +- .../redshift-defaultiamrole-integ.assets.json | 4 ++-- .../redshift-defaultiamrole-integ.template.json | 4 ++-- 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/packages/@aws-cdk/aws-redshift/lib/cluster.ts b/packages/@aws-cdk/aws-redshift/lib/cluster.ts index d19eb96bb6986..7723c0c6a07d2 100644 --- a/packages/@aws-cdk/aws-redshift/lib/cluster.ts +++ b/packages/@aws-cdk/aws-redshift/lib/cluster.ts @@ -737,6 +737,7 @@ export class Cluster extends ClusterBase { policy: AwsCustomResourcePolicy.fromSdkCalls({ resources: AwsCustomResourcePolicy.ANY_RESOURCE, }), + installLatestAwsSdk: false, }); defaultIamRole.grantPassRole(defaultRoleCustomResource.grantPrincipal); diff --git a/packages/@aws-cdk/aws-redshift/test/integ.cluster-defaultiamrole.js.snapshot/manifest.json b/packages/@aws-cdk/aws-redshift/test/integ.cluster-defaultiamrole.js.snapshot/manifest.json index ab8d9386ab0eb..8591dc9c53c77 100644 --- a/packages/@aws-cdk/aws-redshift/test/integ.cluster-defaultiamrole.js.snapshot/manifest.json +++ b/packages/@aws-cdk/aws-redshift/test/integ.cluster-defaultiamrole.js.snapshot/manifest.json @@ -17,7 +17,7 @@ "validateOnSynth": false, "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", - "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/3b8fac91d54694ce82b7d11fb7e162d77477381e3254fead4dee0509fcf0d575.json", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/2a0f381c4c3f8c34bfd6f8afdccd71f2d437e9a354e3383b625429e833e7a53f.json", "requiresBootstrapStackVersion": 6, "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", "additionalDependencies": [ diff --git a/packages/@aws-cdk/aws-redshift/test/integ.cluster-defaultiamrole.js.snapshot/redshift-defaultiamrole-integ.assets.json b/packages/@aws-cdk/aws-redshift/test/integ.cluster-defaultiamrole.js.snapshot/redshift-defaultiamrole-integ.assets.json index 05b0c2b60f3e6..37fe0c765ea75 100644 --- a/packages/@aws-cdk/aws-redshift/test/integ.cluster-defaultiamrole.js.snapshot/redshift-defaultiamrole-integ.assets.json +++ b/packages/@aws-cdk/aws-redshift/test/integ.cluster-defaultiamrole.js.snapshot/redshift-defaultiamrole-integ.assets.json @@ -14,7 +14,7 @@ } } }, - "3b8fac91d54694ce82b7d11fb7e162d77477381e3254fead4dee0509fcf0d575": { + "2a0f381c4c3f8c34bfd6f8afdccd71f2d437e9a354e3383b625429e833e7a53f": { "source": { "path": "redshift-defaultiamrole-integ.template.json", "packaging": "file" @@ -22,7 +22,7 @@ "destinations": { "current_account-current_region": { "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", - "objectKey": "3b8fac91d54694ce82b7d11fb7e162d77477381e3254fead4dee0509fcf0d575.json", + "objectKey": "2a0f381c4c3f8c34bfd6f8afdccd71f2d437e9a354e3383b625429e833e7a53f.json", "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" } } diff --git a/packages/@aws-cdk/aws-redshift/test/integ.cluster-defaultiamrole.js.snapshot/redshift-defaultiamrole-integ.template.json b/packages/@aws-cdk/aws-redshift/test/integ.cluster-defaultiamrole.js.snapshot/redshift-defaultiamrole-integ.template.json index 0ee81c7df1a4e..6f0e3413e69bc 100644 --- a/packages/@aws-cdk/aws-redshift/test/integ.cluster-defaultiamrole.js.snapshot/redshift-defaultiamrole-integ.template.json +++ b/packages/@aws-cdk/aws-redshift/test/integ.cluster-defaultiamrole.js.snapshot/redshift-defaultiamrole-integ.template.json @@ -615,7 +615,7 @@ ] ] }, - "InstallLatestAwsSdk": true + "InstallLatestAwsSdk": false }, "DependsOn": [ "Cluster1defaultroleCustomResourcePolicy6ECBAB35" @@ -932,7 +932,7 @@ ] ] }, - "InstallLatestAwsSdk": true + "InstallLatestAwsSdk": false }, "DependsOn": [ "Cluster2defaultroleCustomResourcePolicy042F9AF5" From 4967340831876c34f7dec9eb3d5e855aaca7e291 Mon Sep 17 00:00:00 2001 From: Rizbir Khan Date: Thu, 9 Feb 2023 10:53:37 +0000 Subject: [PATCH 23/25] modification: cleanup, max roles are nott 10, but subject to a quota --- packages/@aws-cdk/aws-redshift/lib/cluster.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/@aws-cdk/aws-redshift/lib/cluster.ts b/packages/@aws-cdk/aws-redshift/lib/cluster.ts index 7723c0c6a07d2..ca4b48fdf05ff 100644 --- a/packages/@aws-cdk/aws-redshift/lib/cluster.ts +++ b/packages/@aws-cdk/aws-redshift/lib/cluster.ts @@ -299,7 +299,7 @@ export interface ClusterProps { /** * A list of AWS Identity and Access Management (IAM) role that can be used by the cluster to access other AWS services. - * Specify a maximum of 10 roles. + * The maximum number of roles to attach to a cluster is subject to a quota. * * @default - No role is attached to the cluster. */ From ddc5ff80dc4e923de1394e1a70f6a41b8ae828e4 Mon Sep 17 00:00:00 2001 From: Rizbir Khan Date: Thu, 9 Feb 2023 10:55:54 +0000 Subject: [PATCH 24/25] modification: eslint import order --- .../lib/private/database-query-provider/privileges.ts | 2 +- .../lib/private/database-query-provider/table.ts | 2 +- .../lib/private/database-query-provider/user.ts | 2 +- .../lib/private/database-query-provider/util.ts | 2 +- .../@aws-cdk/aws-redshift/lib/private/database-query.ts | 2 +- packages/@aws-cdk/aws-redshift/lib/private/privileges.ts | 6 +++--- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/@aws-cdk/aws-redshift/lib/private/database-query-provider/privileges.ts b/packages/@aws-cdk/aws-redshift/lib/private/database-query-provider/privileges.ts index d95a9f9d97395..f1533bb9cb326 100644 --- a/packages/@aws-cdk/aws-redshift/lib/private/database-query-provider/privileges.ts +++ b/packages/@aws-cdk/aws-redshift/lib/private/database-query-provider/privileges.ts @@ -1,9 +1,9 @@ /* eslint-disable-next-line import/no-unresolved */ import * as AWSLambda from 'aws-lambda'; -import { TablePrivilege, UserTablePrivilegesHandlerProps } from '../handler-props'; import { executeStatement } from './redshift-data'; import { ClusterProps } from './types'; import { makePhysicalId } from './util'; +import { TablePrivilege, UserTablePrivilegesHandlerProps } from '../handler-props'; export async function handler(props: UserTablePrivilegesHandlerProps & ClusterProps, event: AWSLambda.CloudFormationCustomResourceEvent) { const username = props.username; diff --git a/packages/@aws-cdk/aws-redshift/lib/private/database-query-provider/table.ts b/packages/@aws-cdk/aws-redshift/lib/private/database-query-provider/table.ts index 18a9dec634293..ce59679f23589 100644 --- a/packages/@aws-cdk/aws-redshift/lib/private/database-query-provider/table.ts +++ b/packages/@aws-cdk/aws-redshift/lib/private/database-query-provider/table.ts @@ -1,9 +1,9 @@ /* eslint-disable-next-line import/no-unresolved */ import * as AWSLambda from 'aws-lambda'; -import { Column } from '../../table'; import { executeStatement } from './redshift-data'; import { ClusterProps, TableAndClusterProps, TableSortStyle } from './types'; import { areColumnsEqual, getDistKeyColumn, getSortKeyColumns } from './util'; +import { Column } from '../../table'; export async function handler(props: TableAndClusterProps, event: AWSLambda.CloudFormationCustomResourceEvent) { const tableNamePrefix = props.tableName.prefix; diff --git a/packages/@aws-cdk/aws-redshift/lib/private/database-query-provider/user.ts b/packages/@aws-cdk/aws-redshift/lib/private/database-query-provider/user.ts index ae19440230ae9..50e79800980d7 100644 --- a/packages/@aws-cdk/aws-redshift/lib/private/database-query-provider/user.ts +++ b/packages/@aws-cdk/aws-redshift/lib/private/database-query-provider/user.ts @@ -2,10 +2,10 @@ import * as AWSLambda from 'aws-lambda'; /* eslint-disable-next-line import/no-extraneous-dependencies */ import * as SecretsManager from 'aws-sdk/clients/secretsmanager'; -import { UserHandlerProps } from '../handler-props'; import { executeStatement } from './redshift-data'; import { ClusterProps } from './types'; import { makePhysicalId } from './util'; +import { UserHandlerProps } from '../handler-props'; const secretsManager = new SecretsManager(); diff --git a/packages/@aws-cdk/aws-redshift/lib/private/database-query-provider/util.ts b/packages/@aws-cdk/aws-redshift/lib/private/database-query-provider/util.ts index c6fe3709bd136..872ce9df500bf 100644 --- a/packages/@aws-cdk/aws-redshift/lib/private/database-query-provider/util.ts +++ b/packages/@aws-cdk/aws-redshift/lib/private/database-query-provider/util.ts @@ -1,5 +1,5 @@ -import { Column } from '../../table'; import { ClusterProps } from './types'; +import { Column } from '../../table'; export function makePhysicalId(resourceName: string, clusterProps: ClusterProps, requestId: string): string { return `${clusterProps.clusterName}:${clusterProps.databaseName}:${resourceName}:${requestId}`; diff --git a/packages/@aws-cdk/aws-redshift/lib/private/database-query.ts b/packages/@aws-cdk/aws-redshift/lib/private/database-query.ts index d87c72ffdcf8f..8e2f793724d8a 100644 --- a/packages/@aws-cdk/aws-redshift/lib/private/database-query.ts +++ b/packages/@aws-cdk/aws-redshift/lib/private/database-query.ts @@ -5,9 +5,9 @@ import * as secretsmanager from '@aws-cdk/aws-secretsmanager'; import * as cdk from '@aws-cdk/core'; import * as customresources from '@aws-cdk/custom-resources'; import { Construct } from 'constructs'; +import { DatabaseQueryHandlerProps } from './handler-props'; import { Cluster } from '../cluster'; import { DatabaseOptions } from '../database-options'; -import { DatabaseQueryHandlerProps } from './handler-props'; export interface DatabaseQueryProps extends DatabaseOptions { readonly handler: string; diff --git a/packages/@aws-cdk/aws-redshift/lib/private/privileges.ts b/packages/@aws-cdk/aws-redshift/lib/private/privileges.ts index 01b2d5f34ae0d..b9975d7fdec7c 100644 --- a/packages/@aws-cdk/aws-redshift/lib/private/privileges.ts +++ b/packages/@aws-cdk/aws-redshift/lib/private/privileges.ts @@ -1,11 +1,11 @@ import * as cdk from '@aws-cdk/core'; import { Construct } from 'constructs'; -import { DatabaseOptions } from '../database-options'; -import { ITable, TableAction } from '../table'; -import { IUser } from '../user'; import { DatabaseQuery } from './database-query'; import { HandlerName } from './database-query-provider/handler-name'; import { TablePrivilege as SerializedTablePrivilege, UserTablePrivilegesHandlerProps } from './handler-props'; +import { DatabaseOptions } from '../database-options'; +import { ITable, TableAction } from '../table'; +import { IUser } from '../user'; /** * The Redshift table and action that make up a privilege that can be granted to a Redshift user. From 1b8d1044960f78e5a520a2f69c9fbc2da36147e2 Mon Sep 17 00:00:00 2001 From: Rizbir Khan Date: Thu, 9 Feb 2023 10:59:46 +0000 Subject: [PATCH 25/25] modification: changing to use private iam.IRole list instead of string list --- packages/@aws-cdk/aws-redshift/lib/cluster.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/@aws-cdk/aws-redshift/lib/cluster.ts b/packages/@aws-cdk/aws-redshift/lib/cluster.ts index ca4b48fdf05ff..50e3e5e1265a7 100644 --- a/packages/@aws-cdk/aws-redshift/lib/cluster.ts +++ b/packages/@aws-cdk/aws-redshift/lib/cluster.ts @@ -475,7 +475,7 @@ export class Cluster extends ClusterBase { * * **NOTE** Please do not access this directly, use the `addIamRole` method instead. */ - private readonly roleArns: string[]; + private readonly roles: iam.IRole[]; constructor(scope: Construct, id: string, props: ClusterProps) { super(scope, id); @@ -485,7 +485,7 @@ export class Cluster extends ClusterBase { subnetType: ec2.SubnetType.PRIVATE_WITH_EGRESS, }; this.parameterGroup = props.parameterGroup; - this.roleArns = props.roles?.map(role => role.roleArn) ?? []; + this.roles = props?.roles ? [...props.roles] : []; const removalPolicy = props.removalPolicy ?? RemovalPolicy.RETAIN; @@ -565,7 +565,7 @@ export class Cluster extends ClusterBase { nodeType: props.nodeType || NodeType.DC2_LARGE, numberOfNodes: nodeCount, loggingProperties, - iamRoles: Lazy.list({ produce: () => this.roleArns }, { omitEmpty: true }), + iamRoles: Lazy.list({ produce: () => this.roles.map(role => role.roleArn) }, { omitEmpty: true }), dbName: props.defaultDatabaseName || 'default_db', publiclyAccessible: props.publiclyAccessible || false, // Encryption @@ -696,12 +696,12 @@ export class Cluster extends ClusterBase { */ public addDefaultIamRole(defaultIamRole: iam.IRole): void { // Get list of IAM roles attached to cluster - const clusterRoleList = this.roleArns ?? []; + const clusterRoleList = this.roles ?? []; // Check to see if default role is included in list of cluster IAM roles var roleAlreadyOnCluster = false; for (var i = 0; i < clusterRoleList.length; i++) { - if (clusterRoleList[i] == defaultIamRole.roleArn) { + if (clusterRoleList[i] === defaultIamRole) { roleAlreadyOnCluster = true; break; } @@ -749,12 +749,12 @@ export class Cluster extends ClusterBase { * @param role the role to add */ public addIamRole(role: iam.IRole): void { - const clusterRoleList = this.roleArns; + const clusterRoleList = this.roles; - if (clusterRoleList.includes(role.roleArn)) { + if (clusterRoleList.includes(role)) { throw new Error(`Role '${role.roleArn}' is already attached to the cluster`); } - clusterRoleList.push(role.roleArn); + clusterRoleList.push(role); } }