diff --git a/packages/@aws-cdk/aws-location-alpha/lib/api-key.ts b/packages/@aws-cdk/aws-location-alpha/lib/api-key.ts new file mode 100644 index 0000000000000..57a17e7ec4e1c --- /dev/null +++ b/packages/@aws-cdk/aws-location-alpha/lib/api-key.ts @@ -0,0 +1,359 @@ +import { ArnFormat, Aws, IResource, Lazy, Resource, Stack, Token } from 'aws-cdk-lib/core'; +import { Construct } from 'constructs'; +import { CfnAPIKey } from 'aws-cdk-lib/aws-location'; +import { generateUniqueId } from './util'; + +/** + * An API Key + */ +export interface IAPIKey extends IResource { + /** + * The name of the api key + * + * @attribute + */ + readonly keyName: string; + + /** + * The Amazon Resource Name (ARN) of the api key resource + * + * @attribute Arn,IndexArn + */ + readonly keyArn: string; +} + +/** + * Properties for an API Key + */ +export interface APIKeyProps { + /** + * A name for the api key + * + * Must be between 1 and 100 characters and contain only alphanumeric characters, + * hyphens, periods and underscores. + * + * Must be a unique API key name. + * + * @default - A name is automatically generated + */ + readonly keyName?: string; + + /** + * A description for the api key + * + * @default - no description + */ + readonly description?: string; + + /** + * The optional timestamp for when the API key resource will expire + * + * `expireTime` must be set when `noExpiry` is false or undefined. + * When `expireTime` is not set, `noExpiry` must be `true`. + * + * @default undefined - The API Key never expires + */ + readonly expireTime?: Date; + + /** + * `forceDelete` bypasses an API key's expiry conditions and deletes the key. + * Set the parameter true to delete the key or to false to not preemptively delete the API key. + * + * @default undefined - not force delete + */ + readonly forceDelete?: boolean; + + /** + * The boolean flag to be included for updating ExpireTime or Restrictions details. + * Must be set to true to update an API key resource that has been used in the past 7 days. + * False if force update is not preferred. + * + * @default undefined - not force update + */ + readonly forceUpdate?: boolean; + + /** + * Whether the API key should expire. + * + * Set to `true` when `expireTime` is not set. + * When you set `expireTime`, `noExpiry` must be `false` or `undefined`. + * + * @default undefined - The API Key expires at `expireTime` + */ + readonly noExpiry?: boolean; + + /** + * A list of allowed actions for Maps that an API key resource grants permissions to perform. + * + * @default - no actions for Maps are permitted + */ + readonly allowMapsActions?: AllowMapsAction[]; + + /** + * A list of allowed actions for Places that an API key resource grants permissions to perform. + * + * @default - no actions for Places are permitted + */ + readonly allowPlacesActions?: AllowPlacesAction[]; + + /** + * A list of allowed actions for Routes that an API key resource grants permissions to perform. + * + * @default - no actions for Routes are permitted + */ + readonly allowRoutesActions?: AllowRoutesAction[]; + + /** + * An optional list of allowed HTTP referers for which requests must originate from. + * Requests using this API key from other domains will not be allowed. + * + * @see https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-properties-location-apikey-apikeyrestrictions.html#cfn-location-apikey-apikeyrestrictions-allowreferers + * @default - no Referer + */ + readonly allowReferers?: string[]; +} + +/** + * Actions for Maps that an API key resource grants permissions to perform. + * + * @see https://docs.aws.amazon.com/service-authorization/latest/reference/list_amazonlocationservicemaps.html + */ +export enum AllowMapsAction { + /** + * Allows getting static map images. + */ + GET_STATIC_MAP = 'geo-maps:GetStaticMap', + + /** + * Allows getting map tiles for rendering. + */ + GET_TILE = 'geo-maps:GetTile', + + /** + * Allows any maps actions. + */ + ANY = 'geo-maps:*', +} + +/** + * Actions for Places that an API key resource grants permissions to perform. + * + * @see https://docs.aws.amazon.com/service-authorization/latest/reference/list_amazonlocationserviceplaces.html + */ +export enum AllowPlacesAction { + /** + * Allows auto-completion of search text. + */ + AUTOCOMPLETE = 'geo-places:Autocomplete', + + /** + * Allows finding geo coordinates of a known place. + */ + GEOCODE = 'geo-places:Geocode', + + /** + * Allows getting details of a place. + */ + GET_PLACE = 'geo-places:GetPlace', + + /** + * Allows getting nearest address to geo coordinates. + */ + REVERSE_GEOCODE = 'geo-places:ReverseGeocode', + + /** + * Allows category based places search around geo coordinates. + */ + SEARCH_NEARBY = 'geo-places:SearchNearby', + + /** + * Allows place or address search based on free-form text. + */ + SEARCH_TEXT = 'geo-places:SearchText', + + /** + * Allows suggestions based on an incomplete or misspelled query. + */ + SUGGEST = 'geo-places:Suggest', + + /** + * Allows any places actions. + */ + ANY = 'geo-places:*', +} + +/** + * Actions for Routes that an API key resource grants permissions to perform. + * + * @see https://docs.aws.amazon.com/service-authorization/latest/reference/list_amazonlocationserviceroutes.html + */ +export enum AllowRoutesAction { + /** + * Allows isoline calculation. + */ + CALCULATE_ISOLINES = 'geo-routes:CalculateIsolines', + + /** + * Allows point to point routing. + */ + CALCULATE_ROUTES = 'geo-routes:CalculateRoutes', + + /** + * Allows matrix routing. + */ + CALCULATE_ROUTE_MATRIX = 'geo-routes:CalculateRouteMatrix', + + /** + * Allows computing the best sequence of waypoints. + */ + OPTIMIZE_WAYPOINTS = 'geo-routes:OptimizeWaypoints', + + /** + * Allows snapping GPS points to a likely route. + */ + SNAP_TO_ROADS = 'geo-routes:SnapToRoads', + + /** + * Allows any routes actions. + */ + ANY = 'geo-routes:*', +} + +/** + * An API Key + * + * @see https://docs.aws.amazon.com/location/latest/developerguide/using-apikeys.html + */ +export class APIKey extends Resource implements IAPIKey { + /** + * Use an existing api key by name + */ + public static fromAPIKeyName(scope: Construct, id: string, keyName: string): IAPIKey { + const keyArn = Stack.of(scope).formatArn({ + service: 'geo', + resource: 'api-key', + resourceName: keyName, + }); + + return APIKey.fromAPIKeyArn(scope, id, keyArn); + } + + /** + * Use an existing api key by ARN + */ + public static fromAPIKeyArn(scope: Construct, id: string, keyArn: string): IAPIKey { + const parsedArn = Stack.of(scope).splitArn(keyArn, ArnFormat.SLASH_RESOURCE_NAME); + + if (!parsedArn.resourceName) { + throw new Error(`API Key Arn ${keyArn} does not have a resource name.`); + } + + class Import extends Resource implements IAPIKey { + public readonly keyName = parsedArn.resourceName!; + public readonly keyArn = keyArn; + } + + return new Import(scope, id, { + account: parsedArn.account, + region: parsedArn.region, + }); + } + + public readonly keyName: string; + + public readonly keyArn: string; + + /** + * The timestamp for when the api key resource was created in ISO 8601 format + * + * @attribute + */ + public readonly apiKeyCreateTime: string; + + /** + * The timestamp for when the api key resource was last updated in ISO 8601 format + * + * @attribute + */ + public readonly apiKeyUpdateTime: string; + + constructor(scope: Construct, id: string, props: APIKeyProps = {}) { + if (props.description && !Token.isUnresolved(props.description) && props.description.length > 1000) { + throw new Error(`\`description\` must be between 0 and 1000 characters. Received: ${props.description.length} characters`); + } + + if (props.keyName !== undefined && !Token.isUnresolved(props.keyName)) { + if (props.keyName.length < 1 || props.keyName.length > 100) { + throw new Error(`\`keyName\` must be between 1 and 100 characters, got: ${props.keyName.length} characters.`); + } + + if (!/^[-._\w]+$/.test(props.keyName)) { + throw new Error(`\`keyName\` must contain only alphanumeric characters, hyphens, periods and underscores, got: ${props.keyName}.`); + } + } + + super(scope, id, { + physicalName: props.keyName ?? Lazy.string({ produce: () => generateUniqueId(this) }), + }); + + if (props.expireTime !== undefined && props.noExpiry === true) { + throw new Error('`expireTime` must not be set when `noExpiry` has value true.'); + } + + if (props.expireTime === undefined && props.noExpiry !== true) { + throw new Error('`expireTime` must be set when `noExpiry` is false or undefined.'); + } + + if (!props.allowMapsActions && !props.allowPlacesActions && !props.allowRoutesActions) { + throw new Error('At least one of `allowMapsActions`, `allowPlacesActions`, or `allowRoutesActions` must be specified.'); + } + + if (props.allowReferers !== undefined && + (props.allowReferers.length < 1 || props.allowReferers.length > 5)) { + throw new Error(`\`allowReferers\` must be between 1 and 5 elements, got: ${props.allowReferers.length} elements.`); + } + + const apiKey = new CfnAPIKey(this, 'Resource', { + keyName: this.physicalName, + description: props.description, + expireTime: props.expireTime?.toISOString(), + forceDelete: props.forceDelete, + forceUpdate: props.forceUpdate, + noExpiry: props.noExpiry, + restrictions: this._renderRestrictions(props), + }); + + this.keyName = apiKey.ref; + this.keyArn = apiKey.attrArn; + this.apiKeyCreateTime = apiKey.attrCreateTime; + this.apiKeyUpdateTime = apiKey.attrUpdateTime; + } + + /** + * Renders the restrictions property for API Keys + * + * NOTE: added allowResources are AWS managed resources. + */ + private _renderRestrictions(props: APIKeyProps): CfnAPIKey.ApiKeyRestrictionsProperty { + let allowResources = []; + if (props.allowMapsActions) { + allowResources.push(`arn:${Aws.PARTITION}:geo-maps:${Stack.of(this).region}::provider/default`); + } + if (props.allowPlacesActions) { + allowResources.push(`arn:${Aws.PARTITION}:geo-places:${Stack.of(this).region}::provider/default`); + } + if (props.allowRoutesActions) { + allowResources.push(`arn:${Aws.PARTITION}:geo-routes:${Stack.of(this).region}::provider/default`); + } + + return { + allowActions: [ + ...props.allowMapsActions ?? [], + ...props.allowPlacesActions ?? [], + ...props.allowRoutesActions ?? [], + ].map((action) => action.toString()), + allowReferers: props.allowReferers, + allowResources, + }; + } +} diff --git a/packages/@aws-cdk/aws-location-alpha/lib/index.ts b/packages/@aws-cdk/aws-location-alpha/lib/index.ts index 871e150d2d823..6bd084bde4b62 100644 --- a/packages/@aws-cdk/aws-location-alpha/lib/index.ts +++ b/packages/@aws-cdk/aws-location-alpha/lib/index.ts @@ -1,3 +1,4 @@ +export * from './api-key'; export * from './geofence-collection'; export * from './map'; export * from './place-index'; diff --git a/packages/@aws-cdk/aws-location-alpha/test/api-key.test.ts b/packages/@aws-cdk/aws-location-alpha/test/api-key.test.ts new file mode 100644 index 0000000000000..ea610118b862d --- /dev/null +++ b/packages/@aws-cdk/aws-location-alpha/test/api-key.test.ts @@ -0,0 +1,387 @@ +import { Stack } from 'aws-cdk-lib'; +import { Template } from 'aws-cdk-lib/assertions'; +import { AllowMapsAction, AllowPlacesAction, AllowRoutesAction, APIKey } from '../lib'; + +let stack: Stack; +beforeEach(() => { + stack = new Stack(); +}); + +test('create an api key', () => { + new APIKey(stack, 'ApiKey', { + description: 'my-api-key-description', + forceDelete: true, + forceUpdate: true, + noExpiry: true, + allowMapsActions: [ + AllowMapsAction.ANY, + ], + allowPlacesActions: [ + AllowPlacesAction.ANY, + ], + allowRoutesActions: [ + AllowRoutesAction.ANY, + ], + allowReferers: ['https://example.com'], + }); + + Template.fromStack(stack).hasResourceProperties('AWS::Location::APIKey', { + KeyName: 'ApiKey', + Description: 'my-api-key-description', + ForceDelete: true, + ForceUpdate: true, + NoExpiry: true, + Restrictions: { + AllowActions: [ + 'geo-maps:*', + 'geo-places:*', + 'geo-routes:*', + ], + AllowResources: [ + { + 'Fn::Join': [ + '', + [ + 'arn:', + { Ref: 'AWS::Partition' }, + ':geo-maps:', + { Ref: 'AWS::Region' }, + '::provider/default', + ], + ], + }, + { + 'Fn::Join': [ + '', + [ + 'arn:', + { Ref: 'AWS::Partition' }, + ':geo-places:', + { Ref: 'AWS::Region' }, + '::provider/default', + ], + ], + }, + { + 'Fn::Join': [ + '', + [ + 'arn:', + { Ref: 'AWS::Partition' }, + ':geo-routes:', + { Ref: 'AWS::Region' }, + '::provider/default', + ], + ], + }, + ], + AllowReferers: ['https://example.com'], + }, + }); +}); + +test('create an api key with `keyName`', () => { + new APIKey(stack, 'ApiKey', { + keyName: 'my-api-key', + noExpiry: true, + allowMapsActions: [ + AllowMapsAction.ANY, + ], + }); + + Template.fromStack(stack).hasResourceProperties('AWS::Location::APIKey', { + KeyName: 'my-api-key', + NoExpiry: true, + Restrictions: { + AllowActions: [ + 'geo-maps:*', + ], + AllowResources: [ + { + 'Fn::Join': [ + '', + [ + 'arn:', + { Ref: 'AWS::Partition' }, + ':geo-maps:', + { Ref: 'AWS::Region' }, + '::provider/default', + ], + ], + }, + ], + }, + }); +}); + +test('create an api key with `expireTime` when `noExpiry` is not set', () => { + new APIKey(stack, 'ApiKey', { + keyName: 'my-api-key', + expireTime: new Date('2026-01-01T00:00:00Z'), + allowMapsActions: [ + AllowMapsAction.ANY, + ], + }); + + Template.fromStack(stack).hasResourceProperties('AWS::Location::APIKey', { + KeyName: 'my-api-key', + ExpireTime: '2026-01-01T00:00:00.000Z', + Restrictions: { + AllowActions: [ + 'geo-maps:*', + ], + AllowResources: [ + { + 'Fn::Join': [ + '', + [ + 'arn:', + { Ref: 'AWS::Partition' }, + ':geo-maps:', + { Ref: 'AWS::Region' }, + '::provider/default', + ], + ], + }, + ], + }, + }); +}); + +test('create an api key with `expireTime` when `noExpiry` is false', () => { + new APIKey(stack, 'ApiKey', { + keyName: 'my-api-key', + expireTime: new Date('2026-01-01T00:00:00Z'), + noExpiry: false, + allowMapsActions: [ + AllowMapsAction.ANY, + ], + }); + + Template.fromStack(stack).hasResourceProperties('AWS::Location::APIKey', { + KeyName: 'my-api-key', + ExpireTime: '2026-01-01T00:00:00.000Z', + NoExpiry: false, + Restrictions: { + AllowActions: [ + 'geo-maps:*', + ], + AllowResources: [ + { + 'Fn::Join': [ + '', + [ + 'arn:', + { Ref: 'AWS::Partition' }, + ':geo-maps:', + { Ref: 'AWS::Region' }, + '::provider/default', + ], + ], + }, + ], + }, + }); +}); + +test('test `allowMapsActions`', () => { + new APIKey(stack, 'ApiKey', { + noExpiry: true, + allowMapsActions: [ + AllowMapsAction.GET_STATIC_MAP, + AllowMapsAction.GET_TILE, + ], + }); + + Template.fromStack(stack).hasResourceProperties('AWS::Location::APIKey', { + NoExpiry: true, + Restrictions: { + AllowActions: [ + 'geo-maps:GetStaticMap', + 'geo-maps:GetTile', + ], + AllowResources: [ + { + 'Fn::Join': [ + '', + [ + 'arn:', + { Ref: 'AWS::Partition' }, + ':geo-maps:', + { Ref: 'AWS::Region' }, + '::provider/default', + ], + ], + }, + ], + }, + }); +}); + +test('test `allowPlacesActions`', () => { + new APIKey(stack, 'ApiKey', { + noExpiry: true, + allowPlacesActions: [ + AllowPlacesAction.AUTOCOMPLETE, + AllowPlacesAction.GEOCODE, + AllowPlacesAction.GET_PLACE, + AllowPlacesAction.REVERSE_GEOCODE, + AllowPlacesAction.SEARCH_NEARBY, + AllowPlacesAction.SEARCH_TEXT, + AllowPlacesAction.SUGGEST, + ], + }); + + Template.fromStack(stack).hasResourceProperties('AWS::Location::APIKey', { + NoExpiry: true, + Restrictions: { + AllowActions: [ + 'geo-places:Autocomplete', + 'geo-places:Geocode', + 'geo-places:GetPlace', + 'geo-places:ReverseGeocode', + 'geo-places:SearchNearby', + 'geo-places:SearchText', + 'geo-places:Suggest', + ], + AllowResources: [ + { + 'Fn::Join': [ + '', + [ + 'arn:', + { Ref: 'AWS::Partition' }, + ':geo-places:', + { Ref: 'AWS::Region' }, + '::provider/default', + ], + ], + }, + ], + }, + }); +}); + +test('test `allowRoutesActions`', () => { + new APIKey(stack, 'ApiKey', { + noExpiry: true, + allowRoutesActions: [ + AllowRoutesAction.CALCULATE_ISOLINES, + AllowRoutesAction.CALCULATE_ROUTES, + AllowRoutesAction.CALCULATE_ROUTE_MATRIX, + AllowRoutesAction.OPTIMIZE_WAYPOINTS, + AllowRoutesAction.SNAP_TO_ROADS, + ], + }); + + Template.fromStack(stack).hasResourceProperties('AWS::Location::APIKey', { + NoExpiry: true, + Restrictions: { + AllowActions: [ + 'geo-routes:CalculateIsolines', + 'geo-routes:CalculateRoutes', + 'geo-routes:CalculateRouteMatrix', + 'geo-routes:OptimizeWaypoints', + 'geo-routes:SnapToRoads', + ], + AllowResources: [ + { + 'Fn::Join': [ + '', + [ + 'arn:', + { Ref: 'AWS::Partition' }, + ':geo-routes:', + { Ref: 'AWS::Region' }, + '::provider/default', + ], + ], + }, + ], + }, + }); +}); + +test('throws with invalid description', () => { + expect(() => new APIKey(stack, 'ApiKey', { + description: 'a'.repeat(1001), + noExpiry: true, + allowRoutesActions: [ + AllowRoutesAction.CALCULATE_ISOLINES, + ], + })).toThrow('`description` must be between 0 and 1000 characters. Received: 1001 characters'); +}); + +test.each(['', 'a'.repeat(101)])('throws with invalid name, got: %s', (keyName) => { + expect(() => new APIKey(stack, 'ApiKey', { + keyName, + noExpiry: true, + allowRoutesActions: [ + AllowRoutesAction.CALCULATE_ISOLINES, + ], + })).toThrow(`\`keyName\` must be between 1 and 100 characters, got: ${keyName.length} characters.`); +}); + +test('throws with invalid name', () => { + expect(() => new APIKey(stack, 'ApiKey', { + keyName: 'inv@lid', + noExpiry: true, + allowRoutesActions: [ + AllowRoutesAction.CALCULATE_ISOLINES, + ], + })).toThrow('`keyName` must contain only alphanumeric characters, hyphens, periods and underscores, got: inv@lid.'); +}); + +test('throws when `noExpiry` is true and `expireTime` is specified', () => { + expect(() => { + new APIKey(stack, 'ApiKey', { + expireTime: new Date('2026-01-01T00:00:00Z'), + noExpiry: true, + allowRoutesActions: [ + AllowRoutesAction.CALCULATE_ISOLINES, + ], + }); + }).toThrow('`expireTime` must not be set when `noExpiry` has value true.'); +}); + +test('throws when `noExpiry` is not true and `expireTime` is not specified', () => { + expect(() => { + new APIKey(stack, 'ApiKey', { + allowRoutesActions: [ + AllowRoutesAction.CALCULATE_ISOLINES, + ], + }); + }).toThrow('`expireTime` must be set when `noExpiry` is false or undefined.'); +}); + +test('throws when over 0 referers are set', () => { + expect(() => { + new APIKey(stack, 'ApiKey', { + noExpiry: true, + allowRoutesActions: [ + AllowRoutesAction.CALCULATE_ISOLINES, + ], + allowReferers: [ + ], + }); + }).toThrow('`allowReferers` must be between 1 and 5 elements, got: 0 elements.'); +}); + +test('throws when over 5 referers are set', () => { + expect(() => { + new APIKey(stack, 'ApiKey', { + noExpiry: true, + allowRoutesActions: [ + AllowRoutesAction.CALCULATE_ISOLINES, + ], + allowReferers: [ + 'https://example.com', + 'https://example.com', + 'https://example.com', + 'https://example.com', + 'https://example.com', + 'https://example.com', + ], + }); + }).toThrow('`allowReferers` must be between 1 and 5 elements, got: 6 elements.'); +}); diff --git a/packages/@aws-cdk/aws-location-alpha/test/integ.api-key.js.snapshot/ApiKeyTestDefaultTestDeployAssert140AB0A3.assets.json b/packages/@aws-cdk/aws-location-alpha/test/integ.api-key.js.snapshot/ApiKeyTestDefaultTestDeployAssert140AB0A3.assets.json new file mode 100644 index 0000000000000..aadb120cd61e4 --- /dev/null +++ b/packages/@aws-cdk/aws-location-alpha/test/integ.api-key.js.snapshot/ApiKeyTestDefaultTestDeployAssert140AB0A3.assets.json @@ -0,0 +1,19 @@ +{ + "version": "39.0.0", + "files": { + "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22": { + "source": { + "path": "ApiKeyTestDefaultTestDeployAssert140AB0A3.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-location-alpha/test/integ.api-key.js.snapshot/ApiKeyTestDefaultTestDeployAssert140AB0A3.template.json b/packages/@aws-cdk/aws-location-alpha/test/integ.api-key.js.snapshot/ApiKeyTestDefaultTestDeployAssert140AB0A3.template.json new file mode 100644 index 0000000000000..ad9d0fb73d1dd --- /dev/null +++ b/packages/@aws-cdk/aws-location-alpha/test/integ.api-key.js.snapshot/ApiKeyTestDefaultTestDeployAssert140AB0A3.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-location-alpha/test/integ.api-key.js.snapshot/cdk-integ-location-api-key.assets.json b/packages/@aws-cdk/aws-location-alpha/test/integ.api-key.js.snapshot/cdk-integ-location-api-key.assets.json new file mode 100644 index 0000000000000..02cbfde38c61a --- /dev/null +++ b/packages/@aws-cdk/aws-location-alpha/test/integ.api-key.js.snapshot/cdk-integ-location-api-key.assets.json @@ -0,0 +1,19 @@ +{ + "version": "39.0.0", + "files": { + "8475946b719955e8526aa19c95756976d9ecc7dc350cad981fb4c6454aec983b": { + "source": { + "path": "cdk-integ-location-api-key.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "8475946b719955e8526aa19c95756976d9ecc7dc350cad981fb4c6454aec983b.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-location-alpha/test/integ.api-key.js.snapshot/cdk-integ-location-api-key.template.json b/packages/@aws-cdk/aws-location-alpha/test/integ.api-key.js.snapshot/cdk-integ-location-api-key.template.json new file mode 100644 index 0000000000000..3c65535b94ac9 --- /dev/null +++ b/packages/@aws-cdk/aws-location-alpha/test/integ.api-key.js.snapshot/cdk-integ-location-api-key.template.json @@ -0,0 +1,187 @@ +{ + "Resources": { + "APIKeyF5CDB6B6": { + "Type": "AWS::Location::APIKey", + "Properties": { + "Description": "my api key for test", + "ExpireTime": "2026-01-01T00:00:00.000Z", + "KeyName": "my-api-key", + "NoExpiry": false, + "Restrictions": { + "AllowActions": [ + "geo-maps:GetStaticMap", + "geo-maps:GetTile", + "geo-places:Autocomplete", + "geo-places:Geocode", + "geo-places:GetPlace", + "geo-places:ReverseGeocode", + "geo-places:SearchNearby", + "geo-places:SearchText", + "geo-places:Suggest", + "geo-routes:CalculateIsolines", + "geo-routes:CalculateRoutes", + "geo-routes:CalculateRouteMatrix", + "geo-routes:OptimizeWaypoints", + "geo-routes:SnapToRoads" + ], + "AllowReferers": [ + "https://example.com" + ], + "AllowResources": [ + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":geo-maps:", + { + "Ref": "AWS::Region" + }, + "::provider/default" + ] + ] + }, + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":geo-places:", + { + "Ref": "AWS::Region" + }, + "::provider/default" + ] + ] + }, + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":geo-routes:", + { + "Ref": "AWS::Region" + }, + "::provider/default" + ] + ] + } + ] + } + } + }, + "APIKeyAny24D2C8EC": { + "Type": "AWS::Location::APIKey", + "Properties": { + "ForceDelete": true, + "ForceUpdate": true, + "KeyName": "my-api-key-allowd-any-actions", + "NoExpiry": true, + "Restrictions": { + "AllowActions": [ + "geo-maps:*", + "geo-places:*", + "geo-routes:*" + ], + "AllowReferers": [ + "https://example.com" + ], + "AllowResources": [ + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":geo-maps:", + { + "Ref": "AWS::Region" + }, + "::provider/default" + ] + ] + }, + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":geo-places:", + { + "Ref": "AWS::Region" + }, + "::provider/default" + ] + ] + }, + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":geo-routes:", + { + "Ref": "AWS::Region" + }, + "::provider/default" + ] + ] + } + ] + } + } + } + }, + "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-location-alpha/test/integ.api-key.js.snapshot/cdk.out b/packages/@aws-cdk/aws-location-alpha/test/integ.api-key.js.snapshot/cdk.out new file mode 100644 index 0000000000000..91e1a8b9901d5 --- /dev/null +++ b/packages/@aws-cdk/aws-location-alpha/test/integ.api-key.js.snapshot/cdk.out @@ -0,0 +1 @@ +{"version":"39.0.0"} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-location-alpha/test/integ.api-key.js.snapshot/integ.json b/packages/@aws-cdk/aws-location-alpha/test/integ.api-key.js.snapshot/integ.json new file mode 100644 index 0000000000000..7adf48c1e542c --- /dev/null +++ b/packages/@aws-cdk/aws-location-alpha/test/integ.api-key.js.snapshot/integ.json @@ -0,0 +1,12 @@ +{ + "version": "39.0.0", + "testCases": { + "ApiKeyTest/DefaultTest": { + "stacks": [ + "cdk-integ-location-api-key" + ], + "assertionStack": "ApiKeyTest/DefaultTest/DeployAssert", + "assertionStackName": "ApiKeyTestDefaultTestDeployAssert140AB0A3" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-location-alpha/test/integ.api-key.js.snapshot/manifest.json b/packages/@aws-cdk/aws-location-alpha/test/integ.api-key.js.snapshot/manifest.json new file mode 100644 index 0000000000000..c1cf202d954ee --- /dev/null +++ b/packages/@aws-cdk/aws-location-alpha/test/integ.api-key.js.snapshot/manifest.json @@ -0,0 +1,119 @@ +{ + "version": "39.0.0", + "artifacts": { + "cdk-integ-location-api-key.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "cdk-integ-location-api-key.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "cdk-integ-location-api-key": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "cdk-integ-location-api-key.template.json", + "terminationProtection": false, + "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}/8475946b719955e8526aa19c95756976d9ecc7dc350cad981fb4c6454aec983b.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "cdk-integ-location-api-key.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": [ + "cdk-integ-location-api-key.assets" + ], + "metadata": { + "/cdk-integ-location-api-key/APIKey/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "APIKeyF5CDB6B6" + } + ], + "/cdk-integ-location-api-key/APIKeyAny/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "APIKeyAny24D2C8EC" + } + ], + "/cdk-integ-location-api-key/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/cdk-integ-location-api-key/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "cdk-integ-location-api-key" + }, + "ApiKeyTestDefaultTestDeployAssert140AB0A3.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "ApiKeyTestDefaultTestDeployAssert140AB0A3.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "ApiKeyTestDefaultTestDeployAssert140AB0A3": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "ApiKeyTestDefaultTestDeployAssert140AB0A3.template.json", + "terminationProtection": false, + "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": [ + "ApiKeyTestDefaultTestDeployAssert140AB0A3.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": [ + "ApiKeyTestDefaultTestDeployAssert140AB0A3.assets" + ], + "metadata": { + "/ApiKeyTest/DefaultTest/DeployAssert/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/ApiKeyTest/DefaultTest/DeployAssert/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "ApiKeyTest/DefaultTest/DeployAssert" + }, + "Tree": { + "type": "cdk:tree", + "properties": { + "file": "tree.json" + } + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-location-alpha/test/integ.api-key.js.snapshot/tree.json b/packages/@aws-cdk/aws-location-alpha/test/integ.api-key.js.snapshot/tree.json new file mode 100644 index 0000000000000..306f4118bd8c0 --- /dev/null +++ b/packages/@aws-cdk/aws-location-alpha/test/integ.api-key.js.snapshot/tree.json @@ -0,0 +1,286 @@ +{ + "version": "tree-0.1", + "tree": { + "id": "App", + "path": "", + "children": { + "cdk-integ-location-api-key": { + "id": "cdk-integ-location-api-key", + "path": "cdk-integ-location-api-key", + "children": { + "APIKey": { + "id": "APIKey", + "path": "cdk-integ-location-api-key/APIKey", + "children": { + "Resource": { + "id": "Resource", + "path": "cdk-integ-location-api-key/APIKey/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::Location::APIKey", + "aws:cdk:cloudformation:props": { + "description": "my api key for test", + "expireTime": "2026-01-01T00:00:00.000Z", + "keyName": "my-api-key", + "noExpiry": false, + "restrictions": { + "allowActions": [ + "geo-maps:GetStaticMap", + "geo-maps:GetTile", + "geo-places:Autocomplete", + "geo-places:Geocode", + "geo-places:GetPlace", + "geo-places:ReverseGeocode", + "geo-places:SearchNearby", + "geo-places:SearchText", + "geo-places:Suggest", + "geo-routes:CalculateIsolines", + "geo-routes:CalculateRoutes", + "geo-routes:CalculateRouteMatrix", + "geo-routes:OptimizeWaypoints", + "geo-routes:SnapToRoads" + ], + "allowReferers": [ + "https://example.com" + ], + "allowResources": [ + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":geo-maps:", + { + "Ref": "AWS::Region" + }, + "::provider/default" + ] + ] + }, + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":geo-places:", + { + "Ref": "AWS::Region" + }, + "::provider/default" + ] + ] + }, + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":geo-routes:", + { + "Ref": "AWS::Region" + }, + "::provider/default" + ] + ] + } + ] + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_location.CfnAPIKey", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.Resource", + "version": "0.0.0" + } + }, + "APIKeyAny": { + "id": "APIKeyAny", + "path": "cdk-integ-location-api-key/APIKeyAny", + "children": { + "Resource": { + "id": "Resource", + "path": "cdk-integ-location-api-key/APIKeyAny/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::Location::APIKey", + "aws:cdk:cloudformation:props": { + "forceDelete": true, + "forceUpdate": true, + "keyName": "my-api-key-allowd-any-actions", + "noExpiry": true, + "restrictions": { + "allowActions": [ + "geo-maps:*", + "geo-places:*", + "geo-routes:*" + ], + "allowReferers": [ + "https://example.com" + ], + "allowResources": [ + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":geo-maps:", + { + "Ref": "AWS::Region" + }, + "::provider/default" + ] + ] + }, + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":geo-places:", + { + "Ref": "AWS::Region" + }, + "::provider/default" + ] + ] + }, + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":geo-routes:", + { + "Ref": "AWS::Region" + }, + "::provider/default" + ] + ] + } + ] + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_location.CfnAPIKey", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.Resource", + "version": "0.0.0" + } + }, + "BootstrapVersion": { + "id": "BootstrapVersion", + "path": "cdk-integ-location-api-key/BootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnParameter", + "version": "0.0.0" + } + }, + "CheckBootstrapVersion": { + "id": "CheckBootstrapVersion", + "path": "cdk-integ-location-api-key/CheckBootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnRule", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.Stack", + "version": "0.0.0" + } + }, + "ApiKeyTest": { + "id": "ApiKeyTest", + "path": "ApiKeyTest", + "children": { + "DefaultTest": { + "id": "DefaultTest", + "path": "ApiKeyTest/DefaultTest", + "children": { + "Default": { + "id": "Default", + "path": "ApiKeyTest/DefaultTest/Default", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.4.2" + } + }, + "DeployAssert": { + "id": "DeployAssert", + "path": "ApiKeyTest/DefaultTest/DeployAssert", + "children": { + "BootstrapVersion": { + "id": "BootstrapVersion", + "path": "ApiKeyTest/DefaultTest/DeployAssert/BootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnParameter", + "version": "0.0.0" + } + }, + "CheckBootstrapVersion": { + "id": "CheckBootstrapVersion", + "path": "ApiKeyTest/DefaultTest/DeployAssert/CheckBootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnRule", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.Stack", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests-alpha.IntegTestCase", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests-alpha.IntegTest", + "version": "0.0.0" + } + }, + "Tree": { + "id": "Tree", + "path": "Tree", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.4.2" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.App", + "version": "0.0.0" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-location-alpha/test/integ.api-key.ts b/packages/@aws-cdk/aws-location-alpha/test/integ.api-key.ts new file mode 100644 index 0000000000000..43563aefcf101 --- /dev/null +++ b/packages/@aws-cdk/aws-location-alpha/test/integ.api-key.ts @@ -0,0 +1,61 @@ +import { App, Stack } from 'aws-cdk-lib'; +import * as integ from '@aws-cdk/integ-tests-alpha'; +import { Construct } from 'constructs'; +import { AllowMapsAction, AllowPlacesAction, AllowRoutesAction, APIKey } from '../lib'; + +class TestStack extends Stack { + constructor(scope: Construct, id: string) { + super(scope, id); + + new APIKey(this, 'APIKey', { + keyName: 'my-api-key', + description: 'my api key for test', + expireTime: new Date('2026-01-01T00:00:00Z'), + noExpiry: false, + allowMapsActions: [ + AllowMapsAction.GET_STATIC_MAP, + AllowMapsAction.GET_TILE, + ], + allowPlacesActions: [ + AllowPlacesAction.AUTOCOMPLETE, + AllowPlacesAction.GEOCODE, + AllowPlacesAction.GET_PLACE, + AllowPlacesAction.REVERSE_GEOCODE, + AllowPlacesAction.SEARCH_NEARBY, + AllowPlacesAction.SEARCH_TEXT, + AllowPlacesAction.SUGGEST, + ], + allowRoutesActions: [ + AllowRoutesAction.CALCULATE_ISOLINES, + AllowRoutesAction.CALCULATE_ROUTES, + AllowRoutesAction.CALCULATE_ROUTE_MATRIX, + AllowRoutesAction.OPTIMIZE_WAYPOINTS, + AllowRoutesAction.SNAP_TO_ROADS, + ], + allowReferers: ['https://example.com'], + }); + + new APIKey(this, 'APIKeyAny', { + keyName: 'my-api-key-allowd-any-actions', + noExpiry: true, + forceUpdate: true, + forceDelete: true, + allowMapsActions: [ + AllowMapsAction.ANY, + ], + allowPlacesActions: [ + AllowPlacesAction.ANY, + ], + allowRoutesActions: [ + AllowRoutesAction.ANY, + ], + allowReferers: ['https://example.com'], + }); + } +} + +const app = new App(); + +new integ.IntegTest(app, 'ApiKeyTest', { + testCases: [new TestStack(app, 'cdk-integ-location-api-key')], +}); diff --git a/packages/@aws-cdk/integ-runner/lib/recommended-feature-flags.json b/packages/@aws-cdk/integ-runner/lib/recommended-feature-flags.json new file mode 100644 index 0000000000000..30a08f65a928c --- /dev/null +++ b/packages/@aws-cdk/integ-runner/lib/recommended-feature-flags.json @@ -0,0 +1,63 @@ +{ + "@aws-cdk/aws-lambda:recognizeLayerVersion": true, + "@aws-cdk/core:checkSecretUsage": true, + "@aws-cdk/core:target-partitions": [ + "aws", + "aws-cn" + ], + "@aws-cdk-containers/ecs-service-extensions:enableDefaultLogDriver": true, + "@aws-cdk/aws-ec2:uniqueImdsv2TemplateName": true, + "@aws-cdk/aws-ecs:arnFormatIncludesClusterName": true, + "@aws-cdk/aws-iam:minimizePolicies": true, + "@aws-cdk/core:validateSnapshotRemovalPolicy": true, + "@aws-cdk/aws-codepipeline:crossAccountKeyAliasStackSafeResourceName": true, + "@aws-cdk/aws-s3:createDefaultLoggingPolicy": true, + "@aws-cdk/aws-sns-subscriptions:restrictSqsDescryption": true, + "@aws-cdk/aws-apigateway:disableCloudWatchRole": true, + "@aws-cdk/core:enablePartitionLiterals": true, + "@aws-cdk/aws-events:eventsTargetQueueSameAccount": true, + "@aws-cdk/aws-ecs:disableExplicitDeploymentControllerForCircuitBreaker": true, + "@aws-cdk/aws-iam:importedRoleStackSafeDefaultPolicyName": true, + "@aws-cdk/aws-s3:serverAccessLogsUseBucketPolicy": true, + "@aws-cdk/aws-route53-patters:useCertificate": true, + "@aws-cdk/customresources:installLatestAwsSdkDefault": false, + "@aws-cdk/aws-rds:databaseProxyUniqueResourceName": true, + "@aws-cdk/aws-codedeploy:removeAlarmsFromDeploymentGroup": true, + "@aws-cdk/aws-apigateway:authorizerChangeDeploymentLogicalId": true, + "@aws-cdk/aws-ec2:launchTemplateDefaultUserData": true, + "@aws-cdk/aws-secretsmanager:useAttachedSecretResourcePolicyForSecretTargetAttachments": true, + "@aws-cdk/aws-redshift:columnId": true, + "@aws-cdk/aws-stepfunctions-tasks:enableEmrServicePolicyV2": true, + "@aws-cdk/aws-ec2:restrictDefaultSecurityGroup": true, + "@aws-cdk/aws-apigateway:requestValidatorUniqueId": true, + "@aws-cdk/aws-kms:aliasNameRef": true, + "@aws-cdk/aws-autoscaling:generateLaunchTemplateInsteadOfLaunchConfig": true, + "@aws-cdk/core:includePrefixInUniqueNameGeneration": true, + "@aws-cdk/aws-efs:denyAnonymousAccess": true, + "@aws-cdk/aws-opensearchservice:enableOpensearchMultiAzWithStandby": true, + "@aws-cdk/aws-lambda-nodejs:useLatestRuntimeVersion": true, + "@aws-cdk/aws-efs:mountTargetOrderInsensitiveLogicalId": true, + "@aws-cdk/aws-rds:auroraClusterChangeScopeOfInstanceParameterGroupWithEachParameters": true, + "@aws-cdk/aws-appsync:useArnForSourceApiAssociationIdentifier": true, + "@aws-cdk/aws-rds:preventRenderingDeprecatedCredentials": true, + "@aws-cdk/aws-codepipeline-actions:useNewDefaultBranchForCodeCommitSource": true, + "@aws-cdk/aws-cloudwatch-actions:changeLambdaPermissionLogicalIdForLambdaAction": true, + "@aws-cdk/aws-codepipeline:crossAccountKeysDefaultValueToFalse": true, + "@aws-cdk/aws-codepipeline:defaultPipelineTypeToV2": true, + "@aws-cdk/aws-kms:reduceCrossAccountRegionPolicyScope": true, + "@aws-cdk/aws-eks:nodegroupNameAttribute": true, + "@aws-cdk/aws-ec2:ebsDefaultGp3Volume": true, + "@aws-cdk/aws-ecs:removeDefaultDeploymentAlarm": true, + "@aws-cdk/custom-resources:logApiResponseDataPropertyTrueDefault": false, + "@aws-cdk/aws-s3:keepNotificationInImportedBucket": false, + "@aws-cdk/aws-ecs:reduceEc2FargateCloudWatchPermissions": true, + "@aws-cdk/aws-dynamodb:resourcePolicyPerReplica": true, + "@aws-cdk/aws-ec2:ec2SumTImeoutEnabled": true, + "@aws-cdk/aws-appsync:appSyncGraphQLAPIScopeLambdaPermission": true, + "@aws-cdk/aws-rds:setCorrectValueForDatabaseInstanceReadReplicaInstanceResourceId": true, + "@aws-cdk/core:cfnIncludeRejectComplexResourceUpdateCreatePolicyIntrinsics": true, + "@aws-cdk/aws-lambda-nodejs:sdkV3ExcludeSmithyPackages": true, + "@aws-cdk/aws-stepfunctions-tasks:fixRunEcsTaskPolicy": true, + "@aws-cdk/aws-ec2:bastionHostUseAmazonLinux2023ByDefault": true, + "@aws-cdk/aws-route53-targets:userPoolDomainNameMethodWithoutCustomResource": true +} \ No newline at end of file