diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-cloudfront/test/integ.distribution-function-runtime.js.snapshot/CF2RuntimeDefaultTestDeployAssertB25F4037.assets.json b/packages/@aws-cdk-testing/framework-integ/test/aws-cloudfront/test/integ.distribution-function-runtime.js.snapshot/CF2RuntimeDefaultTestDeployAssertB25F4037.assets.json index 52bf1e5df20a8..e81eb8ba9e016 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-cloudfront/test/integ.distribution-function-runtime.js.snapshot/CF2RuntimeDefaultTestDeployAssertB25F4037.assets.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-cloudfront/test/integ.distribution-function-runtime.js.snapshot/CF2RuntimeDefaultTestDeployAssertB25F4037.assets.json @@ -1,13 +1,14 @@ { - "version": "36.0.0", + "version": "48.0.0", "files": { "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22": { + "displayName": "CF2RuntimeDefaultTestDeployAssertB25F4037 Template", "source": { "path": "CF2RuntimeDefaultTestDeployAssertB25F4037.template.json", "packaging": "file" }, "destinations": { - "current_account-current_region": { + "current_account-current_region-d8d86b35": { "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}" diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-cloudfront/test/integ.distribution-function-runtime.js.snapshot/cdk.out b/packages/@aws-cdk-testing/framework-integ/test/aws-cloudfront/test/integ.distribution-function-runtime.js.snapshot/cdk.out index 1f0068d32659a..523a9aac37cbf 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-cloudfront/test/integ.distribution-function-runtime.js.snapshot/cdk.out +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-cloudfront/test/integ.distribution-function-runtime.js.snapshot/cdk.out @@ -1 +1 @@ -{"version":"36.0.0"} \ No newline at end of file +{"version":"48.0.0"} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-cloudfront/test/integ.distribution-function-runtime.js.snapshot/integ-distribution-function.assets.json b/packages/@aws-cdk-testing/framework-integ/test/aws-cloudfront/test/integ.distribution-function-runtime.js.snapshot/integ-distribution-function.assets.json index 4f6d4c6f7b7c1..9b26b3be1b9a6 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-cloudfront/test/integ.distribution-function-runtime.js.snapshot/integ-distribution-function.assets.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-cloudfront/test/integ.distribution-function-runtime.js.snapshot/integ-distribution-function.assets.json @@ -1,15 +1,16 @@ { - "version": "36.0.0", + "version": "48.0.0", "files": { - "f593b032ca2360f7f5beecafd32ec38ebe7a6cb5c25290e00d0066ebb69be3a2": { + "71d966b3880570a9720c508c102fad2c9fc7c38916f00d7a2906d9d1a0587ee5": { + "displayName": "integ-distribution-function Template", "source": { "path": "integ-distribution-function.template.json", "packaging": "file" }, "destinations": { - "current_account-eu-west-1": { + "current_account-eu-west-1-6a047c00": { "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-eu-west-1", - "objectKey": "f593b032ca2360f7f5beecafd32ec38ebe7a6cb5c25290e00d0066ebb69be3a2.json", + "objectKey": "71d966b3880570a9720c508c102fad2c9fc7c38916f00d7a2906d9d1a0587ee5.json", "region": "eu-west-1", "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-eu-west-1" } diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-cloudfront/test/integ.distribution-function-runtime.js.snapshot/integ-distribution-function.template.json b/packages/@aws-cdk-testing/framework-integ/test/aws-cloudfront/test/integ.distribution-function-runtime.js.snapshot/integ-distribution-function.template.json index 27290243856b4..51ff154349bef 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-cloudfront/test/integ.distribution-function-runtime.js.snapshot/integ-distribution-function.template.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-cloudfront/test/integ.distribution-function-runtime.js.snapshot/integ-distribution-function.template.json @@ -35,19 +35,13 @@ { "EventType": "viewer-request", "FunctionARN": { - "Fn::GetAtt": [ - "FunctionRequest95528B2F", - "FunctionARN" - ] + "Ref": "FunctionRequest95528B2F" } }, { "EventType": "viewer-response", "FunctionARN": { - "Fn::GetAtt": [ - "FunctionResponse4EF2D1D3", - "FunctionARN" - ] + "Ref": "FunctionResponse4EF2D1D3" } } ], diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-cloudfront/test/integ.distribution-function-runtime.js.snapshot/integ.json b/packages/@aws-cdk-testing/framework-integ/test/aws-cloudfront/test/integ.distribution-function-runtime.js.snapshot/integ.json index 0d701e61e53a2..4a17dc264347a 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-cloudfront/test/integ.distribution-function-runtime.js.snapshot/integ.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-cloudfront/test/integ.distribution-function-runtime.js.snapshot/integ.json @@ -1,5 +1,5 @@ { - "version": "36.0.0", + "version": "48.0.0", "testCases": { "CF2Runtime/DefaultTest": { "stacks": [ @@ -8,5 +8,6 @@ "assertionStack": "CF2Runtime/DefaultTest/DeployAssert", "assertionStackName": "CF2RuntimeDefaultTestDeployAssertB25F4037" } - } + }, + "minimumCliVersion": "2.1024.0" } \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-cloudfront/test/integ.distribution-function-runtime.js.snapshot/manifest.json b/packages/@aws-cdk-testing/framework-integ/test/aws-cloudfront/test/integ.distribution-function-runtime.js.snapshot/manifest.json index 59a615053acc1..62f45834ad37f 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-cloudfront/test/integ.distribution-function-runtime.js.snapshot/manifest.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-cloudfront/test/integ.distribution-function-runtime.js.snapshot/manifest.json @@ -1,5 +1,5 @@ { - "version": "36.0.0", + "version": "48.0.0", "artifacts": { "integ-distribution-function.assets": { "type": "cdk:asset-manifest", @@ -18,7 +18,7 @@ "validateOnSynth": false, "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-eu-west-1", "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-eu-west-1", - "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-eu-west-1/f593b032ca2360f7f5beecafd32ec38ebe7a6cb5c25290e00d0066ebb69be3a2.json", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-eu-west-1/71d966b3880570a9720c508c102fad2c9fc7c38916f00d7a2906d9d1a0587ee5.json", "requiresBootstrapStackVersion": 6, "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", "additionalDependencies": [ @@ -34,18 +34,59 @@ "integ-distribution-function.assets" ], "metadata": { + "/integ-distribution-function/FunctionRequest": [ + { + "type": "aws:cdk:analytics:construct", + "data": { + "code": "*", + "runtime": "*" + } + } + ], "/integ-distribution-function/FunctionRequest/Resource": [ { "type": "aws:cdk:logicalId", "data": "FunctionRequest95528B2F" } ], + "/integ-distribution-function/FunctionResponse": [ + { + "type": "aws:cdk:analytics:construct", + "data": { + "code": "*", + "runtime": "*" + } + } + ], "/integ-distribution-function/FunctionResponse/Resource": [ { "type": "aws:cdk:logicalId", "data": "FunctionResponse4EF2D1D3" } ], + "/integ-distribution-function/Dist": [ + { + "type": "aws:cdk:analytics:construct", + "data": { + "defaultBehavior": { + "origin": "*", + "cachePolicy": { + "cachePolicyId": "*" + }, + "functionAssociations": [ + { + "function": "*", + "eventType": "viewer-request" + }, + { + "function": "*", + "eventType": "viewer-response" + } + ] + } + } + } + ], "/integ-distribution-function/Dist/Resource": [ { "type": "aws:cdk:logicalId", @@ -144,6 +185,481 @@ "properties": { "file": "tree.json" } + }, + "aws-cdk-lib/feature-flag-report": { + "type": "cdk:feature-flag-report", + "properties": { + "module": "aws-cdk-lib", + "flags": { + "@aws-cdk/aws-signer:signingProfileNamePassedToCfn": { + "recommendedValue": true, + "explanation": "Pass signingProfileName to CfnSigningProfile" + }, + "@aws-cdk/core:newStyleStackSynthesis": { + "recommendedValue": true, + "explanation": "Switch to new stack synthesis method which enables CI/CD", + "unconfiguredBehavesLike": { + "v2": true + } + }, + "@aws-cdk/core:stackRelativeExports": { + "recommendedValue": true, + "explanation": "Name exports based on the construct paths relative to the stack, rather than the global construct path", + "unconfiguredBehavesLike": { + "v2": true + } + }, + "@aws-cdk/aws-rds:lowercaseDbIdentifier": { + "recommendedValue": true, + "explanation": "Force lowercasing of RDS Cluster names in CDK", + "unconfiguredBehavesLike": { + "v2": true + } + }, + "@aws-cdk/aws-apigateway:usagePlanKeyOrderInsensitiveId": { + "recommendedValue": true, + "explanation": "Allow adding/removing multiple UsagePlanKeys independently", + "unconfiguredBehavesLike": { + "v2": true + } + }, + "@aws-cdk/aws-lambda:recognizeVersionProps": { + "recommendedValue": true, + "explanation": "Enable this feature flag to opt in to the updated logical id calculation for Lambda Version created using the `fn.currentVersion`.", + "unconfiguredBehavesLike": { + "v2": true + } + }, + "@aws-cdk/aws-lambda:recognizeLayerVersion": { + "userValue": true, + "recommendedValue": true, + "explanation": "Enable this feature flag to opt in to the updated logical id calculation for Lambda Version created using the `fn.currentVersion`." + }, + "@aws-cdk/aws-cloudfront:defaultSecurityPolicyTLSv1.2_2021": { + "recommendedValue": true, + "explanation": "Enable this feature flag to have cloudfront distributions use the security policy TLSv1.2_2021 by default.", + "unconfiguredBehavesLike": { + "v2": true + } + }, + "@aws-cdk/core:checkSecretUsage": { + "userValue": true, + "recommendedValue": true, + "explanation": "Enable this flag to make it impossible to accidentally use SecretValues in unsafe locations" + }, + "@aws-cdk/core:target-partitions": { + "recommendedValue": [ + "aws", + "aws-cn" + ], + "explanation": "What regions to include in lookup tables of environment agnostic stacks" + }, + "@aws-cdk-containers/ecs-service-extensions:enableDefaultLogDriver": { + "userValue": true, + "recommendedValue": true, + "explanation": "ECS extensions will automatically add an `awslogs` driver if no logging is specified" + }, + "@aws-cdk/aws-ec2:uniqueImdsv2TemplateName": { + "userValue": true, + "recommendedValue": true, + "explanation": "Enable this feature flag to have Launch Templates generated by the `InstanceRequireImdsv2Aspect` use unique names." + }, + "@aws-cdk/aws-ecs:arnFormatIncludesClusterName": { + "userValue": true, + "recommendedValue": true, + "explanation": "ARN format used by ECS. In the new ARN format, the cluster name is part of the resource ID." + }, + "@aws-cdk/aws-iam:minimizePolicies": { + "userValue": true, + "recommendedValue": true, + "explanation": "Minimize IAM policies by combining Statements" + }, + "@aws-cdk/core:validateSnapshotRemovalPolicy": { + "userValue": true, + "recommendedValue": true, + "explanation": "Error on snapshot removal policies on resources that do not support it." + }, + "@aws-cdk/aws-codepipeline:crossAccountKeyAliasStackSafeResourceName": { + "userValue": true, + "recommendedValue": true, + "explanation": "Generate key aliases that include the stack name" + }, + "@aws-cdk/aws-s3:createDefaultLoggingPolicy": { + "userValue": true, + "recommendedValue": true, + "explanation": "Enable this feature flag to create an S3 bucket policy by default in cases where an AWS service would automatically create the Policy if one does not exist." + }, + "@aws-cdk/aws-sns-subscriptions:restrictSqsDescryption": { + "userValue": true, + "recommendedValue": true, + "explanation": "Restrict KMS key policy for encrypted Queues a bit more" + }, + "@aws-cdk/aws-apigateway:disableCloudWatchRole": { + "userValue": true, + "recommendedValue": true, + "explanation": "Make default CloudWatch Role behavior safe for multiple API Gateways in one environment" + }, + "@aws-cdk/core:enablePartitionLiterals": { + "userValue": true, + "recommendedValue": true, + "explanation": "Make ARNs concrete if AWS partition is known" + }, + "@aws-cdk/aws-events:eventsTargetQueueSameAccount": { + "userValue": true, + "recommendedValue": true, + "explanation": "Event Rules may only push to encrypted SQS queues in the same account" + }, + "@aws-cdk/aws-ecs:disableExplicitDeploymentControllerForCircuitBreaker": { + "userValue": true, + "recommendedValue": true, + "explanation": "Avoid setting the \"ECS\" deployment controller when adding a circuit breaker" + }, + "@aws-cdk/aws-iam:importedRoleStackSafeDefaultPolicyName": { + "userValue": true, + "recommendedValue": true, + "explanation": "Enable this feature to by default create default policy names for imported roles that depend on the stack the role is in." + }, + "@aws-cdk/aws-s3:serverAccessLogsUseBucketPolicy": { + "userValue": true, + "recommendedValue": true, + "explanation": "Use S3 Bucket Policy instead of ACLs for Server Access Logging" + }, + "@aws-cdk/aws-route53-patters:useCertificate": { + "userValue": true, + "recommendedValue": true, + "explanation": "Use the official `Certificate` resource instead of `DnsValidatedCertificate`" + }, + "@aws-cdk/customresources:installLatestAwsSdkDefault": { + "userValue": false, + "recommendedValue": false, + "explanation": "Whether to install the latest SDK by default in AwsCustomResource" + }, + "@aws-cdk/aws-rds:databaseProxyUniqueResourceName": { + "userValue": true, + "recommendedValue": true, + "explanation": "Use unique resource name for Database Proxy" + }, + "@aws-cdk/aws-codedeploy:removeAlarmsFromDeploymentGroup": { + "userValue": true, + "recommendedValue": true, + "explanation": "Remove CloudWatch alarms from deployment group" + }, + "@aws-cdk/aws-apigateway:authorizerChangeDeploymentLogicalId": { + "userValue": true, + "recommendedValue": true, + "explanation": "Include authorizer configuration in the calculation of the API deployment logical ID." + }, + "@aws-cdk/aws-ec2:launchTemplateDefaultUserData": { + "userValue": true, + "recommendedValue": true, + "explanation": "Define user data for a launch template by default when a machine image is provided." + }, + "@aws-cdk/aws-secretsmanager:useAttachedSecretResourcePolicyForSecretTargetAttachments": { + "userValue": true, + "recommendedValue": true, + "explanation": "SecretTargetAttachments uses the ResourcePolicy of the attached Secret." + }, + "@aws-cdk/aws-redshift:columnId": { + "userValue": true, + "recommendedValue": true, + "explanation": "Whether to use an ID to track Redshift column changes" + }, + "@aws-cdk/aws-stepfunctions-tasks:enableEmrServicePolicyV2": { + "userValue": true, + "recommendedValue": true, + "explanation": "Enable AmazonEMRServicePolicy_v2 managed policies" + }, + "@aws-cdk/aws-ec2:restrictDefaultSecurityGroup": { + "userValue": true, + "recommendedValue": true, + "explanation": "Restrict access to the VPC default security group" + }, + "@aws-cdk/aws-apigateway:requestValidatorUniqueId": { + "userValue": true, + "recommendedValue": true, + "explanation": "Generate a unique id for each RequestValidator added to a method" + }, + "@aws-cdk/aws-kms:aliasNameRef": { + "userValue": true, + "recommendedValue": true, + "explanation": "KMS Alias name and keyArn will have implicit reference to KMS Key" + }, + "@aws-cdk/aws-kms:applyImportedAliasPermissionsToPrincipal": { + "userValue": true, + "recommendedValue": true, + "explanation": "Enable grant methods on Aliases imported by name to use kms:ResourceAliases condition" + }, + "@aws-cdk/aws-autoscaling:generateLaunchTemplateInsteadOfLaunchConfig": { + "userValue": true, + "recommendedValue": true, + "explanation": "Generate a launch template when creating an AutoScalingGroup" + }, + "@aws-cdk/core:includePrefixInUniqueNameGeneration": { + "userValue": true, + "recommendedValue": true, + "explanation": "Include the stack prefix in the stack name generation process" + }, + "@aws-cdk/aws-efs:denyAnonymousAccess": { + "userValue": true, + "recommendedValue": true, + "explanation": "EFS denies anonymous clients accesses" + }, + "@aws-cdk/aws-opensearchservice:enableOpensearchMultiAzWithStandby": { + "userValue": true, + "recommendedValue": true, + "explanation": "Enables support for Multi-AZ with Standby deployment for opensearch domains" + }, + "@aws-cdk/aws-lambda-nodejs:useLatestRuntimeVersion": { + "userValue": true, + "recommendedValue": true, + "explanation": "Enables aws-lambda-nodejs.Function to use the latest available NodeJs runtime as the default" + }, + "@aws-cdk/aws-efs:mountTargetOrderInsensitiveLogicalId": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, mount targets will have a stable logicalId that is linked to the associated subnet." + }, + "@aws-cdk/aws-rds:auroraClusterChangeScopeOfInstanceParameterGroupWithEachParameters": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, a scope of InstanceParameterGroup for AuroraClusterInstance with each parameters will change." + }, + "@aws-cdk/aws-appsync:useArnForSourceApiAssociationIdentifier": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, will always use the arn for identifiers for CfnSourceApiAssociation in the GraphqlApi construct rather than id." + }, + "@aws-cdk/aws-rds:preventRenderingDeprecatedCredentials": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, creating an RDS database cluster from a snapshot will only render credentials for snapshot credentials." + }, + "@aws-cdk/aws-codepipeline-actions:useNewDefaultBranchForCodeCommitSource": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, the CodeCommit source action is using the default branch name 'main'." + }, + "@aws-cdk/aws-cloudwatch-actions:changeLambdaPermissionLogicalIdForLambdaAction": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, the logical ID of a Lambda permission for a Lambda action includes an alarm ID." + }, + "@aws-cdk/aws-codepipeline:crossAccountKeysDefaultValueToFalse": { + "userValue": true, + "recommendedValue": true, + "explanation": "Enables Pipeline to set the default value for crossAccountKeys to false." + }, + "@aws-cdk/aws-codepipeline:defaultPipelineTypeToV2": { + "userValue": true, + "recommendedValue": true, + "explanation": "Enables Pipeline to set the default pipeline type to V2." + }, + "@aws-cdk/aws-kms:reduceCrossAccountRegionPolicyScope": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, IAM Policy created from KMS key grant will reduce the resource scope to this key only." + }, + "@aws-cdk/pipelines:reduceAssetRoleTrustScope": { + "recommendedValue": true, + "explanation": "Remove the root account principal from PipelineAssetsFileRole trust policy", + "unconfiguredBehavesLike": { + "v2": true + } + }, + "@aws-cdk/aws-eks:nodegroupNameAttribute": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, nodegroupName attribute of the provisioned EKS NodeGroup will not have the cluster name prefix." + }, + "@aws-cdk/aws-ec2:ebsDefaultGp3Volume": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, the default volume type of the EBS volume will be GP3" + }, + "@aws-cdk/aws-ecs:removeDefaultDeploymentAlarm": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, remove default deployment alarm settings" + }, + "@aws-cdk/custom-resources:logApiResponseDataPropertyTrueDefault": { + "userValue": false, + "recommendedValue": false, + "explanation": "When enabled, the custom resource used for `AwsCustomResource` will configure the `logApiResponseData` property as true by default" + }, + "@aws-cdk/aws-s3:keepNotificationInImportedBucket": { + "userValue": false, + "recommendedValue": false, + "explanation": "When enabled, Adding notifications to a bucket in the current stack will not remove notification from imported stack." + }, + "@aws-cdk/aws-stepfunctions-tasks:useNewS3UriParametersForBedrockInvokeModelTask": { + "recommendedValue": true, + "explanation": "When enabled, use new props for S3 URI field in task definition of state machine for bedrock invoke model.", + "unconfiguredBehavesLike": { + "v2": true + } + }, + "@aws-cdk/core:explicitStackTags": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, stack tags need to be assigned explicitly on a Stack." + }, + "@aws-cdk/aws-ecs:enableImdsBlockingDeprecatedFeature": { + "userValue": false, + "recommendedValue": false, + "explanation": "When set to true along with canContainersAccessInstanceRole=false in ECS cluster, new updated commands will be added to UserData to block container accessing IMDS. **Applicable to Linux only. IMPORTANT: See [details.](#aws-cdkaws-ecsenableImdsBlockingDeprecatedFeature)**" + }, + "@aws-cdk/aws-ecs:disableEcsImdsBlocking": { + "userValue": true, + "recommendedValue": true, + "explanation": "When set to true, CDK synth will throw exception if canContainersAccessInstanceRole is false. **IMPORTANT: See [details.](#aws-cdkaws-ecsdisableEcsImdsBlocking)**" + }, + "@aws-cdk/aws-ecs:reduceEc2FargateCloudWatchPermissions": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, we will only grant the necessary permissions when users specify cloudwatch log group through logConfiguration" + }, + "@aws-cdk/aws-dynamodb:resourcePolicyPerReplica": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled will allow you to specify a resource policy per replica, and not copy the source table policy to all replicas" + }, + "@aws-cdk/aws-ec2:ec2SumTImeoutEnabled": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, initOptions.timeout and resourceSignalTimeout values will be summed together." + }, + "@aws-cdk/aws-appsync:appSyncGraphQLAPIScopeLambdaPermission": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, a Lambda authorizer Permission created when using GraphqlApi will be properly scoped with a SourceArn." + }, + "@aws-cdk/aws-rds:setCorrectValueForDatabaseInstanceReadReplicaInstanceResourceId": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, the value of property `instanceResourceId` in construct `DatabaseInstanceReadReplica` will be set to the correct value which is `DbiResourceId` instead of currently `DbInstanceArn`" + }, + "@aws-cdk/core:cfnIncludeRejectComplexResourceUpdateCreatePolicyIntrinsics": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, CFN templates added with `cfn-include` will error if the template contains Resource Update or Create policies with CFN Intrinsics that include non-primitive values." + }, + "@aws-cdk/aws-lambda-nodejs:sdkV3ExcludeSmithyPackages": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, both `@aws-sdk` and `@smithy` packages will be excluded from the Lambda Node.js 18.x runtime to prevent version mismatches in bundled applications." + }, + "@aws-cdk/aws-stepfunctions-tasks:fixRunEcsTaskPolicy": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, the resource of IAM Run Ecs policy generated by SFN EcsRunTask will reference the definition, instead of constructing ARN." + }, + "@aws-cdk/aws-ec2:bastionHostUseAmazonLinux2023ByDefault": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, the BastionHost construct will use the latest Amazon Linux 2023 AMI, instead of Amazon Linux 2." + }, + "@aws-cdk/core:aspectStabilization": { + "recommendedValue": true, + "explanation": "When enabled, a stabilization loop will be run when invoking Aspects during synthesis.", + "unconfiguredBehavesLike": { + "v2": true + } + }, + "@aws-cdk/aws-route53-targets:userPoolDomainNameMethodWithoutCustomResource": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, use a new method for DNS Name of user pool domain target without creating a custom resource." + }, + "@aws-cdk/aws-elasticloadbalancingV2:albDualstackWithoutPublicIpv4SecurityGroupRulesDefault": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, the default security group ingress rules will allow IPv6 ingress from anywhere" + }, + "@aws-cdk/aws-iam:oidcRejectUnauthorizedConnections": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, the default behaviour of OIDC provider will reject unauthorized connections" + }, + "@aws-cdk/core:enableAdditionalMetadataCollection": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, CDK will expand the scope of usage data collected to better inform CDK development and improve communication for security concerns and emerging issues." + }, + "@aws-cdk/aws-lambda:createNewPoliciesWithAddToRolePolicy": { + "userValue": false, + "recommendedValue": false, + "explanation": "[Deprecated] When enabled, Lambda will create new inline policies with AddToRolePolicy instead of adding to the Default Policy Statement" + }, + "@aws-cdk/aws-s3:setUniqueReplicationRoleName": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, CDK will automatically generate a unique role name that is used for s3 object replication." + }, + "@aws-cdk/pipelines:reduceStageRoleTrustScope": { + "recommendedValue": true, + "explanation": "Remove the root account principal from Stage addActions trust policy", + "unconfiguredBehavesLike": { + "v2": true + } + }, + "@aws-cdk/aws-events:requireEventBusPolicySid": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, grantPutEventsTo() will use resource policies with Statement IDs for service principals." + }, + "@aws-cdk/core:aspectPrioritiesMutating": { + "userValue": true, + "recommendedValue": true, + "explanation": "When set to true, Aspects added by the construct library on your behalf will be given a priority of MUTATING." + }, + "@aws-cdk/aws-dynamodb:retainTableReplica": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, table replica will be default to the removal policy of source table unless specified otherwise." + }, + "@aws-cdk/cognito:logUserPoolClientSecretValue": { + "recommendedValue": false, + "explanation": "When disabled, the value of the user pool client secret will not be logged in the custom resource lambda function logs." + }, + "@aws-cdk/pipelines:reduceCrossAccountActionRoleTrustScope": { + "recommendedValue": true, + "explanation": "When enabled, scopes down the trust policy for the cross-account action role", + "unconfiguredBehavesLike": { + "v2": true + } + }, + "@aws-cdk/aws-stepfunctions:useDistributedMapResultWriterV2": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, the resultWriterV2 property of DistributedMap will be used insted of resultWriter" + }, + "@aws-cdk/s3-notifications:addS3TrustKeyPolicyForSnsSubscriptions": { + "userValue": true, + "recommendedValue": true, + "explanation": "Add an S3 trust policy to a KMS key resource policy for SNS subscriptions." + }, + "@aws-cdk/aws-ec2:requirePrivateSubnetsForEgressOnlyInternetGateway": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, the EgressOnlyGateway resource is only created if private subnets are defined in the dual-stack VPC." + }, + "@aws-cdk/aws-ec2-alpha:useResourceIdForVpcV2Migration": { + "recommendedValue": false, + "explanation": "When enabled, use resource IDs for VPC V2 migration" + }, + "@aws-cdk/aws-s3:publicAccessBlockedByDefault": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, setting any combination of options for BlockPublicAccess will automatically set true for any options not defined." + }, + "@aws-cdk/aws-lambda:useCdkManagedLogGroup": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, CDK creates and manages loggroup for the lambda function" + } + } + } } - } + }, + "minimumCliVersion": "2.1025.0" } \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-cloudfront/test/integ.distribution-function-runtime.js.snapshot/tree.json b/packages/@aws-cdk-testing/framework-integ/test/aws-cloudfront/test/integ.distribution-function-runtime.js.snapshot/tree.json index c96bc88fb7f51..4e0a956c0ae09 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-cloudfront/test/integ.distribution-function-runtime.js.snapshot/tree.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-cloudfront/test/integ.distribution-function-runtime.js.snapshot/tree.json @@ -1,265 +1 @@ -{ - "version": "tree-0.1", - "tree": { - "id": "App", - "path": "", - "children": { - "integ-distribution-function": { - "id": "integ-distribution-function", - "path": "integ-distribution-function", - "children": { - "FunctionRequest": { - "id": "FunctionRequest", - "path": "integ-distribution-function/FunctionRequest", - "children": { - "Resource": { - "id": "Resource", - "path": "integ-distribution-function/FunctionRequest/Resource", - "attributes": { - "aws:cdk:cloudformation:type": "AWS::CloudFront::Function", - "aws:cdk:cloudformation:props": { - "autoPublish": true, - "functionCode": "function handler(event) { return event.request }", - "functionConfig": { - "comment": "eu-west-1integ-distributinFunctionRequest8E65DEEB", - "runtime": "cloudfront-js-1.0" - }, - "name": "eu-west-1integ-distributinFunctionRequest8E65DEEB" - } - }, - "constructInfo": { - "fqn": "aws-cdk-lib.aws_cloudfront.CfnFunction", - "version": "0.0.0" - } - } - }, - "constructInfo": { - "fqn": "aws-cdk-lib.aws_cloudfront.Function", - "version": "0.0.0" - } - }, - "FunctionResponse": { - "id": "FunctionResponse", - "path": "integ-distribution-function/FunctionResponse", - "children": { - "Resource": { - "id": "Resource", - "path": "integ-distribution-function/FunctionResponse/Resource", - "attributes": { - "aws:cdk:cloudformation:type": "AWS::CloudFront::Function", - "aws:cdk:cloudformation:props": { - "autoPublish": true, - "functionCode": "function handler(event) { return event.response }", - "functionConfig": { - "comment": "eu-west-1integ-distributiFunctionResponseDD4BADA1", - "runtime": "cloudfront-js-2.0" - }, - "name": "eu-west-1integ-distributiFunctionResponseDD4BADA1" - } - }, - "constructInfo": { - "fqn": "aws-cdk-lib.aws_cloudfront.CfnFunction", - "version": "0.0.0" - } - } - }, - "constructInfo": { - "fqn": "aws-cdk-lib.aws_cloudfront.Function", - "version": "0.0.0" - } - }, - "Dist": { - "id": "Dist", - "path": "integ-distribution-function/Dist", - "children": { - "Origin1": { - "id": "Origin1", - "path": "integ-distribution-function/Dist/Origin1", - "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.3.0" - } - }, - "Resource": { - "id": "Resource", - "path": "integ-distribution-function/Dist/Resource", - "attributes": { - "aws:cdk:cloudformation:type": "AWS::CloudFront::Distribution", - "aws:cdk:cloudformation:props": { - "distributionConfig": { - "enabled": true, - "origins": [ - { - "domainName": "www.example.com", - "id": "integdistributionfunctionDistOrigin1D1E9DF17", - "customOriginConfig": { - "originProtocolPolicy": "https-only" - } - } - ], - "defaultCacheBehavior": { - "pathPattern": "*", - "targetOriginId": "integdistributionfunctionDistOrigin1D1E9DF17", - "cachePolicyId": "4135ea2d-6df8-44a3-9df3-4b5a84be39ad", - "compress": true, - "viewerProtocolPolicy": "allow-all", - "functionAssociations": [ - { - "functionArn": { - "Fn::GetAtt": [ - "FunctionRequest95528B2F", - "FunctionARN" - ] - }, - "eventType": "viewer-request" - }, - { - "functionArn": { - "Fn::GetAtt": [ - "FunctionResponse4EF2D1D3", - "FunctionARN" - ] - }, - "eventType": "viewer-response" - } - ] - }, - "httpVersion": "http2", - "ipv6Enabled": true - } - } - }, - "constructInfo": { - "fqn": "aws-cdk-lib.aws_cloudfront.CfnDistribution", - "version": "0.0.0" - } - } - }, - "constructInfo": { - "fqn": "aws-cdk-lib.aws_cloudfront.Distribution", - "version": "0.0.0" - } - }, - "RequestFunctionArn": { - "id": "RequestFunctionArn", - "path": "integ-distribution-function/RequestFunctionArn", - "constructInfo": { - "fqn": "aws-cdk-lib.CfnOutput", - "version": "0.0.0" - } - }, - "RequestFunctionStage": { - "id": "RequestFunctionStage", - "path": "integ-distribution-function/RequestFunctionStage", - "constructInfo": { - "fqn": "aws-cdk-lib.CfnOutput", - "version": "0.0.0" - } - }, - "ResponseFunctionArn": { - "id": "ResponseFunctionArn", - "path": "integ-distribution-function/ResponseFunctionArn", - "constructInfo": { - "fqn": "aws-cdk-lib.CfnOutput", - "version": "0.0.0" - } - }, - "ResponseFunctionStage": { - "id": "ResponseFunctionStage", - "path": "integ-distribution-function/ResponseFunctionStage", - "constructInfo": { - "fqn": "aws-cdk-lib.CfnOutput", - "version": "0.0.0" - } - }, - "BootstrapVersion": { - "id": "BootstrapVersion", - "path": "integ-distribution-function/BootstrapVersion", - "constructInfo": { - "fqn": "aws-cdk-lib.CfnParameter", - "version": "0.0.0" - } - }, - "CheckBootstrapVersion": { - "id": "CheckBootstrapVersion", - "path": "integ-distribution-function/CheckBootstrapVersion", - "constructInfo": { - "fqn": "aws-cdk-lib.CfnRule", - "version": "0.0.0" - } - } - }, - "constructInfo": { - "fqn": "aws-cdk-lib.Stack", - "version": "0.0.0" - } - }, - "CF2Runtime": { - "id": "CF2Runtime", - "path": "CF2Runtime", - "children": { - "DefaultTest": { - "id": "DefaultTest", - "path": "CF2Runtime/DefaultTest", - "children": { - "Default": { - "id": "Default", - "path": "CF2Runtime/DefaultTest/Default", - "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.3.0" - } - }, - "DeployAssert": { - "id": "DeployAssert", - "path": "CF2Runtime/DefaultTest/DeployAssert", - "children": { - "BootstrapVersion": { - "id": "BootstrapVersion", - "path": "CF2Runtime/DefaultTest/DeployAssert/BootstrapVersion", - "constructInfo": { - "fqn": "aws-cdk-lib.CfnParameter", - "version": "0.0.0" - } - }, - "CheckBootstrapVersion": { - "id": "CheckBootstrapVersion", - "path": "CF2Runtime/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.3.0" - } - } - }, - "constructInfo": { - "fqn": "aws-cdk-lib.App", - "version": "0.0.0" - } - } -} \ No newline at end of file +{"version":"tree-0.1","tree":{"id":"App","path":"","constructInfo":{"fqn":"aws-cdk-lib.App","version":"0.0.0"},"children":{"integ-distribution-function":{"id":"integ-distribution-function","path":"integ-distribution-function","constructInfo":{"fqn":"aws-cdk-lib.Stack","version":"0.0.0"},"children":{"FunctionRequest":{"id":"FunctionRequest","path":"integ-distribution-function/FunctionRequest","constructInfo":{"fqn":"aws-cdk-lib.aws_cloudfront.Function","version":"0.0.0","metadata":[{"code":"*","runtime":"*"}]},"children":{"Resource":{"id":"Resource","path":"integ-distribution-function/FunctionRequest/Resource","constructInfo":{"fqn":"aws-cdk-lib.aws_cloudfront.CfnFunction","version":"0.0.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::CloudFront::Function","aws:cdk:cloudformation:props":{"autoPublish":true,"functionCode":"function handler(event) { return event.request }","functionConfig":{"comment":"eu-west-1integ-distributinFunctionRequest8E65DEEB","runtime":"cloudfront-js-1.0"},"name":"eu-west-1integ-distributinFunctionRequest8E65DEEB"}}}}},"FunctionResponse":{"id":"FunctionResponse","path":"integ-distribution-function/FunctionResponse","constructInfo":{"fqn":"aws-cdk-lib.aws_cloudfront.Function","version":"0.0.0","metadata":[{"code":"*","runtime":"*"}]},"children":{"Resource":{"id":"Resource","path":"integ-distribution-function/FunctionResponse/Resource","constructInfo":{"fqn":"aws-cdk-lib.aws_cloudfront.CfnFunction","version":"0.0.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::CloudFront::Function","aws:cdk:cloudformation:props":{"autoPublish":true,"functionCode":"function handler(event) { return event.response }","functionConfig":{"comment":"eu-west-1integ-distributiFunctionResponseDD4BADA1","runtime":"cloudfront-js-2.0"},"name":"eu-west-1integ-distributiFunctionResponseDD4BADA1"}}}}},"Dist":{"id":"Dist","path":"integ-distribution-function/Dist","constructInfo":{"fqn":"aws-cdk-lib.aws_cloudfront.Distribution","version":"0.0.0","metadata":[{"defaultBehavior":{"origin":"*","cachePolicy":{"cachePolicyId":"*"},"functionAssociations":[{"function":"*","eventType":"viewer-request"},{"function":"*","eventType":"viewer-response"}]}}]},"children":{"Origin1":{"id":"Origin1","path":"integ-distribution-function/Dist/Origin1","constructInfo":{"fqn":"constructs.Construct","version":"10.4.2"}},"Resource":{"id":"Resource","path":"integ-distribution-function/Dist/Resource","constructInfo":{"fqn":"aws-cdk-lib.aws_cloudfront.CfnDistribution","version":"0.0.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::CloudFront::Distribution","aws:cdk:cloudformation:props":{"distributionConfig":{"enabled":true,"origins":[{"domainName":"www.example.com","id":"integdistributionfunctionDistOrigin1D1E9DF17","customOriginConfig":{"originProtocolPolicy":"https-only"}}],"defaultCacheBehavior":{"pathPattern":"*","targetOriginId":"integdistributionfunctionDistOrigin1D1E9DF17","cachePolicyId":"4135ea2d-6df8-44a3-9df3-4b5a84be39ad","compress":true,"viewerProtocolPolicy":"allow-all","functionAssociations":[{"functionArn":{"Ref":"FunctionRequest95528B2F"},"eventType":"viewer-request"},{"functionArn":{"Ref":"FunctionResponse4EF2D1D3"},"eventType":"viewer-response"}]},"httpVersion":"http2","ipv6Enabled":true}}}}}},"RequestFunctionArn":{"id":"RequestFunctionArn","path":"integ-distribution-function/RequestFunctionArn","constructInfo":{"fqn":"aws-cdk-lib.CfnOutput","version":"0.0.0"}},"RequestFunctionStage":{"id":"RequestFunctionStage","path":"integ-distribution-function/RequestFunctionStage","constructInfo":{"fqn":"aws-cdk-lib.CfnOutput","version":"0.0.0"}},"ResponseFunctionArn":{"id":"ResponseFunctionArn","path":"integ-distribution-function/ResponseFunctionArn","constructInfo":{"fqn":"aws-cdk-lib.CfnOutput","version":"0.0.0"}},"ResponseFunctionStage":{"id":"ResponseFunctionStage","path":"integ-distribution-function/ResponseFunctionStage","constructInfo":{"fqn":"aws-cdk-lib.CfnOutput","version":"0.0.0"}},"BootstrapVersion":{"id":"BootstrapVersion","path":"integ-distribution-function/BootstrapVersion","constructInfo":{"fqn":"aws-cdk-lib.CfnParameter","version":"0.0.0"}},"CheckBootstrapVersion":{"id":"CheckBootstrapVersion","path":"integ-distribution-function/CheckBootstrapVersion","constructInfo":{"fqn":"aws-cdk-lib.CfnRule","version":"0.0.0"}}}},"CF2Runtime":{"id":"CF2Runtime","path":"CF2Runtime","constructInfo":{"fqn":"@aws-cdk/integ-tests-alpha.IntegTest","version":"0.0.0"},"children":{"DefaultTest":{"id":"DefaultTest","path":"CF2Runtime/DefaultTest","constructInfo":{"fqn":"@aws-cdk/integ-tests-alpha.IntegTestCase","version":"0.0.0"},"children":{"Default":{"id":"Default","path":"CF2Runtime/DefaultTest/Default","constructInfo":{"fqn":"constructs.Construct","version":"10.4.2"}},"DeployAssert":{"id":"DeployAssert","path":"CF2Runtime/DefaultTest/DeployAssert","constructInfo":{"fqn":"aws-cdk-lib.Stack","version":"0.0.0"},"children":{"BootstrapVersion":{"id":"BootstrapVersion","path":"CF2Runtime/DefaultTest/DeployAssert/BootstrapVersion","constructInfo":{"fqn":"aws-cdk-lib.CfnParameter","version":"0.0.0"}},"CheckBootstrapVersion":{"id":"CheckBootstrapVersion","path":"CF2Runtime/DefaultTest/DeployAssert/CheckBootstrapVersion","constructInfo":{"fqn":"aws-cdk-lib.CfnRule","version":"0.0.0"}}}}}}}},"Tree":{"id":"Tree","path":"Tree","constructInfo":{"fqn":"constructs.Construct","version":"10.4.2"}}}}} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-cloudfront/test/integ.distribution-function.js.snapshot/DistributionFunctionDefaultTestDeployAssert9D427BC1.assets.json b/packages/@aws-cdk-testing/framework-integ/test/aws-cloudfront/test/integ.distribution-function.js.snapshot/DistributionFunctionDefaultTestDeployAssert9D427BC1.assets.json index cc09fc7467d09..33940fe33e118 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-cloudfront/test/integ.distribution-function.js.snapshot/DistributionFunctionDefaultTestDeployAssert9D427BC1.assets.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-cloudfront/test/integ.distribution-function.js.snapshot/DistributionFunctionDefaultTestDeployAssert9D427BC1.assets.json @@ -1,13 +1,14 @@ { - "version": "36.0.0", + "version": "48.0.0", "files": { "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22": { + "displayName": "DistributionFunctionDefaultTestDeployAssert9D427BC1 Template", "source": { "path": "DistributionFunctionDefaultTestDeployAssert9D427BC1.template.json", "packaging": "file" }, "destinations": { - "current_account-current_region": { + "current_account-current_region-d8d86b35": { "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}" diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-cloudfront/test/integ.distribution-function.js.snapshot/cdk.out b/packages/@aws-cdk-testing/framework-integ/test/aws-cloudfront/test/integ.distribution-function.js.snapshot/cdk.out index 1f0068d32659a..523a9aac37cbf 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-cloudfront/test/integ.distribution-function.js.snapshot/cdk.out +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-cloudfront/test/integ.distribution-function.js.snapshot/cdk.out @@ -1 +1 @@ -{"version":"36.0.0"} \ No newline at end of file +{"version":"48.0.0"} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-cloudfront/test/integ.distribution-function.js.snapshot/integ-distribution-function.assets.json b/packages/@aws-cdk-testing/framework-integ/test/aws-cloudfront/test/integ.distribution-function.js.snapshot/integ-distribution-function.assets.json index 7a38a75b120a0..10f82ea6bdab5 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-cloudfront/test/integ.distribution-function.js.snapshot/integ-distribution-function.assets.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-cloudfront/test/integ.distribution-function.js.snapshot/integ-distribution-function.assets.json @@ -1,15 +1,16 @@ { - "version": "36.0.0", + "version": "48.0.0", "files": { - "63f7f5881d7bc5ce53462b42e6c05e558d335432add6b977c8d03e31d2129fa3": { + "6bd62c570264de8125de1033fa877af2aae477e1df850f4b95eb21d74392cde4": { + "displayName": "integ-distribution-function Template", "source": { "path": "integ-distribution-function.template.json", "packaging": "file" }, "destinations": { - "current_account-eu-west-1": { + "current_account-eu-west-1-086938f6": { "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-eu-west-1", - "objectKey": "63f7f5881d7bc5ce53462b42e6c05e558d335432add6b977c8d03e31d2129fa3.json", + "objectKey": "6bd62c570264de8125de1033fa877af2aae477e1df850f4b95eb21d74392cde4.json", "region": "eu-west-1", "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-eu-west-1" } diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-cloudfront/test/integ.distribution-function.js.snapshot/integ-distribution-function.template.json b/packages/@aws-cdk-testing/framework-integ/test/aws-cloudfront/test/integ.distribution-function.js.snapshot/integ-distribution-function.template.json index a0f2c3d71b793..68ad544c3b92f 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-cloudfront/test/integ.distribution-function.js.snapshot/integ-distribution-function.template.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-cloudfront/test/integ.distribution-function.js.snapshot/integ-distribution-function.template.json @@ -23,10 +23,7 @@ { "EventType": "viewer-request", "FunctionARN": { - "Fn::GetAtt": [ - "Function76856677", - "FunctionARN" - ] + "Ref": "Function76856677" } } ], diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-cloudfront/test/integ.distribution-function.js.snapshot/integ.json b/packages/@aws-cdk-testing/framework-integ/test/aws-cloudfront/test/integ.distribution-function.js.snapshot/integ.json index 6fd5eae8f7765..11e14b33e2606 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-cloudfront/test/integ.distribution-function.js.snapshot/integ.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-cloudfront/test/integ.distribution-function.js.snapshot/integ.json @@ -1,5 +1,5 @@ { - "version": "36.0.0", + "version": "48.0.0", "testCases": { "DistributionFunction/DefaultTest": { "stacks": [ @@ -8,5 +8,6 @@ "assertionStack": "DistributionFunction/DefaultTest/DeployAssert", "assertionStackName": "DistributionFunctionDefaultTestDeployAssert9D427BC1" } - } + }, + "minimumCliVersion": "2.1024.0" } \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-cloudfront/test/integ.distribution-function.js.snapshot/manifest.json b/packages/@aws-cdk-testing/framework-integ/test/aws-cloudfront/test/integ.distribution-function.js.snapshot/manifest.json index 93dabc383879f..282e8adaca11c 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-cloudfront/test/integ.distribution-function.js.snapshot/manifest.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-cloudfront/test/integ.distribution-function.js.snapshot/manifest.json @@ -1,5 +1,5 @@ { - "version": "36.0.0", + "version": "48.0.0", "artifacts": { "integ-distribution-function.assets": { "type": "cdk:asset-manifest", @@ -18,7 +18,7 @@ "validateOnSynth": false, "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-eu-west-1", "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-eu-west-1", - "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-eu-west-1/63f7f5881d7bc5ce53462b42e6c05e558d335432add6b977c8d03e31d2129fa3.json", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-eu-west-1/6bd62c570264de8125de1033fa877af2aae477e1df850f4b95eb21d74392cde4.json", "requiresBootstrapStackVersion": 6, "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", "additionalDependencies": [ @@ -34,12 +34,39 @@ "integ-distribution-function.assets" ], "metadata": { + "/integ-distribution-function/Function": [ + { + "type": "aws:cdk:analytics:construct", + "data": { + "code": "*" + } + } + ], "/integ-distribution-function/Function/Resource": [ { "type": "aws:cdk:logicalId", "data": "Function76856677" } ], + "/integ-distribution-function/Dist": [ + { + "type": "aws:cdk:analytics:construct", + "data": { + "defaultBehavior": { + "origin": "*", + "cachePolicy": { + "cachePolicyId": "*" + }, + "functionAssociations": [ + { + "function": "*", + "eventType": "viewer-request" + } + ] + } + } + } + ], "/integ-distribution-function/Dist/Resource": [ { "type": "aws:cdk:logicalId", @@ -126,6 +153,481 @@ "properties": { "file": "tree.json" } + }, + "aws-cdk-lib/feature-flag-report": { + "type": "cdk:feature-flag-report", + "properties": { + "module": "aws-cdk-lib", + "flags": { + "@aws-cdk/aws-signer:signingProfileNamePassedToCfn": { + "recommendedValue": true, + "explanation": "Pass signingProfileName to CfnSigningProfile" + }, + "@aws-cdk/core:newStyleStackSynthesis": { + "recommendedValue": true, + "explanation": "Switch to new stack synthesis method which enables CI/CD", + "unconfiguredBehavesLike": { + "v2": true + } + }, + "@aws-cdk/core:stackRelativeExports": { + "recommendedValue": true, + "explanation": "Name exports based on the construct paths relative to the stack, rather than the global construct path", + "unconfiguredBehavesLike": { + "v2": true + } + }, + "@aws-cdk/aws-rds:lowercaseDbIdentifier": { + "recommendedValue": true, + "explanation": "Force lowercasing of RDS Cluster names in CDK", + "unconfiguredBehavesLike": { + "v2": true + } + }, + "@aws-cdk/aws-apigateway:usagePlanKeyOrderInsensitiveId": { + "recommendedValue": true, + "explanation": "Allow adding/removing multiple UsagePlanKeys independently", + "unconfiguredBehavesLike": { + "v2": true + } + }, + "@aws-cdk/aws-lambda:recognizeVersionProps": { + "recommendedValue": true, + "explanation": "Enable this feature flag to opt in to the updated logical id calculation for Lambda Version created using the `fn.currentVersion`.", + "unconfiguredBehavesLike": { + "v2": true + } + }, + "@aws-cdk/aws-lambda:recognizeLayerVersion": { + "userValue": true, + "recommendedValue": true, + "explanation": "Enable this feature flag to opt in to the updated logical id calculation for Lambda Version created using the `fn.currentVersion`." + }, + "@aws-cdk/aws-cloudfront:defaultSecurityPolicyTLSv1.2_2021": { + "recommendedValue": true, + "explanation": "Enable this feature flag to have cloudfront distributions use the security policy TLSv1.2_2021 by default.", + "unconfiguredBehavesLike": { + "v2": true + } + }, + "@aws-cdk/core:checkSecretUsage": { + "userValue": true, + "recommendedValue": true, + "explanation": "Enable this flag to make it impossible to accidentally use SecretValues in unsafe locations" + }, + "@aws-cdk/core:target-partitions": { + "recommendedValue": [ + "aws", + "aws-cn" + ], + "explanation": "What regions to include in lookup tables of environment agnostic stacks" + }, + "@aws-cdk-containers/ecs-service-extensions:enableDefaultLogDriver": { + "userValue": true, + "recommendedValue": true, + "explanation": "ECS extensions will automatically add an `awslogs` driver if no logging is specified" + }, + "@aws-cdk/aws-ec2:uniqueImdsv2TemplateName": { + "userValue": true, + "recommendedValue": true, + "explanation": "Enable this feature flag to have Launch Templates generated by the `InstanceRequireImdsv2Aspect` use unique names." + }, + "@aws-cdk/aws-ecs:arnFormatIncludesClusterName": { + "userValue": true, + "recommendedValue": true, + "explanation": "ARN format used by ECS. In the new ARN format, the cluster name is part of the resource ID." + }, + "@aws-cdk/aws-iam:minimizePolicies": { + "userValue": true, + "recommendedValue": true, + "explanation": "Minimize IAM policies by combining Statements" + }, + "@aws-cdk/core:validateSnapshotRemovalPolicy": { + "userValue": true, + "recommendedValue": true, + "explanation": "Error on snapshot removal policies on resources that do not support it." + }, + "@aws-cdk/aws-codepipeline:crossAccountKeyAliasStackSafeResourceName": { + "userValue": true, + "recommendedValue": true, + "explanation": "Generate key aliases that include the stack name" + }, + "@aws-cdk/aws-s3:createDefaultLoggingPolicy": { + "userValue": true, + "recommendedValue": true, + "explanation": "Enable this feature flag to create an S3 bucket policy by default in cases where an AWS service would automatically create the Policy if one does not exist." + }, + "@aws-cdk/aws-sns-subscriptions:restrictSqsDescryption": { + "userValue": true, + "recommendedValue": true, + "explanation": "Restrict KMS key policy for encrypted Queues a bit more" + }, + "@aws-cdk/aws-apigateway:disableCloudWatchRole": { + "userValue": true, + "recommendedValue": true, + "explanation": "Make default CloudWatch Role behavior safe for multiple API Gateways in one environment" + }, + "@aws-cdk/core:enablePartitionLiterals": { + "userValue": true, + "recommendedValue": true, + "explanation": "Make ARNs concrete if AWS partition is known" + }, + "@aws-cdk/aws-events:eventsTargetQueueSameAccount": { + "userValue": true, + "recommendedValue": true, + "explanation": "Event Rules may only push to encrypted SQS queues in the same account" + }, + "@aws-cdk/aws-ecs:disableExplicitDeploymentControllerForCircuitBreaker": { + "userValue": true, + "recommendedValue": true, + "explanation": "Avoid setting the \"ECS\" deployment controller when adding a circuit breaker" + }, + "@aws-cdk/aws-iam:importedRoleStackSafeDefaultPolicyName": { + "userValue": true, + "recommendedValue": true, + "explanation": "Enable this feature to by default create default policy names for imported roles that depend on the stack the role is in." + }, + "@aws-cdk/aws-s3:serverAccessLogsUseBucketPolicy": { + "userValue": true, + "recommendedValue": true, + "explanation": "Use S3 Bucket Policy instead of ACLs for Server Access Logging" + }, + "@aws-cdk/aws-route53-patters:useCertificate": { + "userValue": true, + "recommendedValue": true, + "explanation": "Use the official `Certificate` resource instead of `DnsValidatedCertificate`" + }, + "@aws-cdk/customresources:installLatestAwsSdkDefault": { + "userValue": false, + "recommendedValue": false, + "explanation": "Whether to install the latest SDK by default in AwsCustomResource" + }, + "@aws-cdk/aws-rds:databaseProxyUniqueResourceName": { + "userValue": true, + "recommendedValue": true, + "explanation": "Use unique resource name for Database Proxy" + }, + "@aws-cdk/aws-codedeploy:removeAlarmsFromDeploymentGroup": { + "userValue": true, + "recommendedValue": true, + "explanation": "Remove CloudWatch alarms from deployment group" + }, + "@aws-cdk/aws-apigateway:authorizerChangeDeploymentLogicalId": { + "userValue": true, + "recommendedValue": true, + "explanation": "Include authorizer configuration in the calculation of the API deployment logical ID." + }, + "@aws-cdk/aws-ec2:launchTemplateDefaultUserData": { + "userValue": true, + "recommendedValue": true, + "explanation": "Define user data for a launch template by default when a machine image is provided." + }, + "@aws-cdk/aws-secretsmanager:useAttachedSecretResourcePolicyForSecretTargetAttachments": { + "userValue": true, + "recommendedValue": true, + "explanation": "SecretTargetAttachments uses the ResourcePolicy of the attached Secret." + }, + "@aws-cdk/aws-redshift:columnId": { + "userValue": true, + "recommendedValue": true, + "explanation": "Whether to use an ID to track Redshift column changes" + }, + "@aws-cdk/aws-stepfunctions-tasks:enableEmrServicePolicyV2": { + "userValue": true, + "recommendedValue": true, + "explanation": "Enable AmazonEMRServicePolicy_v2 managed policies" + }, + "@aws-cdk/aws-ec2:restrictDefaultSecurityGroup": { + "userValue": true, + "recommendedValue": true, + "explanation": "Restrict access to the VPC default security group" + }, + "@aws-cdk/aws-apigateway:requestValidatorUniqueId": { + "userValue": true, + "recommendedValue": true, + "explanation": "Generate a unique id for each RequestValidator added to a method" + }, + "@aws-cdk/aws-kms:aliasNameRef": { + "userValue": true, + "recommendedValue": true, + "explanation": "KMS Alias name and keyArn will have implicit reference to KMS Key" + }, + "@aws-cdk/aws-kms:applyImportedAliasPermissionsToPrincipal": { + "userValue": true, + "recommendedValue": true, + "explanation": "Enable grant methods on Aliases imported by name to use kms:ResourceAliases condition" + }, + "@aws-cdk/aws-autoscaling:generateLaunchTemplateInsteadOfLaunchConfig": { + "userValue": true, + "recommendedValue": true, + "explanation": "Generate a launch template when creating an AutoScalingGroup" + }, + "@aws-cdk/core:includePrefixInUniqueNameGeneration": { + "userValue": true, + "recommendedValue": true, + "explanation": "Include the stack prefix in the stack name generation process" + }, + "@aws-cdk/aws-efs:denyAnonymousAccess": { + "userValue": true, + "recommendedValue": true, + "explanation": "EFS denies anonymous clients accesses" + }, + "@aws-cdk/aws-opensearchservice:enableOpensearchMultiAzWithStandby": { + "userValue": true, + "recommendedValue": true, + "explanation": "Enables support for Multi-AZ with Standby deployment for opensearch domains" + }, + "@aws-cdk/aws-lambda-nodejs:useLatestRuntimeVersion": { + "userValue": true, + "recommendedValue": true, + "explanation": "Enables aws-lambda-nodejs.Function to use the latest available NodeJs runtime as the default" + }, + "@aws-cdk/aws-efs:mountTargetOrderInsensitiveLogicalId": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, mount targets will have a stable logicalId that is linked to the associated subnet." + }, + "@aws-cdk/aws-rds:auroraClusterChangeScopeOfInstanceParameterGroupWithEachParameters": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, a scope of InstanceParameterGroup for AuroraClusterInstance with each parameters will change." + }, + "@aws-cdk/aws-appsync:useArnForSourceApiAssociationIdentifier": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, will always use the arn for identifiers for CfnSourceApiAssociation in the GraphqlApi construct rather than id." + }, + "@aws-cdk/aws-rds:preventRenderingDeprecatedCredentials": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, creating an RDS database cluster from a snapshot will only render credentials for snapshot credentials." + }, + "@aws-cdk/aws-codepipeline-actions:useNewDefaultBranchForCodeCommitSource": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, the CodeCommit source action is using the default branch name 'main'." + }, + "@aws-cdk/aws-cloudwatch-actions:changeLambdaPermissionLogicalIdForLambdaAction": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, the logical ID of a Lambda permission for a Lambda action includes an alarm ID." + }, + "@aws-cdk/aws-codepipeline:crossAccountKeysDefaultValueToFalse": { + "userValue": true, + "recommendedValue": true, + "explanation": "Enables Pipeline to set the default value for crossAccountKeys to false." + }, + "@aws-cdk/aws-codepipeline:defaultPipelineTypeToV2": { + "userValue": true, + "recommendedValue": true, + "explanation": "Enables Pipeline to set the default pipeline type to V2." + }, + "@aws-cdk/aws-kms:reduceCrossAccountRegionPolicyScope": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, IAM Policy created from KMS key grant will reduce the resource scope to this key only." + }, + "@aws-cdk/pipelines:reduceAssetRoleTrustScope": { + "recommendedValue": true, + "explanation": "Remove the root account principal from PipelineAssetsFileRole trust policy", + "unconfiguredBehavesLike": { + "v2": true + } + }, + "@aws-cdk/aws-eks:nodegroupNameAttribute": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, nodegroupName attribute of the provisioned EKS NodeGroup will not have the cluster name prefix." + }, + "@aws-cdk/aws-ec2:ebsDefaultGp3Volume": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, the default volume type of the EBS volume will be GP3" + }, + "@aws-cdk/aws-ecs:removeDefaultDeploymentAlarm": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, remove default deployment alarm settings" + }, + "@aws-cdk/custom-resources:logApiResponseDataPropertyTrueDefault": { + "userValue": false, + "recommendedValue": false, + "explanation": "When enabled, the custom resource used for `AwsCustomResource` will configure the `logApiResponseData` property as true by default" + }, + "@aws-cdk/aws-s3:keepNotificationInImportedBucket": { + "userValue": false, + "recommendedValue": false, + "explanation": "When enabled, Adding notifications to a bucket in the current stack will not remove notification from imported stack." + }, + "@aws-cdk/aws-stepfunctions-tasks:useNewS3UriParametersForBedrockInvokeModelTask": { + "recommendedValue": true, + "explanation": "When enabled, use new props for S3 URI field in task definition of state machine for bedrock invoke model.", + "unconfiguredBehavesLike": { + "v2": true + } + }, + "@aws-cdk/core:explicitStackTags": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, stack tags need to be assigned explicitly on a Stack." + }, + "@aws-cdk/aws-ecs:enableImdsBlockingDeprecatedFeature": { + "userValue": false, + "recommendedValue": false, + "explanation": "When set to true along with canContainersAccessInstanceRole=false in ECS cluster, new updated commands will be added to UserData to block container accessing IMDS. **Applicable to Linux only. IMPORTANT: See [details.](#aws-cdkaws-ecsenableImdsBlockingDeprecatedFeature)**" + }, + "@aws-cdk/aws-ecs:disableEcsImdsBlocking": { + "userValue": true, + "recommendedValue": true, + "explanation": "When set to true, CDK synth will throw exception if canContainersAccessInstanceRole is false. **IMPORTANT: See [details.](#aws-cdkaws-ecsdisableEcsImdsBlocking)**" + }, + "@aws-cdk/aws-ecs:reduceEc2FargateCloudWatchPermissions": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, we will only grant the necessary permissions when users specify cloudwatch log group through logConfiguration" + }, + "@aws-cdk/aws-dynamodb:resourcePolicyPerReplica": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled will allow you to specify a resource policy per replica, and not copy the source table policy to all replicas" + }, + "@aws-cdk/aws-ec2:ec2SumTImeoutEnabled": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, initOptions.timeout and resourceSignalTimeout values will be summed together." + }, + "@aws-cdk/aws-appsync:appSyncGraphQLAPIScopeLambdaPermission": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, a Lambda authorizer Permission created when using GraphqlApi will be properly scoped with a SourceArn." + }, + "@aws-cdk/aws-rds:setCorrectValueForDatabaseInstanceReadReplicaInstanceResourceId": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, the value of property `instanceResourceId` in construct `DatabaseInstanceReadReplica` will be set to the correct value which is `DbiResourceId` instead of currently `DbInstanceArn`" + }, + "@aws-cdk/core:cfnIncludeRejectComplexResourceUpdateCreatePolicyIntrinsics": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, CFN templates added with `cfn-include` will error if the template contains Resource Update or Create policies with CFN Intrinsics that include non-primitive values." + }, + "@aws-cdk/aws-lambda-nodejs:sdkV3ExcludeSmithyPackages": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, both `@aws-sdk` and `@smithy` packages will be excluded from the Lambda Node.js 18.x runtime to prevent version mismatches in bundled applications." + }, + "@aws-cdk/aws-stepfunctions-tasks:fixRunEcsTaskPolicy": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, the resource of IAM Run Ecs policy generated by SFN EcsRunTask will reference the definition, instead of constructing ARN." + }, + "@aws-cdk/aws-ec2:bastionHostUseAmazonLinux2023ByDefault": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, the BastionHost construct will use the latest Amazon Linux 2023 AMI, instead of Amazon Linux 2." + }, + "@aws-cdk/core:aspectStabilization": { + "recommendedValue": true, + "explanation": "When enabled, a stabilization loop will be run when invoking Aspects during synthesis.", + "unconfiguredBehavesLike": { + "v2": true + } + }, + "@aws-cdk/aws-route53-targets:userPoolDomainNameMethodWithoutCustomResource": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, use a new method for DNS Name of user pool domain target without creating a custom resource." + }, + "@aws-cdk/aws-elasticloadbalancingV2:albDualstackWithoutPublicIpv4SecurityGroupRulesDefault": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, the default security group ingress rules will allow IPv6 ingress from anywhere" + }, + "@aws-cdk/aws-iam:oidcRejectUnauthorizedConnections": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, the default behaviour of OIDC provider will reject unauthorized connections" + }, + "@aws-cdk/core:enableAdditionalMetadataCollection": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, CDK will expand the scope of usage data collected to better inform CDK development and improve communication for security concerns and emerging issues." + }, + "@aws-cdk/aws-lambda:createNewPoliciesWithAddToRolePolicy": { + "userValue": false, + "recommendedValue": false, + "explanation": "[Deprecated] When enabled, Lambda will create new inline policies with AddToRolePolicy instead of adding to the Default Policy Statement" + }, + "@aws-cdk/aws-s3:setUniqueReplicationRoleName": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, CDK will automatically generate a unique role name that is used for s3 object replication." + }, + "@aws-cdk/pipelines:reduceStageRoleTrustScope": { + "recommendedValue": true, + "explanation": "Remove the root account principal from Stage addActions trust policy", + "unconfiguredBehavesLike": { + "v2": true + } + }, + "@aws-cdk/aws-events:requireEventBusPolicySid": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, grantPutEventsTo() will use resource policies with Statement IDs for service principals." + }, + "@aws-cdk/core:aspectPrioritiesMutating": { + "userValue": true, + "recommendedValue": true, + "explanation": "When set to true, Aspects added by the construct library on your behalf will be given a priority of MUTATING." + }, + "@aws-cdk/aws-dynamodb:retainTableReplica": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, table replica will be default to the removal policy of source table unless specified otherwise." + }, + "@aws-cdk/cognito:logUserPoolClientSecretValue": { + "recommendedValue": false, + "explanation": "When disabled, the value of the user pool client secret will not be logged in the custom resource lambda function logs." + }, + "@aws-cdk/pipelines:reduceCrossAccountActionRoleTrustScope": { + "recommendedValue": true, + "explanation": "When enabled, scopes down the trust policy for the cross-account action role", + "unconfiguredBehavesLike": { + "v2": true + } + }, + "@aws-cdk/aws-stepfunctions:useDistributedMapResultWriterV2": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, the resultWriterV2 property of DistributedMap will be used insted of resultWriter" + }, + "@aws-cdk/s3-notifications:addS3TrustKeyPolicyForSnsSubscriptions": { + "userValue": true, + "recommendedValue": true, + "explanation": "Add an S3 trust policy to a KMS key resource policy for SNS subscriptions." + }, + "@aws-cdk/aws-ec2:requirePrivateSubnetsForEgressOnlyInternetGateway": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, the EgressOnlyGateway resource is only created if private subnets are defined in the dual-stack VPC." + }, + "@aws-cdk/aws-ec2-alpha:useResourceIdForVpcV2Migration": { + "recommendedValue": false, + "explanation": "When enabled, use resource IDs for VPC V2 migration" + }, + "@aws-cdk/aws-s3:publicAccessBlockedByDefault": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, setting any combination of options for BlockPublicAccess will automatically set true for any options not defined." + }, + "@aws-cdk/aws-lambda:useCdkManagedLogGroup": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, CDK creates and manages loggroup for the lambda function" + } + } + } } - } + }, + "minimumCliVersion": "2.1025.0" } \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-cloudfront/test/integ.distribution-function.js.snapshot/tree.json b/packages/@aws-cdk-testing/framework-integ/test/aws-cloudfront/test/integ.distribution-function.js.snapshot/tree.json index 33a20a46337cf..6a2d8646f77a4 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-cloudfront/test/integ.distribution-function.js.snapshot/tree.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-cloudfront/test/integ.distribution-function.js.snapshot/tree.json @@ -1,210 +1 @@ -{ - "version": "tree-0.1", - "tree": { - "id": "App", - "path": "", - "children": { - "integ-distribution-function": { - "id": "integ-distribution-function", - "path": "integ-distribution-function", - "children": { - "Function": { - "id": "Function", - "path": "integ-distribution-function/Function", - "children": { - "Resource": { - "id": "Resource", - "path": "integ-distribution-function/Function/Resource", - "attributes": { - "aws:cdk:cloudformation:type": "AWS::CloudFront::Function", - "aws:cdk:cloudformation:props": { - "autoPublish": true, - "functionCode": "function handler(event) { return event.request }", - "functionConfig": { - "comment": "eu-west-1integdistributionfunctionFunctionDCD62A02", - "runtime": "cloudfront-js-1.0" - }, - "name": "eu-west-1integdistributionfunctionFunctionDCD62A02" - } - }, - "constructInfo": { - "fqn": "aws-cdk-lib.aws_cloudfront.CfnFunction", - "version": "0.0.0" - } - } - }, - "constructInfo": { - "fqn": "aws-cdk-lib.aws_cloudfront.Function", - "version": "0.0.0" - } - }, - "Dist": { - "id": "Dist", - "path": "integ-distribution-function/Dist", - "children": { - "Origin1": { - "id": "Origin1", - "path": "integ-distribution-function/Dist/Origin1", - "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.3.0" - } - }, - "Resource": { - "id": "Resource", - "path": "integ-distribution-function/Dist/Resource", - "attributes": { - "aws:cdk:cloudformation:type": "AWS::CloudFront::Distribution", - "aws:cdk:cloudformation:props": { - "distributionConfig": { - "enabled": true, - "origins": [ - { - "domainName": "www.example.com", - "id": "integdistributionfunctionDistOrigin1D1E9DF17", - "customOriginConfig": { - "originProtocolPolicy": "https-only" - } - } - ], - "defaultCacheBehavior": { - "pathPattern": "*", - "targetOriginId": "integdistributionfunctionDistOrigin1D1E9DF17", - "cachePolicyId": "4135ea2d-6df8-44a3-9df3-4b5a84be39ad", - "compress": true, - "viewerProtocolPolicy": "allow-all", - "functionAssociations": [ - { - "functionArn": { - "Fn::GetAtt": [ - "Function76856677", - "FunctionARN" - ] - }, - "eventType": "viewer-request" - } - ] - }, - "httpVersion": "http2", - "ipv6Enabled": true - } - } - }, - "constructInfo": { - "fqn": "aws-cdk-lib.aws_cloudfront.CfnDistribution", - "version": "0.0.0" - } - } - }, - "constructInfo": { - "fqn": "aws-cdk-lib.aws_cloudfront.Distribution", - "version": "0.0.0" - } - }, - "FunctionArn": { - "id": "FunctionArn", - "path": "integ-distribution-function/FunctionArn", - "constructInfo": { - "fqn": "aws-cdk-lib.CfnOutput", - "version": "0.0.0" - } - }, - "FunctionStage": { - "id": "FunctionStage", - "path": "integ-distribution-function/FunctionStage", - "constructInfo": { - "fqn": "aws-cdk-lib.CfnOutput", - "version": "0.0.0" - } - }, - "BootstrapVersion": { - "id": "BootstrapVersion", - "path": "integ-distribution-function/BootstrapVersion", - "constructInfo": { - "fqn": "aws-cdk-lib.CfnParameter", - "version": "0.0.0" - } - }, - "CheckBootstrapVersion": { - "id": "CheckBootstrapVersion", - "path": "integ-distribution-function/CheckBootstrapVersion", - "constructInfo": { - "fqn": "aws-cdk-lib.CfnRule", - "version": "0.0.0" - } - } - }, - "constructInfo": { - "fqn": "aws-cdk-lib.Stack", - "version": "0.0.0" - } - }, - "DistributionFunction": { - "id": "DistributionFunction", - "path": "DistributionFunction", - "children": { - "DefaultTest": { - "id": "DefaultTest", - "path": "DistributionFunction/DefaultTest", - "children": { - "Default": { - "id": "Default", - "path": "DistributionFunction/DefaultTest/Default", - "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.3.0" - } - }, - "DeployAssert": { - "id": "DeployAssert", - "path": "DistributionFunction/DefaultTest/DeployAssert", - "children": { - "BootstrapVersion": { - "id": "BootstrapVersion", - "path": "DistributionFunction/DefaultTest/DeployAssert/BootstrapVersion", - "constructInfo": { - "fqn": "aws-cdk-lib.CfnParameter", - "version": "0.0.0" - } - }, - "CheckBootstrapVersion": { - "id": "CheckBootstrapVersion", - "path": "DistributionFunction/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.3.0" - } - } - }, - "constructInfo": { - "fqn": "aws-cdk-lib.App", - "version": "0.0.0" - } - } -} \ No newline at end of file +{"version":"tree-0.1","tree":{"id":"App","path":"","constructInfo":{"fqn":"aws-cdk-lib.App","version":"0.0.0"},"children":{"integ-distribution-function":{"id":"integ-distribution-function","path":"integ-distribution-function","constructInfo":{"fqn":"aws-cdk-lib.Stack","version":"0.0.0"},"children":{"Function":{"id":"Function","path":"integ-distribution-function/Function","constructInfo":{"fqn":"aws-cdk-lib.aws_cloudfront.Function","version":"0.0.0","metadata":[{"code":"*"}]},"children":{"Resource":{"id":"Resource","path":"integ-distribution-function/Function/Resource","constructInfo":{"fqn":"aws-cdk-lib.aws_cloudfront.CfnFunction","version":"0.0.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::CloudFront::Function","aws:cdk:cloudformation:props":{"autoPublish":true,"functionCode":"function handler(event) { return event.request }","functionConfig":{"comment":"eu-west-1integdistributionfunctionFunctionDCD62A02","runtime":"cloudfront-js-1.0"},"name":"eu-west-1integdistributionfunctionFunctionDCD62A02"}}}}},"Dist":{"id":"Dist","path":"integ-distribution-function/Dist","constructInfo":{"fqn":"aws-cdk-lib.aws_cloudfront.Distribution","version":"0.0.0","metadata":[{"defaultBehavior":{"origin":"*","cachePolicy":{"cachePolicyId":"*"},"functionAssociations":[{"function":"*","eventType":"viewer-request"}]}}]},"children":{"Origin1":{"id":"Origin1","path":"integ-distribution-function/Dist/Origin1","constructInfo":{"fqn":"constructs.Construct","version":"10.4.2"}},"Resource":{"id":"Resource","path":"integ-distribution-function/Dist/Resource","constructInfo":{"fqn":"aws-cdk-lib.aws_cloudfront.CfnDistribution","version":"0.0.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::CloudFront::Distribution","aws:cdk:cloudformation:props":{"distributionConfig":{"enabled":true,"origins":[{"domainName":"www.example.com","id":"integdistributionfunctionDistOrigin1D1E9DF17","customOriginConfig":{"originProtocolPolicy":"https-only"}}],"defaultCacheBehavior":{"pathPattern":"*","targetOriginId":"integdistributionfunctionDistOrigin1D1E9DF17","cachePolicyId":"4135ea2d-6df8-44a3-9df3-4b5a84be39ad","compress":true,"viewerProtocolPolicy":"allow-all","functionAssociations":[{"functionArn":{"Ref":"Function76856677"},"eventType":"viewer-request"}]},"httpVersion":"http2","ipv6Enabled":true}}}}}},"FunctionArn":{"id":"FunctionArn","path":"integ-distribution-function/FunctionArn","constructInfo":{"fqn":"aws-cdk-lib.CfnOutput","version":"0.0.0"}},"FunctionStage":{"id":"FunctionStage","path":"integ-distribution-function/FunctionStage","constructInfo":{"fqn":"aws-cdk-lib.CfnOutput","version":"0.0.0"}},"BootstrapVersion":{"id":"BootstrapVersion","path":"integ-distribution-function/BootstrapVersion","constructInfo":{"fqn":"aws-cdk-lib.CfnParameter","version":"0.0.0"}},"CheckBootstrapVersion":{"id":"CheckBootstrapVersion","path":"integ-distribution-function/CheckBootstrapVersion","constructInfo":{"fqn":"aws-cdk-lib.CfnRule","version":"0.0.0"}}}},"DistributionFunction":{"id":"DistributionFunction","path":"DistributionFunction","constructInfo":{"fqn":"@aws-cdk/integ-tests-alpha.IntegTest","version":"0.0.0"},"children":{"DefaultTest":{"id":"DefaultTest","path":"DistributionFunction/DefaultTest","constructInfo":{"fqn":"@aws-cdk/integ-tests-alpha.IntegTestCase","version":"0.0.0"},"children":{"Default":{"id":"Default","path":"DistributionFunction/DefaultTest/Default","constructInfo":{"fqn":"constructs.Construct","version":"10.4.2"}},"DeployAssert":{"id":"DeployAssert","path":"DistributionFunction/DefaultTest/DeployAssert","constructInfo":{"fqn":"aws-cdk-lib.Stack","version":"0.0.0"},"children":{"BootstrapVersion":{"id":"BootstrapVersion","path":"DistributionFunction/DefaultTest/DeployAssert/BootstrapVersion","constructInfo":{"fqn":"aws-cdk-lib.CfnParameter","version":"0.0.0"}},"CheckBootstrapVersion":{"id":"CheckBootstrapVersion","path":"DistributionFunction/DefaultTest/DeployAssert/CheckBootstrapVersion","constructInfo":{"fqn":"aws-cdk-lib.CfnRule","version":"0.0.0"}}}}}}}},"Tree":{"id":"Tree","path":"Tree","constructInfo":{"fqn":"constructs.Construct","version":"10.4.2"}}}}} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-codepipeline-actions/test/integ.pipeline-elastic-beanstalk-deploy.ts b/packages/@aws-cdk-testing/framework-integ/test/aws-codepipeline-actions/test/integ.pipeline-elastic-beanstalk-deploy.ts index ef7e08a3123b5..57610933c508b 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-codepipeline-actions/test/integ.pipeline-elastic-beanstalk-deploy.ts +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-codepipeline-actions/test/integ.pipeline-elastic-beanstalk-deploy.ts @@ -2,11 +2,13 @@ import * as path from 'path'; import * as codepipeline from 'aws-cdk-lib/aws-codepipeline'; import * as elasticbeanstalk from 'aws-cdk-lib/aws-elasticbeanstalk'; import * as iam from 'aws-cdk-lib/aws-iam'; +import { IManagedPolicy, ManagedPolicyReference } from 'aws-cdk-lib/aws-iam'; import * as s3 from 'aws-cdk-lib/aws-s3'; import * as deploy from 'aws-cdk-lib/aws-s3-deployment'; -import { App, Fn, RemovalPolicy, Stack } from 'aws-cdk-lib'; +import { App, Fn, RemovalPolicy, Stack, UnscopedValidationError } from 'aws-cdk-lib'; import * as integ from '@aws-cdk/integ-tests-alpha'; import * as cpactions from 'aws-cdk-lib/aws-codepipeline-actions'; +import { Node } from 'constructs'; /** * To validate that the deployment actually succeeds, perform the following actions: @@ -43,16 +45,26 @@ const artifact = new deploy.BucketDeployment(stack, 'DeployApp', { extract: false, }); +function makePolicy(arn: string): IManagedPolicy { + return { + managedPolicyArn: arn, + get managedPolicyRef(): ManagedPolicyReference { + return { + policyArn: this.managedPolicyArn, + }; + }, + get node(): Node { + throw new UnscopedValidationError('The result of fromAwsManagedPolicyName can not be used in this API'); + }, + }; +} + const serviceRole = new iam.Role(stack, 'service-role', { roleName: 'codepipeline-elasticbeanstalk-action-test-serivce-role', assumedBy: new iam.ServicePrincipal('elasticbeanstalk.amazonaws.com'), managedPolicies: [ - { - managedPolicyArn: 'arn:aws:iam::aws:policy/service-role/AWSElasticBeanstalkEnhancedHealth', - }, - { - managedPolicyArn: 'arn:aws:iam::aws:policy/AWSElasticBeanstalkManagedUpdatesCustomerRolePolicy', - }, + makePolicy('arn:aws:iam::aws:policy/service-role/AWSElasticBeanstalkEnhancedHealth'), + makePolicy('arn:aws:iam::aws:policy/AWSElasticBeanstalkManagedUpdatesCustomerRolePolicy'), ], }); @@ -60,15 +72,9 @@ const instanceProfileRole = new iam.Role(stack, 'instance-profile-role', { roleName: 'codepipeline-elasticbeanstalk-action-test-instance-profile-role', assumedBy: new iam.ServicePrincipal('ec2.amazonaws.com'), managedPolicies: [ - { - managedPolicyArn: 'arn:aws:iam::aws:policy/AWSElasticBeanstalkWebTier', - }, - { - managedPolicyArn: 'arn:aws:iam::aws:policy/AWSElasticBeanstalkMulticontainerDocker', - }, - { - managedPolicyArn: 'arn:aws:iam::aws:policy/AWSElasticBeanstalkWorkerTier', - }, + makePolicy('arn:aws:iam::aws:policy/AWSElasticBeanstalkWebTier'), + makePolicy('arn:aws:iam::aws:policy/AWSElasticBeanstalkMulticontainerDocker'), + makePolicy('arn:aws:iam::aws:policy/AWSElasticBeanstalkWorkerTier'), ], }); diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-stepfunctions-tasks/test/databrew/integ.start-job-run.ts b/packages/@aws-cdk-testing/framework-integ/test/aws-stepfunctions-tasks/test/databrew/integ.start-job-run.ts index 4d4634c39ea84..3bba2d26fae1e 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-stepfunctions-tasks/test/databrew/integ.start-job-run.ts +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-stepfunctions-tasks/test/databrew/integ.start-job-run.ts @@ -1,9 +1,12 @@ import * as databrew from 'aws-cdk-lib/aws-databrew'; import * as iam from 'aws-cdk-lib/aws-iam'; +import { IManagedPolicy, ManagedPolicyReference } from 'aws-cdk-lib/aws-iam'; import * as s3 from 'aws-cdk-lib/aws-s3'; import * as sfn from 'aws-cdk-lib/aws-stepfunctions'; import * as cdk from 'aws-cdk-lib'; +import { UnscopedValidationError } from 'aws-cdk-lib'; import { GlueDataBrewStartJobRun } from 'aws-cdk-lib/aws-stepfunctions-tasks'; +import { Node } from 'constructs'; /* * Stack verification steps: @@ -11,6 +14,20 @@ import { GlueDataBrewStartJobRun } from 'aws-cdk-lib/aws-stepfunctions-tasks'; * * aws stepfunctions describe-execution --execution-arn : should return status as SUCCEEDED */ +function makePolicy(arn: string): IManagedPolicy { + return { + managedPolicyArn: arn, + get managedPolicyRef(): ManagedPolicyReference { + return { + policyArn: this.managedPolicyArn, + }; + }, + get node(): Node { + throw new UnscopedValidationError('The result of fromAwsManagedPolicyName can not be used in this API'); + }, + }; +} + class GlueDataBrewJobStack extends cdk.Stack { constructor(scope: cdk.App, id: string, props: cdk.StackProps = {}) { super(scope, id, props); @@ -22,9 +39,9 @@ class GlueDataBrewJobStack extends cdk.Stack { }); const role = new iam.Role(this, 'DataBrew Role', { - managedPolicies: [{ - managedPolicyArn: 'arn:aws:iam::aws:policy/service-role/AWSGlueDataBrewServiceRole', - }], + managedPolicies: [ + makePolicy('arn:aws:iam::aws:policy/service-role/AWSGlueDataBrewServiceRole'), + ], path: '/', assumedBy: new iam.ServicePrincipal('databrew.amazonaws.com'), inlinePolicies: { diff --git a/packages/@aws-cdk/aws-apprunner-alpha/lib/service.ts b/packages/@aws-cdk/aws-apprunner-alpha/lib/service.ts index 9fcdf6c146a9c..7c43fffa25ee6 100644 --- a/packages/@aws-cdk/aws-apprunner-alpha/lib/service.ts +++ b/packages/@aws-cdk/aws-apprunner-alpha/lib/service.ts @@ -761,7 +761,7 @@ export interface ServiceProps { * * @default - Use an AWS managed key */ - readonly kmsKey?: kms.IKey; + readonly kmsKey?: kms.IKeyRef; /** * The IP address type for your incoming public network configuration. @@ -1338,7 +1338,7 @@ export class Service extends cdk.Resource implements IService, iam.IGrantable { undefined, }, encryptionConfiguration: this.props.kmsKey ? { - kmsKey: this.props.kmsKey.keyArn, + kmsKey: this.props.kmsKey.keyRef.keyArn, } : undefined, autoScalingConfigurationArn: this.props.autoScalingConfiguration?.autoScalingConfigurationArn, networkConfiguration: { diff --git a/packages/@aws-cdk/aws-bedrock-alpha/bedrock/agents/api-schema.ts b/packages/@aws-cdk/aws-bedrock-alpha/bedrock/agents/api-schema.ts index b64d139327142..1ff418c0123c9 100644 --- a/packages/@aws-cdk/aws-bedrock-alpha/bedrock/agents/api-schema.ts +++ b/packages/@aws-cdk/aws-bedrock-alpha/bedrock/agents/api-schema.ts @@ -1,7 +1,7 @@ import { Construct } from 'constructs'; import * as s3_assets from 'aws-cdk-lib/aws-s3-assets'; import { CfnAgent } from 'aws-cdk-lib/aws-bedrock'; -import { IBucket, Location } from 'aws-cdk-lib/aws-s3'; +import { IBucketRef, Location } from 'aws-cdk-lib/aws-s3'; import { ActionGroupSchema } from './schema-base'; /** @@ -42,9 +42,9 @@ export abstract class ApiSchema extends ActionGroupSchema { * @param bucket - the bucket containing the local file containing the OpenAPI schema for the action group * @param objectKey - object key in the bucket */ - public static fromS3File(bucket: IBucket, objectKey: string): S3ApiSchema { + public static fromS3File(bucket: IBucketRef, objectKey: string): S3ApiSchema { return new S3ApiSchema({ - bucketName: bucket.bucketName, + bucketName: bucket.bucketRef.bucketName, objectKey: objectKey, }); } diff --git a/packages/@aws-cdk/aws-codestar-alpha/lib/github-repository.ts b/packages/@aws-cdk/aws-codestar-alpha/lib/github-repository.ts index 73d54342034a2..c9cdd7c5db22f 100644 --- a/packages/@aws-cdk/aws-codestar-alpha/lib/github-repository.ts +++ b/packages/@aws-cdk/aws-codestar-alpha/lib/github-repository.ts @@ -43,7 +43,7 @@ export interface GitHubRepositoryProps { /** * The name of the Amazon S3 bucket that contains the ZIP file with the content to be committed to the new repository */ - readonly contentsBucket: s3.IBucket; + readonly contentsBucket: s3.IBucketRef; /** * The S3 object key or file name for the ZIP file @@ -103,7 +103,7 @@ export class GitHubRepository extends cdk.Resource implements IGitHubRepository repositoryAccessToken: props.accessToken.unsafeUnwrap(), // Safe usage code: { s3: { - bucket: props.contentsBucket.bucketName, + bucket: props.contentsBucket.bucketRef.bucketName, key: props.contentsKey, objectVersion: props.contentsS3Version, }, diff --git a/packages/@aws-cdk/aws-ec2-alpha/lib/subnet-v2.ts b/packages/@aws-cdk/aws-ec2-alpha/lib/subnet-v2.ts index 08ac4b2f42002..264f816f7f5a4 100644 --- a/packages/@aws-cdk/aws-ec2-alpha/lib/subnet-v2.ts +++ b/packages/@aws-cdk/aws-ec2-alpha/lib/subnet-v2.ts @@ -1,11 +1,21 @@ -import { Resource, Names, Lazy, Tags, Token, ValidationError, UnscopedValidationError } from 'aws-cdk-lib'; -import { CfnSubnet, CfnSubnetRouteTableAssociation, INetworkAcl, IRouteTable, ISubnet, NetworkAcl, SubnetNetworkAclAssociation, SubnetType } from 'aws-cdk-lib/aws-ec2'; +import { Lazy, Names, Resource, Tags, Token, UnscopedValidationError, ValidationError } from 'aws-cdk-lib'; +import { + CfnSubnet, + CfnSubnetRouteTableAssociation, + INetworkAcl, + IRouteTable, + ISubnet, + NetworkAcl, + SubnetNetworkAclAssociation, + SubnetType, +} from 'aws-cdk-lib/aws-ec2'; import { Construct, DependencyGroup, IDependable } from 'constructs'; import { IVpcV2 } from './vpc-v2-base'; import { CidrBlock, CidrBlockIpv6, defaultSubnetName } from './util'; import { RouteTable } from './route'; import { addConstructMetadata, MethodMetadata } from 'aws-cdk-lib/core/lib/metadata-resource'; import { propertyInjectable } from 'aws-cdk-lib/core/lib/prop-injectable'; +import { SubnetReference } from 'aws-cdk-lib/aws-ec2/lib/ec2.generated'; /** * Interface to define subnet CIDR @@ -194,6 +204,12 @@ export class SubnetV2 extends Resource implements ISubnetV2 { */ public readonly routeTable: IRouteTable = { routeTableId: attrs.routeTableId! }; + public get subnetRef(): SubnetReference { + return { + subnetId: this.subnetId, + }; + } + /** * Associate a Network ACL with this subnet * Required here since it is implemented in the ISubnetV2 @@ -245,6 +261,12 @@ export class SubnetV2 extends Resource implements ISubnetV2 { */ public readonly ipv6CidrBlock?: string; + public get subnetRef(): SubnetReference { + return { + subnetId: this.subnetId, + }; + } + /** * The type of subnet (public or private) that this subnet represents. * @attribute SubnetType diff --git a/packages/@aws-cdk/aws-ec2-alpha/lib/util.ts b/packages/@aws-cdk/aws-ec2-alpha/lib/util.ts index 214a42ca84394..71869ba13649e 100644 --- a/packages/@aws-cdk/aws-ec2-alpha/lib/util.ts +++ b/packages/@aws-cdk/aws-ec2-alpha/lib/util.ts @@ -1,6 +1,6 @@ /* eslint no-bitwise: ["error", { "allow": ["~", "|", "<<", "&"] }] */ -import { ISubnet, SubnetType } from 'aws-cdk-lib/aws-ec2'; +import { ISubnet, ISubnetRef, SubnetType } from 'aws-cdk-lib/aws-ec2'; /** * The default names for every subnet type @@ -22,7 +22,7 @@ export function defaultSubnetName(type: SubnetType) { * * All subnet names look like NAME <> "Subnet" <> INDEX */ -export function subnetGroupNameFromConstructId(subnet: ISubnet) { +export function subnetGroupNameFromConstructId(subnet: ISubnetRef) { return subnet.node.id.replace(/Subnet\d+$/, ''); } diff --git a/packages/@aws-cdk/aws-ec2-alpha/lib/vpc-v2-base.ts b/packages/@aws-cdk/aws-ec2-alpha/lib/vpc-v2-base.ts index 2b5fd5877ceba..cd361285acff3 100644 --- a/packages/@aws-cdk/aws-ec2-alpha/lib/vpc-v2-base.ts +++ b/packages/@aws-cdk/aws-ec2-alpha/lib/vpc-v2-base.ts @@ -1,11 +1,45 @@ -import { Aws, Resource, Annotations, ValidationError } from 'aws-cdk-lib'; -import { IVpc, ISubnet, SubnetSelection, SelectedSubnets, EnableVpnGatewayOptions, VpnGateway, VpnConnectionType, CfnVPCGatewayAttachment, CfnVPNGatewayRoutePropagation, VpnConnectionOptions, VpnConnection, ClientVpnEndpointOptions, ClientVpnEndpoint, InterfaceVpcEndpointOptions, InterfaceVpcEndpoint, GatewayVpcEndpointOptions, GatewayVpcEndpoint, FlowLogOptions, FlowLog, FlowLogResourceType, SubnetType, SubnetFilter } from 'aws-cdk-lib/aws-ec2'; +import { Annotations, Aws, Resource, ValidationError } from 'aws-cdk-lib'; +import { + CfnVPCGatewayAttachment, + CfnVPNGatewayRoutePropagation, + ClientVpnEndpoint, + ClientVpnEndpointOptions, + EnableVpnGatewayOptions, + FlowLog, + FlowLogOptions, + FlowLogResourceType, + GatewayVpcEndpoint, + GatewayVpcEndpointOptions, + InterfaceVpcEndpoint, + InterfaceVpcEndpointOptions, + ISubnet, + IVpc, + SelectedSubnets, + SubnetFilter, + SubnetSelection, + SubnetType, + VpnConnection, + VpnConnectionOptions, + VpnConnectionType, + VpnGateway, +} from 'aws-cdk-lib/aws-ec2'; import { allRouteTableIds, flatten, subnetGroupNameFromConstructId } from './util'; -import { IDependable, Dependable, IConstruct, DependencyGroup } from 'constructs'; -import { EgressOnlyInternetGateway, InternetGateway, NatConnectivityType, NatGateway, NatGatewayOptions, Route, VPCPeeringConnection, VPCPeeringConnectionOptions, VPNGatewayV2 } from './route'; +import { Dependable, DependencyGroup, IConstruct, IDependable } from 'constructs'; +import { + EgressOnlyInternetGateway, + InternetGateway, + NatConnectivityType, + NatGateway, + NatGatewayOptions, + Route, + VPCPeeringConnection, + VPCPeeringConnectionOptions, + VPNGatewayV2, +} from './route'; import { ISubnetV2 } from './subnet-v2'; import { AccountPrincipal, Effect, PolicyStatement, Role } from 'aws-cdk-lib/aws-iam'; import { IVPCCidrBlock } from './vpc-v2'; +import { VPCReference } from 'aws-cdk-lib/aws-ec2/lib/ec2.generated'; /** * Options to define EgressOnlyInternetGateway for VPC @@ -327,6 +361,12 @@ export abstract class VpcV2Base extends Resource implements IVpcV2 { }; } + public get vpcRef(): VPCReference { + return { + vpcId: this.vpcId, + }; + } + /** * Adds a VPN Gateway to this VPC * @deprecated use enableVpnGatewayV2 for compatibility with VPCV2.Route diff --git a/packages/@aws-cdk/aws-eks-v2-alpha/lib/cluster.ts b/packages/@aws-cdk/aws-eks-v2-alpha/lib/cluster.ts index 58cc0331db0bf..7239953b77a08 100644 --- a/packages/@aws-cdk/aws-eks-v2-alpha/lib/cluster.ts +++ b/packages/@aws-cdk/aws-eks-v2-alpha/lib/cluster.ts @@ -371,7 +371,7 @@ export interface ClusterCommonOptions { * all etcd volumes used by Amazon EKS are encrypted at the disk-level * using AWS-Managed encryption keys. */ - readonly secretsEncryptionKey?: kms.IKey; + readonly secretsEncryptionKey?: kms.IKeyRef; /** * Specify which IP family is used to assign Kubernetes pod and service IP addresses. @@ -1214,7 +1214,7 @@ export class Cluster extends ClusterBase { ...(props.secretsEncryptionKey ? { encryptionConfig: [{ provider: { - keyArn: props.secretsEncryptionKey.keyArn, + keyArn: props.secretsEncryptionKey.keyRef.keyArn, }, resources: ['secrets'], }], diff --git a/packages/@aws-cdk/aws-glue-alpha/lib/security-configuration.ts b/packages/@aws-cdk/aws-glue-alpha/lib/security-configuration.ts index 392a3011ea801..c93e832b80744 100644 --- a/packages/@aws-cdk/aws-glue-alpha/lib/security-configuration.ts +++ b/packages/@aws-cdk/aws-glue-alpha/lib/security-configuration.ts @@ -76,7 +76,7 @@ export interface S3Encryption { * The KMS key to be used to encrypt the data. * @default no kms key if mode = S3_MANAGED. A key will be created if one is not provided and mode = KMS. */ - readonly kmsKey?: kms.IKey; + readonly kmsKey?: kms.IKeyRef; } /** @@ -92,7 +92,7 @@ export interface CloudWatchEncryption { * The KMS key to be used to encrypt the data. * @default A key will be created if one is not provided. */ - readonly kmsKey?: kms.IKey; + readonly kmsKey?: kms.IKeyRef; } /** @@ -108,7 +108,7 @@ export interface JobBookmarksEncryption { * The KMS key to be used to encrypt the data. * @default A key will be created if one is not provided. */ - readonly kmsKey?: kms.IKey; + readonly kmsKey?: kms.IKeyRef; } /** @@ -179,17 +179,17 @@ export class SecurityConfiguration extends cdk.Resource implements ISecurityConf /** * The KMS key used in CloudWatch encryption if it requires a kms key. */ - public readonly cloudWatchEncryptionKey?: kms.IKey; + public readonly cloudWatchEncryptionKey?: kms.IKeyRef; /** * The KMS key used in job bookmarks encryption if it requires a kms key. */ - public readonly jobBookmarksEncryptionKey?: kms.IKey; + public readonly jobBookmarksEncryptionKey?: kms.IKeyRef; /** * The KMS key used in S3 encryption if it requires a kms key. */ - public readonly s3EncryptionKey?: kms.IKey; + public readonly s3EncryptionKey?: kms.IKeyRef; constructor(scope: constructs.Construct, id: string, props: SecurityConfigurationProps = {}) { super(scope, id, { @@ -216,7 +216,7 @@ export class SecurityConfiguration extends cdk.Resource implements ISecurityConf this.cloudWatchEncryptionKey = props.cloudWatchEncryption.kmsKey || autoCreatedKmsKey; cloudWatchEncryption = { cloudWatchEncryptionMode: props.cloudWatchEncryption.mode, - kmsKeyArn: this.cloudWatchEncryptionKey?.keyArn, + kmsKeyArn: this.cloudWatchEncryptionKey?.keyRef.keyArn, }; } @@ -225,7 +225,7 @@ export class SecurityConfiguration extends cdk.Resource implements ISecurityConf this.jobBookmarksEncryptionKey = props.jobBookmarksEncryption.kmsKey || autoCreatedKmsKey; jobBookmarksEncryption = { jobBookmarksEncryptionMode: props.jobBookmarksEncryption.mode, - kmsKeyArn: this.jobBookmarksEncryptionKey?.keyArn, + kmsKeyArn: this.jobBookmarksEncryptionKey?.keyRef.keyArn, }; } @@ -237,7 +237,7 @@ export class SecurityConfiguration extends cdk.Resource implements ISecurityConf // NOTE: CloudFormations errors out if array is of length > 1. That's why the props don't expose an array s3Encryptions = [{ s3EncryptionMode: props.s3Encryption.mode, - kmsKeyArn: this.s3EncryptionKey?.keyArn, + kmsKeyArn: this.s3EncryptionKey?.keyRef.keyArn, }]; } diff --git a/packages/@aws-cdk/aws-glue-alpha/test/security-configuration.test.ts b/packages/@aws-cdk/aws-glue-alpha/test/security-configuration.test.ts index 8793c3e1a9404..15fc41b0bcc6d 100644 --- a/packages/@aws-cdk/aws-glue-alpha/test/security-configuration.test.ts +++ b/packages/@aws-cdk/aws-glue-alpha/test/security-configuration.test.ts @@ -22,7 +22,7 @@ test('a security configuration with encryption configuration requiring kms key a }, }); - expect(securityConfiguration.cloudWatchEncryptionKey?.keyArn).toEqual(keyArn); + expect(securityConfiguration.cloudWatchEncryptionKey?.keyRef.keyArn).toEqual(keyArn); expect(securityConfiguration.jobBookmarksEncryptionKey).toBeUndefined(); expect(securityConfiguration.s3EncryptionKey).toBeUndefined(); @@ -57,7 +57,7 @@ test('a security configuration with an encryption configuration requiring kms ke EncryptionConfiguration: { CloudWatchEncryption: { CloudWatchEncryptionMode: 'SSE-KMS', - KmsKeyArn: stack.resolve(securityConfiguration.cloudWatchEncryptionKey?.keyArn), + KmsKeyArn: stack.resolve(securityConfiguration.cloudWatchEncryptionKey?.keyRef.keyArn), }, }, }); @@ -82,7 +82,7 @@ test('a security configuration with all encryption configs and mixed kms key inp }); expect(securityConfiguration.cloudWatchEncryptionKey).toBeDefined(); - expect(securityConfiguration.jobBookmarksEncryptionKey?.keyArn).toEqual(keyArn); + expect(securityConfiguration.jobBookmarksEncryptionKey?.keyRef.keyArn).toEqual(keyArn); expect(securityConfiguration.s3EncryptionKey).toBeUndefined(); Template.fromStack(stack).resourceCountIs('AWS::KMS::Key', 1); @@ -93,7 +93,7 @@ test('a security configuration with all encryption configs and mixed kms key inp CloudWatchEncryption: { CloudWatchEncryptionMode: 'SSE-KMS', // auto-created kms key - KmsKeyArn: stack.resolve(securityConfiguration.cloudWatchEncryptionKey?.keyArn), + KmsKeyArn: stack.resolve(securityConfiguration.cloudWatchEncryptionKey?.keyRef.keyArn), }, JobBookmarksEncryption: { JobBookmarksEncryptionMode: 'CSE-KMS', diff --git a/packages/@aws-cdk/aws-ivs-alpha/lib/recording-configuration.ts b/packages/@aws-cdk/aws-ivs-alpha/lib/recording-configuration.ts index 2363f5f70e3e5..2ca929aa6edfd 100644 --- a/packages/@aws-cdk/aws-ivs-alpha/lib/recording-configuration.ts +++ b/packages/@aws-cdk/aws-ivs-alpha/lib/recording-configuration.ts @@ -1,5 +1,5 @@ import { CfnRecordingConfiguration } from 'aws-cdk-lib/aws-ivs'; -import { IBucket } from 'aws-cdk-lib/aws-s3'; +import { IBucketRef } from 'aws-cdk-lib/aws-s3'; import { Duration, Fn, IResource, Resource, Stack, Token } from 'aws-cdk-lib/core'; import { Construct } from 'constructs'; import { RenditionConfiguration } from './rendition-configuration'; @@ -14,7 +14,7 @@ export interface RecordingConfigurationProps { /** * S3 bucket where recorded videos will be stored. */ - readonly bucket: IBucket; + readonly bucket: IBucketRef; /** * The name of the Recording configuration. @@ -146,7 +146,7 @@ export class RecordingConfiguration extends Resource implements IRecordingConfig const resource = new CfnRecordingConfiguration(this, 'Resource', { destinationConfiguration: { s3: { - bucketName: this.props.bucket.bucketName, + bucketName: this.props.bucket.bucketRef.bucketName, }, }, name: this.props.recordingConfigurationName, diff --git a/packages/@aws-cdk/aws-location-alpha/lib/geofence-collection.ts b/packages/@aws-cdk/aws-location-alpha/lib/geofence-collection.ts index 022c8fc10d527..da3382694ce9b 100644 --- a/packages/@aws-cdk/aws-location-alpha/lib/geofence-collection.ts +++ b/packages/@aws-cdk/aws-location-alpha/lib/geofence-collection.ts @@ -53,7 +53,7 @@ export interface GeofenceCollectionProps { * @default - Use an AWS managed key * @see https://docs.aws.amazon.com/location/latest/developerguide/encryption-at-rest.html */ - readonly kmsKey?: kms.IKey; + readonly kmsKey?: kms.IKeyRef; } /** @@ -141,7 +141,7 @@ export class GeofenceCollection extends Resource implements IGeofenceCollection const geofenceCollection = new CfnGeofenceCollection(this, 'Resource', { collectionName: this.physicalName, description: props.description, - kmsKeyId: props.kmsKey?.keyArn, + kmsKeyId: props.kmsKey?.keyRef.keyArn, }); this.geofenceCollectionName = geofenceCollection.ref; diff --git a/packages/@aws-cdk/aws-location-alpha/lib/tracker.ts b/packages/@aws-cdk/aws-location-alpha/lib/tracker.ts index 9219dd678eec5..494da529f92ff 100644 --- a/packages/@aws-cdk/aws-location-alpha/lib/tracker.ts +++ b/packages/@aws-cdk/aws-location-alpha/lib/tracker.ts @@ -62,7 +62,7 @@ export interface TrackerProps { * * @default - Use an AWS managed key */ - readonly kmsKey?: kms.IKey; + readonly kmsKey?: kms.IKeyRef; /** * Whether to opt-in to the Bounding Polygon Queries feature with customer managed key @@ -210,7 +210,7 @@ export class Tracker extends Resource implements ITracker { description: props.description, eventBridgeEnabled: props.eventBridgeEnabled, kmsKeyEnableGeospatialQueries: props.kmsKeyEnableGeospatialQueries, - kmsKeyId: props.kmsKey?.keyArn, + kmsKeyId: props.kmsKey?.keyRef.keyArn, positionFiltering: props.positionFiltering, }); diff --git a/packages/@aws-cdk/aws-msk-alpha/lib/cluster.ts b/packages/@aws-cdk/aws-msk-alpha/lib/cluster.ts index 113acacc32010..bdfd7d2693dc7 100644 --- a/packages/@aws-cdk/aws-msk-alpha/lib/cluster.ts +++ b/packages/@aws-cdk/aws-msk-alpha/lib/cluster.ts @@ -181,7 +181,7 @@ export interface EbsStorageInfo { * * @default Uses AWS managed CMK (aws/kafka) */ - readonly encryptionKey?: kms.IKey; + readonly encryptionKey?: kms.IKeyRef; } /** @@ -381,7 +381,7 @@ export interface SaslAuthProps { * * @default - CMK will be created with alias msk/{clusterName}/sasl/scram */ - readonly key?: kms.IKey; + readonly key?: kms.IKeyRef; } /** @@ -522,7 +522,7 @@ export class Cluster extends ClusterBase { const encryptionAtRest = props.ebsStorageInfo?.encryptionKey ? { dataVolumeKmsKeyId: - props.ebsStorageInfo.encryptionKey.keyId, + props.ebsStorageInfo.encryptionKey.keyRef.keyId, } : undefined; // MSK will create the managed key diff --git a/packages/@aws-cdk/aws-neptune-alpha/lib/cluster.ts b/packages/@aws-cdk/aws-neptune-alpha/lib/cluster.ts index de9bf4d3a3437..b6886af2dda54 100644 --- a/packages/@aws-cdk/aws-neptune-alpha/lib/cluster.ts +++ b/packages/@aws-cdk/aws-neptune-alpha/lib/cluster.ts @@ -213,7 +213,7 @@ export interface DatabaseClusterProps { * * @default - default master key. */ - readonly kmsKey?: kms.IKey; + readonly kmsKey?: kms.IKeyRef; /** * Whether to enable storage encryption @@ -687,7 +687,7 @@ export class DatabaseCluster extends DatabaseClusterBase implements IDatabaseClu preferredBackupWindow: props.preferredBackupWindow, preferredMaintenanceWindow: props.preferredMaintenanceWindow, // Encryption - kmsKeyId: props.kmsKey?.keyArn, + kmsKeyId: props.kmsKey?.keyRef.keyArn, // CloudWatch Logs exports enableCloudwatchLogsExports: props.cloudwatchLogsExports?.map(logType => logType.value), storageEncrypted, diff --git a/packages/@aws-cdk/aws-redshift-alpha/lib/cluster.ts b/packages/@aws-cdk/aws-redshift-alpha/lib/cluster.ts index 6933c51ceabcd..7f3c389273f61 100644 --- a/packages/@aws-cdk/aws-redshift-alpha/lib/cluster.ts +++ b/packages/@aws-cdk/aws-redshift-alpha/lib/cluster.ts @@ -313,7 +313,7 @@ export interface ClusterProps { * * @default - AWS-managed key, if encryption at rest is enabled */ - readonly encryptionKey?: kms.IKey; + readonly encryptionKey?: kms.IKeyRef; /** * A preferred maintenance window day/time range. Should be specified as a range ddd:hh24:mi-ddd:hh24:mi (24H Clock UTC). @@ -692,7 +692,7 @@ export class Cluster extends ClusterBase { dbName: props.defaultDatabaseName || 'default_db', publiclyAccessible: props.publiclyAccessible || false, // Encryption - kmsKeyId: props.encryptionKey?.keyId, + kmsKeyId: props.encryptionKey?.keyRef.keyId, encrypted: props.encrypted ?? true, classic: props.classicResizing, elasticIp: props.elasticIp, diff --git a/packages/@aws-cdk/aws-s3objectlambda-alpha/lib/access-point.ts b/packages/@aws-cdk/aws-s3objectlambda-alpha/lib/access-point.ts index 314cdfc29cfeb..a257406238c8c 100644 --- a/packages/@aws-cdk/aws-s3objectlambda-alpha/lib/access-point.ts +++ b/packages/@aws-cdk/aws-s3objectlambda-alpha/lib/access-point.ts @@ -51,7 +51,7 @@ export interface AccessPointProps { /** * The bucket to which this access point belongs. */ - readonly bucket: s3.IBucket; + readonly bucket: s3.IBucketRef; /** * The Lambda function used to transform objects. @@ -224,7 +224,7 @@ export class AccessPoint extends AccessPointBase { } const supporting = new s3.CfnAccessPoint(this, 'SupportingAccessPoint', { - bucket: props.bucket.bucketName, + bucket: props.bucket.bucketRef.bucketName, }); const allowedFeatures = []; diff --git a/packages/@aws-cdk/aws-sagemaker-alpha/lib/endpoint-config.ts b/packages/@aws-cdk/aws-sagemaker-alpha/lib/endpoint-config.ts index b4c9e02521324..cd7c0c1bcfba1 100644 --- a/packages/@aws-cdk/aws-sagemaker-alpha/lib/endpoint-config.ts +++ b/packages/@aws-cdk/aws-sagemaker-alpha/lib/endpoint-config.ts @@ -136,7 +136,7 @@ export interface EndpointConfigProps { * * @default - none */ - readonly encryptionKey?: kms.IKey; + readonly encryptionKey?: kms.IKeyRef; /** * A list of instance production variants. You can always add more variants later by calling @@ -219,7 +219,7 @@ export class EndpointConfig extends cdk.Resource implements IEndpointConfig { // create the endpoint configuration resource const endpointConfig = new CfnEndpointConfig(this, 'EndpointConfig', { - kmsKeyId: (props.encryptionKey) ? props.encryptionKey.keyArn : undefined, + kmsKeyId: (props.encryptionKey) ? props.encryptionKey.keyRef.keyArn : undefined, endpointConfigName: this.physicalName, productionVariants: cdk.Lazy.any({ produce: () => this.renderInstanceProductionVariants() }), }); diff --git a/packages/aws-cdk-lib/aws-apigateway/lib/access-log.ts b/packages/aws-cdk-lib/aws-apigateway/lib/access-log.ts index ee704ec56d18a..70fd1ad609a62 100644 --- a/packages/aws-cdk-lib/aws-apigateway/lib/access-log.ts +++ b/packages/aws-cdk-lib/aws-apigateway/lib/access-log.ts @@ -1,4 +1,4 @@ -import { IStage } from './stage'; +import { IStageRef } from './apigateway.generated'; import * as firehose from '../../aws-kinesisfirehose'; import { ILogGroup } from '../../aws-logs'; import { ValidationError } from '../../core/lib/errors'; @@ -10,7 +10,7 @@ export interface IAccessLogDestination { /** * Binds this destination to the RestApi Stage. */ - bind(stage: IStage): AccessLogDestinationConfig; + bind(stage: IStageRef): AccessLogDestinationConfig; } /** @@ -33,7 +33,7 @@ export class LogGroupLogDestination implements IAccessLogDestination { /** * Binds this destination to the CloudWatch Logs. */ - public bind(_stage: IStage): AccessLogDestinationConfig { + public bind(_stage: IStageRef): AccessLogDestinationConfig { return { destinationArn: this.logGroup.logGroupArn, }; @@ -50,7 +50,7 @@ export class FirehoseLogDestination implements IAccessLogDestination { /** * Binds this destination to the Firehose delivery stream. */ - public bind(stage: IStage): AccessLogDestinationConfig { + public bind(stage: IStageRef): AccessLogDestinationConfig { if (!this.stream.deliveryStreamName?.startsWith('amazon-apigateway-')) { throw new ValidationError(`Firehose delivery stream name for access log destination must begin with 'amazon-apigateway-', got '${this.stream.deliveryStreamName}'`, stage); } diff --git a/packages/aws-cdk-lib/aws-apigateway/lib/api-definition.ts b/packages/aws-cdk-lib/aws-apigateway/lib/api-definition.ts index 291e4864fc5a4..d00ffd9101302 100644 --- a/packages/aws-cdk-lib/aws-apigateway/lib/api-definition.ts +++ b/packages/aws-cdk-lib/aws-apigateway/lib/api-definition.ts @@ -1,6 +1,5 @@ import { Construct, Node } from 'constructs'; -import { CfnRestApi } from './apigateway.generated'; -import { IRestApi } from './restapi'; +import { CfnRestApi, IRestApiRef } from './apigateway.generated'; import * as s3 from '../../aws-s3'; import * as s3_assets from '../../aws-s3-assets'; import { UnscopedValidationError, ValidationError } from '../../core/lib/errors'; @@ -89,7 +88,7 @@ export abstract class ApiDefinition { * Definition to bind to it. Specifically it's required to allow assets to add * metadata for tooling like SAM CLI to be able to find their origins. */ - public bindAfterCreate(_scope: Construct, _restApi: IRestApi) { + public bindAfterCreate(_scope: Construct, _restApi: IRestApiRef) { return; } } @@ -134,14 +133,14 @@ export interface ApiDefinitionConfig { export class S3ApiDefinition extends ApiDefinition { private bucketName: string; - constructor(bucket: s3.IBucket, private key: string, private objectVersion?: string) { + constructor(bucket: s3.IBucketRef, private key: string, private objectVersion?: string) { super(); - if (!bucket.bucketName) { + if (!bucket.bucketRef.bucketName) { throw new ValidationError('bucketName is undefined for the provided bucket', bucket); } - this.bucketName = bucket.bucketName; + this.bucketName = bucket.bucketRef.bucketName; } public bind(_scope: Construct): ApiDefinitionConfig { @@ -209,7 +208,7 @@ export class AssetApiDefinition extends ApiDefinition { }; } - public bindAfterCreate(scope: Construct, restApi: IRestApi) { + public bindAfterCreate(scope: Construct, restApi: IRestApiRef) { if (!scope.node.tryGetContext(cxapi.ASSET_RESOURCE_METADATA_ENABLED_CONTEXT)) { return; // not enabled } diff --git a/packages/aws-cdk-lib/aws-apigateway/lib/api-key.ts b/packages/aws-cdk-lib/aws-apigateway/lib/api-key.ts index 4b64d47d74e88..06155af14da80 100644 --- a/packages/aws-cdk-lib/aws-apigateway/lib/api-key.ts +++ b/packages/aws-cdk-lib/aws-apigateway/lib/api-key.ts @@ -1,5 +1,5 @@ import { Construct } from 'constructs'; -import { CfnApiKey } from './apigateway.generated'; +import { ApiKeyReference, CfnApiKey, IApiKeyRef, IStageRef } from './apigateway.generated'; import { ResourceOptions } from './resource'; import { IRestApi } from './restapi'; import { IStage } from './stage'; @@ -14,7 +14,7 @@ import { propertyInjectable } from '../../core/lib/prop-injectable'; * API keys are alphanumeric string values that you distribute to * app developer customers to grant access to your API */ -export interface IApiKey extends IResourceBase { +export interface IApiKey extends IResourceBase, IApiKeyRef { /** * The API key ID. * @attribute @@ -138,6 +138,12 @@ abstract class ApiKeyBase extends Resource implements IApiKey { resourceArns: [this.keyArn], }); } + + public get apiKeyRef(): ApiKeyReference { + return { + apiKeyId: this.keyId, + }; + } } /** @@ -201,7 +207,7 @@ export class ApiKey extends ApiKeyBase { }); } - private renderStageKeys(resources?: IRestApi[], stages?: IStage[]): CfnApiKey.StageKeyProperty[] | undefined { + private renderStageKeys(resources?: IRestApi[], stages?: IStageRef[]): CfnApiKey.StageKeyProperty[] | undefined { if (!resources && !stages) { return undefined; } @@ -222,7 +228,7 @@ export class ApiKey extends ApiKeyBase { return { restApiId, stageName }; }) : stages ? stages.map((stage => { - return { restApiId: stage.restApi.restApiId, stageName: stage.stageName }; + return { restApiId: stage.stageRef.restApiId, stageName: stage.stageRef.stageName }; })) : undefined; } } diff --git a/packages/aws-cdk-lib/aws-apigateway/lib/base-path-mapping.ts b/packages/aws-cdk-lib/aws-apigateway/lib/base-path-mapping.ts index ce7b047b2f7a9..f1138d6cfef55 100644 --- a/packages/aws-cdk-lib/aws-apigateway/lib/base-path-mapping.ts +++ b/packages/aws-cdk-lib/aws-apigateway/lib/base-path-mapping.ts @@ -1,7 +1,6 @@ import { Construct } from 'constructs'; -import { CfnBasePathMapping } from './apigateway.generated'; -import { IDomainName } from './domain-name'; -import { IRestApi, RestApiBase } from './restapi'; +import { CfnBasePathMapping, IDomainNameRef, IRestApiRef } from './apigateway.generated'; +import { RestApiBase } from './restapi'; import { Stage } from './stage'; import { Resource, Token } from '../../core'; import { ValidationError } from '../../core/lib/errors'; @@ -39,12 +38,12 @@ export interface BasePathMappingProps extends BasePathMappingOptions { /** * The DomainName to associate with this base path mapping. */ - readonly domainName: IDomainName; + readonly domainName: IDomainNameRef; /** * The RestApi resource to target. */ - readonly restApi: IRestApi; + readonly restApi: IRestApiRef; } /** @@ -87,8 +86,8 @@ export class BasePathMapping extends Resource { new CfnBasePathMapping(this, 'Resource', { basePath: props.basePath, - domainName: props.domainName.domainName, - restApiId: props.restApi.restApiId, + domainName: props.domainName.domainNameRef.domainName, + restApiId: props.restApi.restApiRef.restApiId, stage: stage?.stageName, }); } diff --git a/packages/aws-cdk-lib/aws-apigateway/lib/deployment.ts b/packages/aws-cdk-lib/aws-apigateway/lib/deployment.ts index e00ab6675b896..2b7402a5b9a42 100644 --- a/packages/aws-cdk-lib/aws-apigateway/lib/deployment.ts +++ b/packages/aws-cdk-lib/aws-apigateway/lib/deployment.ts @@ -1,6 +1,6 @@ import { ArtifactMetadataEntryType } from '@aws-cdk/cloud-assembly-schema'; import { Construct } from 'constructs'; -import { CfnDeployment } from './apigateway.generated'; +import { CfnDeployment, IRestApiRef } from './apigateway.generated'; import { Method } from './method'; import { IRestApi, RestApi, SpecRestApi, RestApiBase } from './restapi'; import { Lazy, RemovalPolicy, Resource, CfnResource } from '../../core'; @@ -153,19 +153,19 @@ export class Deployment extends Resource { interface LatestDeploymentResourceProps { readonly description?: string; - readonly restApi: IRestApi; + readonly restApi: IRestApiRef; readonly stageName?: string; } class LatestDeploymentResource extends CfnDeployment { private readonly hashComponents = new Array(); private readonly originalLogicalId: string; - private readonly api: IRestApi; + private readonly api: IRestApiRef; constructor(scope: Construct, id: string, props: LatestDeploymentResourceProps) { super(scope, id, { description: props.description, - restApiId: props.restApi.restApiId, + restApiId: props.restApi.restApiRef.restApiId, stageName: props.stageName, }); diff --git a/packages/aws-cdk-lib/aws-apigateway/lib/domain-name.ts b/packages/aws-cdk-lib/aws-apigateway/lib/domain-name.ts index f55bf1a809e29..5e09dcd25f76e 100644 --- a/packages/aws-cdk-lib/aws-apigateway/lib/domain-name.ts +++ b/packages/aws-cdk-lib/aws-apigateway/lib/domain-name.ts @@ -1,12 +1,11 @@ import { Construct } from 'constructs'; -import { CfnDomainName } from './apigateway.generated'; +import { CfnDomainName, DomainNameReference, IDomainNameRef, IRestApiRef, IStageRef } from './apigateway.generated'; import { BasePathMapping, BasePathMappingOptions } from './base-path-mapping'; import { EndpointType, IRestApi } from './restapi'; -import { IStage } from './stage'; import * as apigwv2 from '../../aws-apigatewayv2'; import * as acm from '../../aws-certificatemanager'; import { IBucket } from '../../aws-s3'; -import { IResource, Names, Resource, Token } from '../../core'; +import { Arn, IResource, Names, Resource, Stack, Token } from '../../core'; import { ValidationError } from '../../core/lib/errors'; import { addConstructMetadata, MethodMetadata } from '../../core/lib/metadata-resource'; import { propertyInjectable } from '../../core/lib/prop-injectable'; @@ -94,7 +93,7 @@ export interface DomainNameProps extends DomainNameOptions { readonly mapping?: IRestApi; } -export interface IDomainName extends IResource { +export interface IDomainName extends IResource, IDomainNameRef { /** * The domain name (e.g. `example.com`) * @@ -132,12 +131,22 @@ export class DomainName extends Resource implements IDomainName { public readonly domainName = attrs.domainName; public readonly domainNameAliasDomainName = attrs.domainNameAliasTarget; public readonly domainNameAliasHostedZoneId = attrs.domainNameAliasHostedZoneId; + + public readonly domainNameRef = { + domainName: this.domainName, + domainNameArn: Arn.format({ + service: 'apigateway', + resource: 'domainnames', + resourceName: attrs.domainName, + }, Stack.of(scope)), + }; } return new Import(scope, id); } public readonly domainName: string; + public readonly domainNameRef: DomainNameReference; public readonly domainNameAliasDomainName: string; public readonly domainNameAliasHostedZoneId: string; private readonly basePaths = new Set(); @@ -168,6 +177,7 @@ export class DomainName extends Resource implements IDomainName { }); this.domainName = resource.ref; + this.domainNameRef = resource.domainNameRef; this.domainNameAliasDomainName = edge ? resource.attrDistributionDomainName @@ -217,7 +227,7 @@ export class DomainName extends Resource implements IDomainName { * @param options Options for mapping to base path with or without a stage */ @MethodMetadata() - public addBasePathMapping(targetApi: IRestApi, options: BasePathMappingOptions = {}): BasePathMapping { + public addBasePathMapping(targetApi: IRestApiRef, options: BasePathMappingOptions = {}): BasePathMapping { if (this.basePaths.has(options.basePath)) { throw new ValidationError(`DomainName ${this.node.id} already has a mapping for path ${options.basePath}`, this); } @@ -247,7 +257,7 @@ export class DomainName extends Resource implements IDomainName { * @param options Options for mapping to a stage */ @MethodMetadata() - public addApiMapping(targetStage: IStage, options: ApiMappingOptions = {}): void { + public addApiMapping(targetStage: IStageRef, options: ApiMappingOptions = {}): void { if (this.basePaths.has(options.basePath)) { throw new ValidationError(`DomainName ${this.node.id} already has a mapping for path ${options.basePath}`, this); } @@ -255,8 +265,8 @@ export class DomainName extends Resource implements IDomainName { this.basePaths.add(options.basePath); const id = `Map:${options.basePath ?? 'none'}=>${Names.nodeUniqueId(targetStage.node)}`; new apigwv2.CfnApiMapping(this, id, { - apiId: targetStage.restApi.restApiId, - stage: targetStage.stageName, + apiId: targetStage.stageRef.restApiId, + stage: targetStage.stageRef.stageName, domainName: this.domainName, apiMappingKey: options.basePath, }); diff --git a/packages/aws-cdk-lib/aws-apigateway/lib/gateway-response.ts b/packages/aws-cdk-lib/aws-apigateway/lib/gateway-response.ts index 7eb62ee350e2f..d28ae7291dced 100644 --- a/packages/aws-cdk-lib/aws-apigateway/lib/gateway-response.ts +++ b/packages/aws-cdk-lib/aws-apigateway/lib/gateway-response.ts @@ -1,5 +1,10 @@ import { Construct } from 'constructs'; -import { CfnGatewayResponse, CfnGatewayResponseProps } from './apigateway.generated'; +import { + CfnGatewayResponse, + CfnGatewayResponseProps, + GatewayResponseReference, + IGatewayResponseRef, +} from './apigateway.generated'; import { IRestApi } from './restapi'; import { IResource, Resource } from '../../core'; import { addConstructMetadata } from '../../core/lib/metadata-resource'; @@ -8,7 +13,7 @@ import { propertyInjectable } from '../../core/lib/prop-injectable'; /** * Represents gateway response resource. */ -export interface IGatewayResponse extends IResource { +export interface IGatewayResponse extends IResource, IGatewayResponseRef { } /** @@ -61,6 +66,20 @@ export class GatewayResponse extends Resource implements IGatewayResponse { /** Uniquely identifies this class. */ public static readonly PROPERTY_INJECTION_ID: string = 'aws-cdk-lib.aws-apigateway.GatewayResponse'; + /** + * Reference an existing GatewayResponse given a gateway response ID. + */ + public static fromGatewayResponseId(scope: Construct, id: string, gatewayResponseId: string): IGatewayResponse { + class Import extends Resource implements IGatewayResponse { + public readonly gatewayResponseRef = { + gatewayResponseId: gatewayResponseId, + }; + } + return new Import(scope, id); + } + + public readonly gatewayResponseRef: GatewayResponseReference; + constructor(scope: Construct, id: string, props: GatewayResponseProps) { super(scope, id); // Enhanced CDK Analytics Telemetry @@ -86,6 +105,7 @@ export class GatewayResponse extends Resource implements IGatewayResponse { }); } + this.gatewayResponseRef = resource.gatewayResponseRef; this.node.defaultChild = resource; } diff --git a/packages/aws-cdk-lib/aws-apigateway/lib/method.ts b/packages/aws-cdk-lib/aws-apigateway/lib/method.ts index 62ecedfac079e..16b8c227f6242 100644 --- a/packages/aws-cdk-lib/aws-apigateway/lib/method.ts +++ b/packages/aws-cdk-lib/aws-apigateway/lib/method.ts @@ -1,6 +1,6 @@ import { Construct } from 'constructs'; import { ApiGatewayMetrics } from './apigateway-canned-metrics.generated'; -import { CfnMethod, CfnMethodProps } from './apigateway.generated'; +import { CfnMethod, CfnMethodProps, IStageRef } from './apigateway.generated'; import { Authorizer, IAuthorizer } from './authorizer'; import { Integration, IntegrationConfig } from './integration'; import { MockIntegration } from './integrations/mock'; @@ -9,7 +9,6 @@ import { IModel } from './model'; import { IRequestValidator, RequestValidatorOptions } from './requestvalidator'; import { IResource } from './resource'; import { IRestApi, RestApi, RestApiBase } from './restapi'; -import { IStage } from './stage'; import { validateHttpMethod } from './util'; import * as cloudwatch from '../../aws-cloudwatch'; import * as iam from '../../aws-iam'; @@ -439,11 +438,11 @@ export class Method extends Resource { * Returns the given named metric for this API method */ @MethodMetadata() - public metric(metricName: string, stage: IStage, props?: cloudwatch.MetricOptions): cloudwatch.Metric { + public metric(metricName: string, stage: IStageRef, props?: cloudwatch.MetricOptions): cloudwatch.Metric { return new cloudwatch.Metric({ namespace: 'AWS/ApiGateway', metricName, - dimensionsMap: { ApiName: this.api.restApiName, Method: this.httpMethod, Resource: this.resource.path, Stage: stage.stageName }, + dimensionsMap: { ApiName: this.api.restApiName, Method: this.httpMethod, Resource: this.resource.path, Stage: stage.stageRef.stageName }, ...props, }).attachTo(this); } @@ -454,7 +453,7 @@ export class Method extends Resource { * @default - sum over 5 minutes */ @MethodMetadata() - public metricClientError(stage: IStage, props?: cloudwatch.MetricOptions): cloudwatch.Metric { + public metricClientError(stage: IStageRef, props?: cloudwatch.MetricOptions): cloudwatch.Metric { return this.cannedMetric(ApiGatewayMetrics._4XxErrorSum, stage, props); } @@ -464,7 +463,7 @@ export class Method extends Resource { * @default - sum over 5 minutes */ @MethodMetadata() - public metricServerError(stage: IStage, props?: cloudwatch.MetricOptions): cloudwatch.Metric { + public metricServerError(stage: IStageRef, props?: cloudwatch.MetricOptions): cloudwatch.Metric { return this.cannedMetric(ApiGatewayMetrics._5XxErrorSum, stage, props); } @@ -474,7 +473,7 @@ export class Method extends Resource { * @default - sum over 5 minutes */ @MethodMetadata() - public metricCacheHitCount(stage: IStage, props?: cloudwatch.MetricOptions): cloudwatch.Metric { + public metricCacheHitCount(stage: IStageRef, props?: cloudwatch.MetricOptions): cloudwatch.Metric { return this.cannedMetric(ApiGatewayMetrics.cacheHitCountSum, stage, props); } @@ -485,7 +484,7 @@ export class Method extends Resource { * @default - sum over 5 minutes */ @MethodMetadata() - public metricCacheMissCount(stage: IStage, props?: cloudwatch.MetricOptions): cloudwatch.Metric { + public metricCacheMissCount(stage: IStageRef, props?: cloudwatch.MetricOptions): cloudwatch.Metric { return this.cannedMetric(ApiGatewayMetrics.cacheMissCountSum, stage, props); } @@ -495,7 +494,7 @@ export class Method extends Resource { * @default - sample count over 5 minutes */ @MethodMetadata() - public metricCount(stage: IStage, props?: cloudwatch.MetricOptions): cloudwatch.Metric { + public metricCount(stage: IStageRef, props?: cloudwatch.MetricOptions): cloudwatch.Metric { return this.cannedMetric(ApiGatewayMetrics.countSum, stage, { statistic: 'SampleCount', ...props, @@ -509,7 +508,7 @@ export class Method extends Resource { * @default - average over 5 minutes. */ @MethodMetadata() - public metricIntegrationLatency(stage: IStage, props?: cloudwatch.MetricOptions): cloudwatch.Metric { + public metricIntegrationLatency(stage: IStageRef, props?: cloudwatch.MetricOptions): cloudwatch.Metric { return this.cannedMetric(ApiGatewayMetrics.integrationLatencyAverage, stage, props); } @@ -521,7 +520,7 @@ export class Method extends Resource { * @default - average over 5 minutes. */ @MethodMetadata() - public metricLatency(stage: IStage, props?: cloudwatch.MetricOptions): cloudwatch.Metric { + public metricLatency(stage: IStageRef, props?: cloudwatch.MetricOptions): cloudwatch.Metric { return this.cannedMetric(ApiGatewayMetrics.latencyAverage, stage, props); } @@ -544,9 +543,9 @@ export class Method extends Resource { Method: string; Resource: string; Stage: string; - }) => cloudwatch.MetricProps, stage: IStage, props?: cloudwatch.MetricOptions) { + }) => cloudwatch.MetricProps, stage: IStageRef, props?: cloudwatch.MetricOptions) { return new cloudwatch.Metric({ - ...fn({ ApiName: this.api.restApiName, Method: this.httpMethod, Resource: this.resource.path, Stage: stage.stageName }), + ...fn({ ApiName: this.api.restApiName, Method: this.httpMethod, Resource: this.resource.path, Stage: stage.stageRef.stageName }), ...props, }).attachTo(this); } diff --git a/packages/aws-cdk-lib/aws-apigateway/lib/resource.ts b/packages/aws-cdk-lib/aws-apigateway/lib/resource.ts index 6354f91908e72..0ab22237c352f 100644 --- a/packages/aws-cdk-lib/aws-apigateway/lib/resource.ts +++ b/packages/aws-cdk-lib/aws-apigateway/lib/resource.ts @@ -1,16 +1,16 @@ import { Construct } from 'constructs'; -import { CfnResource, CfnResourceProps } from './apigateway.generated'; +import { CfnResource, CfnResourceProps, IResourceRef, ResourceReference } from './apigateway.generated'; import { Cors, CorsOptions } from './cors'; import { Integration } from './integration'; import { MockIntegration } from './integrations'; -import { Method, MethodOptions, AuthorizationType } from './method'; +import { AuthorizationType, Method, MethodOptions } from './method'; import { IRestApi, RestApi } from './restapi'; import { IResource as IResourceBase, Resource as ResourceConstruct } from '../../core'; import { ValidationError } from '../../core/lib/errors'; import { addConstructMetadata, MethodMetadata } from '../../core/lib/metadata-resource'; import { propertyInjectable } from '../../core/lib/prop-injectable'; -export interface IResource extends IResourceBase { +export interface IResource extends IResourceBase, IResourceRef { /** * The parent of this resource or undefined for the root resource. */ @@ -383,6 +383,13 @@ export abstract class ResourceBase extends ResourceConstruct implements IResourc public get url(): string { return this.restApi.urlForPath(this.path); } + + public get resourceRef(): ResourceReference { + return { + resourceId: this.resourceId, + restApiId: this.api.restApiId, + }; + } } /** diff --git a/packages/aws-cdk-lib/aws-apigateway/lib/restapi.ts b/packages/aws-cdk-lib/aws-apigateway/lib/restapi.ts index b008a77e3c19e..9d5d86fca66df 100644 --- a/packages/aws-cdk-lib/aws-apigateway/lib/restapi.ts +++ b/packages/aws-cdk-lib/aws-apigateway/lib/restapi.ts @@ -2,7 +2,7 @@ import { Construct } from 'constructs'; import { ApiDefinition } from './api-definition'; import { ApiKey, ApiKeyOptions, IApiKey } from './api-key'; import { ApiGatewayMetrics } from './apigateway-canned-metrics.generated'; -import { CfnAccount, CfnRestApi } from './apigateway.generated'; +import { CfnAccount, CfnRestApi, IRestApiRef, RestApiReference } from './apigateway.generated'; import { CorsOptions } from './cors'; import { Deployment } from './deployment'; import { DomainName, DomainNameOptions } from './domain-name'; @@ -17,7 +17,18 @@ import { UsagePlan, UsagePlanProps } from './usage-plan'; import * as cloudwatch from '../../aws-cloudwatch'; import * as ec2 from '../../aws-ec2'; import * as iam from '../../aws-iam'; -import { ArnFormat, CfnOutput, IResource as IResourceBase, Resource, Stack, Token, FeatureFlags, RemovalPolicy, Size, Lazy } from '../../core'; +import { + ArnFormat, + CfnOutput, + FeatureFlags, + IResource as IResourceBase, + Lazy, + RemovalPolicy, + Resource, + Size, + Stack, + Token, +} from '../../core'; import { ValidationError } from '../../core/lib/errors'; import { addConstructMetadata, MethodMetadata } from '../../core/lib/metadata-resource'; import { propertyInjectable } from '../../core/lib/prop-injectable'; @@ -27,7 +38,7 @@ import { APIGATEWAY_DISABLE_CLOUDWATCH_ROLE } from '../../cx-api'; const RESTAPI_SYMBOL = Symbol.for('@aws-cdk/aws-apigateway.RestApiBase'); const APIGATEWAY_RESTAPI_SYMBOL = Symbol.for('@aws-cdk/aws-apigateway.RestApi'); -export interface IRestApi extends IResourceBase { +export interface IRestApi extends IResourceBase, IRestApiRef { /** * The ID of this API Gateway RestApi. * @attribute @@ -393,7 +404,7 @@ export abstract class RestApiBase extends Resource implements IRestApi, iam.IRes private _latestDeployment?: Deployment; private _domainName?: DomainName; - private _allowedVpcEndpoints: Set = new Set(); + private _allowedVpcEndpoints: Set = new Set(); protected resourcePolicy?: iam.PolicyDocument; protected cloudWatchAccount?: CfnAccount; @@ -495,7 +506,7 @@ export abstract class RestApiBase extends Resource implements IRestApi, iam.IRes const endpoints = Lazy.list({ produce: () => { - return Array.from(this._allowedVpcEndpoints).map(endpoint => endpoint.vpcEndpointId); + return Array.from(this._allowedVpcEndpoints).map(endpoint => endpoint.vpcEndpointRef.vpcEndpointId); }, }); @@ -738,6 +749,12 @@ export abstract class RestApiBase extends Resource implements IRestApi, iam.IRes ...props, }).attachTo(this); } + + public get restApiRef(): RestApiReference { + return { + restApiId: this.restApiId, + }; + } } /** diff --git a/packages/aws-cdk-lib/aws-apigateway/lib/stage.ts b/packages/aws-cdk-lib/aws-apigateway/lib/stage.ts index 1e519e97efe6b..90e17c621619e 100644 --- a/packages/aws-cdk-lib/aws-apigateway/lib/stage.ts +++ b/packages/aws-cdk-lib/aws-apigateway/lib/stage.ts @@ -1,8 +1,8 @@ import { Construct } from 'constructs'; import { AccessLogFormat, IAccessLogDestination } from './access-log'; -import { IApiKey, ApiKeyOptions, ApiKey } from './api-key'; +import { ApiKey, ApiKeyOptions, IApiKey } from './api-key'; import { ApiGatewayMetrics } from './apigateway-canned-metrics.generated'; -import { CfnStage } from './apigateway.generated'; +import { CfnStage, IStageRef, StageReference } from './apigateway.generated'; import { Deployment } from './deployment'; import { IRestApi, RestApiBase } from './restapi'; import { parseMethodOptionsPath } from './util'; @@ -15,7 +15,7 @@ import { propertyInjectable } from '../../core/lib/prop-injectable'; /** * Represents an APIGateway Stage. */ -export interface IStage extends IResource { +export interface IStage extends IResource, IStageRef { /** * Name of this stage. * @attribute @@ -357,6 +357,13 @@ export abstract class StageBase extends Resource implements IStage { ...props, }).attachTo(this); } + + public get stageRef(): StageReference { + return { + stageName: this.stageName, + restApiId: this.restApi.restApiId, + }; + } } @propertyInjectable diff --git a/packages/aws-cdk-lib/aws-apigateway/lib/usage-plan.ts b/packages/aws-cdk-lib/aws-apigateway/lib/usage-plan.ts index 97f516d629f7c..0405704063f44 100644 --- a/packages/aws-cdk-lib/aws-apigateway/lib/usage-plan.ts +++ b/packages/aws-cdk-lib/aws-apigateway/lib/usage-plan.ts @@ -1,6 +1,6 @@ import { Construct } from 'constructs'; import { IApiKey } from './api-key'; -import { CfnUsagePlan, CfnUsagePlanKey } from './apigateway.generated'; +import { CfnUsagePlan, CfnUsagePlanKey, IApiKeyRef, IUsagePlanRef, UsagePlanReference } from './apigateway.generated'; import { Method } from './method'; import { IRestApi } from './restapi'; import { Stage } from './stage'; @@ -161,7 +161,7 @@ export interface AddApiKeyOptions { /** * A UsagePlan, either managed by this CDK app, or imported. */ -export interface IUsagePlan extends IResource { +export interface IUsagePlan extends IResource, IUsagePlanRef { /** * Id of the usage plan * @attribute @@ -174,7 +174,7 @@ export interface IUsagePlan extends IResource { * @param apiKey the api key to associate with this usage plan * @param options options that control the behaviour of this method */ - addApiKey(apiKey: IApiKey, options?: AddApiKeyOptions): void; + addApiKey(apiKey: IApiKeyRef, options?: AddApiKeyOptions): void; } @@ -191,7 +191,7 @@ abstract class UsagePlanBase extends Resource implements IUsagePlan { * @param apiKey the api key to associate with this usage plan * @param options options that control the behaviour of this method */ - public addApiKey(apiKey: IApiKey, options?: AddApiKeyOptions): void { + public addApiKey(apiKey: IApiKeyRef, options?: AddApiKeyOptions): void { let id: string; const prefix = 'UsagePlanKeyResource'; @@ -203,7 +203,7 @@ abstract class UsagePlanBase extends Resource implements IUsagePlan { } const resource = new CfnUsagePlanKey(this, id, { - keyId: apiKey.keyId, + keyId: apiKey.apiKeyRef.apiKeyId, keyType: UsagePlanKeyType.API_KEY, usagePlanId: this.usagePlanId, }); @@ -211,6 +211,12 @@ abstract class UsagePlanBase extends Resource implements IUsagePlan { resource.overrideLogicalId(options?.overrideLogicalId); } } + + public get usagePlanRef(): UsagePlanReference { + return { + usagePlanId: this.usagePlanId, + }; + } } @propertyInjectable diff --git a/packages/aws-cdk-lib/aws-apigateway/lib/vpc-link.ts b/packages/aws-cdk-lib/aws-apigateway/lib/vpc-link.ts index 544d23e64dfec..983e12a2c4b7c 100644 --- a/packages/aws-cdk-lib/aws-apigateway/lib/vpc-link.ts +++ b/packages/aws-cdk-lib/aws-apigateway/lib/vpc-link.ts @@ -1,5 +1,5 @@ import { Construct } from 'constructs'; -import { CfnVpcLink } from './apigateway.generated'; +import { CfnVpcLink, IVpcLinkRef, VpcLinkReference } from './apigateway.generated'; import * as elbv2 from '../../aws-elasticloadbalancingv2'; import { IResource, Lazy, Names, Resource } from '../../core'; import { addConstructMetadata, MethodMetadata } from '../../core/lib/metadata-resource'; @@ -8,7 +8,7 @@ import { propertyInjectable } from '../../core/lib/prop-injectable'; /** * Represents an API Gateway VpcLink */ -export interface IVpcLink extends IResource { +export interface IVpcLink extends IResource, IVpcLinkRef { /** * Physical ID of the VpcLink resource * @attribute @@ -56,6 +56,9 @@ export class VpcLink extends Resource implements IVpcLink { public static fromVpcLinkId(scope: Construct, id: string, vpcLinkId: string): IVpcLink { class Import extends Resource implements IVpcLink { public vpcLinkId = vpcLinkId; + public vpcLinkRef = { + vpcLinkId: vpcLinkId, + }; } return new Import(scope, id); @@ -67,6 +70,8 @@ export class VpcLink extends Resource implements IVpcLink { */ public readonly vpcLinkId: string; + public readonly vpcLinkRef: VpcLinkReference; + private readonly _targets = new Array(); constructor(scope: Construct, id: string, props: VpcLinkProps = {}) { @@ -83,6 +88,7 @@ export class VpcLink extends Resource implements IVpcLink { targetArns: Lazy.list({ produce: () => this.renderTargets() }), }); + this.vpcLinkRef = cfnResource.vpcLinkRef; this.vpcLinkId = cfnResource.ref; if (props.targets) { diff --git a/packages/aws-cdk-lib/aws-apigatewayv2/lib/http/integration.ts b/packages/aws-cdk-lib/aws-apigatewayv2/lib/http/integration.ts index 7759e642a9250..6a635ee2ed6b4 100644 --- a/packages/aws-cdk-lib/aws-apigatewayv2/lib/http/integration.ts +++ b/packages/aws-cdk-lib/aws-apigatewayv2/lib/http/integration.ts @@ -2,7 +2,7 @@ import { Construct } from 'constructs'; import { IHttpApi } from './api'; import { HttpMethod, IHttpRoute } from './route'; import { CfnIntegration } from '.././index'; -import { IRole } from '../../../aws-iam'; +import { IRoleRef } from '../../../aws-iam'; import { Aws, Duration, Resource } from '../../../core'; import { ValidationError } from '../../../core/lib/errors'; import { addConstructMetadata } from '../../../core/lib/metadata-resource'; @@ -100,8 +100,8 @@ export abstract class IntegrationCredentials { /** * Use the specified role for integration requests */ - public static fromRole(role: IRole): IntegrationCredentials { - return { credentialsArn: role.roleArn }; + public static fromRole(role: IRoleRef): IntegrationCredentials { + return { credentialsArn: role.roleRef.roleArn }; } /** Use the calling user's identity to call the integration */ diff --git a/packages/aws-cdk-lib/aws-apigatewayv2/lib/http/vpc-link.ts b/packages/aws-cdk-lib/aws-apigatewayv2/lib/http/vpc-link.ts index 40c5db6e13779..5ab6ea0018aa6 100644 --- a/packages/aws-cdk-lib/aws-apigatewayv2/lib/http/vpc-link.ts +++ b/packages/aws-cdk-lib/aws-apigatewayv2/lib/http/vpc-link.ts @@ -48,7 +48,7 @@ export interface VpcLinkProps { * * @default - no security groups. Use `addSecurityGroups` to add security groups */ - readonly securityGroups?: ec2.ISecurityGroup[]; + readonly securityGroups?: ec2.ISecurityGroupRef[]; } /** @@ -89,8 +89,8 @@ export class VpcLink extends Resource implements IVpcLink { public readonly vpcLinkId: string; public readonly vpc: ec2.IVpc; - private readonly subnets = new Array(); - private readonly securityGroups = new Array(); + private readonly subnets = new Array(); + private readonly securityGroups = new Array(); constructor(scope: Construct, id: string, props: VpcLinkProps) { super(scope, id); @@ -118,7 +118,7 @@ export class VpcLink extends Resource implements IVpcLink { * Adds the provided subnets to the vpc link */ @MethodMetadata() - public addSubnets(...subnets: ec2.ISubnet[]) { + public addSubnets(...subnets: ec2.ISubnetRef[]) { this.subnets.push(...subnets); } @@ -126,15 +126,15 @@ export class VpcLink extends Resource implements IVpcLink { * Adds the provided security groups to the vpc link */ @MethodMetadata() - public addSecurityGroups(...groups: ec2.ISecurityGroup[]) { + public addSecurityGroups(...groups: ec2.ISecurityGroupRef[]) { this.securityGroups.push(...groups); } private renderSubnets() { - return this.subnets.map(subnet => subnet.subnetId); + return this.subnets.map(subnet => subnet.subnetRef.subnetId); } private renderSecurityGroups() { - return this.securityGroups.map(sg => sg.securityGroupId); + return this.securityGroups.map(sg => sg.securityGroupRef.securityGroupId); } } diff --git a/packages/aws-cdk-lib/aws-appconfig/lib/configuration.ts b/packages/aws-cdk-lib/aws-appconfig/lib/configuration.ts index bd1bd9740f918..38ff271162d9a 100644 --- a/packages/aws-cdk-lib/aws-appconfig/lib/configuration.ts +++ b/packages/aws-cdk-lib/aws-appconfig/lib/configuration.ts @@ -14,7 +14,7 @@ import * as lambda from '../../aws-lambda'; import * as s3 from '../../aws-s3'; import * as sm from '../../aws-secretsmanager'; import * as ssm from '../../aws-ssm'; -import { PhysicalName, Stack, ArnFormat, Names, RemovalPolicy } from '../../core'; +import { PhysicalName, Stack, ArnFormat, Names, RemovalPolicy, ValidationError } from '../../core'; import * as mimeTypes from 'mime-types'; import { DeletionProtectionCheck } from './util'; @@ -403,7 +403,7 @@ export interface HostedConfigurationProps extends ConfigurationProps { * * @default None */ - readonly kmsKey?: kms.IKey; + readonly kmsKey?: kms.IKeyRef; } /** @@ -472,7 +472,7 @@ export class HostedConfiguration extends ConfigurationBase { type: this.type, validators: this.validators, deletionProtectionCheck: this.deletionProtectionCheck, - kmsKeyIdentifier: props.kmsKey?.keyArn, + kmsKeyIdentifier: props.kmsKey?.keyRef.keyArn, }); this.configurationProfileId = this._cfnConfigurationProfile.ref; this.configurationProfileArn = Stack.of(this).formatArn({ @@ -531,7 +531,7 @@ export interface SourcedConfigurationOptions extends ConfigurationOptions { * * @default - A role is generated. */ - readonly retrievalRole?: iam.IRole; + readonly retrievalRole?: iam.IRoleRef; } /** @@ -556,7 +556,7 @@ export interface SourcedConfigurationProps extends ConfigurationProps { * * @default - Auto generated if location type is not ConfigurationSourceType.CODE_PIPELINE otherwise no role specified. */ - readonly retrievalRole?: iam.IRole; + readonly retrievalRole?: iam.IRoleRef; } /** @@ -574,11 +574,6 @@ export class SourcedConfiguration extends ConfigurationBase { */ public readonly versionNumber?: string; - /** - * The IAM role to retrieve the configuration. - */ - public readonly retrievalRole?: iam.IRole; - /** * The key to decrypt the configuration if applicable. This key * can be used when storing configuration in AWS Secrets Manager, Systems Manager Parameter Store, @@ -598,6 +593,7 @@ export class SourcedConfiguration extends ConfigurationBase { private readonly locationUri: string; private readonly _cfnConfigurationProfile: CfnConfigurationProfile; + private readonly _retrievalRole?: iam.IRoleRef; constructor(scope: Construct, id: string, props: SourcedConfigurationProps) { super(scope, id, props); @@ -606,7 +602,7 @@ export class SourcedConfiguration extends ConfigurationBase { this.locationUri = this.location.locationUri; this.versionNumber = props.versionNumber; this.sourceKey = this.location.key; - this.retrievalRole = props.retrievalRole ?? this.getRetrievalRole(); + this._retrievalRole = props.retrievalRole ?? this.getRetrievalRole(); this._cfnConfigurationProfile = new CfnConfigurationProfile(this, 'Resource', { applicationId: this.applicationId, locationUri: this.locationUri, @@ -630,6 +626,19 @@ export class SourcedConfiguration extends ConfigurationBase { this.deployConfigToEnvironments(); } + /** + * The IAM role to retrieve the configuration. + */ + public get retrievalRole(): iam.IRole | undefined { + if (!this._retrievalRole) { + return undefined; + } + if ('grant' in this._retrievalRole) { + return this._retrievalRole as iam.IRole; + } + throw new ValidationError(`Retrieval role does not implement IRole: ${this._retrievalRole.constructor.name}`, this); + } + private getRetrievalRole(): iam.Role | undefined { // Check if the configuration source is not from CodePipeline if (this.location.type != ConfigurationSourceType.CODE_PIPELINE) { diff --git a/packages/aws-cdk-lib/aws-appconfig/lib/environment.ts b/packages/aws-cdk-lib/aws-appconfig/lib/environment.ts index aff8848679565..24d726578f07c 100644 --- a/packages/aws-cdk-lib/aws-appconfig/lib/environment.ts +++ b/packages/aws-cdk-lib/aws-appconfig/lib/environment.ts @@ -403,10 +403,10 @@ export abstract class Monitor { * @param alarm The Amazon CloudWatch alarm. * @param alarmRole The IAM role for AWS AppConfig to view the alarm state. */ - public static fromCloudWatchAlarm(alarm: cloudwatch.IAlarm, alarmRole?: iam.IRole): Monitor { + public static fromCloudWatchAlarm(alarm: cloudwatch.IAlarm, alarmRole?: iam.IRoleRef): Monitor { return { alarmArn: alarm.alarmArn, - alarmRoleArn: alarmRole?.roleArn, + alarmRoleArn: alarmRole?.roleRef.roleArn, monitorType: MonitorType.CLOUDWATCH, }; } diff --git a/packages/aws-cdk-lib/aws-appconfig/lib/extension.ts b/packages/aws-cdk-lib/aws-appconfig/lib/extension.ts index 81ce7ebcbc49f..ac0247accabab 100644 --- a/packages/aws-cdk-lib/aws-appconfig/lib/extension.ts +++ b/packages/aws-cdk-lib/aws-appconfig/lib/extension.ts @@ -496,7 +496,7 @@ export class Extension extends Resource implements IExtension { public readonly extensionVersionNumber: number; private readonly _cfnExtension: CfnExtension; - private executionRole?: iam.IRole; + private executionRole?: iam.IRoleRef; constructor(scope: Construct, id: string, props: ExtensionProps) { super(scope, id, { @@ -527,7 +527,7 @@ export class Extension extends Resource implements IExtension { Uri: extensionUri, ...(sourceType === SourceType.EVENTS || cur.invokeWithoutExecutionRole ? {} - : { RoleArn: this.executionRole?.roleArn || this.getExecutionRole(cur.eventDestination, name).roleArn }), + : { RoleArn: this.executionRole?.roleRef.roleArn || this.getExecutionRole(cur.eventDestination, name).roleRef.roleArn }), ...(cur.description ? { Description: cur.description } : {}), }, ]; @@ -556,7 +556,7 @@ export class Extension extends Resource implements IExtension { }); } - private getExecutionRole(eventDestination: IEventDestination, actionName: string): iam.IRole { + private getExecutionRole(eventDestination: IEventDestination, actionName: string): iam.IRoleRef { const versionNumber = this.latestVersionNumber ? this.latestVersionNumber + 1 : 1; const combinedObjects = stringifyObjects(this.name, versionNumber, actionName); this.executionRole = new iam.Role(this, `Role${getHash(combinedObjects)}`, { diff --git a/packages/aws-cdk-lib/aws-appsync/lib/appsync-common.ts b/packages/aws-cdk-lib/aws-appsync/lib/appsync-common.ts index 33df34a0cc295..a22e80d4324d0 100644 --- a/packages/aws-cdk-lib/aws-appsync/lib/appsync-common.ts +++ b/packages/aws-cdk-lib/aws-appsync/lib/appsync-common.ts @@ -1,6 +1,6 @@ import { EventApiBase } from './eventapi'; import { ICertificate } from '../../aws-certificatemanager'; -import { IRole } from '../../aws-iam'; +import { IRoleRef } from '../../aws-iam'; import { RetentionDays } from '../../aws-logs'; import { Stack, ArnFormat } from '../../core'; @@ -121,7 +121,7 @@ export interface AppSyncLogConfig { * * @default - None */ - readonly role?: IRole; + readonly role?: IRoleRef; /** * The number of days log events are kept in CloudWatch Logs. diff --git a/packages/aws-cdk-lib/aws-appsync/lib/eventapi.ts b/packages/aws-cdk-lib/aws-appsync/lib/eventapi.ts index acb40be824a1b..e1b973734ec38 100644 --- a/packages/aws-cdk-lib/aws-appsync/lib/eventapi.ts +++ b/packages/aws-cdk-lib/aws-appsync/lib/eventapi.ts @@ -756,11 +756,11 @@ export class EventApi extends EventApiBase { private setupLogConfig(config?: AppSyncLogConfig) { if (!config) return; const logsRoleArn: string = - config.role?.roleArn ?? + config.role?.roleRef.roleArn ?? new Role(this, 'ApiLogsRole', { assumedBy: new ServicePrincipal('appsync.amazonaws.com'), managedPolicies: [ManagedPolicy.fromAwsManagedPolicyName('service-role/AWSAppSyncPushToCloudWatchLogs')], - }).roleArn; + }).roleRef.roleArn; const fieldLogLevel: AppSyncFieldLogLevel = config.fieldLogLevel ?? AppSyncFieldLogLevel.NONE; return { cloudWatchLogsRoleArn: logsRoleArn, diff --git a/packages/aws-cdk-lib/aws-appsync/lib/graphqlapi.ts b/packages/aws-cdk-lib/aws-appsync/lib/graphqlapi.ts index ef7cb3526d6ae..4f51df490fbd3 100644 --- a/packages/aws-cdk-lib/aws-appsync/lib/graphqlapi.ts +++ b/packages/aws-cdk-lib/aws-appsync/lib/graphqlapi.ts @@ -5,7 +5,7 @@ import { ISchema, SchemaFile } from './schema'; import { MergeType, addSourceApiAutoMergePermission, addSourceGraphQLPermission } from './source-api-association'; import { ICertificate } from '../../aws-certificatemanager'; import { IUserPool } from '../../aws-cognito'; -import { ManagedPolicy, Role, IRole, ServicePrincipal } from '../../aws-iam'; +import { ManagedPolicy, Role, IRole, ServicePrincipal, IRoleRef } from '../../aws-iam'; import { IFunction } from '../../aws-lambda'; import { ILogGroup, LogGroup, LogRetention, RetentionDays } from '../../aws-logs'; import { CfnResource, Duration, Expiration, FeatureFlags, IResolvable, Lazy, Stack, Token, ValidationError } from '../../core'; @@ -231,7 +231,7 @@ export interface LogConfig { * * @default - None */ - readonly role?: IRole; + readonly role?: IRoleRef; /** * The number of days log events are kept in CloudWatch Logs. @@ -865,7 +865,7 @@ export class GraphqlApi extends GraphqlApiBase { private setupLogConfig(config?: LogConfig) { if (!config) return undefined; - const logsRoleArn: string = config.role?.roleArn ?? new Role(this, 'ApiLogsRole', { + const logsRoleArn: string = config.role?.roleRef.roleArn ?? new Role(this, 'ApiLogsRole', { assumedBy: new ServicePrincipal('appsync.amazonaws.com'), managedPolicies: [ ManagedPolicy.fromAwsManagedPolicyName('service-role/AWSAppSyncPushToCloudWatchLogs'), diff --git a/packages/aws-cdk-lib/aws-appsync/test/integ-assets/appsync-js-pipeline.js b/packages/aws-cdk-lib/aws-appsync/test/integ-assets/appsync-js-pipeline.js index cfc6716b9273a..9fc18f5a4a73e 100644 --- a/packages/aws-cdk-lib/aws-appsync/test/integ-assets/appsync-js-pipeline.js +++ b/packages/aws-cdk-lib/aws-appsync/test/integ-assets/appsync-js-pipeline.js @@ -1,9 +1 @@ -// The before step -export function request(...args) { - return {} -} - -// The after step -export function response(ctx) { - return ctx.prev.result -} +"use strict";var __defProp=Object.defineProperty;var __getOwnPropDesc=Object.getOwnPropertyDescriptor;var __getOwnPropNames=Object.getOwnPropertyNames;var __hasOwnProp=Object.prototype.hasOwnProperty;var __export=(target,all)=>{for(var name in all)__defProp(target,name,{get:all[name],enumerable:!0})},__copyProps=(to,from,except,desc)=>{if(from&&typeof from=="object"||typeof from=="function")for(let key of __getOwnPropNames(from))!__hasOwnProp.call(to,key)&&key!==except&&__defProp(to,key,{get:()=>from[key],enumerable:!(desc=__getOwnPropDesc(from,key))||desc.enumerable});return to};var __toCommonJS=mod=>__copyProps(__defProp({},"__esModule",{value:!0}),mod);var appsync_js_pipeline_exports={};__export(appsync_js_pipeline_exports,{request:()=>request,response:()=>response});module.exports=__toCommonJS(appsync_js_pipeline_exports);function request(...args){return{}}function response(ctx){return ctx.prev.result}0&&(module.exports={request,response}); diff --git a/packages/aws-cdk-lib/aws-appsync/test/integ-assets/appsync-js-resolver.js b/packages/aws-cdk-lib/aws-appsync/test/integ-assets/appsync-js-resolver.js index 74a309765f2c2..3ba08eb272a15 100644 --- a/packages/aws-cdk-lib/aws-appsync/test/integ-assets/appsync-js-resolver.js +++ b/packages/aws-cdk-lib/aws-appsync/test/integ-assets/appsync-js-resolver.js @@ -1,22 +1 @@ -import { util } from '@aws-appsync/utils' - -export function request(ctx) { - const id = util.autoId() - const name = ctx.args.name; - - ctx.args.input = { - id, - name, - } - - return { - version: '2018-05-29', - operation: 'PutItem', - key: { id: util.dynamodb.toDynamoDB(ctx.args.input.id) }, - attributeValues: util.dynamodb.toMapValues(ctx.args.input), - }; -} - -export function response(ctx) { - return ctx.result; -} +"use strict";var __defProp=Object.defineProperty;var __getOwnPropDesc=Object.getOwnPropertyDescriptor;var __getOwnPropNames=Object.getOwnPropertyNames;var __hasOwnProp=Object.prototype.hasOwnProperty;var __export=(target,all)=>{for(var name in all)__defProp(target,name,{get:all[name],enumerable:!0})},__copyProps=(to,from,except,desc)=>{if(from&&typeof from=="object"||typeof from=="function")for(let key of __getOwnPropNames(from))!__hasOwnProp.call(to,key)&&key!==except&&__defProp(to,key,{get:()=>from[key],enumerable:!(desc=__getOwnPropDesc(from,key))||desc.enumerable});return to};var __toCommonJS=mod=>__copyProps(__defProp({},"__esModule",{value:!0}),mod);var appsync_js_resolver_exports={};__export(appsync_js_resolver_exports,{request:()=>request,response:()=>response});module.exports=__toCommonJS(appsync_js_resolver_exports);var import_utils=require("@aws-appsync/utils");function request(ctx){const id=import_utils.util.autoId(),name=ctx.args.name;return ctx.args.input={id,name},{version:"2018-05-29",operation:"PutItem",key:{id:import_utils.util.dynamodb.toDynamoDB(ctx.args.input.id)},attributeValues:import_utils.util.dynamodb.toMapValues(ctx.args.input)}}function response(ctx){return ctx.result}0&&(module.exports={request,response}); diff --git a/packages/aws-cdk-lib/aws-appsync/test/integ-assets/js-resolver-assertion/index.js b/packages/aws-cdk-lib/aws-appsync/test/integ-assets/js-resolver-assertion/index.js index db0dbed85dfd5..9bc4764c5e5da 100644 --- a/packages/aws-cdk-lib/aws-appsync/test/integ-assets/js-resolver-assertion/index.js +++ b/packages/aws-cdk-lib/aws-appsync/test/integ-assets/js-resolver-assertion/index.js @@ -1,24 +1,6 @@ -exports.handler = async function(event) { - console.log(event); - - let myHeaders = new Headers(); - myHeaders.append("x-api-key", event.apiKey); - myHeaders.append("Content-Type", "application/json"); - - const query = JSON.stringify({ - query: "mutation MyMutation {\n addTest(name: \"123\") {\n id\n name\n }\n}", - variables: {} - }); - - const requestOptions = { - method: 'POST', - headers: myHeaders, - body: query, - redirect: 'follow' - }; - - const response = await fetch(event.hostname, requestOptions) - .then(response => response.json()) - - return response; -} +"use strict";exports.handler=async function(event){console.log(event);let myHeaders=new Headers;myHeaders.append("x-api-key",event.apiKey),myHeaders.append("Content-Type","application/json");const query=JSON.stringify({query:`mutation MyMutation { + addTest(name: "123") { + id + name + } +}`,variables:{}}),requestOptions={method:"POST",headers:myHeaders,body:query,redirect:"follow"};return await fetch(event.hostname,requestOptions).then(response2=>response2.json())}; diff --git a/packages/aws-cdk-lib/aws-appsync/test/verify/eventapi-lambda-direct/handler.js b/packages/aws-cdk-lib/aws-appsync/test/verify/eventapi-lambda-direct/handler.js index 4025ef824c917..9aa0f644f3e1a 100644 --- a/packages/aws-cdk-lib/aws-appsync/test/verify/eventapi-lambda-direct/handler.js +++ b/packages/aws-cdk-lib/aws-appsync/test/verify/eventapi-lambda-direct/handler.js @@ -1,20 +1 @@ -exports.handler = async (context) => { - if (context.info.operation === 'PUBLISH') { - return { - events: context.events.map((ev) => { - // Transform the event - return { - id: ev.id, - payload: { - ...ev.payload, - with: `hello world from ${ev.id}` - } - } - }) - } - } else if (context.info.operation === 'SUBSCRIBE') { - - } else { - throw new Error('Unknown operation'); - } -}; \ No newline at end of file +"use strict";exports.handler=async context=>{if(context.info.operation==="PUBLISH")return{events:context.events.map(ev=>({id:ev.id,payload:{...ev.payload,with:`hello world from ${ev.id}`}}))};if(context.info.operation!=="SUBSCRIBE")throw new Error("Unknown operation")}; diff --git a/packages/aws-cdk-lib/aws-appsync/test/verify/eventapi-lambda-ds/handler.js b/packages/aws-cdk-lib/aws-appsync/test/verify/eventapi-lambda-ds/handler.js index b26d65260d76b..8dcb10945793e 100644 --- a/packages/aws-cdk-lib/aws-appsync/test/verify/eventapi-lambda-ds/handler.js +++ b/packages/aws-cdk-lib/aws-appsync/test/verify/eventapi-lambda-ds/handler.js @@ -1,4 +1 @@ -exports.handler = async (event, context) => { - console.log("Do something with the events!"); - console.log(event); -}; \ No newline at end of file +"use strict";exports.handler=async(event,context)=>{console.log("Do something with the events!"),console.log(event)}; diff --git a/packages/aws-cdk-lib/aws-appsync/test/verify/iam-query/iam-query.js b/packages/aws-cdk-lib/aws-appsync/test/verify/iam-query/iam-query.js index 11e895d2e2c54..243023f19292e 100644 --- a/packages/aws-cdk-lib/aws-appsync/test/verify/iam-query/iam-query.js +++ b/packages/aws-cdk-lib/aws-appsync/test/verify/iam-query/iam-query.js @@ -1,40 +1,6 @@ -require('isomorphic-fetch'); -const AWS = require('aws-sdk/global'); -const gql = require('graphql-tag'); -const appsync = require('aws-appsync'); - -const config = { - url: process.env.APPSYNC_ENDPOINT, - region: process.env.AWS_REGION, - auth: { - type: appsync.AUTH_TYPE.AWS_IAM, - credentials: AWS.config.credentials, - }, - disableOffline: true -}; - -const getTests = -`query getTests { +"use strict";require("isomorphic-fetch");const AWS=require("aws-sdk/global"),gql=require("graphql-tag"),appsync=require("aws-appsync"),config={url:process.env.APPSYNC_ENDPOINT,region:process.env.AWS_REGION,auth:{type:appsync.AUTH_TYPE.AWS_IAM,credentials:AWS.config.credentials},disableOffline:!0},getTests=`query getTests { getTests { id version } -}`; - -const client = new appsync.AWSAppSyncClient(config); - -exports.handler = (event, context, callback) => { - - (async () => { - try { - const result = await client.query({ - query: gql(getTests) - }); - console.log(result.data); - callback(null, result.data); - } catch (e) { - console.warn('Error sending mutation: ', e); - callback(Error(e)); - } - })(); -}; \ No newline at end of file +}`,client=new appsync.AWSAppSyncClient(config);exports.handler=(event,context,callback)=>{(async()=>{try{const result=await client.query({query:gql(getTests)});console.log(result.data),callback(null,result.data)}catch(e){console.warn("Error sending mutation: ",e),callback(Error(e))}})()}; diff --git a/packages/aws-cdk-lib/aws-appsync/test/verify/lambda-tutorial/lambda-tutorial.js b/packages/aws-cdk-lib/aws-appsync/test/verify/lambda-tutorial/lambda-tutorial.js index 5a08f83ef4f0a..2fd7fd0a41753 100644 --- a/packages/aws-cdk-lib/aws-appsync/test/verify/lambda-tutorial/lambda-tutorial.js +++ b/packages/aws-cdk-lib/aws-appsync/test/verify/lambda-tutorial/lambda-tutorial.js @@ -1,70 +1 @@ -exports.handler = (event, context, callback) => { - console.log("Received event {}", JSON.stringify(event, 3)); - var posts = { - "1": {"id": "1", "title": "First book", "author": "Author1", "url": "https://amazon.com/", "content": "SAMPLE TEXT AUTHOR 1 SAMPLE TEXT AUTHOR 1 SAMPLE TEXT AUTHOR 1 SAMPLE TEXT AUTHOR 1 SAMPLE TEXT AUTHOR 1 SAMPLE TEXT AUTHOR 1", "ups": "100", "downs": "10"}, - "2": {"id": "2", "title": "Second book", "author": "Author2", "url": "https://amazon.com", "content": "SAMPLE TEXT AUTHOR 2 SAMPLE TEXT AUTHOR 2 SAMPLE TEXT", "ups": "100", "downs": "10"}, - "3": {"id": "3", "title": "Third book", "author": "Author3", "url": null, "content": null, "ups": null, "downs": null }, - "4": {"id": "4", "title": "Fourth book", "author": "Author4", "url": "https://www.amazon.com/", "content": "SAMPLE TEXT AUTHOR 4 SAMPLE TEXT AUTHOR 4 SAMPLE TEXT AUTHOR 4 SAMPLE TEXT AUTHOR 4 SAMPLE TEXT AUTHOR 4 SAMPLE TEXT AUTHOR 4 SAMPLE TEXT AUTHOR 4 SAMPLE TEXT AUTHOR 4", "ups": "1000", "downs": "0"}, - "5": {"id": "5", "title": "Fifth book", "author": "Author5", "url": "https://www.amazon.com/", "content": "SAMPLE TEXT AUTHOR 5 SAMPLE TEXT AUTHOR 5 SAMPLE TEXT AUTHOR 5 SAMPLE TEXT AUTHOR 5 SAMPLE TEXT", "ups": "50", "downs": "0"} }; - - var relatedPosts = { - "1": [posts['4']], - "2": [posts['3'], posts['5']], - "3": [posts['2'], posts['1']], - "4": [posts['2'], posts['1']], - "5": [] - }; - const isBatch = Array.isArray(event); - if (isBatch) { - console.log("Got an BatchInvoke Request. The payload has %d items to resolve.", event.length); - const field = event[0].field; - switch(field) { - case "relatedPostsMaxBatchSize": - case "relatedPosts": - var results = []; - // the response MUST contain the same number - // of entries as the payload array - for (var i=0; i< event.length; i++) { - console.log("post {}", JSON.stringify(event[i].source)); - results.push(relatedPosts[event[i].source.id]); - } - console.log("results {}", JSON.stringify(results)); - callback(null, results); - break; - default: - callback("Unknown field, unable to resolve" + field, null); - break; - } - } - else { - console.log("Got an Invoke Request."); - switch(event.field) { - case "getPost": - var id = event.arguments.id; - callback(null, posts[id]); - break; - case "allPosts": - var values = []; - for(var d in posts){ - values.push(posts[d]); - } - callback(null, values); - break; - case "addPost": - // return the arguments back - callback(null, event.arguments); - break; - case "addPostErrorWithData": - var id = event.arguments.id; - var result = posts[id]; - // attached additional error information to the post - result.errorMessage = 'Error with the mutation, data has changed'; - result.errorType = 'MUTATION_ERROR'; - callback(null, result); - break; - default: - callback("Unknown field, unable to resolve" + event.field, null); - break; - } - } -}; \ No newline at end of file +"use strict";exports.handler=(event,context,callback)=>{console.log("Received event {}",JSON.stringify(event,3));var posts={1:{id:"1",title:"First book",author:"Author1",url:"https://amazon.com/",content:"SAMPLE TEXT AUTHOR 1 SAMPLE TEXT AUTHOR 1 SAMPLE TEXT AUTHOR 1 SAMPLE TEXT AUTHOR 1 SAMPLE TEXT AUTHOR 1 SAMPLE TEXT AUTHOR 1",ups:"100",downs:"10"},2:{id:"2",title:"Second book",author:"Author2",url:"https://amazon.com",content:"SAMPLE TEXT AUTHOR 2 SAMPLE TEXT AUTHOR 2 SAMPLE TEXT",ups:"100",downs:"10"},3:{id:"3",title:"Third book",author:"Author3",url:null,content:null,ups:null,downs:null},4:{id:"4",title:"Fourth book",author:"Author4",url:"https://www.amazon.com/",content:"SAMPLE TEXT AUTHOR 4 SAMPLE TEXT AUTHOR 4 SAMPLE TEXT AUTHOR 4 SAMPLE TEXT AUTHOR 4 SAMPLE TEXT AUTHOR 4 SAMPLE TEXT AUTHOR 4 SAMPLE TEXT AUTHOR 4 SAMPLE TEXT AUTHOR 4",ups:"1000",downs:"0"},5:{id:"5",title:"Fifth book",author:"Author5",url:"https://www.amazon.com/",content:"SAMPLE TEXT AUTHOR 5 SAMPLE TEXT AUTHOR 5 SAMPLE TEXT AUTHOR 5 SAMPLE TEXT AUTHOR 5 SAMPLE TEXT",ups:"50",downs:"0"}},relatedPosts={1:[posts[4]],2:[posts[3],posts[5]],3:[posts[2],posts[1]],4:[posts[2],posts[1]],5:[]};if(Array.isArray(event)){console.log("Got an BatchInvoke Request. The payload has %d items to resolve.",event.length);const field=event[0].field;switch(field){case"relatedPostsMaxBatchSize":case"relatedPosts":for(var results=[],i=0;i this.accessPolicy.toJSON() }), - encryptionKeyArn: props.encryptionKey && props.encryptionKey.keyArn, + encryptionKeyArn: props.encryptionKey && props.encryptionKey.keyRef.keyArn, notifications, lockConfiguration: renderLockConfiguration(this, props.lockConfiguration), }); diff --git a/packages/aws-cdk-lib/aws-batch/lib/managed-compute-environment.ts b/packages/aws-cdk-lib/aws-batch/lib/managed-compute-environment.ts index e6a5ec999a2cc..4da2c55a2ea1f 100644 --- a/packages/aws-cdk-lib/aws-batch/lib/managed-compute-environment.ts +++ b/packages/aws-cdk-lib/aws-batch/lib/managed-compute-environment.ts @@ -1,4 +1,4 @@ -import { Construct } from 'constructs'; +import { Construct, IConstruct } from 'constructs'; import { CfnComputeEnvironment } from './batch.generated'; import { IComputeEnvironment, ComputeEnvironmentBase, ComputeEnvironmentProps } from './compute-environment-base'; import * as ec2 from '../../aws-ec2'; @@ -595,7 +595,7 @@ export interface ManagedEc2EcsComputeEnvironmentProps extends ManagedComputeEnvi * * @default - no placement group */ - readonly placementGroup?: ec2.IPlacementGroup; + readonly placementGroup?: ec2.IPlacementGroupRef; } /** @@ -650,8 +650,8 @@ export class ManagedEc2EcsComputeEnvironment extends ManagedComputeEnvironmentBa public readonly instanceRole?: iam.IRole; public readonly launchTemplate?: ec2.ILaunchTemplate; public readonly minvCpus?: number; - public readonly placementGroup?: ec2.IPlacementGroup; + private readonly _placementGroup?: ec2.IPlacementGroupRef; private readonly instanceProfile: iam.CfnInstanceProfile; constructor(scope: Construct, id: string, props: ManagedEc2EcsComputeEnvironmentProps) { @@ -684,7 +684,7 @@ export class ManagedEc2EcsComputeEnvironment extends ManagedComputeEnvironmentBa this.launchTemplate = props.launchTemplate; this.minvCpus = props.minvCpus ?? DEFAULT_MIN_VCPUS; - this.placementGroup = props.placementGroup; + this._placementGroup = props.placementGroup; validateVCpus(this, this.minvCpus, this.maxvCpus); validateSpotConfig(this, this.spot, this.spotBidPercentage, this.spotFleetRole); @@ -713,7 +713,7 @@ export class ManagedEc2EcsComputeEnvironment extends ManagedComputeEnvironmentBa imageType: image.imageType ?? EcsMachineImageType.ECS_AL2, }; }), - placementGroup: this.placementGroup?.placementGroupName, + placementGroup: props.placementGroup?.placementGroupRef.groupName, tags: this.tags.renderedTags as any, }, }); @@ -728,6 +728,10 @@ export class ManagedEc2EcsComputeEnvironment extends ManagedComputeEnvironmentBa this.node.addValidation({ validate: () => validateInstances(this.instanceTypes, this.instanceClasses, props.useOptimalInstanceClasses) }); } + public get placementGroup(): ec2.IPlacementGroup | undefined { + return this._placementGroup ? asPlacementGroup(this._placementGroup, this) : undefined; + } + @MethodMetadata() public addInstanceType(instanceType: ec2.InstanceType): void { this.instanceTypes.push(instanceType); @@ -984,7 +988,7 @@ export interface ManagedEc2EksComputeEnvironmentProps extends ManagedComputeEnvi * * @default - no placement group */ - readonly placementGroup?: ec2.IPlacementGroup; + readonly placementGroup?: ec2.IPlacementGroupRef; } /** @@ -1010,8 +1014,8 @@ export class ManagedEc2EksComputeEnvironment extends ManagedComputeEnvironmentBa public readonly instanceRole?: iam.IRole; public readonly launchTemplate?: ec2.ILaunchTemplate; public readonly minvCpus?: number; - public readonly placementGroup?: ec2.IPlacementGroup; + private readonly _placementGroup?: ec2.IPlacementGroupRef; private readonly instanceProfile: iam.CfnInstanceProfile; constructor(scope: Construct, id: string, props: ManagedEc2EksComputeEnvironmentProps) { @@ -1037,7 +1041,7 @@ export class ManagedEc2EksComputeEnvironment extends ManagedComputeEnvironmentBa this.launchTemplate = props.launchTemplate; this.minvCpus = props.minvCpus ?? DEFAULT_MIN_VCPUS; - this.placementGroup = props.placementGroup; + this._placementGroup = props.placementGroup; validateVCpus(this, this.minvCpus, this.maxvCpus); validateSpotConfig(this, this.spot, this.spotBidPercentage); @@ -1067,7 +1071,7 @@ export class ManagedEc2EksComputeEnvironment extends ManagedComputeEnvironmentBa imageType: image.imageType ?? EksMachineImageType.EKS_AL2, }; }), - placementGroup: this.placementGroup?.placementGroupName, + placementGroup: props.placementGroup?.placementGroupRef.groupName, tags: this.tags.renderedTags as any, }, }); @@ -1082,6 +1086,10 @@ export class ManagedEc2EksComputeEnvironment extends ManagedComputeEnvironmentBa this.node.addValidation({ validate: () => validateInstances(this.instanceTypes, this.instanceClasses, props.useOptimalInstanceClasses) }); } + public get placementGroup(): ec2.IPlacementGroup | undefined { + return this._placementGroup ? asPlacementGroup(this._placementGroup, this) : undefined; + } + @MethodMetadata() public addInstanceType(instanceType: ec2.InstanceType): void { this.instanceTypes.push(instanceType); @@ -1270,3 +1278,10 @@ function baseManagedResourceProperties(baseComputeEnvironment: ManagedComputeEnv const DEFAULT_MIN_VCPUS = 0; const DEFAULT_MAX_VCPUS = 256; + +function asPlacementGroup(x: ec2.IPlacementGroupRef, scope: IConstruct): ec2.IPlacementGroup { + if ('placementGroupName' in x) { + return x as ec2.IPlacementGroup; + } + throw new ValidationError(`Provided placement group is not an instance of IPlacementGroup: ${x.constructor.name}`, scope); +} diff --git a/packages/aws-cdk-lib/aws-cloudfront/lib/cache-policy.ts b/packages/aws-cdk-lib/aws-cloudfront/lib/cache-policy.ts index 7f71cd318daa0..c9f58b7969788 100644 --- a/packages/aws-cdk-lib/aws-cloudfront/lib/cache-policy.ts +++ b/packages/aws-cdk-lib/aws-cloudfront/lib/cache-policy.ts @@ -1,13 +1,22 @@ -import { Construct } from 'constructs'; -import { CfnCachePolicy } from './cloudfront.generated'; -import { Duration, Names, Resource, Stack, Token, UnscopedValidationError, ValidationError, withResolved } from '../../core'; +import { Construct, Node } from 'constructs'; +import { CachePolicyReference, CfnCachePolicy, ICachePolicyRef } from './cloudfront.generated'; +import { + Duration, + Names, + Resource, + Stack, + Token, + UnscopedValidationError, + ValidationError, + withResolved, +} from '../../core'; import { addConstructMetadata } from '../../core/lib/metadata-resource'; import { propertyInjectable } from '../../core/lib/prop-injectable'; /** * Represents a Cache Policy */ -export interface ICachePolicy { +export interface ICachePolicy extends ICachePolicyRef { /** * The ID of the cache policy * @attribute @@ -130,18 +139,30 @@ export class CachePolicy extends Resource implements ICachePolicy { public static fromCachePolicyId(scope: Construct, id: string, cachePolicyId: string): ICachePolicy { return new class extends Resource implements ICachePolicy { public readonly cachePolicyId = cachePolicyId; + public readonly cachePolicyRef = { + cachePolicyId: cachePolicyId, + }; }(scope, id); } /** Use an existing managed cache policy. */ private static fromManagedCachePolicy(managedCachePolicyId: string): ICachePolicy { return new class implements ICachePolicy { + public get node(): Node { + throw new UnscopedValidationError('The result of fromManagedCachePolicy can not be used in this API'); + } + public readonly cachePolicyId = managedCachePolicyId; + public readonly cachePolicyRef = { + cachePolicyId: managedCachePolicyId, + }; }(); } public readonly cachePolicyId: string; + public readonly cachePolicyRef: CachePolicyReference; + constructor(scope: Construct, id: string, props: CachePolicyProps = {}) { super(scope, id, { physicalName: props.cachePolicyName, @@ -185,6 +206,7 @@ export class CachePolicy extends Resource implements ICachePolicy { }, }); + this.cachePolicyRef = resource.cachePolicyRef; this.cachePolicyId = resource.ref; } diff --git a/packages/aws-cdk-lib/aws-cloudfront/lib/distribution.ts b/packages/aws-cdk-lib/aws-cloudfront/lib/distribution.ts index 7239d8aa5c46a..19b0934064dea 100644 --- a/packages/aws-cdk-lib/aws-cloudfront/lib/distribution.ts +++ b/packages/aws-cdk-lib/aws-cloudfront/lib/distribution.ts @@ -1,21 +1,38 @@ import { Construct } from 'constructs'; -import { ICachePolicy } from './cache-policy'; -import { CfnDistribution, CfnMonitoringSubscription } from './cloudfront.generated'; +import { + CfnDistribution, + CfnMonitoringSubscription, + DistributionReference, + ICachePolicyRef, + IDistributionRef, + IKeyGroupRef, + IOriginRequestPolicyRef, + IRealtimeLogConfigRef, + IResponseHeadersPolicyRef, +} from './cloudfront.generated'; import { FunctionAssociation } from './function'; import { GeoRestriction } from './geo-restriction'; -import { IKeyGroup } from './key-group'; import { IOrigin, OriginBindConfig, OriginBindOptions, OriginSelectionCriteria } from './origin'; -import { IOriginRequestPolicy } from './origin-request-policy'; import { CacheBehavior } from './private/cache-behavior'; import { formatDistributionArn, grant } from './private/utils'; -import { IRealtimeLogConfig } from './realtime-log-config'; -import { IResponseHeadersPolicy } from './response-headers-policy'; import * as acm from '../../aws-certificatemanager'; import * as cloudwatch from '../../aws-cloudwatch'; import * as iam from '../../aws-iam'; import * as lambda from '../../aws-lambda'; import * as s3 from '../../aws-s3'; -import { ArnFormat, IResource, Lazy, Resource, Stack, Token, Duration, Names, FeatureFlags, Annotations, ValidationError } from '../../core'; +import { + Annotations, + ArnFormat, + Duration, + FeatureFlags, + IResource, + Lazy, + Names, + Resource, + Stack, + Token, + ValidationError, +} from '../../core'; import { addConstructMetadata, MethodMetadata } from '../../core/lib/metadata-resource'; import { propertyInjectable } from '../../core/lib/prop-injectable'; import { CLOUDFRONT_DEFAULT_SECURITY_POLICY_TLS_V1_2_2021 } from '../../cx-api'; @@ -23,7 +40,7 @@ import { CLOUDFRONT_DEFAULT_SECURITY_POLICY_TLS_V1_2_2021 } from '../../cx-api'; /** * Interface for CloudFront distributions */ -export interface IDistribution extends IResource { +export interface IDistribution extends IResource, IDistributionRef { /** * The domain name of the Distribution, such as d111111abcdef8.cloudfront.net. * @@ -297,6 +314,9 @@ export class Distribution extends Resource implements IDistribution { public readonly domainName: string; public readonly distributionDomainName: string; public readonly distributionId: string; + public readonly distributionRef = { + distributionId: attrs.distributionId, + }; constructor() { super(scope, id); @@ -320,6 +340,7 @@ export class Distribution extends Resource implements IDistribution { public readonly domainName: string; public readonly distributionDomainName: string; public readonly distributionId: string; + public readonly distributionRef: DistributionReference; private readonly httpVersion: HttpVersion; private readonly defaultBehavior: CacheBehavior; @@ -396,6 +417,7 @@ export class Distribution extends Resource implements IDistribution { }, }); + this.distributionRef = distribution.distributionRef; this.domainName = distribution.attrDomainName; this.distributionDomainName = distribution.attrDomainName; this.distributionId = distribution.ref; @@ -1073,7 +1095,7 @@ export interface AddBehaviorOptions { * @see https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/controlling-the-cache-key.html. * @default CachePolicy.CACHING_OPTIMIZED */ - readonly cachePolicy?: ICachePolicy; + readonly cachePolicy?: ICachePolicyRef; /** * Whether you want CloudFront to automatically compress certain files for this cache behavior. @@ -1090,21 +1112,21 @@ export interface AddBehaviorOptions { * * @default - none */ - readonly originRequestPolicy?: IOriginRequestPolicy; + readonly originRequestPolicy?: IOriginRequestPolicyRef; /** * The real-time log configuration to be attached to this cache behavior. * * @default - none */ - readonly realtimeLogConfig?: IRealtimeLogConfig; + readonly realtimeLogConfig?: IRealtimeLogConfigRef; /** * The response headers policy for this behavior. The response headers policy determines which headers are included in responses * * @default - none */ - readonly responseHeadersPolicy?: IResponseHeadersPolicy; + readonly responseHeadersPolicy?: IResponseHeadersPolicyRef; /** * Set this to true to indicate you want to distribute media files in the Microsoft Smooth Streaming format using this behavior. @@ -1141,7 +1163,7 @@ export interface AddBehaviorOptions { * @default - no KeyGroups are associated with cache behavior * @see https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/PrivateContent.html */ - readonly trustedKeyGroups?: IKeyGroup[]; + readonly trustedKeyGroups?: IKeyGroupRef[]; /** * Enables your CloudFront distribution to receive gRPC requests and to proxy them directly to your origins. diff --git a/packages/aws-cdk-lib/aws-cloudfront/lib/experimental/edge-function.ts b/packages/aws-cdk-lib/aws-cloudfront/lib/experimental/edge-function.ts index 9470d9879a7a8..335553ad02a7e 100644 --- a/packages/aws-cdk-lib/aws-cloudfront/lib/experimental/edge-function.ts +++ b/packages/aws-cdk-lib/aws-cloudfront/lib/experimental/edge-function.ts @@ -88,6 +88,19 @@ export class EdgeFunction extends Resource implements lambda.IVersion { this.node.defaultChild = this._edgeFunction; } + public get versionRef(): lambda.VersionReference { + return { + functionArn: this.functionArn, + }; + } + + public get functionRef(): lambda.FunctionReference { + return { + functionArn: this.functionArn, + functionName: this.functionName, + }; + } + public get lambda(): lambda.IFunction { return this._edgeFunction; } diff --git a/packages/aws-cdk-lib/aws-cloudfront/lib/function.ts b/packages/aws-cdk-lib/aws-cloudfront/lib/function.ts index ce2aff9e7e32a..18bfcd5a186a1 100644 --- a/packages/aws-cdk-lib/aws-cloudfront/lib/function.ts +++ b/packages/aws-cdk-lib/aws-cloudfront/lib/function.ts @@ -1,7 +1,6 @@ import * as fs from 'fs'; import { Construct } from 'constructs'; -import { CfnFunction } from './cloudfront.generated'; -import { IKeyValueStore } from './key-value-store'; +import { CfnFunction, FunctionReference, IFunctionRef, IKeyValueStoreRef } from './cloudfront.generated'; import { IResource, Lazy, Names, Resource, Stack, ValidationError } from '../../core'; import { addConstructMetadata } from '../../core/lib/metadata-resource'; import { propertyInjectable } from '../../core/lib/prop-injectable'; @@ -73,7 +72,7 @@ class FileCode extends FunctionCode { /** * Represents a CloudFront Function */ -export interface IFunction extends IResource { +export interface IFunction extends IResource, IFunctionRef { /** * The name of the function. * @attribute @@ -144,7 +143,7 @@ export interface FunctionProps { * * @default - no key value store is associated */ - readonly keyValueStore?: IKeyValueStore; + readonly keyValueStore?: IKeyValueStoreRef; /** * A flag that determines whether to automatically publish the function to the LIVE stage when it’s created. @@ -170,6 +169,9 @@ export class Function extends Resource implements IFunction { public readonly functionName = attrs.functionName; public readonly functionArn = attrs.functionArn; public readonly functionRuntime = attrs.functionRuntime ?? FunctionRuntime.JS_1_0.value; + public readonly functionRef = { + functionArn: attrs.functionArn, + }; }(scope, id); } @@ -194,6 +196,8 @@ export class Function extends Resource implements IFunction { */ public readonly functionRuntime: string; + public readonly functionRef: FunctionReference; + constructor(scope: Construct, id: string, props: FunctionProps) { super(scope, id); // Enhanced CDK Analytics Telemetry @@ -214,11 +218,12 @@ export class Function extends Resource implements IFunction { functionConfig: { comment: props.comment ?? this.functionName, runtime: this.functionRuntime, - keyValueStoreAssociations: props.keyValueStore ? [{ keyValueStoreArn: props.keyValueStore.keyValueStoreArn }] : undefined, + keyValueStoreAssociations: props.keyValueStore ? [{ keyValueStoreArn: props.keyValueStore.keyValueStoreRef.keyValueStoreArn }] : undefined, }, name: this.functionName, }); + this.functionRef = resource.functionRef; this.functionArn = resource.attrFunctionArn; this.functionStage = resource.attrStage; } @@ -262,7 +267,7 @@ export interface FunctionAssociation { /** * The CloudFront function that will be invoked. */ - readonly function: IFunction; + readonly function: IFunctionRef; /** The type of event which should invoke the function. */ readonly eventType: FunctionEventType; diff --git a/packages/aws-cdk-lib/aws-cloudfront/lib/key-group.ts b/packages/aws-cdk-lib/aws-cloudfront/lib/key-group.ts index 1dc6acbfb8557..551fd3c4bd6bd 100644 --- a/packages/aws-cdk-lib/aws-cloudfront/lib/key-group.ts +++ b/packages/aws-cdk-lib/aws-cloudfront/lib/key-group.ts @@ -1,6 +1,5 @@ import { Construct } from 'constructs'; -import { CfnKeyGroup } from './cloudfront.generated'; -import { IPublicKey } from './public-key'; +import { CfnKeyGroup, IKeyGroupRef, IPublicKeyRef, KeyGroupReference } from './cloudfront.generated'; import { IResource, Names, Resource } from '../../core'; import { addConstructMetadata } from '../../core/lib/metadata-resource'; import { propertyInjectable } from '../../core/lib/prop-injectable'; @@ -8,7 +7,7 @@ import { propertyInjectable } from '../../core/lib/prop-injectable'; /** * Represents a Key Group */ -export interface IKeyGroup extends IResource { +export interface IKeyGroup extends IResource, IKeyGroupRef { /** * The ID of the key group. * @attribute @@ -35,7 +34,7 @@ export interface KeyGroupProps { /** * A list of public keys to add to the key group. */ - readonly items: IPublicKey[]; + readonly items: IPublicKeyRef[]; } /** @@ -52,10 +51,15 @@ export class KeyGroup extends Resource implements IKeyGroup { public static fromKeyGroupId(scope: Construct, id: string, keyGroupId: string): IKeyGroup { return new class extends Resource implements IKeyGroup { public readonly keyGroupId = keyGroupId; + public readonly keyGroupRef = { + keyGroupId: keyGroupId, + }; }(scope, id); } public readonly keyGroupId: string; + public readonly keyGroupRef: KeyGroupReference; + constructor(scope: Construct, id: string, props: KeyGroupProps) { super(scope, id); // Enhanced CDK Analytics Telemetry @@ -65,10 +69,11 @@ export class KeyGroup extends Resource implements IKeyGroup { keyGroupConfig: { name: props.keyGroupName ?? this.generateName(), comment: props.comment, - items: props.items.map(key => key.publicKeyId), + items: props.items.map(key => key.publicKeyRef.publicKeyId), }, }); + this.keyGroupRef = resource.keyGroupRef; this.keyGroupId = resource.ref; } diff --git a/packages/aws-cdk-lib/aws-cloudfront/lib/key-value-store.ts b/packages/aws-cdk-lib/aws-cloudfront/lib/key-value-store.ts index 937a21a208b4f..68c000abbea37 100644 --- a/packages/aws-cdk-lib/aws-cloudfront/lib/key-value-store.ts +++ b/packages/aws-cdk-lib/aws-cloudfront/lib/key-value-store.ts @@ -2,10 +2,10 @@ import * as fs from 'fs'; import { join } from 'path'; import { Construct } from 'constructs'; -import { CfnKeyValueStore } from './cloudfront.generated'; +import { CfnKeyValueStore, IKeyValueStoreRef, KeyValueStoreReference } from './cloudfront.generated'; import * as s3 from '../../aws-s3'; import * as s3_assets from '../../aws-s3-assets'; -import { Resource, IResource, Lazy, Names, Stack, Arn, ArnFormat, FileSystem, ValidationError } from '../../core'; +import { Arn, ArnFormat, FileSystem, IResource, Lazy, Names, Resource, Stack, ValidationError } from '../../core'; import { addConstructMetadata } from '../../core/lib/metadata-resource'; import { propertyInjectable } from '../../core/lib/prop-injectable'; @@ -191,7 +191,7 @@ export interface KeyValueStoreProps { /** * A CloudFront Key Value Store. */ -export interface IKeyValueStore extends IResource { +export interface IKeyValueStore extends IResource, IKeyValueStoreRef { /** * The ARN of the Key Value Store. * @@ -235,6 +235,10 @@ export class KeyValueStore extends Resource implements IKeyValueStore { return new class Import extends Resource implements IKeyValueStore { readonly keyValueStoreArn: string = keyValueStoreArn; readonly keyValueStoreId: string = storeId!; + readonly keyValueStoreRef = { + keyValueStoreArn: keyValueStoreArn, + keyValueStoreName: storeId!, + }; constructor() { super(scope, id, { environmentFromArn: keyValueStoreArn, @@ -250,6 +254,7 @@ export class KeyValueStore extends Resource implements IKeyValueStore { readonly keyValueStoreArn: string; readonly keyValueStoreId: string; readonly keyValueStoreStatus: string; + readonly keyValueStoreRef: KeyValueStoreReference; constructor(scope: Construct, id: string, props?: KeyValueStoreProps) { super(scope, id, { @@ -266,6 +271,7 @@ export class KeyValueStore extends Resource implements IKeyValueStore { importSource: props?.source?._bind(this), }); + this.keyValueStoreRef = resource.keyValueStoreRef; this.keyValueStoreArn = resource.attrArn; this.keyValueStoreId = resource.attrId; this.keyValueStoreStatus = resource.attrStatus; diff --git a/packages/aws-cdk-lib/aws-cloudfront/lib/origin-access-identity.ts b/packages/aws-cdk-lib/aws-cloudfront/lib/origin-access-identity.ts index 017d2f2243b48..bd21d3ee18439 100644 --- a/packages/aws-cdk-lib/aws-cloudfront/lib/origin-access-identity.ts +++ b/packages/aws-cdk-lib/aws-cloudfront/lib/origin-access-identity.ts @@ -1,5 +1,9 @@ import { Construct } from 'constructs'; -import { CfnCloudFrontOriginAccessIdentity } from './cloudfront.generated'; +import { + CfnCloudFrontOriginAccessIdentity, + CloudFrontOriginAccessIdentityReference, + ICloudFrontOriginAccessIdentityRef, +} from './cloudfront.generated'; import * as iam from '../../aws-iam'; import * as cdk from '../../core'; import { addConstructMetadata } from '../../core/lib/metadata-resource'; @@ -20,7 +24,7 @@ export interface OriginAccessIdentityProps { /** * Interface for CloudFront OriginAccessIdentity */ -export interface IOriginAccessIdentity extends cdk.IResource, iam.IGrantable { +export interface IOriginAccessIdentity extends cdk.IResource, iam.IGrantable, ICloudFrontOriginAccessIdentityRef { /** * The Origin Access Identity Id (physical id) * It is misnamed and superseded by the correctly named originAccessIdentityId @@ -108,6 +112,9 @@ export class OriginAccessIdentity extends OriginAccessIdentityBase implements IO public readonly originAccessIdentityId = originAccessIdentityId; public readonly originAccessIdentityName = originAccessIdentityId; public readonly grantPrincipal = new iam.ArnPrincipal(this.arn()); + public readonly cloudFrontOriginAccessIdentityRef = { + cloudFrontOriginAccessIdentityId: originAccessIdentityId, + }; constructor(s: Construct, i: string) { super(s, i, { physicalName: originAccessIdentityId }); } @@ -149,6 +156,8 @@ export class OriginAccessIdentity extends OriginAccessIdentityBase implements IO */ public readonly originAccessIdentityId: string; + public readonly cloudFrontOriginAccessIdentityRef: CloudFrontOriginAccessIdentityReference; + /** * CDK L1 resource */ @@ -164,6 +173,7 @@ export class OriginAccessIdentity extends OriginAccessIdentityBase implements IO this.resource = new CfnCloudFrontOriginAccessIdentity(this, 'Resource', { cloudFrontOriginAccessIdentityConfig: { comment }, }); + this.cloudFrontOriginAccessIdentityRef = this.resource.cloudFrontOriginAccessIdentityRef; // physical id - OAI Id this.originAccessIdentityId = this.getResourceNameAttribute(this.resource.ref); diff --git a/packages/aws-cdk-lib/aws-cloudfront/lib/origin-request-policy.ts b/packages/aws-cdk-lib/aws-cloudfront/lib/origin-request-policy.ts index 1b2cf143cea41..dade440dc6db1 100644 --- a/packages/aws-cdk-lib/aws-cloudfront/lib/origin-request-policy.ts +++ b/packages/aws-cdk-lib/aws-cloudfront/lib/origin-request-policy.ts @@ -1,5 +1,5 @@ -import { Construct } from 'constructs'; -import { CfnOriginRequestPolicy } from './cloudfront.generated'; +import { Construct, Node } from 'constructs'; +import { CfnOriginRequestPolicy, IOriginRequestPolicyRef, OriginRequestPolicyReference } from './cloudfront.generated'; import { Names, Resource, Token, UnscopedValidationError, ValidationError } from '../../core'; import { addConstructMetadata } from '../../core/lib/metadata-resource'; import { propertyInjectable } from '../../core/lib/prop-injectable'; @@ -7,7 +7,7 @@ import { propertyInjectable } from '../../core/lib/prop-injectable'; /** * Represents a Origin Request Policy */ -export interface IOriginRequestPolicy { +export interface IOriginRequestPolicy extends IOriginRequestPolicyRef { /** * The ID of the origin request policy * @attribute @@ -79,17 +79,28 @@ export class OriginRequestPolicy extends Resource implements IOriginRequestPolic public static fromOriginRequestPolicyId(scope: Construct, id: string, originRequestPolicyId: string): IOriginRequestPolicy { return new class extends Resource implements IOriginRequestPolicy { public readonly originRequestPolicyId = originRequestPolicyId; + public readonly originRequestPolicyRef = { + originRequestPolicyId: originRequestPolicyId, + }; }(scope, id); } /** Use an existing managed origin request policy. */ private static fromManagedOriginRequestPolicy(managedOriginRequestPolicyId: string): IOriginRequestPolicy { return new class implements IOriginRequestPolicy { + public get node(): Node { + throw new UnscopedValidationError('The result of fromManagedOriginRequestPolicy can not be used in this API'); + } + public readonly originRequestPolicyId = managedOriginRequestPolicyId; + public readonly originRequestPolicyRef = { + originRequestPolicyId: managedOriginRequestPolicyId, + }; }(); } public readonly originRequestPolicyId: string; + public readonly originRequestPolicyRef: OriginRequestPolicyReference; constructor(scope: Construct, id: string, props: OriginRequestPolicyProps = {}) { super(scope, id, { @@ -126,6 +137,7 @@ export class OriginRequestPolicy extends Resource implements IOriginRequestPolic }, }); + this.originRequestPolicyRef = resource.originRequestPolicyRef; this.originRequestPolicyId = resource.ref; } } diff --git a/packages/aws-cdk-lib/aws-cloudfront/lib/private/cache-behavior.ts b/packages/aws-cdk-lib/aws-cloudfront/lib/private/cache-behavior.ts index 8aaa82a0d39bb..7a8179b180616 100644 --- a/packages/aws-cdk-lib/aws-cloudfront/lib/private/cache-behavior.ts +++ b/packages/aws-cdk-lib/aws-cloudfront/lib/private/cache-behavior.ts @@ -55,15 +55,15 @@ export class CacheBehavior { targetOriginId: this.originId, allowedMethods: this.props.allowedMethods?.methods, cachedMethods: this.props.cachedMethods?.methods, - cachePolicyId: (this.props.cachePolicy ?? CachePolicy.CACHING_OPTIMIZED).cachePolicyId, + cachePolicyId: (this.props.cachePolicy?.cachePolicyRef ?? CachePolicy.CACHING_OPTIMIZED).cachePolicyId, compress: this.props.compress ?? true, - originRequestPolicyId: this.props.originRequestPolicy?.originRequestPolicyId, - realtimeLogConfigArn: this.props?.realtimeLogConfig?.realtimeLogConfigArn, - responseHeadersPolicyId: this.props.responseHeadersPolicy?.responseHeadersPolicyId, + originRequestPolicyId: this.props.originRequestPolicy?.originRequestPolicyRef.originRequestPolicyId, + realtimeLogConfigArn: this.props?.realtimeLogConfig?.realtimeLogConfigRef.realtimeLogConfigArn, + responseHeadersPolicyId: this.props.responseHeadersPolicy?.responseHeadersPolicyRef.responseHeadersPolicyId, smoothStreaming: this.props.smoothStreaming, viewerProtocolPolicy: this.props.viewerProtocolPolicy ?? ViewerProtocolPolicy.ALLOW_ALL, functionAssociations: this.props.functionAssociations?.map(association => ({ - functionArn: association.function.functionArn, + functionArn: association.function.functionRef.functionArn, eventType: association.eventType.toString(), })), lambdaFunctionAssociations: this.props.edgeLambdas?.map(edgeLambda => ({ @@ -71,7 +71,7 @@ export class CacheBehavior { eventType: edgeLambda.eventType.toString(), includeBody: edgeLambda.includeBody, })), - trustedKeyGroups: this.props.trustedKeyGroups?.map(keyGroup => keyGroup.keyGroupId), + trustedKeyGroups: this.props.trustedKeyGroups?.map(keyGroup => keyGroup.keyGroupRef.keyGroupId), grpcConfig: this.props.enableGrpc !== undefined ? { enabled: this.props.enableGrpc, diff --git a/packages/aws-cdk-lib/aws-cloudfront/lib/public-key.ts b/packages/aws-cdk-lib/aws-cloudfront/lib/public-key.ts index 7aced44e88891..dc96da9eea5fa 100644 --- a/packages/aws-cdk-lib/aws-cloudfront/lib/public-key.ts +++ b/packages/aws-cdk-lib/aws-cloudfront/lib/public-key.ts @@ -1,5 +1,5 @@ import { Construct } from 'constructs'; -import { CfnPublicKey } from './cloudfront.generated'; +import { CfnPublicKey, IPublicKeyRef, PublicKeyReference } from './cloudfront.generated'; import { IResource, Names, Resource, Token, ValidationError } from '../../core'; import { addConstructMetadata } from '../../core/lib/metadata-resource'; import { propertyInjectable } from '../../core/lib/prop-injectable'; @@ -7,7 +7,7 @@ import { propertyInjectable } from '../../core/lib/prop-injectable'; /** * Represents a Public Key */ -export interface IPublicKey extends IResource { +export interface IPublicKey extends IResource, IPublicKeyRef { /** * The ID of the key group. * @attribute @@ -54,10 +54,14 @@ export class PublicKey extends Resource implements IPublicKey { public static fromPublicKeyId(scope: Construct, id: string, publicKeyId: string): IPublicKey { return new class extends Resource implements IPublicKey { public readonly publicKeyId = publicKeyId; + public readonly publicKeyRef = { + publicKeyId: publicKeyId, + }; }(scope, id); } public readonly publicKeyId: string; + public readonly publicKeyRef: PublicKeyReference; constructor(scope: Construct, id: string, props: PublicKeyProps) { super(scope, id); @@ -77,6 +81,7 @@ export class PublicKey extends Resource implements IPublicKey { }, }); + this.publicKeyRef = resource.publicKeyRef; this.publicKeyId = resource.ref; } diff --git a/packages/aws-cdk-lib/aws-cloudfront/lib/realtime-log-config.ts b/packages/aws-cdk-lib/aws-cloudfront/lib/realtime-log-config.ts index e0360b83e00b2..a72894f309c1b 100644 --- a/packages/aws-cdk-lib/aws-cloudfront/lib/realtime-log-config.ts +++ b/packages/aws-cdk-lib/aws-cloudfront/lib/realtime-log-config.ts @@ -1,5 +1,5 @@ import { Construct } from 'constructs'; -import { CfnRealtimeLogConfig } from './cloudfront.generated'; +import { CfnRealtimeLogConfig, IRealtimeLogConfigRef, RealtimeLogConfigReference } from './cloudfront.generated'; import { Endpoint } from '../'; import { IResource, Lazy, Names, Resource, ValidationError } from '../../core'; import { addConstructMetadata } from '../../core/lib/metadata-resource'; @@ -8,7 +8,7 @@ import { propertyInjectable } from '../../core/lib/prop-injectable'; /** * Represents Realtime Log Configuration */ -export interface IRealtimeLogConfig extends IResource { +export interface IRealtimeLogConfig extends IResource, IRealtimeLogConfigRef { /** * The name of the realtime log config. * @attribute @@ -58,6 +58,7 @@ export class RealtimeLogConfig extends Resource implements IRealtimeLogConfig { public static readonly PROPERTY_INJECTION_ID: string = 'aws-cdk-lib.aws-cloudfront.RealtimeLogConfig'; public readonly realtimeLogConfigName: string; public readonly realtimeLogConfigArn: string; + public readonly realtimeLogConfigRef: RealtimeLogConfigReference; constructor(scope: Construct, id: string, props: RealtimeLogConfigProps) { super(scope, id, { @@ -78,6 +79,7 @@ export class RealtimeLogConfig extends Resource implements IRealtimeLogConfig { name: this.physicalName, samplingRate: props.samplingRate, }); + this.realtimeLogConfigRef = resource.realtimeLogConfigRef; this.realtimeLogConfigArn = this.getResourceArnAttribute(resource.attrArn, { service: 'cloudfront', diff --git a/packages/aws-cdk-lib/aws-cloudfront/lib/response-headers-policy.ts b/packages/aws-cdk-lib/aws-cloudfront/lib/response-headers-policy.ts index ea7816c0c59b1..9617057d1c3ed 100644 --- a/packages/aws-cdk-lib/aws-cloudfront/lib/response-headers-policy.ts +++ b/packages/aws-cdk-lib/aws-cloudfront/lib/response-headers-policy.ts @@ -1,13 +1,17 @@ -import { Construct } from 'constructs'; -import { CfnResponseHeadersPolicy } from './cloudfront.generated'; -import { Duration, Names, Resource, Token, ValidationError, withResolved } from '../../core'; +import { Construct, Node } from 'constructs'; +import { + CfnResponseHeadersPolicy, + IResponseHeadersPolicyRef, + ResponseHeadersPolicyReference, +} from './cloudfront.generated'; +import { Duration, Names, Resource, Token, UnscopedValidationError, ValidationError, withResolved } from '../../core'; import { addConstructMetadata } from '../../core/lib/metadata-resource'; import { propertyInjectable } from '../../core/lib/prop-injectable'; /** * Represents a response headers policy. */ -export interface IResponseHeadersPolicy { +export interface IResponseHeadersPolicy extends IResponseHeadersPolicyRef { /** * The ID of the response headers policy * @attribute @@ -97,17 +101,28 @@ export class ResponseHeadersPolicy extends Resource implements IResponseHeadersP public static fromResponseHeadersPolicyId(scope: Construct, id: string, responseHeadersPolicyId: string): IResponseHeadersPolicy { class Import extends Resource implements IResponseHeadersPolicy { public readonly responseHeadersPolicyId = responseHeadersPolicyId; + public readonly responseHeadersPolicyRef = { + responseHeadersPolicyId: responseHeadersPolicyId, + }; } return new Import(scope, id); } private static fromManagedResponseHeadersPolicy(managedResponseHeadersPolicyId: string): IResponseHeadersPolicy { return new class implements IResponseHeadersPolicy { + public get node(): Node { + throw new UnscopedValidationError('The result of fromManagedResponseHeadersPolicy can not be used in this API'); + } + public readonly responseHeadersPolicyId = managedResponseHeadersPolicyId; + public readonly responseHeadersPolicyRef = { + responseHeadersPolicyId: managedResponseHeadersPolicyId, + }; }; } public readonly responseHeadersPolicyId: string; + public readonly responseHeadersPolicyRef: ResponseHeadersPolicyReference; constructor(scope: Construct, id: string, props: ResponseHeadersPolicyProps = {}) { super(scope, id, { @@ -132,6 +147,7 @@ export class ResponseHeadersPolicy extends Resource implements IResponseHeadersP }, }); + this.responseHeadersPolicyRef = resource.responseHeadersPolicyRef; this.responseHeadersPolicyId = resource.ref; } diff --git a/packages/aws-cdk-lib/aws-cloudfront/lib/vpc-origin.ts b/packages/aws-cdk-lib/aws-cloudfront/lib/vpc-origin.ts index 26d18c576600b..b1d87b560cfb1 100644 --- a/packages/aws-cdk-lib/aws-cloudfront/lib/vpc-origin.ts +++ b/packages/aws-cdk-lib/aws-cloudfront/lib/vpc-origin.ts @@ -1,16 +1,26 @@ import { Construct } from 'constructs'; -import { CfnVpcOrigin } from './cloudfront.generated'; +import { CfnVpcOrigin, IVpcOriginRef, VpcOriginReference } from './cloudfront.generated'; import { OriginProtocolPolicy, OriginSslPolicy } from '../'; import { IInstance } from '../../aws-ec2'; import { IApplicationLoadBalancer, INetworkLoadBalancer } from '../../aws-elasticloadbalancingv2'; -import { ArnFormat, IResource, ITaggableV2, Names, Resource, Stack, TagManager, Token, ValidationError } from '../../core'; +import { + ArnFormat, + IResource, + ITaggableV2, + Names, + Resource, + Stack, + TagManager, + Token, + ValidationError, +} from '../../core'; import { addConstructMetadata } from '../../core/lib/metadata-resource'; import { propertyInjectable } from '../../core/lib/prop-injectable'; /** * Represents a VPC origin. */ -export interface IVpcOrigin extends IResource { +export interface IVpcOrigin extends IResource, IVpcOriginRef { /** * The VPC origin ARN. * @attribute @@ -184,6 +194,10 @@ export class VpcOrigin extends Resource implements IVpcOrigin, ITaggableV2 { readonly vpcOriginArn = vpcOriginArn; readonly vpcOriginId = vpcOriginId!; readonly domainName = attrs.domainName; + readonly vpcOriginRef = { + vpcOriginArn: vpcOriginArn, + vpcOriginId: vpcOriginId!, + }; } return new Import(scope, id); @@ -204,6 +218,8 @@ export class VpcOrigin extends Resource implements IVpcOrigin, ITaggableV2 { */ readonly domainName?: string; + readonly vpcOriginRef: VpcOriginReference; + readonly cdkTagManager: TagManager; constructor(scope: Construct, id: string, props: VpcOriginProps) { @@ -225,6 +241,7 @@ export class VpcOrigin extends Resource implements IVpcOrigin, ITaggableV2 { }, }); + this.vpcOriginRef = resource.vpcOriginRef; this.vpcOriginArn = this.getResourceArnAttribute(resource.attrArn, { service: 'cloudfront', region: '', diff --git a/packages/aws-cdk-lib/aws-cloudfront/lib/web-distribution.ts b/packages/aws-cdk-lib/aws-cloudfront/lib/web-distribution.ts index eed039f5d366c..25d444b53cea6 100644 --- a/packages/aws-cdk-lib/aws-cloudfront/lib/web-distribution.ts +++ b/packages/aws-cdk-lib/aws-cloudfront/lib/web-distribution.ts @@ -1,6 +1,15 @@ import { Construct } from 'constructs'; -import { CfnDistribution } from './cloudfront.generated'; -import { HttpVersion, IDistribution, LambdaEdgeEventType, OriginProtocolPolicy, PriceClass, ViewerProtocolPolicy, SSLMethod, SecurityPolicyProtocol } from './distribution'; +import { CfnDistribution, DistributionReference } from './cloudfront.generated'; +import { + HttpVersion, + IDistribution, + LambdaEdgeEventType, + OriginProtocolPolicy, + PriceClass, + SecurityPolicyProtocol, + SSLMethod, + ViewerProtocolPolicy, +} from './distribution'; import { FunctionAssociation } from './function'; import { GeoRestriction } from './geo-restriction'; import { IKeyGroup } from './key-group'; @@ -760,6 +769,9 @@ export class CloudFrontWebDistribution extends cdk.Resource implements IDistribu public readonly domainName: string; public readonly distributionDomainName: string; public readonly distributionId: string; + public readonly distributionRef = { + distributionId: attrs.distributionId, + }; constructor() { super(scope, id); @@ -807,6 +819,8 @@ export class CloudFrontWebDistribution extends cdk.Resource implements IDistribu */ public readonly distributionId: string; + public readonly distributionRef: DistributionReference; + /** * Maps our methods to the string arrays they are */ @@ -1001,6 +1015,7 @@ export class CloudFrontWebDistribution extends cdk.Resource implements IDistribu } const distribution = new CfnDistribution(this, 'CFDistribution', { distributionConfig }); + this.distributionRef = distribution.distributionRef; this.node.defaultChild = distribution; this.domainName = distribution.attrDomainName; this.distributionDomainName = distribution.attrDomainName; @@ -1052,7 +1067,7 @@ export class CloudFrontWebDistribution extends cdk.Resource implements IDistribu if (input.functionAssociations) { toReturn = Object.assign(toReturn, { functionAssociations: input.functionAssociations.map(association => ({ - functionArn: association.function.functionArn, + functionArn: association.function.functionRef.functionArn, eventType: association.eventType.toString(), })), }); diff --git a/packages/aws-cdk-lib/aws-cloudfront/test/distribution.test.ts b/packages/aws-cdk-lib/aws-cloudfront/test/distribution.test.ts index d921ed393e216..caae9579c0ea1 100644 --- a/packages/aws-cdk-lib/aws-cloudfront/test/distribution.test.ts +++ b/packages/aws-cdk-lib/aws-cloudfront/test/distribution.test.ts @@ -920,10 +920,7 @@ describe('with CloudFront functions', () => { { EventType: 'viewer-request', FunctionARN: { - 'Fn::GetAtt': [ - 'TestFunction22AD90FC', - 'FunctionARN', - ], + Ref: 'TestFunction22AD90FC', }, }, ], @@ -1303,7 +1300,7 @@ test('render distribution behavior with realtime log config', () => { DistributionConfig: { DefaultCacheBehavior: { RealtimeLogConfigArn: { - 'Fn::GetAtt': ['RealtimeConfigB6004E8E', 'Arn'], + Ref: 'RealtimeConfigB6004E8E', }, }, }, @@ -1349,14 +1346,14 @@ test('render distribution behavior with realtime log config - multiple behaviors DistributionConfig: { DefaultCacheBehavior: { RealtimeLogConfigArn: { - 'Fn::GetAtt': ['RealtimeConfigB6004E8E', 'Arn'], + Ref: 'RealtimeConfigB6004E8E', }, TargetOriginId: 'StackMyDistOrigin1D6D5E535', }, CacheBehaviors: [{ PathPattern: '/api/*', RealtimeLogConfigArn: { - 'Fn::GetAtt': ['RealtimeConfigB6004E8E', 'Arn'], + Ref: 'RealtimeConfigB6004E8E', }, TargetOriginId: 'StackMyDistOrigin20B96F3AD', }], diff --git a/packages/aws-cdk-lib/aws-cloudfront/test/web-distribution.test.ts b/packages/aws-cdk-lib/aws-cloudfront/test/web-distribution.test.ts index 39a86dbfab129..8f6b642b7f850 100644 --- a/packages/aws-cdk-lib/aws-cloudfront/test/web-distribution.test.ts +++ b/packages/aws-cdk-lib/aws-cloudfront/test/web-distribution.test.ts @@ -810,10 +810,7 @@ added the ellipsis so a user would know there was more to r...`, { 'EventType': 'viewer-request', 'FunctionARN': { - 'Fn::GetAtt': [ - 'TestFunction22AD90FC', - 'FunctionARN', - ], + 'Ref': 'TestFunction22AD90FC', }, }, ], diff --git a/packages/aws-cdk-lib/aws-cloudtrail/lib/cloudtrail.ts b/packages/aws-cdk-lib/aws-cloudtrail/lib/cloudtrail.ts index 6acb4d0060b24..a50f4e11ac66c 100644 --- a/packages/aws-cdk-lib/aws-cloudtrail/lib/cloudtrail.ts +++ b/packages/aws-cdk-lib/aws-cloudtrail/lib/cloudtrail.ts @@ -85,7 +85,7 @@ export interface TrailProps { * @default - No encryption. * @deprecated - use encryptionKey instead. */ - readonly kmsKey?: kms.IKey; + readonly kmsKey?: kms.IKeyRef; /** The AWS Key Management Service (AWS KMS) key ID that you want to use to encrypt CloudTrail logs. * @@ -345,7 +345,7 @@ export class Trail extends Resource { isMultiRegionTrail: props.isMultiRegionTrail == null ? true : props.isMultiRegionTrail, includeGlobalServiceEvents: props.includeGlobalServiceEvents == null ? true : props.includeGlobalServiceEvents, trailName: this.physicalName, - kmsKeyId: props.encryptionKey?.keyArn ?? props.kmsKey?.keyArn, + kmsKeyId: props.encryptionKey?.keyArn ?? props.kmsKey?.keyRef.keyArn, s3BucketName: this.s3bucket.bucketName, s3KeyPrefix: props.s3KeyPrefix, cloudWatchLogsLogGroupArn: this.logGroup?.logGroupArn, @@ -458,7 +458,7 @@ export class Trail extends Resource { @MethodMetadata() public addS3EventSelector(s3Selector: S3EventSelector[], options: AddEventSelectorOptions = {}) { if (s3Selector.length === 0) { return; } - const dataResourceValues = s3Selector.map((sel) => `${sel.bucket.bucketArn}/${sel.objectPrefix ?? ''}`); + const dataResourceValues = s3Selector.map((sel) => `${sel.bucket.bucketRef.bucketArn}/${sel.objectPrefix ?? ''}`); return this.addEventSelector(DataResourceType.S3_OBJECT, dataResourceValues, options); } @@ -543,7 +543,7 @@ export enum ManagementEventSources { */ export interface S3EventSelector { /** S3 bucket */ - readonly bucket: s3.IBucket; + readonly bucket: s3.IBucketRef; /** * Data events for objects whose key matches this prefix will be logged. diff --git a/packages/aws-cdk-lib/aws-codecommit/lib/repository.ts b/packages/aws-cdk-lib/aws-codecommit/lib/repository.ts index f58e9523f84ec..d893f47de5132 100644 --- a/packages/aws-cdk-lib/aws-codecommit/lib/repository.ts +++ b/packages/aws-cdk-lib/aws-codecommit/lib/repository.ts @@ -505,7 +505,7 @@ export interface RepositoryProps { * * @default - Use an AWS managed key */ - readonly kmsKey?: kms.IKey; + readonly kmsKey?: kms.IKeyRef; } /** @@ -576,7 +576,7 @@ export class Repository extends RepositoryBase { repositoryDescription: props.description, triggers: Lazy.any({ produce: () => this.triggers }, { omitEmptyArray: true }), code: (props.code?.bind(this))?.code, - kmsKeyId: props.kmsKey?.keyArn, + kmsKeyId: props.kmsKey?.keyRef.keyArn, }); this.repositoryName = this.getResourceNameAttribute(repository.attrName); diff --git a/packages/aws-cdk-lib/aws-codepipeline-actions/lib/elastic-beanstalk/deploy-action.ts b/packages/aws-cdk-lib/aws-codepipeline-actions/lib/elastic-beanstalk/deploy-action.ts index 05251be979a49..84a1b6e7d80eb 100644 --- a/packages/aws-cdk-lib/aws-codepipeline-actions/lib/elastic-beanstalk/deploy-action.ts +++ b/packages/aws-cdk-lib/aws-codepipeline-actions/lib/elastic-beanstalk/deploy-action.ts @@ -1,6 +1,6 @@ -import { Construct } from 'constructs'; +import { Construct, Node } from 'constructs'; import * as codepipeline from '../../../aws-codepipeline'; -import { Aws } from '../../../core'; +import { Aws, UnscopedValidationError } from '../../../core'; import { Action } from '../action'; import { deployArtifactBounds } from '../common'; @@ -51,7 +51,15 @@ export class ElasticBeanstalkDeployAction extends Action { ): codepipeline.ActionConfig { // Per https://docs.aws.amazon.com/elasticbeanstalk/latest/dg/AWSHowTo.iam.managed-policies.html // it doesn't seem we can scope this down further for the codepipeline action. - options.role.addManagedPolicy({ managedPolicyArn: `arn:${Aws.PARTITION}:iam::aws:policy/AdministratorAccess-AWSElasticBeanstalk` }); + + const policyArn = `arn:${Aws.PARTITION}:iam::aws:policy/AdministratorAccess-AWSElasticBeanstalk`; + options.role.addManagedPolicy({ + get node(): Node { + throw new UnscopedValidationError('This object can not be used in this API'); + }, + managedPolicyArn: policyArn, + managedPolicyRef: { policyArn }, + }); // the Action's Role needs to read from the Bucket to get artifacts options.bucket.grantRead(options.role); diff --git a/packages/aws-cdk-lib/aws-cognito-identitypool/lib/identitypool.ts b/packages/aws-cdk-lib/aws-cognito-identitypool/lib/identitypool.ts index 57109f086bbbe..6b63886f1d1f4 100644 --- a/packages/aws-cdk-lib/aws-cognito-identitypool/lib/identitypool.ts +++ b/packages/aws-cdk-lib/aws-cognito-identitypool/lib/identitypool.ts @@ -1,7 +1,7 @@ import { Construct } from 'constructs'; import { IUserPoolAuthenticationProvider } from './identitypool-user-pool-authentication-provider'; import { CfnIdentityPool, CfnIdentityPoolRoleAttachment, IUserPool, IUserPoolClient } from '../../aws-cognito'; -import { IOpenIdConnectProvider, ISamlProvider, Role, FederatedPrincipal, IRole } from '../../aws-iam'; +import { Role, FederatedPrincipal, IRole, IRoleRef, IOIDCProviderRef, ISAMLProviderRef } from '../../aws-iam'; import { Resource, IResource, Stack, ArnFormat, Lazy, Token, ValidationError, UnscopedValidationError } from '../../core'; import { addConstructMetadata, MethodMetadata } from '../../core/lib/metadata-resource'; import { propertyInjectable } from '../../core/lib/prop-injectable'; @@ -253,13 +253,13 @@ export interface IdentityPoolAuthenticationProviders { * The OpenIdConnect Provider associated with this Identity Pool * @default - no OpenIdConnectProvider */ - readonly openIdConnectProviders?: IOpenIdConnectProvider[]; + readonly openIdConnectProviders?: IOIDCProviderRef[]; /** * The Security Assertion Markup Language provider associated with this Identity Pool * @default - no SamlProvider */ - readonly samlProviders?: ISamlProvider[]; + readonly samlProviders?: ISAMLProviderRef[]; /** * The developer provider name to associate with this Identity Pool @@ -463,11 +463,11 @@ export class IdentityPool extends Resource implements IIdentityPool { if (providers && providers.length) this.cognitoIdentityProviders = providers; const openIdConnectProviderArns = authProviders.openIdConnectProviders ? authProviders.openIdConnectProviders.map(openIdProvider => - openIdProvider.openIdConnectProviderArn, + openIdProvider.oidcProviderRef.oidcProviderArn, ) : undefined; const samlProviderArns = authProviders.samlProviders ? authProviders.samlProviders.map(samlProvider => - samlProvider.samlProviderArn, + samlProvider.samlProviderRef.samlProviderArn, ) : undefined; let supportedLoginProviders:any = {}; @@ -568,13 +568,13 @@ interface IdentityPoolRoleAttachmentProps { * Default authenticated (User) Role * @default - No default authenticated Role will be added */ - readonly authenticatedRole?: IRole; + readonly authenticatedRole?: IRoleRef; /** * Default unauthenticated (Guest) Role * @default - No default unauthenticated Role will be added */ - readonly unauthenticatedRole?: IRole; + readonly unauthenticatedRole?: IRoleRef; /** * Rules for mapping roles to users @@ -611,8 +611,8 @@ class IdentityPoolRoleAttachment extends Resource implements IIdentityPoolRoleAt let roles: any = undefined, roleMappings: any = undefined; if (props.authenticatedRole || props.unauthenticatedRole) { roles = {}; - if (props.authenticatedRole) roles.authenticated = props.authenticatedRole.roleArn; - if (props.unauthenticatedRole) roles.unauthenticated = props.unauthenticatedRole.roleArn; + if (props.authenticatedRole) roles.authenticated = props.authenticatedRole.roleRef.roleArn; + if (props.unauthenticatedRole) roles.unauthenticated = props.unauthenticatedRole.roleRef.roleArn; } if (mappings) { roleMappings = this.configureRoleMappings(...mappings); diff --git a/packages/aws-cdk-lib/aws-cognito/lib/user-pool-client.ts b/packages/aws-cdk-lib/aws-cognito/lib/user-pool-client.ts index 3804e50ac2ab6..ec92565c2d3c4 100644 --- a/packages/aws-cdk-lib/aws-cognito/lib/user-pool-client.ts +++ b/packages/aws-cdk-lib/aws-cognito/lib/user-pool-client.ts @@ -3,7 +3,7 @@ import { CfnUserPoolClient } from './cognito.generated'; import { IUserPool } from './user-pool'; import { ClientAttributes } from './user-pool-attr'; import { IUserPoolResourceServer, ResourceServerScope } from './user-pool-resource-server'; -import { IRole } from '../../aws-iam'; +import { IRoleRef } from '../../aws-iam'; import { CfnApp } from '../../aws-pinpoint'; import { IResource, Resource, Duration, Stack, SecretValue, Token, FeatureFlags } from '../../core'; import { ValidationError } from '../../core/lib/errors'; @@ -407,7 +407,7 @@ export interface AnalyticsConfiguration { * The IAM role that has the permissions required for Amazon Cognito to publish events to Amazon Pinpoint analytics. * @default - no configuration, you need to specify either this property along with `applicationId` and `externalId` or `application`. */ - readonly role?: IRole; + readonly role?: IRoleRef; /** * If `true`, Amazon Cognito includes user data in the events that it publishes to Amazon Pinpoint analytics. @@ -722,7 +722,7 @@ export class UserPoolClient extends Resource implements IUserPoolClient { applicationArn: analytics.application?.attrArn, applicationId: analytics.applicationId, externalId: analytics.externalId, - roleArn: analytics.role?.roleArn, + roleArn: analytics.role?.roleRef.roleArn, userDataShared: analytics.shareUserData, }; } diff --git a/packages/aws-cdk-lib/aws-cognito/lib/user-pool-group.ts b/packages/aws-cdk-lib/aws-cognito/lib/user-pool-group.ts index b614ae6b6be54..623510becf132 100644 --- a/packages/aws-cdk-lib/aws-cognito/lib/user-pool-group.ts +++ b/packages/aws-cdk-lib/aws-cognito/lib/user-pool-group.ts @@ -1,7 +1,7 @@ import { Construct } from 'constructs'; import { CfnUserPoolGroup } from './cognito.generated'; import { IUserPool } from './user-pool'; -import { IRole } from '../../aws-iam'; +import { IRoleRef } from '../../aws-iam'; import { IResource, Resource, Token } from '../../core'; import { ValidationError } from '../../core/lib/errors'; import { addConstructMetadata } from '../../core/lib/metadata-resource'; @@ -58,7 +58,7 @@ export interface UserPoolGroupOptions { * * @default - no description */ - readonly role?: IRole; + readonly role?: IRoleRef; } /** @@ -121,7 +121,7 @@ export class UserPoolGroup extends Resource implements IUserPoolGroup { description: props.description, groupName: props.groupName, precedence: props.precedence, - roleArn: props.role?.roleArn, + roleArn: props.role?.roleRef.roleArn, }); this.groupName = resource.ref; diff --git a/packages/aws-cdk-lib/aws-cognito/lib/user-pool.ts b/packages/aws-cdk-lib/aws-cognito/lib/user-pool.ts index 8432ead5ff37b..20b7bfca96c93 100644 --- a/packages/aws-cdk-lib/aws-cognito/lib/user-pool.ts +++ b/packages/aws-cdk-lib/aws-cognito/lib/user-pool.ts @@ -9,8 +9,8 @@ import { UserPoolEmail, UserPoolEmailConfig } from './user-pool-email'; import { UserPoolGroup, UserPoolGroupOptions } from './user-pool-group'; import { IUserPoolIdentityProvider } from './user-pool-idp'; import { UserPoolResourceServer, UserPoolResourceServerOptions } from './user-pool-resource-server'; -import { Grant, IGrantable, IRole, PolicyDocument, PolicyStatement, Role, ServicePrincipal } from '../../aws-iam'; -import { IKey } from '../../aws-kms'; +import { Grant, IGrantable, IRoleRef, PolicyDocument, PolicyStatement, Role, ServicePrincipal } from '../../aws-iam'; +import { IKeyRef } from '../../aws-kms'; import * as lambda from '../../aws-lambda'; import { ArnFormat, Duration, IResource, Lazy, Names, RemovalPolicy, Resource, Stack, Token } from '../../core'; import { ValidationError } from '../../core/lib/errors'; @@ -707,7 +707,7 @@ export interface UserPoolProps { * * @default - a new IAM role is created. */ - readonly smsRole?: IRole; + readonly smsRole?: IRoleRef; /** * The 'ExternalId' that Cognito service must be using when assuming the `smsRole`, if the role is restricted with an 'sts:ExternalId' conditional. @@ -898,7 +898,7 @@ export interface UserPoolProps { * @see https://docs.aws.amazon.com/cognito/latest/developerguide/user-pool-lambda-custom-sender-triggers.html * @default - no key ID configured */ - readonly customSenderKmsKey?: IKey; + readonly customSenderKmsKey?: IKeyRef; /** * The user pool's Advanced Security Mode @@ -1141,7 +1141,7 @@ export class UserPool extends UserPoolBase { if (props.customSenderKmsKey) { const kmsKey = props.customSenderKmsKey; - (this.triggers as any).kmsKeyId = kmsKey.keyArn; + (this.triggers as any).kmsKeyId = kmsKey.keyRef.keyArn; } if (props.lambdaTriggers) { @@ -1436,7 +1436,7 @@ export class UserPool extends UserPoolBase { if (props.smsRole) { return { - snsCallerArn: props.smsRole.roleArn, + snsCallerArn: props.smsRole.roleRef.roleArn, externalId: props.smsRoleExternalId, snsRegion: props.snsRegion, }; @@ -1478,7 +1478,7 @@ export class UserPool extends UserPoolBase { }); return { externalId: smsRoleExternalId, - snsCallerArn: smsRole.roleArn, + snsCallerArn: smsRole.roleRef.roleArn, snsRegion: props.snsRegion, }; } diff --git a/packages/aws-cdk-lib/aws-config/lib/managed-rules.ts b/packages/aws-cdk-lib/aws-config/lib/managed-rules.ts index 65637dc642bc7..b70e979cc12c8 100644 --- a/packages/aws-cdk-lib/aws-config/lib/managed-rules.ts +++ b/packages/aws-cdk-lib/aws-config/lib/managed-rules.ts @@ -67,7 +67,7 @@ export interface CloudFormationStackDriftDetectionCheckProps extends RuleProps { * * @default - A role will be created */ - readonly role?: iam.IRole; + readonly role?: iam.IRoleRef; } /** @@ -82,14 +82,14 @@ export interface CloudFormationStackDriftDetectionCheckProps extends RuleProps { export class CloudFormationStackDriftDetectionCheck extends ManagedRule { /** Uniquely identifies this class. */ public static readonly PROPERTY_INJECTION_ID: string = 'aws-cdk-lib.aws-config.CloudFormationStackDriftDetectionCheck'; - private readonly role: iam.IRole; + private readonly role: iam.IRoleRef; constructor(scope: Construct, id: string, props: CloudFormationStackDriftDetectionCheckProps = {}) { super(scope, id, { ...props, identifier: ManagedRuleIdentifiers.CLOUDFORMATION_STACK_DRIFT_DETECTION_CHECK, inputParameters: { - cloudformationRoleArn: Lazy.string({ produce: () => this.role.roleArn }), + cloudformationRoleArn: Lazy.string({ produce: () => this.role.roleRef.roleArn }), }, }); // Enhanced CDK Analytics Telemetry diff --git a/packages/aws-cdk-lib/aws-ec2/lib/bastion-host.ts b/packages/aws-cdk-lib/aws-ec2/lib/bastion-host.ts index 5fd32e94d4852..015d43eaca432 100644 --- a/packages/aws-cdk-lib/aws-ec2/lib/bastion-host.ts +++ b/packages/aws-cdk-lib/aws-ec2/lib/bastion-host.ts @@ -1,5 +1,5 @@ import { Construct } from 'constructs'; -import { InstanceArchitecture, InstanceClass, InstanceSize, InstanceType } from '.'; +import { InstanceArchitecture, InstanceClass, InstanceReference, InstanceSize, InstanceType } from '.'; import { CloudFormationInit } from './cfn-init'; import { Connections } from './connections'; import { ApplyCloudFormationInitOptions, IInstance, Instance } from './instance'; @@ -242,6 +242,12 @@ export class BastionHostLinux extends Resource implements IInstance { }); } + public get instanceRef(): InstanceReference { + return { + instanceId: this.instanceId, + }; + } + /** * Returns the AmazonLinuxCpuType corresponding to the given instance architecture * @param architecture the instance architecture value to convert diff --git a/packages/aws-cdk-lib/aws-ec2/lib/cfn-init-elements.ts b/packages/aws-cdk-lib/aws-ec2/lib/cfn-init-elements.ts index e7c688434ae97..244f69caef1fa 100644 --- a/packages/aws-cdk-lib/aws-ec2/lib/cfn-init-elements.ts +++ b/packages/aws-cdk-lib/aws-ec2/lib/cfn-init-elements.ts @@ -1018,11 +1018,11 @@ export abstract class InitSource extends InitElement { * This block is the same every time (modulo bucket name), so it has the same * key every time so the blocks are merged into one in the final render. */ -function standardS3Auth(role: iam.IRole, bucketName: string) { +function standardS3Auth(role: iam.IRoleRef, bucketName: string) { return { S3AccessCreds: { type: 'S3', - roleName: role.roleName, + roleName: role.roleRef.roleName, buckets: [bucketName], }, }; diff --git a/packages/aws-cdk-lib/aws-ec2/lib/client-vpn-authorization-rule.ts b/packages/aws-cdk-lib/aws-ec2/lib/client-vpn-authorization-rule.ts index e619faf27d652..089679c767436 100644 --- a/packages/aws-cdk-lib/aws-ec2/lib/client-vpn-authorization-rule.ts +++ b/packages/aws-cdk-lib/aws-ec2/lib/client-vpn-authorization-rule.ts @@ -1,6 +1,5 @@ import { Construct } from 'constructs'; -import { IClientVpnEndpoint } from './client-vpn-endpoint-types'; -import { CfnClientVpnAuthorizationRule } from './ec2.generated'; +import { CfnClientVpnAuthorizationRule, IClientVpnEndpointRef } from './ec2.generated'; import { Resource, ValidationError } from '../../core'; import { addConstructMetadata } from '../../core/lib/metadata-resource'; import { propertyInjectable } from '../../core/lib/prop-injectable'; @@ -39,14 +38,14 @@ export interface ClientVpnAuthorizationRuleProps extends ClientVpnAuthorizationR * The client VPN endpoint to which to add the rule. * @default clientVpnEndpoint is required */ - readonly clientVpnEndpoint?: IClientVpnEndpoint; + readonly clientVpnEndpoint?: IClientVpnEndpointRef; /** * The client VPN endpoint to which to add the rule. * @deprecated Use `clientVpnEndpoint` instead * @default clientVpnEndpoint is required */ - readonly clientVpnEndoint?: IClientVpnEndpoint; + readonly clientVpnEndoint?: IClientVpnEndpointRef; } /** @@ -76,7 +75,7 @@ export class ClientVpnAuthorizationRule extends Resource { // Enhanced CDK Analytics Telemetry addConstructMetadata(this, props); new CfnClientVpnAuthorizationRule(this, 'Resource', { - clientVpnEndpointId: clientVpnEndpoint!.endpointId, + clientVpnEndpointId: clientVpnEndpoint!.clientVpnEndpointRef.clientVpnEndpointId, targetNetworkCidr: props.cidr, accessGroupId: props.groupId, authorizeAllGroups: !props.groupId, diff --git a/packages/aws-cdk-lib/aws-ec2/lib/client-vpn-endpoint-types.ts b/packages/aws-cdk-lib/aws-ec2/lib/client-vpn-endpoint-types.ts index 19577829cb489..9c51b9f695aa8 100644 --- a/packages/aws-cdk-lib/aws-ec2/lib/client-vpn-endpoint-types.ts +++ b/packages/aws-cdk-lib/aws-ec2/lib/client-vpn-endpoint-types.ts @@ -1,11 +1,12 @@ import { IDependable } from 'constructs'; import { IConnectable } from './connections'; +import { IClientVpnEndpointRef } from './ec2.generated'; import { IResource } from '../../core'; /** * A client VPN endpoint */ -export interface IClientVpnEndpoint extends IResource, IConnectable { +export interface IClientVpnEndpoint extends IResource, IConnectable, IClientVpnEndpointRef { /** * The endpoint ID */ diff --git a/packages/aws-cdk-lib/aws-ec2/lib/client-vpn-endpoint.ts b/packages/aws-cdk-lib/aws-ec2/lib/client-vpn-endpoint.ts index e3852c60027db..412d91a7d2f03 100644 --- a/packages/aws-cdk-lib/aws-ec2/lib/client-vpn-endpoint.ts +++ b/packages/aws-cdk-lib/aws-ec2/lib/client-vpn-endpoint.ts @@ -1,13 +1,22 @@ import { Construct, DependencyGroup, IDependable } from 'constructs'; import { ClientVpnAuthorizationRule, ClientVpnAuthorizationRuleOptions } from './client-vpn-authorization-rule'; -import { IClientVpnConnectionHandler, IClientVpnEndpoint, TransportProtocol, VpnPort } from './client-vpn-endpoint-types'; +import { + IClientVpnConnectionHandler, + IClientVpnEndpoint, + TransportProtocol, + VpnPort, +} from './client-vpn-endpoint-types'; import { ClientVpnRoute, ClientVpnRouteOptions } from './client-vpn-route'; import { Connections } from './connections'; -import { CfnClientVpnEndpoint, CfnClientVpnTargetNetworkAssociation } from './ec2.generated'; +import { + CfnClientVpnEndpoint, + CfnClientVpnTargetNetworkAssociation, + ClientVpnEndpointReference, +} from './ec2.generated'; import { CidrBlock } from './network-util'; import { ISecurityGroup, SecurityGroup } from './security-group'; import { IVpc, SubnetSelection } from './vpc'; -import { ISamlProvider } from '../../aws-iam'; +import { ISAMLProviderRef } from '../../aws-iam'; import * as logs from '../../aws-logs'; import { CfnOutput, Resource, Token, UnscopedValidationError, ValidationError } from '../../core'; import { addConstructMetadata, MethodMetadata } from '../../core/lib/metadata-resource'; @@ -220,7 +229,7 @@ export abstract class ClientVpnUserBasedAuthentication { } /** Federated authentication */ - public static federated(samlProvider: ISamlProvider, selfServiceSamlProvider?: ISamlProvider): ClientVpnUserBasedAuthentication { + public static federated(samlProvider: ISAMLProviderRef, selfServiceSamlProvider?: ISAMLProviderRef): ClientVpnUserBasedAuthentication { return new FederatedAuthentication(samlProvider, selfServiceSamlProvider); } @@ -248,7 +257,7 @@ class ActiveDirectoryAuthentication extends ClientVpnUserBasedAuthentication { * Federated authentication */ class FederatedAuthentication extends ClientVpnUserBasedAuthentication { - constructor(private readonly samlProvider: ISamlProvider, private readonly selfServiceSamlProvider?: ISamlProvider) { + constructor(private readonly samlProvider: ISAMLProviderRef, private readonly selfServiceSamlProvider?: ISAMLProviderRef) { super(); } @@ -256,8 +265,8 @@ class FederatedAuthentication extends ClientVpnUserBasedAuthentication { return { type: 'federated-authentication', federatedAuthentication: { - samlProviderArn: this.samlProvider.samlProviderArn, - selfServiceSamlProviderArn: this.selfServiceSamlProvider?.samlProviderArn, + samlProviderArn: this.samlProvider.samlProviderRef.samlProviderArn, + selfServiceSamlProviderArn: this.selfServiceSamlProvider?.samlProviderRef.samlProviderArn, }, }; } @@ -306,6 +315,12 @@ export class ClientVpnEndpoint extends Resource implements IClientVpnEndpoint { public readonly endpointId = attrs.endpointId; public readonly connections = new Connections({ securityGroups: attrs.securityGroups }); public readonly targetNetworksAssociated: IDependable = new DependencyGroup(); + + public get clientVpnEndpointRef(): ClientVpnEndpointReference { + return { + clientVpnEndpointId: this.endpointId, + }; + } } return new Import(scope, id); } @@ -435,6 +450,12 @@ export class ClientVpnEndpoint extends Resource implements IClientVpnEndpoint { } } + public get clientVpnEndpointRef(): ClientVpnEndpointReference { + return { + clientVpnEndpointId: this.endpointId, + }; + } + /** * Adds an authorization rule to this endpoint */ diff --git a/packages/aws-cdk-lib/aws-ec2/lib/client-vpn-route.ts b/packages/aws-cdk-lib/aws-ec2/lib/client-vpn-route.ts index 004903d4542fe..f8e64034a42fe 100644 --- a/packages/aws-cdk-lib/aws-ec2/lib/client-vpn-route.ts +++ b/packages/aws-cdk-lib/aws-ec2/lib/client-vpn-route.ts @@ -1,7 +1,6 @@ import { Construct } from 'constructs'; import { IClientVpnEndpoint } from './client-vpn-endpoint-types'; -import { CfnClientVpnRoute } from './ec2.generated'; -import { ISubnet } from './vpc'; +import { CfnClientVpnRoute, ISubnetRef } from './ec2.generated'; import { Resource, ValidationError } from '../../core'; import { addConstructMetadata } from '../../core/lib/metadata-resource'; import { propertyInjectable } from '../../core/lib/prop-injectable'; @@ -45,8 +44,8 @@ export abstract class ClientVpnRouteTarget { * The specified subnet must be an existing target network of the client VPN * endpoint. */ - public static subnet(subnet: ISubnet): ClientVpnRouteTarget { - return { subnetId: subnet.subnetId }; + public static subnet(subnet: ISubnetRef): ClientVpnRouteTarget { + return { subnetId: subnet.subnetRef.subnetId }; } /** diff --git a/packages/aws-cdk-lib/aws-ec2/lib/instance.ts b/packages/aws-cdk-lib/aws-ec2/lib/instance.ts index 28fb29d42f4fb..e907179325323 100644 --- a/packages/aws-cdk-lib/aws-ec2/lib/instance.ts +++ b/packages/aws-cdk-lib/aws-ec2/lib/instance.ts @@ -1,21 +1,32 @@ - import { Construct } from 'constructs'; import { InstanceRequireImdsv2Aspect } from './aspects'; import { CloudFormationInit } from './cfn-init'; import { Connections, IConnectable } from './connections'; -import { CfnInstance } from './ec2.generated'; +import { CfnInstance, IInstanceRef, InstanceReference, IPlacementGroupRef } from './ec2.generated'; import { InstanceType } from './instance-types'; import { IKeyPair } from './key-pair'; import { CpuCredits, InstanceInitiatedShutdownBehavior } from './launch-template'; import { IMachineImage, OperatingSystemType } from './machine-image'; -import { IPlacementGroup } from './placement-group'; import { instanceBlockDeviceMappings } from './private/ebs-util'; import { ISecurityGroup, SecurityGroup } from './security-group'; import { UserData } from './user-data'; import { BlockDevice } from './volume'; import { IVpc, Subnet, SubnetSelection } from './vpc'; import * as iam from '../../aws-iam'; -import { Annotations, Aspects, Duration, FeatureFlags, Fn, IResource, Lazy, Resource, Stack, Tags, Token, ValidationError } from '../../core'; +import { + Annotations, + Aspects, + Duration, + FeatureFlags, + Fn, + IResource, + Lazy, + Resource, + Stack, + Tags, + Token, + ValidationError, +} from '../../core'; import { md5hash } from '../../core/lib/helpers-internal'; import { addConstructMetadata, MethodMetadata } from '../../core/lib/metadata-resource'; import { mutatingAspectPrio32333 } from '../../core/lib/private/aspect-prio'; @@ -27,7 +38,7 @@ import * as cxapi from '../../cx-api'; */ const NAME_TAG: string = 'Name'; -export interface IInstance extends IResource, IConnectable, iam.IGrantable { +export interface IInstance extends IResource, IConnectable, iam.IGrantable, IInstanceRef { /** * The instance's ID * @@ -361,7 +372,7 @@ export interface InstanceProps { * * @default - no placement group will be used for this instance. */ - readonly placementGroup?: IPlacementGroup; + readonly placementGroup?: IPlacementGroupRef; /** * Whether the instance is enabled for AWS Nitro Enclaves. @@ -609,7 +620,7 @@ export class Instance extends Resource implements IInstance { ebsOptimized: props.ebsOptimized, disableApiTermination: props.disableApiTermination, instanceInitiatedShutdownBehavior: props.instanceInitiatedShutdownBehavior, - placementGroupName: props.placementGroup?.placementGroupName, + placementGroupName: props.placementGroup?.placementGroupRef.groupName, enclaveOptions: props.enclaveEnabled !== undefined ? { enabled: props.enclaveEnabled } : undefined, hibernationOptions: props.hibernationEnabled !== undefined ? { configured: props.hibernationEnabled } : undefined, ipv6AddressCount: props.ipv6AddressCount, @@ -685,6 +696,12 @@ export class Instance extends Resource implements IInstance { } } + public get instanceRef(): InstanceReference { + return { + instanceId: this.instanceId, + }; + } + /** * Add the security group to the instance. * diff --git a/packages/aws-cdk-lib/aws-ec2/lib/key-pair.ts b/packages/aws-cdk-lib/aws-ec2/lib/key-pair.ts index 97528dbec48ec..5d8c0e0c5e82d 100644 --- a/packages/aws-cdk-lib/aws-ec2/lib/key-pair.ts +++ b/packages/aws-cdk-lib/aws-ec2/lib/key-pair.ts @@ -1,8 +1,8 @@ import { Construct } from 'constructs'; -import { CfnKeyPair } from './ec2.generated'; +import { CfnKeyPair, IKeyPairRef, KeyPairReference } from './ec2.generated'; import { OperatingSystemType } from './machine-image'; -import { StringParameter, IStringParameter } from '../../aws-ssm'; -import { Resource, ResourceProps, Names, Lazy, IResource, ValidationError } from '../../core'; +import { IStringParameter, StringParameter } from '../../aws-ssm'; +import { IResource, Lazy, Names, Resource, ResourceProps, ValidationError } from '../../core'; import { addConstructMetadata } from '../../core/lib/metadata-resource'; import { propertyInjectable } from '../../core/lib/prop-injectable'; @@ -96,7 +96,7 @@ export interface KeyPairAttributes { /** * An EC2 Key Pair. */ -export interface IKeyPair extends IResource { +export interface IKeyPair extends IResource, IKeyPairRef { /** * The name of the key pair. * @@ -148,6 +148,12 @@ export class KeyPair extends Resource implements IKeyPair { this.type = attrs.type; } + public get keyPairRef(): KeyPairReference { + return { + keyName: this.keyPairName, + }; + } + /** * Used internally to determine whether the key pair is compatible with an OS type. * @@ -234,6 +240,12 @@ export class KeyPair extends Resource implements IKeyPair { this.format = keyFormat; } + public get keyPairRef(): KeyPairReference { + return { + keyName: this.keyPairName, + }; + } + /** * Whether the key material was imported. * diff --git a/packages/aws-cdk-lib/aws-ec2/lib/launch-template.ts b/packages/aws-cdk-lib/aws-ec2/lib/launch-template.ts index 29903871cd8e7..efc8b4b088685 100644 --- a/packages/aws-cdk-lib/aws-ec2/lib/launch-template.ts +++ b/packages/aws-cdk-lib/aws-ec2/lib/launch-template.ts @@ -1,10 +1,9 @@ import { Construct } from 'constructs'; import { Connections, IConnectable } from './connections'; -import { CfnLaunchTemplate } from './ec2.generated'; +import { CfnLaunchTemplate, ILaunchTemplateRef, IPlacementGroupRef, LaunchTemplateReference } from './ec2.generated'; import { InstanceType } from './instance-types'; import { IKeyPair } from './key-pair'; import { IMachineImage, MachineImageConfig, OperatingSystemType } from './machine-image'; -import { IPlacementGroup } from './placement-group'; import { launchTemplateBlockDeviceMappings } from './private/ebs-util'; import { ISecurityGroup } from './security-group'; import { UserData } from './user-data'; @@ -14,15 +13,15 @@ import { Annotations, Duration, Expiration, + FeatureFlags, Fn, IResource, Lazy, Resource, TagManager, - TagType, Tags, + TagType, Token, - FeatureFlags, ValidationError, } from '../../core'; import { addConstructMetadata, MethodMetadata } from '../../core/lib/metadata-resource'; @@ -75,7 +74,7 @@ export enum InstanceInitiatedShutdownBehavior { /** * Interface for LaunchTemplate-like objects. */ -export interface ILaunchTemplate extends IResource { +export interface ILaunchTemplate extends IResource, ILaunchTemplateRef { /** * The version number of this launch template to use * @@ -450,7 +449,7 @@ export interface LaunchTemplateProps { * * @default - no placement group will be used for this launch template. */ - readonly placementGroup?: IPlacementGroup; + readonly placementGroup?: IPlacementGroupRef; } /** @@ -527,6 +526,16 @@ export class LaunchTemplate extends Resource implements ILaunchTemplate, iam.IGr public readonly versionNumber = attrs.versionNumber ?? LaunchTemplateSpecialVersions.DEFAULT_VERSION; public readonly launchTemplateId? = attrs.launchTemplateId; public readonly launchTemplateName? = attrs.launchTemplateName; + + public get launchTemplateRef(): LaunchTemplateReference { + if (!this.launchTemplateId) { + throw new ValidationError('You must set launchTemplateId in LaunchTemplate.fromLaunchTemplateAttributes() in order to use the LaunchTemplate in this API', this); + } + + return { + launchTemplateId: this.launchTemplateId, + }; + } } return new Import(scope, id); } @@ -800,7 +809,7 @@ export class LaunchTemplate extends Resource implements ILaunchTemplate, iam.IGr metadataOptions: this.renderMetadataOptions(props), networkInterfaces, placement: props.placementGroup ? { - groupName: props.placementGroup.placementGroupName, + groupName: props.placementGroup.placementGroupRef.groupName, } : undefined, // Fields not yet implemented: @@ -852,6 +861,12 @@ export class LaunchTemplate extends Resource implements ILaunchTemplate, iam.IGr this.versionNumber = Token.asString(resource.getAtt('LatestVersionNumber')); } + public get launchTemplateRef(): LaunchTemplateReference { + return { + launchTemplateId: this.launchTemplateId!, + }; + } + private renderMetadataOptions(props: LaunchTemplateProps) { let requireMetadataOptions = false; // if requireImdsv2 is true, httpTokens must be required. diff --git a/packages/aws-cdk-lib/aws-ec2/lib/network-acl.ts b/packages/aws-cdk-lib/aws-ec2/lib/network-acl.ts index 5788cde1d905b..54d56464bde2d 100644 --- a/packages/aws-cdk-lib/aws-ec2/lib/network-acl.ts +++ b/packages/aws-cdk-lib/aws-ec2/lib/network-acl.ts @@ -1,8 +1,16 @@ import { Construct } from 'constructs'; -import { CfnNetworkAcl, CfnNetworkAclEntry, CfnSubnetNetworkAclAssociation } from './ec2.generated'; +import { + CfnNetworkAcl, + CfnNetworkAclEntry, + CfnSubnetNetworkAclAssociation, + INetworkAclEntryRef, + INetworkAclRef, + ISubnetNetworkAclAssociationRef, NetworkAclEntryReference, NetworkAclReference, SubnetNetworkAclAssociationReference, +} from './ec2.generated'; import { AclCidr, AclTraffic } from './network-acl-types'; import { ISubnet, IVpc, SubnetSelection } from './vpc'; import { IResource, Resource, Tags } from '../../core'; +import { asNetworkAcl, asSubnet } from './private/conversions'; import { addConstructMetadata, MethodMetadata } from '../../core/lib/metadata-resource'; import { propertyInjectable } from '../../core/lib/prop-injectable'; @@ -13,10 +21,8 @@ const NAME_TAG: string = 'Name'; /** * A NetworkAcl - * - * */ -export interface INetworkAcl extends IResource { +export interface INetworkAcl extends IResource, INetworkAclRef { /** * ID for the current Network ACL * @attribute @@ -37,6 +43,12 @@ export interface INetworkAcl extends IResource { abstract class NetworkAclBase extends Resource implements INetworkAcl { public abstract readonly networkAclId: string; + public get networkAclRef(): NetworkAclReference { + return { + networkAclId: this.networkAclId, + }; + } + /** * Add a new entry to the ACL */ @@ -175,12 +187,11 @@ export enum Action { * * */ -export interface INetworkAclEntry extends IResource { +export interface INetworkAclEntry extends IResource, INetworkAclEntryRef { /** * The network ACL. */ readonly networkAcl: INetworkAcl; - } /** @@ -190,6 +201,7 @@ export interface INetworkAclEntry extends IResource { */ abstract class NetworkAclEntryBase extends Resource implements INetworkAclEntry { public abstract readonly networkAcl: INetworkAcl; + public abstract readonly networkAclEntryRef: NetworkAclEntryReference; } /** @@ -280,7 +292,9 @@ export interface NetworkAclEntryProps extends CommonNetworkAclEntryOptions { export class NetworkAclEntry extends NetworkAclEntryBase { /** Uniquely identifies this class. */ public static readonly PROPERTY_INJECTION_ID: string = 'aws-cdk-lib.aws-ec2.NetworkAclEntry'; + public readonly networkAcl: INetworkAcl; + public readonly networkAclEntryRef: NetworkAclEntryReference; constructor(scope: Construct, id: string, props: NetworkAclEntryProps) { super(scope, id, { @@ -291,14 +305,15 @@ export class NetworkAclEntry extends NetworkAclEntryBase { this.networkAcl = props.networkAcl; - new CfnNetworkAclEntry(this, 'Resource', { - networkAclId: this.networkAcl.networkAclId, + const resource = new CfnNetworkAclEntry(this, 'Resource', { + networkAclId: this.networkAcl.networkAclRef.networkAclId, ruleNumber: props.ruleNumber, ruleAction: props.ruleAction ?? Action.ALLOW, egress: props.direction !== undefined ? props.direction === TrafficDirection.EGRESS : undefined, ...props.traffic.toTrafficConfig(), ...props.cidr.toCidrConfig(), }); + this.networkAclEntryRef = resource.networkAclEntryRef; } } @@ -307,7 +322,7 @@ export class NetworkAclEntry extends NetworkAclEntryBase { * * */ -export interface ISubnetNetworkAclAssociation extends IResource { +export interface ISubnetNetworkAclAssociation extends IResource, ISubnetNetworkAclAssociationRef { /** * ID for the current SubnetNetworkAclAssociation * @attribute @@ -333,14 +348,11 @@ export interface SubnetNetworkAclAssociationProps { /** * The Network ACL this association is defined for - * - * @attribute */ - readonly networkAcl: INetworkAcl; + readonly networkAcl: INetworkAclRef; /** * ID of the Subnet - * @attribute */ readonly subnet: ISubnet; } @@ -352,7 +364,14 @@ export interface SubnetNetworkAclAssociationProps { */ abstract class SubnetNetworkAclAssociationBase extends Resource implements ISubnetNetworkAclAssociation { public abstract readonly subnetNetworkAclAssociationAssociationId: string; + + public get subnetNetworkAclAssociationRef(): SubnetNetworkAclAssociationReference { + return { + associationId: this.subnetNetworkAclAssociationAssociationId, + }; + } } + @propertyInjectable export class SubnetNetworkAclAssociation extends SubnetNetworkAclAssociationBase { /** Uniquely identifies this class. */ @@ -373,19 +392,10 @@ export class SubnetNetworkAclAssociation extends SubnetNetworkAclAssociationBase */ public readonly subnetNetworkAclAssociationAssociationId: string; - /** - * ID for the current Network ACL - * @attribute - */ - public readonly networkAcl: INetworkAcl; - - /** - * ID of the Subnet - * @attribute - */ - public readonly subnet: ISubnet; + private readonly _subnet: ISubnet; private association: CfnSubnetNetworkAclAssociation; + private readonly _networkAcl: INetworkAclRef; constructor(scope: Construct, id: string, props: SubnetNetworkAclAssociationProps) { super(scope, id, { @@ -395,12 +405,26 @@ export class SubnetNetworkAclAssociation extends SubnetNetworkAclAssociationBase addConstructMetadata(this, props); this.association = new CfnSubnetNetworkAclAssociation(this, 'Resource', { - networkAclId: props.networkAcl.networkAclId, - subnetId: props.subnet.subnetId, + networkAclId: props.networkAcl.networkAclRef.networkAclId, + subnetId: props.subnet.subnetRef.subnetId, }); - this.networkAcl = props.networkAcl; - this.subnet = props.subnet; + this._networkAcl = props.networkAcl; + this._subnet = props.subnet; this.subnetNetworkAclAssociationAssociationId = this.association.attrAssociationId; } + + /** + * ID of the Subnet + */ + public get subnet(): ISubnet { + return asSubnet(this._subnet, this); + } + + /** + * ID for the current Network ACL + */ + public get networkAcl(): INetworkAcl { + return asNetworkAcl(this._networkAcl, this); + } } diff --git a/packages/aws-cdk-lib/aws-ec2/lib/placement-group.ts b/packages/aws-cdk-lib/aws-ec2/lib/placement-group.ts index edf0f64bc219c..56d20ee178b88 100644 --- a/packages/aws-cdk-lib/aws-ec2/lib/placement-group.ts +++ b/packages/aws-cdk-lib/aws-ec2/lib/placement-group.ts @@ -1,5 +1,5 @@ import { Construct } from 'constructs'; -import { CfnPlacementGroup } from './ec2.generated'; +import { CfnPlacementGroup, IPlacementGroupRef, PlacementGroupReference } from './ec2.generated'; import { IResource, Resource, ValidationError } from '../../core'; import { addConstructMetadata } from '../../core/lib/metadata-resource'; import { propertyInjectable } from '../../core/lib/prop-injectable'; @@ -9,7 +9,7 @@ import { propertyInjectable } from '../../core/lib/prop-injectable'; * * @see https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/placement-groups.html */ -export interface IPlacementGroup extends IResource { +export interface IPlacementGroup extends IResource, IPlacementGroupRef { /** * The name of this placement group * @@ -160,6 +160,11 @@ export class PlacementGroup extends Resource implements IPlacementGroup { public static fromPlacementGroupName(scope: Construct, id: string, placementGroupName: string): IPlacementGroup { class Import extends Resource implements IPlacementGroup { public readonly placementGroupName = placementGroupName; + public get placementGroupRef(): PlacementGroupReference { + return { + groupName: this.placementGroupName, + }; + } } return new Import(scope, id); @@ -211,4 +216,10 @@ export class PlacementGroup extends Resource implements IPlacementGroup { resourceName: this.physicalName, }); } + + public get placementGroupRef(): PlacementGroupReference { + return { + groupName: this.placementGroupName, + }; + } } diff --git a/packages/aws-cdk-lib/aws-ec2/lib/prefix-list.ts b/packages/aws-cdk-lib/aws-ec2/lib/prefix-list.ts index e996b3ac7acc8..c546940be0b43 100644 --- a/packages/aws-cdk-lib/aws-ec2/lib/prefix-list.ts +++ b/packages/aws-cdk-lib/aws-ec2/lib/prefix-list.ts @@ -1,16 +1,16 @@ import { Construct } from 'constructs'; import { Connections } from './connections'; -import { CfnPrefixList } from './ec2.generated'; +import { CfnPrefixList, IPrefixListRef, PrefixListReference } from './ec2.generated'; import { IPeer } from './peer'; import * as cxschema from '../../cloud-assembly-schema'; -import { IResource, Lazy, Resource, Names, ContextProvider, Token, ValidationError } from '../../core'; +import { ContextProvider, IResource, Lazy, Names, Resource, Stack, Token, ValidationError } from '../../core'; import { addConstructMetadata } from '../../core/lib/metadata-resource'; import { propertyInjectable } from '../../core/lib/prop-injectable'; /** * A prefix list */ -export interface IPrefixList extends IResource, IPeer { +export interface IPrefixList extends IResource, IPeer, IPrefixListRef { /** * The ID of the prefix list * @@ -89,6 +89,8 @@ abstract class PrefixListBase extends Resource implements IPrefixList { */ public readonly canInlineRule = false; + public abstract readonly prefixListRef: PrefixListReference; + /** * A unique identifier for this connection peer */ @@ -158,6 +160,17 @@ export class PrefixList extends PrefixListBase { public static fromPrefixListId(scope: Construct, id: string, prefixListId: string): IPrefixList { class Import extends PrefixListBase { public readonly prefixListId = prefixListId; + + public get prefixListRef(): PrefixListReference { + return { + prefixListArn: Stack.of(scope).formatArn({ + service: 'ec2', + resource: 'prefix-list', + resourceName: this.prefixListId, + }), + prefixListId, + }; + } } return new Import(scope, id); } @@ -291,4 +304,11 @@ export class PrefixList extends PrefixListBase { this.version = prefixList.attrVersion; this.addressFamily = prefixList.addressFamily; } + + public get prefixListRef(): PrefixListReference { + return { + prefixListArn: this.prefixListArn, + prefixListId: this.prefixListId, + }; + } } diff --git a/packages/aws-cdk-lib/aws-ec2/lib/private/conversions.ts b/packages/aws-cdk-lib/aws-ec2/lib/private/conversions.ts new file mode 100644 index 0000000000000..6a7d66e8a1f17 --- /dev/null +++ b/packages/aws-cdk-lib/aws-ec2/lib/private/conversions.ts @@ -0,0 +1,19 @@ +import { IConstruct } from 'constructs'; +import { ValidationError } from '../../../core/lib/errors'; +import { INetworkAclRef, ISubnetRef } from '../ec2.generated'; +import { INetworkAcl } from '../network-acl'; +import { ISubnet } from '../vpc'; + +export function asNetworkAcl(x: INetworkAclRef, scope: IConstruct): INetworkAcl { + if ('addEntry' in x) { + return x as INetworkAcl; + } + throw new ValidationError(`Provided networkAcl is not an instance of INetworkAcl: ${x.constructor.name}`, scope); +} + +export function asSubnet(x: ISubnetRef, scope: IConstruct): ISubnet { + if ('subnetId' in x) { + return x as ISubnet; + } + throw new ValidationError(`Provided subnet is not an instance of ISubnet: ${x.constructor.name}`, scope); +} diff --git a/packages/aws-cdk-lib/aws-ec2/lib/security-group.ts b/packages/aws-cdk-lib/aws-ec2/lib/security-group.ts index 821d38c819bd1..623cb772bfba0 100644 --- a/packages/aws-cdk-lib/aws-ec2/lib/security-group.ts +++ b/packages/aws-cdk-lib/aws-ec2/lib/security-group.ts @@ -1,11 +1,22 @@ import { Construct } from 'constructs'; import { Connections } from './connections'; -import { CfnSecurityGroup, CfnSecurityGroupEgress, CfnSecurityGroupIngress } from './ec2.generated'; +import { CfnSecurityGroup, CfnSecurityGroupEgress, CfnSecurityGroupIngress, ISecurityGroupRef, SecurityGroupReference } from './ec2.generated'; import { IPeer, Peer } from './peer'; import { Port } from './port'; import { IVpc } from './vpc'; import * as cxschema from '../../cloud-assembly-schema'; -import { Annotations, ContextProvider, IResource, Lazy, Names, Resource, ResourceProps, Stack, Token, ValidationError } from '../../core'; +import { + Annotations, + ContextProvider, + IResource, + Lazy, + Names, + Resource, + ResourceProps, + Stack, + Token, + ValidationError, +} from '../../core'; import { addConstructMetadata, MethodMetadata } from '../../core/lib/metadata-resource'; import { propertyInjectable } from '../../core/lib/prop-injectable'; import * as cxapi from '../../cx-api'; @@ -17,7 +28,7 @@ const SECURITY_GROUP_DISABLE_INLINE_RULES_CONTEXT_KEY = '@aws-cdk/aws-ec2.securi /** * Interface for security group-like objects */ -export interface ISecurityGroup extends IResource, IPeer { +export interface ISecurityGroup extends IResource, IPeer, ISecurityGroupRef { /** * ID for the current security group * @attribute @@ -79,6 +90,12 @@ abstract class SecurityGroupBase extends Resource implements ISecurityGroup { Object.defineProperty(this, SECURITY_GROUP_SYMBOL, { value: true }); } + public get securityGroupRef(): SecurityGroupReference { + return { + securityGroupId: this.securityGroupId, + }; + } + public get uniqueId() { return Names.nodeUniqueId(this.node); } diff --git a/packages/aws-cdk-lib/aws-ec2/lib/user-data.ts b/packages/aws-cdk-lib/aws-ec2/lib/user-data.ts index 8fc6cf1894732..4c047a428598e 100644 --- a/packages/aws-cdk-lib/aws-ec2/lib/user-data.ts +++ b/packages/aws-cdk-lib/aws-ec2/lib/user-data.ts @@ -1,5 +1,5 @@ import { OperatingSystemType } from './machine-image'; -import { IBucket } from '../../aws-s3'; +import { IBucketRef } from '../../aws-s3'; import { Fn, Resource, Stack, CfnResource, UnscopedValidationError } from '../../core'; /** @@ -40,7 +40,7 @@ export interface S3DownloadOptions { /** * Name of the S3 bucket to download from */ - readonly bucket: IBucket; + readonly bucket: IBucketRef; /** * The key of the file to download @@ -175,7 +175,7 @@ class LinuxUserData extends UserData { } public addS3DownloadCommand(params: S3DownloadOptions): string { - const s3Path = `s3://${params.bucket.bucketName}/${params.bucketKey}`; + const s3Path = `s3://${params.bucket.bucketRef.bucketName}/${params.bucketKey}`; const localPath = ( params.localFile && params.localFile.length !== 0 ) ? params.localFile : `/tmp/${ params.bucketKey }`; this.addCommands( `mkdir -p $(dirname '${localPath}')`, @@ -238,7 +238,7 @@ class WindowsUserData extends UserData { const localPath = ( params.localFile && params.localFile.length !== 0 ) ? params.localFile : `C:/temp/${ params.bucketKey }`; this.addCommands( `mkdir (Split-Path -Path '${localPath}' ) -ea 0`, - `Read-S3Object -BucketName '${params.bucket.bucketName}' -key '${params.bucketKey}' -file '${localPath}' -ErrorAction Stop` + (params.region !== undefined ? ` -Region ${params.region}` : ''), + `Read-S3Object -BucketName '${params.bucket.bucketRef.bucketName}' -key '${params.bucketKey}' -file '${localPath}' -ErrorAction Stop` + (params.region !== undefined ? ` -Region ${params.region}` : ''), ); return localPath; } diff --git a/packages/aws-cdk-lib/aws-ec2/lib/volume.ts b/packages/aws-cdk-lib/aws-ec2/lib/volume.ts index b8e408032fbce..8a854f88683c0 100644 --- a/packages/aws-cdk-lib/aws-ec2/lib/volume.ts +++ b/packages/aws-cdk-lib/aws-ec2/lib/volume.ts @@ -1,9 +1,21 @@ import { Construct } from 'constructs'; -import { CfnVolume } from './ec2.generated'; -import { IInstance } from './instance'; +import { CfnVolume, IInstanceRef, IVolumeRef, VolumeReference } from './ec2.generated'; import { AccountRootPrincipal, Grant, IGrantable } from '../../aws-iam'; import { IKey, ViaServicePrincipal } from '../../aws-kms'; -import { IResource, Resource, Size, SizeRoundingBehavior, Stack, Token, Tags, Names, RemovalPolicy, FeatureFlags, UnscopedValidationError, ValidationError } from '../../core'; +import { + FeatureFlags, + IResource, + Names, + RemovalPolicy, + Resource, + Size, + SizeRoundingBehavior, + Stack, + Tags, + Token, + UnscopedValidationError, + ValidationError, +} from '../../core'; import { md5hash } from '../../core/lib/helpers-internal'; import { addConstructMetadata } from '../../core/lib/metadata-resource'; import { propertyInjectable } from '../../core/lib/prop-injectable'; @@ -265,7 +277,7 @@ export enum EbsDeviceVolumeType { /** * An EBS Volume in AWS EC2. */ -export interface IVolume extends IResource { +export interface IVolume extends IResource, IVolumeRef { /** * The EBS Volume's ID * @@ -297,7 +309,7 @@ export interface IVolume extends IResource { * volume to. If not specified, then permission is granted to attach * to all instances in this account. */ - grantAttachVolume(grantee: IGrantable, instances?: IInstance[]): Grant; + grantAttachVolume(grantee: IGrantable, instances?: IInstanceRef[]): Grant; /** * Grants permission to attach the Volume by a ResourceTag condition. If you are looking to @@ -328,7 +340,7 @@ export interface IVolume extends IResource { * volume from. If not specified, then permission is granted to detach * from all instances in this account. */ - grantDetachVolume(grantee: IGrantable, instances?: IInstance[]): Grant; + grantDetachVolume(grantee: IGrantable, instances?: IInstanceRef[]): Grant; /** * Grants permission to detach the Volume by a ResourceTag condition. @@ -514,7 +526,13 @@ abstract class VolumeBase extends Resource implements IVolume { public abstract readonly availabilityZone: string; public abstract readonly encryptionKey?: IKey; - public grantAttachVolume(grantee: IGrantable, instances?: IInstance[]): Grant { + public get volumeRef(): VolumeReference { + return { + volumeId: this.volumeId, + }; + } + + public grantAttachVolume(grantee: IGrantable, instances?: IInstanceRef[]): Grant { const result = Grant.addToPrincipal({ grantee, actions: ['ec2:AttachVolume'], @@ -560,7 +578,7 @@ abstract class VolumeBase extends Resource implements IVolume { return result; } - public grantDetachVolume(grantee: IGrantable, instances?: IInstance[]): Grant { + public grantDetachVolume(grantee: IGrantable, instances?: IInstanceRef[]): Grant { const result = Grant.addToPrincipal({ grantee, actions: ['ec2:DetachVolume'], @@ -589,14 +607,14 @@ abstract class VolumeBase extends Resource implements IVolume { return result; } - private collectGrantResourceArns(instances?: IInstance[]): string[] { + private collectGrantResourceArns(instances?: IInstanceRef[]): string[] { const stack = Stack.of(this); const resourceArns: string[] = [ `arn:${stack.partition}:ec2:${stack.region}:${stack.account}:volume/${this.volumeId}`, ]; const instanceArnPrefix = `arn:${stack.partition}:ec2:${stack.region}:${stack.account}:instance`; if (instances) { - instances.forEach(instance => resourceArns.push(`${instanceArnPrefix}/${instance?.instanceId}`)); + instances.forEach(instance => resourceArns.push(`${instanceArnPrefix}/${instance?.instanceRef.instanceId}`)); } else { resourceArns.push(`${instanceArnPrefix}/*`); } diff --git a/packages/aws-cdk-lib/aws-ec2/lib/vpc-endpoint-service.ts b/packages/aws-cdk-lib/aws-ec2/lib/vpc-endpoint-service.ts index 2c9c6fb5993ef..2cc09d4401a6e 100644 --- a/packages/aws-cdk-lib/aws-ec2/lib/vpc-endpoint-service.ts +++ b/packages/aws-cdk-lib/aws-ec2/lib/vpc-endpoint-service.ts @@ -1,5 +1,10 @@ import { Construct } from 'constructs'; -import { CfnVPCEndpointService, CfnVPCEndpointServicePermissions } from './ec2.generated'; +import { + CfnVPCEndpointService, + CfnVPCEndpointServicePermissions, + IVPCEndpointServiceRef, + VPCEndpointServiceReference, +} from './ec2.generated'; import { ArnPrincipal } from '../../aws-iam'; import { Aws, Fn, IResource, Resource, Stack, Token, ValidationError } from '../../core'; import { addConstructMetadata } from '../../core/lib/metadata-resource'; @@ -38,7 +43,7 @@ export interface IVpcEndpointServiceLoadBalancer { * A VPC endpoint service. * */ -export interface IVpcEndpointService extends IResource { +export interface IVpcEndpointService extends IResource, IVPCEndpointServiceRef { /** * The service name of the VPC Endpoint Service that clients use to connect to, * like com.amazonaws.vpce..vpce-svc-xxxxxxxxxxxxxxxx @@ -172,6 +177,10 @@ export class VpcEndpointService extends Resource implements IVpcEndpointService }); } } + + public get vpcEndpointServiceRef(): VPCEndpointServiceReference { + return this.endpointService.vpcEndpointServiceRef; + } } /** diff --git a/packages/aws-cdk-lib/aws-ec2/lib/vpc-endpoint.ts b/packages/aws-cdk-lib/aws-ec2/lib/vpc-endpoint.ts index 4e1d9c501e88f..4ba09e67bb364 100644 --- a/packages/aws-cdk-lib/aws-ec2/lib/vpc-endpoint.ts +++ b/packages/aws-cdk-lib/aws-ec2/lib/vpc-endpoint.ts @@ -1,6 +1,6 @@ import { Construct } from 'constructs'; import { Connections, IConnectable } from './connections'; -import { CfnVPCEndpoint } from './ec2.generated'; +import { CfnVPCEndpoint, IVPCEndpointRef, VPCEndpointReference } from './ec2.generated'; import { Peer } from './peer'; import { Port } from './port'; import { ISecurityGroup, SecurityGroup } from './security-group'; @@ -15,7 +15,7 @@ import { propertyInjectable } from '../../core/lib/prop-injectable'; /** * A VPC endpoint. */ -export interface IVpcEndpoint extends IResource { +export interface IVpcEndpoint extends IResource, IVPCEndpointRef { /** * The VPC endpoint identifier. * @attribute @@ -28,6 +28,12 @@ export abstract class VpcEndpoint extends Resource implements IVpcEndpoint { protected policyDocument?: iam.PolicyDocument; + public get vpcEndpointRef(): VPCEndpointReference { + return { + vpcEndpointId: this.vpcEndpointId, + }; + } + /** * Adds a statement to the policy document of the VPC endpoint. The statement * must have a Principal. @@ -981,6 +987,11 @@ export class InterfaceVpcEndpoint extends VpcEndpoint implements IInterfaceVpcEn defaultPort: Port.tcp(attrs.port), securityGroups, }); + public get vpcEndpointRef(): VPCEndpointReference { + return { + vpcEndpointId: this.vpcEndpointId, + }; + } } return new Import(scope, id); diff --git a/packages/aws-cdk-lib/aws-ec2/lib/vpc-flow-logs.ts b/packages/aws-cdk-lib/aws-ec2/lib/vpc-flow-logs.ts index f217f2206b363..9fa355676f24d 100644 --- a/packages/aws-cdk-lib/aws-ec2/lib/vpc-flow-logs.ts +++ b/packages/aws-cdk-lib/aws-ec2/lib/vpc-flow-logs.ts @@ -1,10 +1,20 @@ import { Construct } from 'constructs'; -import { CfnFlowLog } from './ec2.generated'; -import { ISubnet, IVpc } from './vpc'; +import { CfnFlowLog, FlowLogReference, IFlowLogRef, ISubnetRef } from './ec2.generated'; +import { IVpc } from './vpc'; import * as iam from '../../aws-iam'; import * as logs from '../../aws-logs'; import * as s3 from '../../aws-s3'; -import { IResource, PhysicalName, RemovalPolicy, Resource, FeatureFlags, Stack, Tags, CfnResource, ValidationError } from '../../core'; +import { + CfnResource, + FeatureFlags, + IResource, + PhysicalName, + RemovalPolicy, + Resource, + Stack, + Tags, + ValidationError, +} from '../../core'; import { addConstructMetadata } from '../../core/lib/metadata-resource'; import { propertyInjectable } from '../../core/lib/prop-injectable'; import { S3_CREATE_DEFAULT_LOGGING_POLICY } from '../../cx-api'; @@ -17,7 +27,7 @@ const NAME_TAG: string = 'Name'; /** * A FlowLog */ -export interface IFlowLog extends IResource { +export interface IFlowLog extends IResource, IFlowLogRef { /** * The Id of the VPC Flow Log * @@ -73,10 +83,10 @@ export abstract class FlowLogResourceType { /** * The subnet to attach the Flow Log to */ - public static fromSubnet(subnet: ISubnet): FlowLogResourceType { + public static fromSubnet(subnet: ISubnetRef): FlowLogResourceType { return { resourceType: 'Subnet', - resourceId: subnet.subnetId, + resourceId: subnet.subnetRef.subnetId, }; } @@ -804,6 +814,12 @@ abstract class FlowLogBase extends Resource implements IFlowLog { * @attribute */ public abstract readonly flowLogId: string; + + public get flowLogRef(): FlowLogReference { + return { + flowLogId: this.flowLogId, + }; + } } /** diff --git a/packages/aws-cdk-lib/aws-ec2/lib/vpc.ts b/packages/aws-cdk-lib/aws-ec2/lib/vpc.ts index deb37e89639e7..e4c57b7af904c 100644 --- a/packages/aws-cdk-lib/aws-ec2/lib/vpc.ts +++ b/packages/aws-cdk-lib/aws-ec2/lib/vpc.ts @@ -1,35 +1,80 @@ import { Construct, Dependable, DependencyGroup, IConstruct, IDependable, Node } from 'constructs'; import { ClientVpnEndpoint, ClientVpnEndpointOptions } from './client-vpn-endpoint'; import { - CfnEIP, CfnEgressOnlyInternetGateway, CfnInternetGateway, CfnNatGateway, CfnRoute, CfnRouteTable, CfnSubnet, - CfnSubnetRouteTableAssociation, CfnVPC, CfnVPCCidrBlock, CfnVPCGatewayAttachment, CfnVPNGatewayRoutePropagation, + CfnEgressOnlyInternetGateway, + CfnEIP, + CfnInternetGateway, + CfnNatGateway, + CfnRoute, + CfnRouteTable, + CfnSubnet, + CfnSubnetRouteTableAssociation, + CfnVPC, + CfnVPCCidrBlock, + CfnVPCGatewayAttachment, + CfnVPNGatewayRoutePropagation, + ISubnetRef, + IVPCRef, SubnetReference, VPCReference, } from './ec2.generated'; -import { AllocatedSubnet, IIpAddresses, RequestedSubnet, IpAddresses, IIpv6Addresses, Ipv6Addresses } from './ip-addresses'; +import { + AllocatedSubnet, + IIpAddresses, + IIpv6Addresses, + IpAddresses, + Ipv6Addresses, + RequestedSubnet, +} from './ip-addresses'; import { NatProvider } from './nat'; import { INetworkAcl, NetworkAcl, SubnetNetworkAclAssociation } from './network-acl'; import { SubnetFilter } from './subnet'; -import { allRouteTableIds, defaultSubnetName, flatten, ImportSubnetGroup, subnetGroupNameFromConstructId, subnetId } from './util'; -import { GatewayVpcEndpoint, GatewayVpcEndpointAwsService, GatewayVpcEndpointOptions, InterfaceVpcEndpoint, InterfaceVpcEndpointOptions } from './vpc-endpoint'; +import { + allRouteTableIds, + defaultSubnetName, + flatten, + ImportSubnetGroup, + subnetGroupNameFromConstructId, + subnetId, +} from './util'; +import { + GatewayVpcEndpoint, + GatewayVpcEndpointAwsService, + GatewayVpcEndpointOptions, + InterfaceVpcEndpoint, + InterfaceVpcEndpointOptions, +} from './vpc-endpoint'; import { FlowLog, FlowLogOptions, FlowLogResourceType } from './vpc-flow-logs'; import { VpcLookupOptions } from './vpc-lookup'; import { EnableVpnGatewayOptions, VpnConnection, VpnConnectionOptions, VpnConnectionType, VpnGateway } from './vpn'; import * as cxschema from '../../cloud-assembly-schema'; import { - Arn, Annotations, ContextProvider, - IResource, Fn, Lazy, Resource, Stack, Token, Tags, Names, CustomResource, FeatureFlags, - ValidationError, + Annotations, + Arn, + ContextProvider, + CustomResource, + FeatureFlags, + Fn, + IResource, + Lazy, + Names, + Resource, + Stack, + Tags, + Token, UnscopedValidationError, + ValidationError, } from '../../core'; import { addConstructMetadata, MethodMetadata } from '../../core/lib/metadata-resource'; import { propertyInjectable } from '../../core/lib/prop-injectable'; -import { RestrictDefaultSgProvider } from '../../custom-resource-handlers/dist/aws-ec2/restrict-default-sg-provider.generated'; +import { + RestrictDefaultSgProvider, +} from '../../custom-resource-handlers/dist/aws-ec2/restrict-default-sg-provider.generated'; import * as cxapi from '../../cx-api'; import { EC2_RESTRICT_DEFAULT_SECURITY_GROUP } from '../../cx-api'; const VPC_SUBNET_SYMBOL = Symbol.for('@aws-cdk/aws-ec2.VpcSubnet'); const FAKE_AZ_NAME = 'fake-az'; -export interface ISubnet extends IResource { +export interface ISubnet extends IResource, ISubnetRef { /** * The Availability Zone the subnet is located in */ @@ -74,7 +119,7 @@ export interface IRouteTable { readonly routeTableId: string; } -export interface IVpc extends IResource { +export interface IVpc extends IResource, IVPCRef { /** * Identifier for this VPC * @attribute @@ -471,6 +516,12 @@ abstract class VpcBase extends Resource implements IVpc { */ protected _vpnGatewayId?: string; + public get vpcRef(): VPCReference { + return { + vpcId: this.vpcId, + }; + } + /** * Returns IDs of selected subnets */ @@ -2099,6 +2150,8 @@ export class Subnet extends Resource implements ISubnet { */ public readonly routeTable: IRouteTable; + public readonly subnetRef: SubnetReference; + public readonly internetConnectivityEstablished: IDependable; private readonly _internetConnectivityEstablished = new DependencyGroup(); @@ -2129,11 +2182,12 @@ export class Subnet extends Resource implements ISubnet { this.subnetAvailabilityZone = subnet.attrAvailabilityZone; this.subnetIpv6CidrBlocks = subnet.attrIpv6CidrBlocks; this.subnetOutpostArn = subnet.attrOutpostArn; + this.subnetRef = subnet.subnetRef; // subnet.attrNetworkAclAssociationId is the default ACL after the subnet // was just created. However, the ACL can be replaced at a later time. this._networkAcl = NetworkAcl.fromNetworkAclId(this, 'Acl', subnet.attrNetworkAclAssociationId); - this.subnetNetworkAclAssociationId = Lazy.string({ produce: () => this._networkAcl.networkAclId }); + this.subnetNetworkAclAssociationId = Lazy.string({ produce: () => this._networkAcl.networkAclRef.networkAclId }); this.node.defaultChild = subnet; const table = new CfnRouteTable(this, 'RouteTable', { @@ -2686,6 +2740,12 @@ class ImportedSubnet extends Resource implements ISubnet, IPublicSubnet, IPrivat }; } + public get subnetRef(): SubnetReference { + return { + subnetId: this.subnetId, + }; + } + public get availabilityZone(): string { if (!this._availabilityZone) { // eslint-disable-next-line max-len diff --git a/packages/aws-cdk-lib/aws-ec2/lib/vpn.ts b/packages/aws-cdk-lib/aws-ec2/lib/vpn.ts index 67c0cb987f6de..f59a079d697b0 100644 --- a/packages/aws-cdk-lib/aws-ec2/lib/vpn.ts +++ b/packages/aws-cdk-lib/aws-ec2/lib/vpn.ts @@ -5,6 +5,8 @@ import { CfnVPNConnection, CfnVPNConnectionRoute, CfnVPNGateway, + IVPNConnectionRef, + IVPNGatewayRef, VPNConnectionReference, VPNGatewayReference, } from './ec2.generated'; import { IVpc, SubnetSelection } from './vpc'; import * as cloudwatch from '../../aws-cloudwatch'; @@ -12,7 +14,7 @@ import { IResource, Resource, SecretValue, Token, ValidationError } from '../../ import { addConstructMetadata } from '../../core/lib/metadata-resource'; import { propertyInjectable } from '../../core/lib/prop-injectable'; -export interface IVpnConnection extends IResource { +export interface IVpnConnection extends IResource, IVPNConnectionRef { /** * The id of the VPN connection. * @attribute VpnConnectionId @@ -38,7 +40,7 @@ export interface IVpnConnection extends IResource { /** * The virtual private gateway interface */ -export interface IVpnGateway extends IResource { +export interface IVpnGateway extends IResource, IVPNGatewayRef { /** * The virtual private gateway Id @@ -172,6 +174,8 @@ export class VpnGateway extends Resource implements IVpnGateway { */ public readonly gatewayId: string; + public readonly vpnGatewayRef: VPNGatewayReference; + constructor(scope: Construct, id: string, props: VpnGatewayProps) { super(scope, id); // Enhanced CDK Analytics Telemetry @@ -182,6 +186,7 @@ export class VpnGateway extends Resource implements IVpnGateway { // to be created for the CfnVPNGateway (and 'Resource' would not do that). const vpnGW = new CfnVPNGateway(this, 'Default', props); this.gatewayId = vpnGW.ref; + this.vpnGatewayRef = vpnGW.vpnGatewayRef; } } @@ -220,6 +225,12 @@ export abstract class VpnConnectionBase extends Resource implements IVpnConnecti public abstract readonly customerGatewayId: string; public abstract readonly customerGatewayIp: string; public abstract readonly customerGatewayAsn: number; + + public get vpnConnectionRef(): VPNConnectionReference { + return { + vpnConnectionId: this.customerGatewayId, + }; + } } /** diff --git a/packages/aws-cdk-lib/aws-ec2/test/client-vpn-authorization-rule.test.ts b/packages/aws-cdk-lib/aws-ec2/test/client-vpn-authorization-rule.test.ts index 8ff999a1829e2..e870e30a45a90 100644 --- a/packages/aws-cdk-lib/aws-ec2/test/client-vpn-authorization-rule.test.ts +++ b/packages/aws-cdk-lib/aws-ec2/test/client-vpn-authorization-rule.test.ts @@ -24,6 +24,9 @@ describe('ClientVpnAuthorizationRule constructor', () => { connections: new Connections(), node: stack.node, applyRemovalPolicy: () => { }, + clientVpnEndpointRef: { + clientVpnEndpointId: 'myClientVpnEndpoint', + }, }; new ClientVpnAuthorizationRule(stack, 'NormalRule', { cidr: '10.0.10.0/32', @@ -52,6 +55,9 @@ describe('ClientVpnAuthorizationRule constructor', () => { connections: new Connections(), node: stack.node, applyRemovalPolicy: () => { }, + clientVpnEndpointRef: { + clientVpnEndpointId: 'typoTypo', + }, }; const clientVpnEndpoint: IClientVpnEndpoint = { endpointId: 'myClientVpnEndpoint', @@ -61,6 +67,9 @@ describe('ClientVpnAuthorizationRule constructor', () => { connections: new Connections(), node: stack.node, applyRemovalPolicy: () => { }, + clientVpnEndpointRef: { + clientVpnEndpointId: 'myClientVpnEndpoint', + }, }; expect(() => { new ClientVpnAuthorizationRule(stack, 'RuleBothEndointAndEndpoint', { @@ -92,6 +101,9 @@ describe('ClientVpnAuthorizationRule constructor', () => { connections: new Connections(), node: stack.node, applyRemovalPolicy: () => { }, + clientVpnEndpointRef: { + clientVpnEndpointId: 'myClientVpnEndpoint', + }, }; new ClientVpnAuthorizationRule(stack, 'RuleWithEndointTypo', { cidr: '10.0.10.0/32', diff --git a/packages/aws-cdk-lib/aws-ecr/lib/repository.ts b/packages/aws-cdk-lib/aws-ecr/lib/repository.ts index 9424ab9aae637..1b4a7e6412c95 100644 --- a/packages/aws-cdk-lib/aws-ecr/lib/repository.ts +++ b/packages/aws-cdk-lib/aws-ecr/lib/repository.ts @@ -548,7 +548,7 @@ export interface RepositoryProps { * @default - If encryption is set to `KMS` and this property is undefined, * an AWS managed KMS key is used. */ - readonly encryptionKey?: kms.IKey; + readonly encryptionKey?: kms.IKeyRef; /** * Life cycle rules to apply to this registry @@ -979,7 +979,7 @@ export class Repository extends RepositoryBase { if (encryptionType === RepositoryEncryption.KMS) { return { encryptionType: 'KMS', - kmsKey: props.encryptionKey?.keyArn, + kmsKey: props.encryptionKey?.keyRef.keyArn, }; } diff --git a/packages/aws-cdk-lib/aws-ecs/lib/base/base-service.ts b/packages/aws-cdk-lib/aws-ecs/lib/base/base-service.ts index 09e04d5b177c2..74d1c0b304e56 100644 --- a/packages/aws-cdk-lib/aws-ecs/lib/base/base-service.ts +++ b/packages/aws-cdk-lib/aws-ecs/lib/base/base-service.ts @@ -283,7 +283,7 @@ export interface ServiceConnectTlsConfiguration { * * @default - none */ - readonly kmsKey?: kms.IKey; + readonly kmsKey?: kms.IKeyRef; /** * The IAM role that's associated with the Service Connect TLS. @@ -1023,7 +1023,7 @@ export abstract class BaseService extends Resource issuerCertificateAuthority: { awsPcaAuthorityArn: svc.tls.awsPcaAuthorityArn, }, - kmsKey: svc.tls.kmsKey?.keyArn, + kmsKey: svc.tls.kmsKey?.keyRef.keyArn, roleArn: svc.tls.role?.roleArn, } : undefined; @@ -1176,7 +1176,7 @@ export abstract class BaseService extends Resource })); } - if (logConfiguration?.s3Bucket?.bucketName) { + if (logConfiguration?.s3Bucket?.bucketRef.bucketName) { this.taskDefinition.addToTaskRolePolicy(new iam.PolicyStatement({ actions: [ 's3:GetBucketLocation', @@ -1187,14 +1187,14 @@ export abstract class BaseService extends Resource actions: [ 's3:PutObject', ], - resources: [`arn:${this.stack.partition}:s3:::${logConfiguration.s3Bucket.bucketName}/*`], + resources: [`arn:${this.stack.partition}:s3:::${logConfiguration.s3Bucket.bucketRef.bucketName}/*`], })); if (logConfiguration.s3EncryptionEnabled) { this.taskDefinition.addToTaskRolePolicy(new iam.PolicyStatement({ actions: [ 's3:GetEncryptionConfiguration', ], - resources: [`arn:${this.stack.partition}:s3:::${logConfiguration.s3Bucket.bucketName}`], + resources: [`arn:${this.stack.partition}:s3:::${logConfiguration.s3Bucket.bucketRef.bucketName}`], })); } } diff --git a/packages/aws-cdk-lib/aws-ecs/lib/cluster.ts b/packages/aws-cdk-lib/aws-ecs/lib/cluster.ts index cd97c450c7bd8..0cbd66d9e31ef 100644 --- a/packages/aws-cdk-lib/aws-ecs/lib/cluster.ts +++ b/packages/aws-cdk-lib/aws-ecs/lib/cluster.ts @@ -9,7 +9,7 @@ import * as ec2 from '../../aws-ec2'; import * as iam from '../../aws-iam'; import { PolicyStatement, ServicePrincipal } from '../../aws-iam'; import * as kms from '../../aws-kms'; -import { IKey } from '../../aws-kms'; +import { IKey, IKeyRef } from '../../aws-kms'; import * as logs from '../../aws-logs'; import * as s3 from '../../aws-s3'; import * as cloudmap from '../../aws-servicediscovery'; @@ -439,7 +439,7 @@ export class Cluster extends Resource implements ICluster { }, managedStorageConfiguration: this._managedStorageConfiguration && { fargateEphemeralStorageKmsKeyId: this._managedStorageConfiguration.fargateEphemeralStorageKmsKey?.keyId, - kmsKeyId: this._managedStorageConfiguration.kmsKey?.keyId, + kmsKeyId: this._managedStorageConfiguration.kmsKey?.keyRef.keyId, }, }; } @@ -1495,7 +1495,7 @@ export interface ManagedStorageConfiguration { * * @default - Encrypted using AWS-managed key */ - readonly kmsKey?: IKey; + readonly kmsKey?: IKeyRef; } /** diff --git a/packages/aws-cdk-lib/aws-efs/lib/efs-file-system.ts b/packages/aws-cdk-lib/aws-efs/lib/efs-file-system.ts index 014e4cea807db..87d5045d58d74 100644 --- a/packages/aws-cdk-lib/aws-efs/lib/efs-file-system.ts +++ b/packages/aws-cdk-lib/aws-efs/lib/efs-file-system.ts @@ -230,7 +230,7 @@ export interface FileSystemProps { * * @default - if 'encrypted' is true, the default key for EFS (/aws/elasticfilesystem) is used */ - readonly kmsKey?: kms.IKey; + readonly kmsKey?: kms.IKeyRef; /** * A policy used by EFS lifecycle management to transition files to the Infrequent Access (IA) storage class. @@ -803,7 +803,7 @@ export class FileSystem extends FileSystemBase { this._resource = new CfnFileSystem(this, 'Resource', { encrypted: encrypted, - kmsKeyId: props.kmsKey?.keyArn, + kmsKeyId: props.kmsKey?.keyRef.keyArn, lifecyclePolicies: lifecyclePolicies.length > 0 ? lifecyclePolicies : undefined, performanceMode: props.performanceMode, throughputMode: props.throughputMode, diff --git a/packages/aws-cdk-lib/aws-eks/lib/cluster-resource.ts b/packages/aws-cdk-lib/aws-eks/lib/cluster-resource.ts index 6ed813b6a682e..958d48c459278 100644 --- a/packages/aws-cdk-lib/aws-eks/lib/cluster-resource.ts +++ b/packages/aws-cdk-lib/aws-eks/lib/cluster-resource.ts @@ -21,7 +21,7 @@ export interface ClusterResourceProps { readonly vpc: ec2.IVpc; readonly environment?: { [key: string]: string }; readonly subnets?: ec2.ISubnet[]; - readonly secretsEncryptionKey?: kms.IKey; + readonly secretsEncryptionKey?: kms.IKeyRef; readonly onEventLayer?: lambda.ILayerVersion; readonly clusterHandlerSecurityGroup?: ec2.ISecurityGroup; readonly tags?: { [key: string]: string }; @@ -212,7 +212,7 @@ export class ClusterResource extends Construct { 'kms:DescribeKey', 'kms:CreateGrant', ], - resources: [props.secretsEncryptionKey.keyArn], + resources: [props.secretsEncryptionKey.keyRef.keyArn], })); } diff --git a/packages/aws-cdk-lib/aws-eks/lib/cluster.ts b/packages/aws-cdk-lib/aws-eks/lib/cluster.ts index 016591964d5ee..9e2a04184429f 100644 --- a/packages/aws-cdk-lib/aws-eks/lib/cluster.ts +++ b/packages/aws-cdk-lib/aws-eks/lib/cluster.ts @@ -664,7 +664,7 @@ export interface ClusterOptions extends CommonClusterOptions { * all etcd volumes used by Amazon EKS are encrypted at the disk-level * using AWS-Managed encryption keys. */ - readonly secretsEncryptionKey?: kms.IKey; + readonly secretsEncryptionKey?: kms.IKeyRef; /** * Specify which IP family is used to assign Kubernetes pod and service IP addresses. @@ -1753,7 +1753,7 @@ export class Cluster extends ClusterBase { ...(props.secretsEncryptionKey ? { encryptionConfig: [{ provider: { - keyArn: props.secretsEncryptionKey.keyArn, + keyArn: props.secretsEncryptionKey.keyRef.keyArn, }, resources: ['secrets'], }], diff --git a/packages/aws-cdk-lib/aws-elasticloadbalancingv2/lib/alb/trust-store-revocation.ts b/packages/aws-cdk-lib/aws-elasticloadbalancingv2/lib/alb/trust-store-revocation.ts index e3d527472188c..18eee1666b659 100644 --- a/packages/aws-cdk-lib/aws-elasticloadbalancingv2/lib/alb/trust-store-revocation.ts +++ b/packages/aws-cdk-lib/aws-elasticloadbalancingv2/lib/alb/trust-store-revocation.ts @@ -1,6 +1,6 @@ import { Construct } from 'constructs'; import { ITrustStore } from './trust-store'; -import { IBucket } from '../../../aws-s3'; +import { IBucketRef } from '../../../aws-s3'; import { Resource } from '../../../core'; import { addConstructMetadata } from '../../../core/lib/metadata-resource'; import { propertyInjectable } from '../../../core/lib/prop-injectable'; @@ -36,7 +36,7 @@ export interface RevocationContent { /** * The Amazon S3 bucket for the revocation file */ - readonly bucket: IBucket; + readonly bucket: IBucketRef; /** * The Amazon S3 path for the revocation file @@ -78,7 +78,7 @@ export class TrustStoreRevocation extends Resource { trustStoreArn: props.trustStore.trustStoreArn, revocationContents: props.revocationContents?.map(content => ({ revocationType: content.revocationType, - s3Bucket: content.bucket.bucketName, + s3Bucket: content.bucket.bucketRef.bucketName, s3Key: content.key, s3ObjectVersion: content.version, })), diff --git a/packages/aws-cdk-lib/aws-elasticloadbalancingv2/lib/alb/trust-store.ts b/packages/aws-cdk-lib/aws-elasticloadbalancingv2/lib/alb/trust-store.ts index fa27a64cec7e4..ed421255ba3a6 100644 --- a/packages/aws-cdk-lib/aws-elasticloadbalancingv2/lib/alb/trust-store.ts +++ b/packages/aws-cdk-lib/aws-elasticloadbalancingv2/lib/alb/trust-store.ts @@ -1,6 +1,6 @@ import { Construct } from 'constructs'; -import { IBucket } from '../../../aws-s3'; -import { IResource, Resource, Fn, Names, Lazy, Token } from '../../../core'; +import { IBucketRef } from '../../../aws-s3'; +import { Fn, IResource, Lazy, Names, Resource, Token } from '../../../core'; import { ValidationError } from '../../../core/lib/errors'; import { addConstructMetadata } from '../../../core/lib/metadata-resource'; import { propertyInjectable } from '../../../core/lib/prop-injectable'; @@ -38,7 +38,7 @@ export interface TrustStoreProps { /** * The bucket that the trust store is hosted in */ - readonly bucket: IBucket; + readonly bucket: IBucketRef; /** * The key in S3 to look at for the trust store @@ -126,7 +126,7 @@ export class TrustStore extends Resource implements ITrustStore { const resource = new CfnTrustStore(this, 'Resource', { name: this.physicalName, - caCertificatesBundleS3Bucket: props.bucket.bucketName, + caCertificatesBundleS3Bucket: props.bucket.bucketRef.bucketName, caCertificatesBundleS3Key: props.key, caCertificatesBundleS3ObjectVersion: props.version, }); diff --git a/packages/aws-cdk-lib/aws-elasticsearch/lib/domain.ts b/packages/aws-cdk-lib/aws-elasticsearch/lib/domain.ts index e1ee0428c9924..48eb034cd32b6 100644 --- a/packages/aws-cdk-lib/aws-elasticsearch/lib/domain.ts +++ b/packages/aws-cdk-lib/aws-elasticsearch/lib/domain.ts @@ -380,7 +380,7 @@ export interface EncryptionAtRestOptions { * @default - uses default aws/es KMS key. * @deprecated use opensearchservice module instead */ - readonly kmsKey?: kms.IKey; + readonly kmsKey?: kms.IKeyRef; } /** @@ -1841,7 +1841,7 @@ export class Domain extends DomainBase implements IDomain, ec2.IConnectable { encryptionAtRestOptions: { enabled: encryptionAtRestEnabled, kmsKeyId: encryptionAtRestEnabled - ? props.encryptionAtRest?.kmsKey?.keyId + ? props.encryptionAtRest?.kmsKey?.keyRef.keyId : undefined, }, nodeToNodeEncryptionOptions: { enabled: nodeToNodeEncryptionEnabled }, @@ -1970,7 +1970,7 @@ export class Domain extends DomainBase implements IDomain, ec2.IConnectable { // empircal evidence shows this is indeed required: https://github.com/aws/aws-cdk/issues/11412 this.accessPolicy.grantPrincipal.addToPrincipalPolicy(new iam.PolicyStatement({ actions: ['kms:List*', 'kms:Describe*', 'kms:CreateGrant'], - resources: [this.encryptionAtRestOptions.kmsKey.keyArn], + resources: [this.encryptionAtRestOptions.kmsKey.keyRef.keyArn], effect: iam.Effect.ALLOW, })); } diff --git a/packages/aws-cdk-lib/aws-events/lib/rule.ts b/packages/aws-cdk-lib/aws-events/lib/rule.ts index 5ec0870f5d28e..8974dca825288 100644 --- a/packages/aws-cdk-lib/aws-events/lib/rule.ts +++ b/packages/aws-cdk-lib/aws-events/lib/rule.ts @@ -7,7 +7,7 @@ import { IRule } from './rule-ref'; import { Schedule } from './schedule'; import { IRuleTarget } from './target'; import { mergeEventPattern, renderEventPattern } from './util'; -import { IRole, PolicyStatement, Role, ServicePrincipal } from '../../aws-iam'; +import { IRole, IRoleRef, PolicyStatement, Role, ServicePrincipal } from '../../aws-iam'; import { App, IResource, Lazy, Names, Resource, Stack, Token, TokenComparison, PhysicalName, ArnFormat, Annotations, ValidationError } from '../../core'; import { addConstructMetadata, MethodMetadata } from '../../core/lib/metadata-resource'; import { propertyInjectable } from '../../core/lib/prop-injectable'; @@ -61,7 +61,7 @@ export interface RuleProps extends EventCommonOptions { * * @default - No role associated */ - readonly role?: IRole; + readonly role?: IRoleRef; } /** @@ -129,7 +129,7 @@ export class Rule extends Resource implements IRule { eventPattern: Lazy.any({ produce: () => this._renderEventPattern() }), targets: Lazy.any({ produce: () => this.renderTargets() }), eventBusName: props.eventBus && props.eventBus.eventBusName, - roleArn: props.role?.roleArn, + roleArn: props.role?.roleRef.roleArn, }); this.ruleArn = this.getResourceArnAttribute(resource.attrArn, { diff --git a/packages/aws-cdk-lib/aws-fsx/lib/file-system.ts b/packages/aws-cdk-lib/aws-fsx/lib/file-system.ts index cfb71a8021367..226d9689fd4bd 100644 --- a/packages/aws-cdk-lib/aws-fsx/lib/file-system.ts +++ b/packages/aws-cdk-lib/aws-fsx/lib/file-system.ts @@ -1,5 +1,5 @@ import { Connections, IConnectable, ISecurityGroup, IVpc } from '../../aws-ec2'; -import { IKey } from '../../aws-kms'; +import { IKeyRef } from '../../aws-kms'; import { RemovalPolicy, Resource } from '../../core'; /** @@ -54,7 +54,7 @@ export interface FileSystemProps { * * @default - the aws/fsx default KMS key for the AWS account being deployed into. */ - readonly kmsKey?: IKey; + readonly kmsKey?: IKeyRef; /** * Security Group to assign to this file system. diff --git a/packages/aws-cdk-lib/aws-fsx/lib/lustre-file-system.ts b/packages/aws-cdk-lib/aws-fsx/lib/lustre-file-system.ts index d4bb8e483765f..7af63ed1937cd 100644 --- a/packages/aws-cdk-lib/aws-fsx/lib/lustre-file-system.ts +++ b/packages/aws-cdk-lib/aws-fsx/lib/lustre-file-system.ts @@ -349,7 +349,7 @@ export class LustreFileSystem extends FileSystemBase { fileSystemType: LustreFileSystem.DEFAULT_FILE_SYSTEM_TYPE, subnetIds: [props.vpcSubnet.subnetId], backupId: props.backupId, - kmsKeyId: (props.kmsKey ? props.kmsKey.keyId : undefined), + kmsKeyId: (props.kmsKey ? props.kmsKey.keyRef.keyId : undefined), lustreConfiguration, securityGroupIds: [securityGroup.securityGroupId], storageCapacity: props.storageCapacityGiB, diff --git a/packages/aws-cdk-lib/aws-iam/lib/access-key.ts b/packages/aws-cdk-lib/aws-iam/lib/access-key.ts index 78347c7540ada..8b7788f09780b 100644 --- a/packages/aws-cdk-lib/aws-iam/lib/access-key.ts +++ b/packages/aws-cdk-lib/aws-iam/lib/access-key.ts @@ -1,5 +1,5 @@ import { Construct } from 'constructs'; -import { CfnAccessKey } from './iam.generated'; +import { AccessKeyReference, CfnAccessKey, IAccessKeyRef } from './iam.generated'; import { IUser } from './user'; import { IResource, Resource, SecretValue } from '../../core'; import { addConstructMetadata } from '../../core/lib/metadata-resource'; @@ -30,7 +30,7 @@ export enum AccessKeyStatus { * * @see https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_access-keys.html */ -export interface IAccessKey extends IResource { +export interface IAccessKey extends IResource, IAccessKeyRef { /** * The Access Key ID. * @@ -84,6 +84,7 @@ export interface AccessKeyProps { export class AccessKey extends Resource implements IAccessKey { /** Uniquely identifies this class. */ public static readonly PROPERTY_INJECTION_ID: string = 'aws-cdk-lib.aws-iam.AccessKey'; + public readonly accessKeyRef: AccessKeyReference; public readonly accessKeyId: string; public readonly secretAccessKey: SecretValue; @@ -98,6 +99,7 @@ export class AccessKey extends Resource implements IAccessKey { }); this.accessKeyId = accessKey.ref; + this.accessKeyRef = accessKey.accessKeyRef; this.secretAccessKey = SecretValue.resourceAttribute(accessKey.attrSecretAccessKey); } diff --git a/packages/aws-cdk-lib/aws-iam/lib/group.ts b/packages/aws-cdk-lib/aws-iam/lib/group.ts index bb9673cea5724..83c744a7775c5 100644 --- a/packages/aws-cdk-lib/aws-iam/lib/group.ts +++ b/packages/aws-cdk-lib/aws-iam/lib/group.ts @@ -1,5 +1,5 @@ import { Construct } from 'constructs'; -import { CfnGroup } from './iam.generated'; +import { CfnGroup, GroupReference, IGroupRef } from './iam.generated'; import { IIdentity } from './identity-base'; import { IManagedPolicy } from './managed-policy'; import { Policy } from './policy'; @@ -16,7 +16,7 @@ import { propertyInjectable } from '../../core/lib/prop-injectable'; * * @see https://docs.aws.amazon.com/IAM/latest/UserGuide/id_groups.html */ -export interface IGroup extends IIdentity { +export interface IGroup extends IIdentity, IGroupRef { /** * Returns the IAM Group Name * @@ -85,6 +85,13 @@ abstract class GroupBase extends Resource implements IGroup { return new ArnPrincipal(this.groupArn).policyFragment; } + public get groupRef(): GroupReference { + return { + groupName: this.groupName, + groupArn: this.groupArn, + }; + } + /** * Attaches a policy to this group. * @param policy The policy to attach. diff --git a/packages/aws-cdk-lib/aws-iam/lib/instance-profile.ts b/packages/aws-cdk-lib/aws-iam/lib/instance-profile.ts index 4d30009fa20da..bad03c6bf0c46 100644 --- a/packages/aws-cdk-lib/aws-iam/lib/instance-profile.ts +++ b/packages/aws-cdk-lib/aws-iam/lib/instance-profile.ts @@ -1,15 +1,15 @@ import { Construct } from 'constructs'; -import { CfnInstanceProfile } from './iam.generated'; +import { CfnInstanceProfile, IInstanceProfileRef, InstanceProfileReference } from './iam.generated'; import { ServicePrincipal } from './principals'; import { IRole, Role } from './role'; -import { Resource, Arn, Stack, IResource, PhysicalName } from '../../core'; +import { Arn, IResource, PhysicalName, Resource, Stack } from '../../core'; import { addConstructMetadata } from '../../core/lib/metadata-resource'; import { propertyInjectable } from '../../core/lib/prop-injectable'; /** * Represents an IAM Instance Profile */ -export interface IInstanceProfile extends IResource { +export interface IInstanceProfile extends IResource, IInstanceProfileRef { /** * The InstanceProfile's name. * @attribute @@ -99,6 +99,13 @@ abstract class InstanceProfileBase extends Resource implements IInstanceProfile public get role(): IRole | undefined { return this._role; } + + public get instanceProfileRef(): InstanceProfileReference { + return { + instanceProfileName: this.instanceProfileName, + instanceProfileArn: this.instanceProfileArn, + }; + } } /** diff --git a/packages/aws-cdk-lib/aws-iam/lib/lazy-role.ts b/packages/aws-cdk-lib/aws-iam/lib/lazy-role.ts index 3bc1d1e503d36..caac20887d3f6 100644 --- a/packages/aws-cdk-lib/aws-iam/lib/lazy-role.ts +++ b/packages/aws-cdk-lib/aws-iam/lib/lazy-role.ts @@ -1,5 +1,6 @@ import { Construct } from 'constructs'; import { Grant } from './grant'; +import { RoleReference } from './iam.generated'; import { IManagedPolicy } from './managed-policy'; import { Policy } from './policy'; import { PolicyStatement } from './policy-statement'; @@ -99,6 +100,10 @@ export class LazyRole extends cdk.Resource implements IRole { return this.instantiate().roleArn; } + public get roleRef(): RoleReference { + return this.instantiate().roleRef; + } + /** * Returns the stable and unique string identifying the role (i.e. AIDAJQABLZS4A3QDU576Q) * diff --git a/packages/aws-cdk-lib/aws-iam/lib/managed-policy.ts b/packages/aws-cdk-lib/aws-iam/lib/managed-policy.ts index 93e53d082fb50..869f1f78f0ac1 100644 --- a/packages/aws-cdk-lib/aws-iam/lib/managed-policy.ts +++ b/packages/aws-cdk-lib/aws-iam/lib/managed-policy.ts @@ -1,13 +1,19 @@ -import { Construct } from 'constructs'; -import { IGroup } from './group'; -import { CfnManagedPolicy } from './iam.generated'; +import { Construct, Node } from 'constructs'; +import { + CfnManagedPolicy, + IGroupRef, + IManagedPolicyRef, + IRoleRef, + IUserRef, + ManagedPolicyReference, +} from './iam.generated'; import { PolicyDocument } from './policy-document'; import { PolicyStatement } from './policy-statement'; import { AddToPrincipalPolicyResult, IGrantable, IPrincipal, PrincipalPolicyFragment } from './principals'; import { undefinedIfEmpty } from './private/util'; import { IRole } from './role'; import { IUser } from './user'; -import { ArnFormat, Resource, Stack, Arn, Aws, UnscopedValidationError } from '../../core'; +import { Arn, ArnFormat, Aws, Resource, Stack, UnscopedValidationError } from '../../core'; import { getCustomizeRolesConfig, PolicySynthesizer } from '../../core/lib/helpers-internal'; import { addConstructMetadata, MethodMetadata } from '../../core/lib/metadata-resource'; import { propertyInjectable } from '../../core/lib/prop-injectable'; @@ -15,7 +21,7 @@ import { propertyInjectable } from '../../core/lib/prop-injectable'; /** * A managed policy */ -export interface IManagedPolicy { +export interface IManagedPolicy extends IManagedPolicyRef { /** * The ARN of the managed policy * @attribute @@ -79,7 +85,7 @@ export interface ManagedPolicyProps { * * @default - No groups. */ - readonly groups?: IGroup[]; + readonly groups?: IGroupRef[]; /** * Initial set of permissions to add to this policy document. @@ -123,6 +129,11 @@ export class ManagedPolicy extends Resource implements IManagedPolicy, IGrantabl resource: 'policy', resourceName: managedPolicyName, }); + public get managedPolicyRef(): ManagedPolicyReference { + return { + policyArn: this.managedPolicyArn, + }; + } } return new Import(scope, id); } @@ -149,6 +160,11 @@ export class ManagedPolicy extends Resource implements IManagedPolicy, IGrantabl public static fromManagedPolicyArn(scope: Construct, id: string, managedPolicyArn: string): IManagedPolicy { class Import extends Resource implements IManagedPolicy { public readonly managedPolicyArn = managedPolicyArn; + public get managedPolicyRef(): ManagedPolicyReference { + return { + policyArn: this.managedPolicyArn, + }; + } } return new Import(scope, id); } @@ -172,6 +188,14 @@ export class ManagedPolicy extends Resource implements IManagedPolicy, IGrantabl resource: 'policy', resourceName: managedPolicyName, }); + public get managedPolicyRef(): ManagedPolicyReference { + return { + policyArn: this.managedPolicyArn, + }; + } + public get node(): Node { + throw new UnscopedValidationError('The result of fromAwsManagedPolicyName can not be used in this API'); + } } return new AwsManagedPolicy(); } @@ -211,9 +235,9 @@ export class ManagedPolicy extends Resource implements IManagedPolicy, IGrantabl public readonly grantPrincipal: IPrincipal; - private readonly roles = new Array(); - private readonly users = new Array(); - private readonly groups = new Array(); + private readonly roles = new Array(); + private readonly users = new Array(); + private readonly groups = new Array(); private readonly _precreatedPolicy?: IManagedPolicy; constructor(scope: Construct, id: string, props: ManagedPolicyProps = {}) { @@ -243,9 +267,9 @@ export class ManagedPolicy extends Resource implements IManagedPolicy, IGrantabl managedPolicyName: this.physicalName, description: this.description, path: this.path, - roles: undefinedIfEmpty(() => this.roles.map(r => r.roleName)), - users: undefinedIfEmpty(() => this.users.map(u => u.userName)), - groups: undefinedIfEmpty(() => this.groups.map(g => g.groupName)), + roles: undefinedIfEmpty(() => this.roles.map(r => r.roleRef.roleName)), + users: undefinedIfEmpty(() => this.users.map(u => u.userRef.userName)), + groups: undefinedIfEmpty(() => this.groups.map(g => g.groupRef.groupName)), }); // arn:aws:iam::123456789012:policy/teststack-CreateTestDBPolicy-16M23YE3CS700 @@ -279,6 +303,12 @@ export class ManagedPolicy extends Resource implements IManagedPolicy, IGrantabl this.node.addValidation({ validate: () => this.validateManagedPolicy() }); } + public get managedPolicyRef(): ManagedPolicyReference { + return { + policyArn: this.managedPolicyArn, + }; + } + /** * Adds a statement to the policy document. */ @@ -291,8 +321,8 @@ export class ManagedPolicy extends Resource implements IManagedPolicy, IGrantabl * Attaches this policy to a user. */ @MethodMetadata() - public attachToUser(user: IUser) { - if (this.users.find(u => u.userArn === user.userArn)) { return; } + public attachToUser(user: IUserRef) { + if (this.users.find(u => u.userRef.userArn === user.userRef.userArn)) { return; } this.users.push(user); } @@ -301,7 +331,7 @@ export class ManagedPolicy extends Resource implements IManagedPolicy, IGrantabl */ @MethodMetadata() public attachToRole(role: IRole) { - if (this.roles.find(r => r.roleArn === role.roleArn)) { return; } + if (this.roles.find(r => r.roleRef.roleArn === role.roleArn)) { return; } this.roles.push(role); } @@ -309,8 +339,8 @@ export class ManagedPolicy extends Resource implements IManagedPolicy, IGrantabl * Attaches this policy to a group. */ @MethodMetadata() - public attachToGroup(group: IGroup) { - if (this.groups.find(g => g.groupArn === group.groupArn)) { return; } + public attachToGroup(group: IGroupRef) { + if (this.groups.find(g => g.groupRef.groupArn === group.groupRef.groupArn)) { return; } this.groups.push(group); } diff --git a/packages/aws-cdk-lib/aws-iam/lib/oidc-provider-native.ts b/packages/aws-cdk-lib/aws-iam/lib/oidc-provider-native.ts index 2d379f3e66643..0ecc6c8c67af5 100644 --- a/packages/aws-cdk-lib/aws-iam/lib/oidc-provider-native.ts +++ b/packages/aws-cdk-lib/aws-iam/lib/oidc-provider-native.ts @@ -1,5 +1,5 @@ import { Construct } from 'constructs'; -import { CfnOIDCProvider } from './iam.generated'; +import { CfnOIDCProvider, IOIDCProviderRef, OIDCProviderReference } from './iam.generated'; import { Arn, IResource, Resource, Token, ValidationError } from '../../core'; import { addConstructMetadata } from '../../core/lib/metadata-resource'; import { propertyInjectable } from '../../core/lib/prop-injectable'; @@ -8,7 +8,7 @@ import { propertyInjectable } from '../../core/lib/prop-injectable'; * Represents an IAM OpenID Connect provider. * */ -export interface IOidcProvider extends IResource { +export interface IOidcProvider extends IResource, IOIDCProviderRef { /** * The Amazon Resource Name (ARN) of the IAM OpenID Connect provider. * @@ -134,6 +134,12 @@ export class OidcProviderNative extends Resource implements IOidcProvider { class Import extends Resource implements IOidcProvider { public readonly oidcProviderArn = oidcProviderArn; public readonly oidcProviderIssuer = resourceName; + + public get oidcProviderRef(): OIDCProviderReference { + return { + oidcProviderArn: this.oidcProviderArn, + }; + } } return new Import(scope, id); @@ -227,4 +233,10 @@ export class OidcProviderNative extends Resource implements IOidcProvider { this.oidcProviderThumbprints = Token.asString(props.thumbprints); } + + public get oidcProviderRef(): OIDCProviderReference { + return { + oidcProviderArn: this.oidcProviderArn, + }; + } } diff --git a/packages/aws-cdk-lib/aws-iam/lib/oidc-provider.ts b/packages/aws-cdk-lib/aws-iam/lib/oidc-provider.ts index 00a2256f400b5..b3922e2fdbaea 100644 --- a/packages/aws-cdk-lib/aws-iam/lib/oidc-provider.ts +++ b/packages/aws-cdk-lib/aws-iam/lib/oidc-provider.ts @@ -1,12 +1,6 @@ import { Construct } from 'constructs'; -import { - Arn, - CustomResource, - FeatureFlags, - IResource, - Resource, - Token, -} from '../../core'; +import { IOIDCProviderRef, OIDCProviderReference } from './iam.generated'; +import { Arn, CustomResource, FeatureFlags, IResource, Resource, Token } from '../../core'; import { addConstructMetadata } from '../../core/lib/metadata-resource'; import { propertyInjectable } from '../../core/lib/prop-injectable'; import { OidcProvider } from '../../custom-resource-handlers/dist/aws-iam/oidc-provider.generated'; @@ -18,7 +12,7 @@ const RESOURCE_TYPE = 'Custom::AWSCDKOpenIdConnectProvider'; * Represents an IAM OpenID Connect provider. * */ -export interface IOpenIdConnectProvider extends IResource { +export interface IOpenIdConnectProvider extends IResource, IOIDCProviderRef { /** * The Amazon Resource Name (ARN) of the IAM OpenID Connect provider. */ @@ -137,6 +131,11 @@ export class OpenIdConnectProvider extends Resource implements IOpenIdConnectPro class Import extends Resource implements IOpenIdConnectProvider { public readonly openIdConnectProviderArn = openIdConnectProviderArn; public readonly openIdConnectProviderIssuer = resourceName; + public get oidcProviderRef(): OIDCProviderReference { + return { + oidcProviderArn: this.openIdConnectProviderArn, + }; + } } return new Import(scope, id); @@ -189,6 +188,12 @@ export class OpenIdConnectProvider extends Resource implements IOpenIdConnectPro this.openIdConnectProviderthumbprints = Token.asString(resource.getAtt('Thumbprints')); } + public get oidcProviderRef(): OIDCProviderReference { + return { + oidcProviderArn: this.openIdConnectProviderArn, + }; + } + private getOrCreateProvider() { return OidcProvider.getOrCreateProvider(this, RESOURCE_TYPE, { policyStatements: [ diff --git a/packages/aws-cdk-lib/aws-iam/lib/policy.ts b/packages/aws-cdk-lib/aws-iam/lib/policy.ts index 8ff9fd8df455f..8dff699522d64 100644 --- a/packages/aws-cdk-lib/aws-iam/lib/policy.ts +++ b/packages/aws-cdk-lib/aws-iam/lib/policy.ts @@ -1,13 +1,13 @@ import { Construct } from 'constructs'; import { IGroup } from './group'; -import { CfnPolicy } from './iam.generated'; +import { CfnPolicy, IPolicyRef, PolicyReference } from './iam.generated'; import { PolicyDocument } from './policy-document'; import { PolicyStatement } from './policy-statement'; import { AddToPrincipalPolicyResult, IGrantable, IPrincipal, PrincipalPolicyFragment } from './principals'; import { generatePolicyName, undefinedIfEmpty } from './private/util'; import { IRole } from './role'; import { IUser } from './user'; -import { IResource, Lazy, Resource, UnscopedValidationError } from '../../core'; +import { IResource, Lazy, Resource, UnscopedValidationError, ValidationError } from '../../core'; import { addConstructMetadata, MethodMetadata } from '../../core/lib/metadata-resource'; import { propertyInjectable } from '../../core/lib/prop-injectable'; @@ -16,7 +16,7 @@ import { propertyInjectable } from '../../core/lib/prop-injectable'; * * @see https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies_manage.html */ -export interface IPolicy extends IResource { +export interface IPolicy extends IResource, IPolicyRef { /** * The name of this policy. * @@ -117,6 +117,10 @@ export class Policy extends Resource implements IPolicy, IGrantable { public static fromPolicyName(scope: Construct, id: string, policyName: string): IPolicy { class Import extends Resource implements IPolicy { public readonly policyName = policyName; + + public get policyRef(): PolicyReference { + throw new ValidationError('Cannot use a Policy.fromPolicyName() here.', this); + } } return new Import(scope, id); @@ -128,6 +132,7 @@ export class Policy extends Resource implements IPolicy, IGrantable { public readonly document = new PolicyDocument(); public readonly grantPrincipal: IPrincipal; + public readonly policyRef: PolicyReference; private readonly _policyName: string; private readonly roles = new Array(); @@ -172,6 +177,10 @@ export class Policy extends Resource implements IPolicy, IGrantable { groups: undefinedIfEmpty(() => this.groups.map(g => g.groupName)), }); + this.policyRef = { + policyId: resource.attrId, + }; + this._policyName = this.physicalName!; this.force = props.force ?? false; diff --git a/packages/aws-cdk-lib/aws-iam/lib/principals.ts b/packages/aws-cdk-lib/aws-iam/lib/principals.ts index 2651acf9a7d72..4f33045a7ccbd 100644 --- a/packages/aws-cdk-lib/aws-iam/lib/principals.ts +++ b/packages/aws-cdk-lib/aws-iam/lib/principals.ts @@ -1,5 +1,5 @@ import { IDependable } from 'constructs'; -import { IOpenIdConnectProvider } from './oidc-provider'; +import { IOIDCProviderRef, ISAMLProviderRef } from './iam.generated'; import { PolicyDocument } from './policy-document'; import { Condition, Conditions, PolicyStatement } from './policy-statement'; import { defaultAddPrincipalToAssumeRole } from './private/assume-role-policy'; @@ -756,8 +756,8 @@ export class OpenIdConnectPrincipal extends WebIdentityPrincipal { * @param conditions The conditions under which the policy is in effect. * See [the IAM documentation](https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_elements_condition.html). */ - constructor(openIdConnectProvider: IOpenIdConnectProvider, conditions: Conditions = {}) { - super(openIdConnectProvider.openIdConnectProviderArn, conditions ?? {}); + constructor(openIdConnectProvider: IOIDCProviderRef, conditions: Conditions = {}) { + super(openIdConnectProvider.oidcProviderRef.oidcProviderArn, conditions ?? {}); } public get policyFragment(): PrincipalPolicyFragment { @@ -773,8 +773,8 @@ export class OpenIdConnectPrincipal extends WebIdentityPrincipal { * Principal entity that represents a SAML federated identity provider */ export class SamlPrincipal extends FederatedPrincipal { - constructor(samlProvider: ISamlProvider, conditions: Conditions) { - super(samlProvider.samlProviderArn, conditions, 'sts:AssumeRoleWithSAML'); + constructor(samlProvider: ISAMLProviderRef, conditions: Conditions) { + super(samlProvider.samlProviderRef.samlProviderArn, conditions, 'sts:AssumeRoleWithSAML'); } public toString() { @@ -791,7 +791,7 @@ export class SamlConsolePrincipal extends SamlPrincipal { super(samlProvider, { ...conditions, StringEquals: { - 'SAML:aud': RegionInfo.get(samlProvider.stack.region).samlSignOnUrl ?? 'https://signin.aws.amazon.com/saml', + 'SAML:aud': RegionInfo.get(cdk.Stack.of(samlProvider).region).samlSignOnUrl ?? 'https://signin.aws.amazon.com/saml', }, }); } diff --git a/packages/aws-cdk-lib/aws-iam/lib/private/immutable-role.ts b/packages/aws-cdk-lib/aws-iam/lib/private/immutable-role.ts index 8e127abbae815..b80a940b4e692 100644 --- a/packages/aws-cdk-lib/aws-iam/lib/private/immutable-role.ts +++ b/packages/aws-cdk-lib/aws-iam/lib/private/immutable-role.ts @@ -3,6 +3,7 @@ import { Resource } from '../../../core'; import { addConstructMetadata, MethodMetadata } from '../../../core/lib/metadata-resource'; import { propertyInjectable } from '../../../core/lib/prop-injectable'; import { Grant } from '../grant'; +import { RoleReference } from '../iam.generated'; import { IManagedPolicy } from '../managed-policy'; import { Policy } from '../policy'; import { PolicyStatement } from '../policy-statement'; @@ -49,6 +50,13 @@ export class ImmutableRole extends Resource implements IRole { this.node.defaultChild = role.node.defaultChild; } + public get roleRef(): RoleReference { + return { + roleName: this.roleName, + roleArn: this.roleArn, + }; + } + @MethodMetadata() public attachInlinePolicy(_policy: Policy): void { // do nothing diff --git a/packages/aws-cdk-lib/aws-iam/lib/private/imported-role.ts b/packages/aws-cdk-lib/aws-iam/lib/private/imported-role.ts index 862d562f0154e..a55b0e0f96729 100644 --- a/packages/aws-cdk-lib/aws-iam/lib/private/imported-role.ts +++ b/packages/aws-cdk-lib/aws-iam/lib/private/imported-role.ts @@ -1,15 +1,22 @@ import { Construct } from 'constructs'; import { MAX_POLICY_NAME_LEN } from './util'; -import { FeatureFlags, Names, Resource, Token, TokenComparison, Annotations } from '../../../core'; +import { Annotations, FeatureFlags, Names, Resource, Token, TokenComparison } from '../../../core'; import { addConstructMetadata, MethodMetadata } from '../../../core/lib/metadata-resource'; import { propertyInjectable } from '../../../core/lib/prop-injectable'; import { IAM_IMPORTED_ROLE_STACK_SAFE_DEFAULT_POLICY_NAME } from '../../../cx-api'; import { Grant } from '../grant'; +import { RoleReference } from '../iam.generated'; import { IManagedPolicy, ManagedPolicy } from '../managed-policy'; import { Policy } from '../policy'; import { PolicyStatement } from '../policy-statement'; -import { IComparablePrincipal, IPrincipal, ArnPrincipal, AddToPrincipalPolicyResult, PrincipalPolicyFragment } from '../principals'; -import { IRole, FromRoleArnOptions } from '../role'; +import { + AddToPrincipalPolicyResult, + ArnPrincipal, + IComparablePrincipal, + IPrincipal, + PrincipalPolicyFragment, +} from '../principals'; +import { FromRoleArnOptions, IRole } from '../role'; import { AttachedPolicies } from '../util'; export interface ImportedRoleProps extends FromRoleArnOptions { @@ -46,6 +53,13 @@ export class ImportedRole extends Resource implements IRole, IComparablePrincipa this.principalAccount = props.account; } + public get roleRef(): RoleReference { + return { + roleName: this.roleName, + roleArn: this.roleArn, + }; + } + @MethodMetadata() public addToPolicy(statement: PolicyStatement): boolean { return this.addToPrincipalPolicy(statement).statementAdded; diff --git a/packages/aws-cdk-lib/aws-iam/lib/private/precreated-role.ts b/packages/aws-cdk-lib/aws-iam/lib/private/precreated-role.ts index 49945af103cc8..a4ce53077cc84 100644 --- a/packages/aws-cdk-lib/aws-iam/lib/private/precreated-role.ts +++ b/packages/aws-cdk-lib/aws-iam/lib/private/precreated-role.ts @@ -4,6 +4,7 @@ import { PolicySynthesizer } from '../../../core/lib/helpers-internal'; import { addConstructMetadata, MethodMetadata } from '../../../core/lib/metadata-resource'; import { propertyInjectable } from '../../../core/lib/prop-injectable'; import { Grant } from '../grant'; +import { RoleReference } from '../iam.generated'; import { IManagedPolicy } from '../managed-policy'; import { Policy } from '../policy'; import { PolicyDocument } from '../policy-document'; @@ -105,6 +106,13 @@ export class PrecreatedRole extends Resource implements IRole { }); } + public get roleRef(): RoleReference { + return { + roleName: this.roleName, + roleArn: this.roleArn, + }; + } + @MethodMetadata() public attachInlinePolicy(policy: Policy): void { const statements = policy.document.toJSON()?.Statement; diff --git a/packages/aws-cdk-lib/aws-iam/lib/role.ts b/packages/aws-cdk-lib/aws-iam/lib/role.ts index a036dd24900dc..50a60aba63b98 100644 --- a/packages/aws-cdk-lib/aws-iam/lib/role.ts +++ b/packages/aws-cdk-lib/aws-iam/lib/role.ts @@ -1,12 +1,19 @@ -import { Construct, IConstruct, DependencyGroup, Node } from 'constructs'; +import { Construct, DependencyGroup, IConstruct, Node } from 'constructs'; import { Grant } from './grant'; -import { CfnRole } from './iam.generated'; +import { CfnRole, IRoleRef, RoleReference } from './iam.generated'; import { IIdentity } from './identity-base'; import { IManagedPolicy, ManagedPolicy } from './managed-policy'; import { Policy } from './policy'; import { PolicyDocument } from './policy-document'; import { PolicyStatement } from './policy-statement'; -import { AccountPrincipal, AddToPrincipalPolicyResult, ArnPrincipal, IPrincipal, PrincipalPolicyFragment, ServicePrincipal } from './principals'; +import { + AccountPrincipal, + AddToPrincipalPolicyResult, + ArnPrincipal, + IPrincipal, + PrincipalPolicyFragment, + ServicePrincipal, +} from './principals'; import { defaultAddPrincipalToAssumeRole } from './private/assume-role-policy'; import { ImmutableRole } from './private/immutable-role'; import { ImportedRole } from './private/imported-role'; @@ -14,8 +21,25 @@ import { MutatingPolicyDocumentAdapter } from './private/policydoc-adapter'; import { PrecreatedRole } from './private/precreated-role'; import { AttachedPolicies, UniqueStringSet } from './private/util'; import * as cxschema from '../../cloud-assembly-schema'; -import { ArnFormat, Duration, Resource, Stack, Token, TokenComparison, Aspects, Annotations, RemovalPolicy, ContextProvider, ValidationError } from '../../core'; -import { getCustomizeRolesConfig, getPrecreatedRoleConfig, CUSTOMIZE_ROLES_CONTEXT_KEY, CustomizeRoleConfig } from '../../core/lib/helpers-internal'; +import { + Annotations, + ArnFormat, + Aspects, + ContextProvider, + Duration, + RemovalPolicy, + Resource, + Stack, + Token, + TokenComparison, + ValidationError, +} from '../../core'; +import { + CUSTOMIZE_ROLES_CONTEXT_KEY, + CustomizeRoleConfig, + getCustomizeRolesConfig, + getPrecreatedRoleConfig, +} from '../../core/lib/helpers-internal'; import { addConstructMetadata, MethodMetadata } from '../../core/lib/metadata-resource'; import { mutatingAspectPrio32333 } from '../../core/lib/private/aspect-prio'; import { propertyInjectable } from '../../core/lib/prop-injectable'; @@ -577,6 +601,13 @@ export class Role extends Resource implements IRole { this.node.addValidation({ validate: () => this.validateRole() }); } + public get roleRef(): RoleReference { + return { + roleName: this.roleName, + roleArn: this.roleArn, + }; + } + /** * Adds a permission to the role's default policy document. * If there is no default policy attached to this role, it will be created. @@ -787,7 +818,7 @@ export class Role extends Resource implements IRole { /** * A Role object */ -export interface IRole extends IIdentity { +export interface IRole extends IIdentity, IRoleRef { /** * Returns the ARN of this role. * diff --git a/packages/aws-cdk-lib/aws-iam/lib/saml-provider.ts b/packages/aws-cdk-lib/aws-iam/lib/saml-provider.ts index d9a29348baae0..211e40e19ba8c 100644 --- a/packages/aws-cdk-lib/aws-iam/lib/saml-provider.ts +++ b/packages/aws-cdk-lib/aws-iam/lib/saml-provider.ts @@ -1,6 +1,6 @@ import * as fs from 'fs'; import { Construct } from 'constructs'; -import { CfnSAMLProvider } from './iam.generated'; +import { CfnSAMLProvider, ISAMLProviderRef, SAMLProviderReference } from './iam.generated'; import { IResource, Resource, Token, ValidationError } from '../../core'; import { addConstructMetadata } from '../../core/lib/metadata-resource'; import { propertyInjectable } from '../../core/lib/prop-injectable'; @@ -8,7 +8,7 @@ import { propertyInjectable } from '../../core/lib/prop-injectable'; /** * A SAML provider */ -export interface ISamlProvider extends IResource { +export interface ISamlProvider extends IResource, ISAMLProviderRef { /** * The Amazon Resource Name (ARN) of the provider * @@ -83,6 +83,7 @@ export class SamlProvider extends Resource implements ISamlProvider { public static fromSamlProviderArn(scope: Construct, id: string, samlProviderArn: string): ISamlProvider { class Import extends Resource implements ISamlProvider { public readonly samlProviderArn = samlProviderArn; + public samlProviderRef: SAMLProviderReference = { samlProviderArn }; } return new Import(scope, id); } @@ -105,4 +106,10 @@ export class SamlProvider extends Resource implements ISamlProvider { this.samlProviderArn = samlProvider.ref; } + + public get samlProviderRef(): SAMLProviderReference { + return { + samlProviderArn: this.samlProviderArn, + }; + } } diff --git a/packages/aws-cdk-lib/aws-iam/lib/user.ts b/packages/aws-cdk-lib/aws-iam/lib/user.ts index 547ae436ba7d8..77a62e5d9d303 100644 --- a/packages/aws-cdk-lib/aws-iam/lib/user.ts +++ b/packages/aws-cdk-lib/aws-iam/lib/user.ts @@ -1,6 +1,6 @@ import { Construct } from 'constructs'; import { IGroup } from './group'; -import { CfnUser, CfnUserToGroupAddition } from './iam.generated'; +import { CfnUser, CfnUserToGroupAddition, IUserRef, UserReference } from './iam.generated'; import { IIdentity } from './identity-base'; import { IManagedPolicy } from './managed-policy'; import { Policy } from './policy'; @@ -16,7 +16,7 @@ import { propertyInjectable } from '../../core/lib/prop-injectable'; * * @see https://docs.aws.amazon.com/IAM/latest/UserGuide/id_users.html */ -export interface IUser extends IIdentity { +export interface IUser extends IIdentity, IUserRef { /** * The user's name * @attribute @@ -215,7 +215,7 @@ export class User extends Resource implements IIdentity, IUser { public addToGroup(group: IGroup): void { new CfnUserToGroupAddition(Stack.of(group), `${this.userName}Group${this.groupId}`, { - groupName: group.groupName, + groupName: group.groupRef.groupName, users: [this.userName], }); this.groupId += 1; @@ -229,6 +229,13 @@ export class User extends Resource implements IIdentity, IUser { public addManagedPolicy(_policy: IManagedPolicy): void { throw new ValidationError('Cannot add managed policy to imported User', this); } + + public get userRef(): UserReference { + return { + userName: this.userName, + userArn: this.userArn, + }; + } } return new Import(scope, id); @@ -297,6 +304,13 @@ export class User extends Resource implements IIdentity, IUser { } } + public get userRef(): UserReference { + return { + userName: this.userName, + userArn: this.userArn, + }; + } + /** * Adds this user to a group. */ diff --git a/packages/aws-cdk-lib/aws-iam/test/custom-resource/index.js b/packages/aws-cdk-lib/aws-iam/test/custom-resource/index.js index e69de29bb2d1d..3918c74e44633 100644 --- a/packages/aws-cdk-lib/aws-iam/test/custom-resource/index.js +++ b/packages/aws-cdk-lib/aws-iam/test/custom-resource/index.js @@ -0,0 +1 @@ +"use strict"; diff --git a/packages/aws-cdk-lib/aws-kms/lib/alias.ts b/packages/aws-cdk-lib/aws-kms/lib/alias.ts index 801ace2813050..46c12d809e1a4 100644 --- a/packages/aws-cdk-lib/aws-kms/lib/alias.ts +++ b/packages/aws-cdk-lib/aws-kms/lib/alias.ts @@ -1,6 +1,6 @@ import { Construct } from 'constructs'; import { IKey } from './key'; -import { CfnAlias } from './kms.generated'; +import { AliasReference, CfnAlias, IAliasRef, KeyReference } from './kms.generated'; import * as iam from '../../aws-iam'; import * as perms from './private/perms'; import { FeatureFlags, RemovalPolicy, Resource, Stack, Token, Tokenization, ValidationError } from '../../core'; @@ -15,7 +15,7 @@ const DISALLOWED_PREFIX = REQUIRED_ALIAS_PREFIX + 'aws/'; * A KMS Key alias. * An alias can be used in all places that expect a key. */ -export interface IAlias extends IKey { +export interface IAlias extends IKey, IAliasRef { /** * The name of the alias. * @@ -62,6 +62,16 @@ abstract class AliasBase extends Resource implements IAlias { public abstract readonly aliasTargetKey: IKey; + public get aliasRef(): AliasReference { + return { + aliasName: this.aliasName, + }; + } + + public get keyRef(): KeyReference { + return this.aliasTargetKey.keyRef; + } + /** * The ARN of the alias. * @@ -209,6 +219,10 @@ export class Alias extends AliasBase { return { statementAdded: false }; } + public get keyRef(): KeyReference { + return this.aliasTargetKey.keyRef; + } + public grant(grantee: iam.IGrantable, ...actions: string[]): iam.Grant { if (!FeatureFlags.of(this).isEnabled(KMS_APPLY_IMPORTED_ALIAS_PERMISSIONS_TO_PRINCIPAL)) { return iam.Grant.drop(grantee, ''); @@ -229,6 +243,12 @@ export class Alias extends AliasBase { }); } + public get aliasRef(): AliasReference { + return { + aliasName: this.aliasName, + }; + } + public grantDecrypt(grantee: iam.IGrantable): iam.Grant { return this.grant(grantee, ...perms.DECRYPT_ACTIONS); } diff --git a/packages/aws-cdk-lib/aws-kms/lib/key.ts b/packages/aws-cdk-lib/aws-kms/lib/key.ts index aeebd716af54f..461a309cafe55 100644 --- a/packages/aws-cdk-lib/aws-kms/lib/key.ts +++ b/packages/aws-cdk-lib/aws-kms/lib/key.ts @@ -1,7 +1,7 @@ import { Construct } from 'constructs'; import { Alias } from './alias'; import { KeyLookupOptions } from './key-lookup'; -import { CfnKey } from './kms.generated'; +import { CfnKey, IKeyRef, KeyReference } from './kms.generated'; import * as perms from './private/perms'; import * as iam from '../../aws-iam'; import * as cxschema from '../../cloud-assembly-schema'; @@ -27,7 +27,7 @@ import * as cxapi from '../../cx-api'; /** * A KMS Key, either managed by this CDK app, or imported. */ -export interface IKey extends IResource { +export interface IKey extends IResource, IKeyRef { /** * The ARN of the key. * @@ -141,6 +141,13 @@ abstract class KeyBase extends Resource implements IKey { this.node.addValidation({ validate: () => this.policy?.validateForResourcePolicy() ?? [] }); } + public get keyRef(): KeyReference { + return { + keyArn: this.keyArn, + keyId: this.keyId, + }; + } + /** * Defines a new alias for the key. */ @@ -784,8 +791,8 @@ export class Key extends KeyBase { * This method can only be used if the `returnDummyKeyOnMissing` option * is set to `true` in the `options` for the `Key.fromLookup()` method. */ - public static isLookupDummy(key: IKey): boolean { - return key.keyId === Key.DEFAULT_DUMMY_KEY_ID; + public static isLookupDummy(key: IKeyRef): boolean { + return key.keyRef.keyId === Key.DEFAULT_DUMMY_KEY_ID; } public readonly keyArn: string; diff --git a/packages/aws-cdk-lib/aws-lambda/lib/alias.ts b/packages/aws-cdk-lib/aws-lambda/lib/alias.ts index e7b0797d34db3..9c4703d7adca5 100644 --- a/packages/aws-cdk-lib/aws-lambda/lib/alias.ts +++ b/packages/aws-cdk-lib/aws-lambda/lib/alias.ts @@ -3,7 +3,7 @@ import { Architecture } from './architecture'; import { EventInvokeConfigOptions } from './event-invoke-config'; import { IFunction, QualifiedFunctionBase } from './function-base'; import { extractQualifierFromArn, IVersion } from './lambda-version'; -import { CfnAlias } from './lambda.generated'; +import { AliasReference, CfnAlias, IAliasRef } from './lambda.generated'; import { ScalableFunctionAttribute } from './private/scalable-function-attribute'; import { AutoScalingOptions, IScalableFunctionAttribute } from './scalable-attribute-api'; import * as appscaling from '../../aws-applicationautoscaling'; @@ -14,7 +14,7 @@ import { ValidationError } from '../../core/lib/errors'; import { addConstructMetadata, MethodMetadata } from '../../core/lib/metadata-resource'; import { propertyInjectable } from '../../core/lib/prop-injectable'; -export interface IAlias extends IFunction { +export interface IAlias extends IFunction, IAliasRef { /** * Name of this alias. * @@ -109,6 +109,12 @@ export class Alias extends QualifiedFunctionBase implements IAlias { protected readonly canCreatePermissions = this._isStackAccount(); protected readonly qualifier = attrs.aliasName; + + public get aliasRef(): AliasReference { + return { + aliasArn: this.functionArn, + }; + } } return new Imported(scope, id); } @@ -202,6 +208,12 @@ export class Alias extends QualifiedFunctionBase implements IAlias { this.functionName = `${this.stack.splitArn(this.functionArn, ArnFormat.COLON_RESOURCE_NAME).resourceName!}:${this.aliasName}`; } + public get aliasRef(): AliasReference { + return { + aliasArn: this.functionArn, + }; + } + public get grantPrincipal() { return this.version.grantPrincipal; } diff --git a/packages/aws-cdk-lib/aws-lambda/lib/code-signing-config.ts b/packages/aws-cdk-lib/aws-lambda/lib/code-signing-config.ts index 613c9d1adf882..f398e6d07aed4 100644 --- a/packages/aws-cdk-lib/aws-lambda/lib/code-signing-config.ts +++ b/packages/aws-cdk-lib/aws-lambda/lib/code-signing-config.ts @@ -1,5 +1,5 @@ import { Construct } from 'constructs'; -import { CfnCodeSigningConfig } from './lambda.generated'; +import { CfnCodeSigningConfig, CodeSigningConfigReference, ICodeSigningConfigRef } from './lambda.generated'; import { ISigningProfile } from '../../aws-signer'; import { ArnFormat, IResource, Resource, Stack } from '../../core'; import { ValidationError } from '../../core/lib/errors'; @@ -25,7 +25,7 @@ export enum UntrustedArtifactOnDeployment { /** * A Code Signing Config */ -export interface ICodeSigningConfig extends IResource { +export interface ICodeSigningConfig extends IResource, ICodeSigningConfigRef { /** * The ARN of Code Signing Config * @attribute @@ -100,6 +100,12 @@ export class CodeSigningConfig extends Resource implements ICodeSigningConfig { constructor() { super(scope, id); } + + public get codeSigningConfigRef(): CodeSigningConfigReference { + return { + codeSigningConfigArn: this.codeSigningConfigArn, + }; + } } return new Import(); } @@ -128,4 +134,10 @@ export class CodeSigningConfig extends Resource implements ICodeSigningConfig { this.codeSigningConfigArn = resource.attrCodeSigningConfigArn; this.codeSigningConfigId = resource.attrCodeSigningConfigId; } + + public get codeSigningConfigRef(): CodeSigningConfigReference { + return { + codeSigningConfigArn: this.codeSigningConfigArn, + }; + } } diff --git a/packages/aws-cdk-lib/aws-lambda/lib/code.ts b/packages/aws-cdk-lib/aws-lambda/lib/code.ts index 3f72eab2b9c8f..2b7ea33756bc6 100644 --- a/packages/aws-cdk-lib/aws-lambda/lib/code.ts +++ b/packages/aws-cdk-lib/aws-lambda/lib/code.ts @@ -3,7 +3,7 @@ import { Construct } from 'constructs'; import * as ecr from '../../aws-ecr'; import * as ecr_assets from '../../aws-ecr-assets'; import * as iam from '../../aws-iam'; -import { IKey } from '../../aws-kms'; +import { IKeyRef } from '../../aws-kms'; import * as s3 from '../../aws-s3'; import * as s3_assets from '../../aws-s3-assets'; import * as cdk from '../../core'; @@ -338,7 +338,7 @@ export class S3CodeV2 extends Code { objectKey: this.key, objectVersion: this.options?.objectVersion, }, - sourceKMSKeyArn: this.options?.sourceKMSKey?.keyArn, + sourceKMSKeyArn: this.options?.sourceKMSKey?.keyRef.keyArn, }; } } @@ -400,7 +400,7 @@ export class AssetCode extends Code { bucketName: this.asset.s3BucketName, objectKey: this.asset.s3ObjectKey, }, - sourceKMSKeyArn: this.options.sourceKMSKey?.keyArn, + sourceKMSKeyArn: this.options.sourceKMSKey?.keyRef.keyArn, }; } @@ -450,7 +450,7 @@ export interface CfnParametersCodeProps { * The ARN of the KMS key used to encrypt the handler code. * @default - the default server-side encryption with Amazon S3 managed keys(SSE-S3) key will be used. */ - readonly sourceKMSKey?: IKey; + readonly sourceKMSKey?: IKeyRef; } /** @@ -463,7 +463,7 @@ export class CfnParametersCode extends Code { public readonly isInline = false; private _bucketNameParam?: cdk.CfnParameter; private _objectKeyParam?: cdk.CfnParameter; - private _sourceKMSKey?: IKey; + private _sourceKMSKey?: IKeyRef; constructor(props: CfnParametersCodeProps = {}) { super(); @@ -491,7 +491,7 @@ export class CfnParametersCode extends Code { bucketName: this._bucketNameParam.valueAsString, objectKey: this._objectKeyParam.valueAsString, }, - sourceKMSKeyArn: this._sourceKMSKey?.keyArn, + sourceKMSKeyArn: this._sourceKMSKey?.keyRef.keyArn, }; } @@ -720,5 +720,5 @@ export interface BucketOptions { * The ARN of the KMS key used to encrypt the handler code. * @default - the default server-side encryption with Amazon S3 managed keys(SSE-S3) key will be used. */ - readonly sourceKMSKey?: IKey; + readonly sourceKMSKey?: IKeyRef; } diff --git a/packages/aws-cdk-lib/aws-lambda/lib/event-source-mapping.ts b/packages/aws-cdk-lib/aws-lambda/lib/event-source-mapping.ts index df14694d7bbc7..b89a3693013d3 100644 --- a/packages/aws-cdk-lib/aws-lambda/lib/event-source-mapping.ts +++ b/packages/aws-cdk-lib/aws-lambda/lib/event-source-mapping.ts @@ -1,7 +1,7 @@ import { Construct } from 'constructs'; import { IEventSourceDlq } from './dlq'; import { IFunction } from './function-base'; -import { CfnEventSourceMapping } from './lambda.generated'; +import { CfnEventSourceMapping, EventSourceMappingReference, IEventSourceMappingRef } from './lambda.generated'; import { ISchemaRegistry } from './schema-registry'; import * as iam from '../../aws-iam'; import { IKey } from '../../aws-kms'; @@ -352,7 +352,7 @@ export interface EventSourceMappingProps extends EventSourceMappingOptions { * Represents an event source mapping for a lambda function. * @see https://docs.aws.amazon.com/lambda/latest/dg/invocation-eventsourcemapping.html */ -export interface IEventSourceMapping extends cdk.IResource { +export interface IEventSourceMapping extends cdk.IResource, IEventSourceMappingRef { /** * The identifier for this EventSourceMapping * @attribute @@ -401,6 +401,13 @@ export class EventSourceMapping extends cdk.Resource implements IEventSourceMapp class Import extends cdk.Resource implements IEventSourceMapping { public readonly eventSourceMappingId = eventSourceMappingId; public readonly eventSourceMappingArn = eventSourceMappingArn; + + public get eventSourceMappingRef(): EventSourceMappingReference { + return { + eventSourceMappingId, + eventSourceMappingArn, + }; + } } return new Import(scope, id); } @@ -570,6 +577,13 @@ export class EventSourceMapping extends cdk.Resource implements IEventSourceMapp this.eventSourceMappingArn = EventSourceMapping.formatArn(this, this.eventSourceMappingId); } + public get eventSourceMappingRef(): EventSourceMappingReference { + return { + eventSourceMappingId: this.eventSourceMappingId, + eventSourceMappingArn: this.eventSourceMappingArn, + }; + } + private validateKafkaConsumerGroupIdOrThrow(kafkaConsumerGroupId: string) { if (cdk.Token.isUnresolved(kafkaConsumerGroupId)) { return; diff --git a/packages/aws-cdk-lib/aws-lambda/lib/function-base.ts b/packages/aws-cdk-lib/aws-lambda/lib/function-base.ts index 0004962970d2b..646cf08f488dc 100644 --- a/packages/aws-cdk-lib/aws-lambda/lib/function-base.ts +++ b/packages/aws-cdk-lib/aws-lambda/lib/function-base.ts @@ -5,20 +5,20 @@ import { Architecture } from './architecture'; import { EventInvokeConfig, EventInvokeConfigOptions } from './event-invoke-config'; import { IEventSource } from './event-source'; import { EventSourceMapping, EventSourceMappingOptions } from './event-source-mapping'; -import { FunctionUrlAuthType, FunctionUrlOptions, FunctionUrl } from './function-url'; +import { FunctionUrl, FunctionUrlAuthType, FunctionUrlOptions } from './function-url'; import { IVersion } from './lambda-version'; -import { CfnPermission } from './lambda.generated'; +import { CfnPermission, FunctionReference, IFunctionRef, VersionReference } from './lambda.generated'; import { Permission } from './permission'; import { addAlias, flatMap } from './util'; import * as cloudwatch from '../../aws-cloudwatch'; import * as ec2 from '../../aws-ec2'; import * as iam from '../../aws-iam'; -import { Annotations, ArnFormat, IResource, Resource, Token, Stack, FeatureFlags } from '../../core'; +import { Annotations, ArnFormat, FeatureFlags, IResource, Resource, Stack, Token } from '../../core'; import { ValidationError } from '../../core/lib/errors'; import { MethodMetadata } from '../../core/lib/metadata-resource'; import * as cxapi from '../../cx-api'; -export interface IFunction extends IResource, ec2.IConnectable, iam.IGrantable { +export interface IFunction extends IResource, ec2.IConnectable, iam.IGrantable, IFunctionRef { /** * The name of the function. @@ -342,6 +342,13 @@ export abstract class FunctionBase extends Resource implements IFunction, ec2.IC */ private _hasAddedArrayTokenStatements: boolean = false; + public get functionRef(): FunctionReference { + return { + functionName: this.functionName, + functionArn: this.functionArn, + }; + } + /** * A warning will be added to functions under the following conditions: * - permissions that include `lambda:InvokeFunction` are added to the unqualified function. @@ -857,6 +864,16 @@ class LatestVersion extends FunctionBase implements IVersion { this.lambda = lambda; } + public get versionRef(): VersionReference { + return { + functionArn: this.functionRef.functionArn, + }; + } + + public get functionRef() { + return this.lambda.functionRef; + } + public get functionArn() { return `${this.lambda.functionArn}:${this.version}`; } diff --git a/packages/aws-cdk-lib/aws-lambda/lib/function.ts b/packages/aws-cdk-lib/aws-lambda/lib/function.ts index 864faae421823..0eef2a936794f 100644 --- a/packages/aws-cdk-lib/aws-lambda/lib/function.ts +++ b/packages/aws-cdk-lib/aws-lambda/lib/function.ts @@ -3,7 +3,6 @@ import { AdotInstrumentationConfig, AdotLambdaExecWrapper } from './adot-layers' import { AliasOptions, Alias } from './alias'; import { Architecture } from './architecture'; import { Code, CodeConfig } from './code'; -import { ICodeSigningConfig } from './code-signing-config'; import { EventInvokeConfigOptions } from './event-invoke-config'; import { IEventSource } from './event-source'; import { FileSystem } from './filesystem'; @@ -12,7 +11,7 @@ import { calculateFunctionHash, trimFromStart } from './function-hash'; import { Handler } from './handler'; import { LambdaInsightsVersion } from './lambda-insights'; import { Version, VersionOptions } from './lambda-version'; -import { CfnFunction } from './lambda.generated'; +import { CfnFunction, ICodeSigningConfigRef } from './lambda.generated'; import { LayerVersion, ILayerVersion } from './layers'; import { LogRetentionRetryOptions } from './log-retention'; import { ParamsAndSecretsLayerVersion } from './params-and-secrets-layers'; @@ -524,14 +523,14 @@ export interface FunctionOptions extends EventInvokeConfigOptions { * * @default - AWS Lambda creates and uses an AWS managed customer master key (CMK). */ - readonly environmentEncryption?: kms.IKey; + readonly environmentEncryption?: kms.IKeyRef; /** * Code signing config associated with this function * * @default - Not Sign the Code */ - readonly codeSigningConfig?: ICodeSigningConfig; + readonly codeSigningConfig?: ICodeSigningConfigRef; /** * DEPRECATED @@ -1090,9 +1089,9 @@ export class Function extends FunctionBase { entryPoint: code.image?.entrypoint, workingDirectory: code.image?.workingDirectory, }), - kmsKeyArn: props.environmentEncryption?.keyArn, + kmsKeyArn: props.environmentEncryption?.keyRef.keyArn, fileSystemConfigs, - codeSigningConfigArn: props.codeSigningConfig?.codeSigningConfigArn, + codeSigningConfigArn: props.codeSigningConfig?.codeSigningConfigRef.codeSigningConfigArn, architectures: this._architecture ? [this._architecture.name] : undefined, runtimeManagementConfig: props.runtimeManagementMode?.runtimeManagementConfig, snapStart: this.configureSnapStart(props), diff --git a/packages/aws-cdk-lib/aws-lambda/lib/lambda-version.ts b/packages/aws-cdk-lib/aws-lambda/lib/lambda-version.ts index 4738d3b0ab95d..9b518678f73d9 100644 --- a/packages/aws-cdk-lib/aws-lambda/lib/lambda-version.ts +++ b/packages/aws-cdk-lib/aws-lambda/lib/lambda-version.ts @@ -5,7 +5,7 @@ import { Architecture } from './architecture'; import { EventInvokeConfigOptions } from './event-invoke-config'; import { Function } from './function'; import { IFunction, QualifiedFunctionBase } from './function-base'; -import { CfnVersion } from './lambda.generated'; +import { CfnVersion, IVersionRef, VersionReference } from './lambda.generated'; import { addAlias } from './util'; import * as cloudwatch from '../../aws-cloudwatch'; import { Fn, Lazy, RemovalPolicy, Token } from '../../core'; @@ -13,7 +13,7 @@ import { ValidationError } from '../../core/lib/errors'; import { addConstructMetadata, MethodMetadata } from '../../core/lib/metadata-resource'; import { propertyInjectable } from '../../core/lib/prop-injectable'; -export interface IVersion extends IFunction { +export interface IVersion extends IFunction, IVersionRef { /** * The most recently deployed version of this function. * @attribute @@ -154,6 +154,12 @@ export class Version extends QualifiedFunctionBase implements IVersion { } return this.functionArn; } + + public get versionRef(): VersionReference { + return { + functionArn: this.functionArn, + }; + } } return new Import(scope, id); } @@ -181,6 +187,12 @@ export class Version extends QualifiedFunctionBase implements IVersion { } return this.functionArn; } + + public get versionRef(): VersionReference { + return { + functionArn: this.functionArn, + }; + } } return new Import(scope, id); } @@ -231,6 +243,12 @@ export class Version extends QualifiedFunctionBase implements IVersion { } } + public get versionRef(): VersionReference { + return { + functionArn: this.functionArn, + }; + } + public get grantPrincipal() { return this.lambda.grantPrincipal; } diff --git a/packages/aws-cdk-lib/aws-lambda/lib/layers.ts b/packages/aws-cdk-lib/aws-lambda/lib/layers.ts index c5c196380a4bf..eb3b5d040e862 100644 --- a/packages/aws-cdk-lib/aws-lambda/lib/layers.ts +++ b/packages/aws-cdk-lib/aws-lambda/lib/layers.ts @@ -1,7 +1,12 @@ import { Construct } from 'constructs'; import { Architecture } from './architecture'; import { Code } from './code'; -import { CfnLayerVersion, CfnLayerVersionPermission } from './lambda.generated'; +import { + CfnLayerVersion, + CfnLayerVersionPermission, + ILayerVersionRef, + LayerVersionReference, +} from './lambda.generated'; import { Runtime } from './runtime'; import { IResource, RemovalPolicy, Resource } from '../../core'; import { ValidationError } from '../../core/lib/errors'; @@ -64,7 +69,7 @@ export interface LayerVersionProps extends LayerVersionOptions { readonly code: Code; } -export interface ILayerVersion extends IResource { +export interface ILayerVersion extends IResource, ILayerVersionRef { /** * The ARN of the Lambda Layer version that this Layer defines. * @attribute @@ -99,6 +104,12 @@ abstract class LayerVersionBase extends Resource implements ILayerVersion { public abstract readonly layerVersionArn: string; public abstract readonly compatibleRuntimes?: Runtime[]; + public get layerVersionRef(): LayerVersionReference { + return { + layerVersionArn: this.layerVersionArn, + }; + } + public addPermission(id: string, permission: LayerVersionPermission) { if (permission.organizationId != null && permission.accountId !== '*') { throw new ValidationError(`OrganizationId can only be specified if AwsAccountId is '*', but it is ${permission.accountId}`, this); diff --git a/packages/aws-cdk-lib/aws-logs/lib/cross-account-destination.ts b/packages/aws-cdk-lib/aws-logs/lib/cross-account-destination.ts index e6e1c8fdae054..382cac70bb6d3 100644 --- a/packages/aws-cdk-lib/aws-logs/lib/cross-account-destination.ts +++ b/packages/aws-cdk-lib/aws-logs/lib/cross-account-destination.ts @@ -24,7 +24,7 @@ export interface CrossAccountDestinationProps { * * The role must be assumable by 'logs.{REGION}.amazonaws.com'. */ - readonly role: iam.IRole; + readonly role: iam.IRoleRef; /** * The log destination target's ARN @@ -84,7 +84,7 @@ export class CrossAccountDestination extends cdk.Resource implements ILogSubscri destinationName: this.physicalName!, // Must be stringified policy destinationPolicy: this.lazyStringifiedPolicyDocument(), - roleArn: props.role.roleArn, + roleArn: props.role.roleRef.roleArn, targetArn: props.targetArn, }); diff --git a/packages/aws-cdk-lib/aws-logs/lib/data-protection-policy.ts b/packages/aws-cdk-lib/aws-logs/lib/data-protection-policy.ts index 1d7674b34113a..731c06f49031d 100644 --- a/packages/aws-cdk-lib/aws-logs/lib/data-protection-policy.ts +++ b/packages/aws-cdk-lib/aws-logs/lib/data-protection-policy.ts @@ -1,7 +1,8 @@ import { Construct } from 'constructs'; import { ILogGroup } from './log-group'; -import { IBucket } from '../../aws-s3'; +import { IBucketRef } from '../../aws-s3'; import { Stack, UnscopedValidationError } from '../../core'; + /** * Creates a data protection policy for CloudWatch Logs log groups. */ @@ -32,7 +33,7 @@ export class DataProtectionPolicy { if (this.dataProtectionPolicyProps.s3BucketAuditDestination) { findingsDestination.S3 = { - Bucket: this.dataProtectionPolicyProps.s3BucketAuditDestination.bucketName, + Bucket: this.dataProtectionPolicyProps.s3BucketAuditDestination.bucketRef.bucketName, }; } @@ -162,7 +163,7 @@ export interface DataProtectionPolicyProps { * * @default - no S3 bucket audit destination */ - readonly s3BucketAuditDestination?: IBucket; + readonly s3BucketAuditDestination?: IBucketRef; /** * Amazon Data Firehose delivery stream to send audit findings to. The delivery stream must already exist. diff --git a/packages/aws-cdk-lib/aws-logs/lib/log-group.ts b/packages/aws-cdk-lib/aws-logs/lib/log-group.ts index 67621ec498031..903e6bf900032 100644 --- a/packages/aws-cdk-lib/aws-logs/lib/log-group.ts +++ b/packages/aws-cdk-lib/aws-logs/lib/log-group.ts @@ -514,7 +514,7 @@ export interface LogGroupProps { * * @default Server-side encryption managed by the CloudWatch Logs service */ - readonly encryptionKey?: kms.IKey; + readonly encryptionKey?: kms.IKeyRef; /** * Name of the log group. @@ -666,7 +666,7 @@ export class LogGroup extends LogGroupBase { } const resource = new CfnLogGroup(this, 'Resource', { - kmsKeyId: props.encryptionKey?.keyArn, + kmsKeyId: props.encryptionKey?.keyRef.keyArn, logGroupClass, logGroupName: this.physicalName, retentionInDays, diff --git a/packages/aws-cdk-lib/aws-logs/lib/subscription-filter.ts b/packages/aws-cdk-lib/aws-logs/lib/subscription-filter.ts index 10268f11cbfe1..a6b2331f3e5b5 100644 --- a/packages/aws-cdk-lib/aws-logs/lib/subscription-filter.ts +++ b/packages/aws-cdk-lib/aws-logs/lib/subscription-filter.ts @@ -80,7 +80,7 @@ export class SubscriptionFilter extends Resource { new CfnSubscriptionFilter(this, 'Resource', { logGroupName: props.logGroup.logGroupName, destinationArn: destProps.arn, - roleArn: destProps.role && destProps.role.roleArn, + roleArn: destProps.role?.roleArn, filterPattern: props.filterPattern.logPatternString, filterName: this.physicalName, distribution: props.distribution, diff --git a/packages/aws-cdk-lib/aws-opensearchservice/lib/domain.ts b/packages/aws-cdk-lib/aws-opensearchservice/lib/domain.ts index 26691f38c8f1c..a466b8d4bd83f 100644 --- a/packages/aws-cdk-lib/aws-opensearchservice/lib/domain.ts +++ b/packages/aws-cdk-lib/aws-opensearchservice/lib/domain.ts @@ -258,7 +258,7 @@ export interface EncryptionAtRestOptions { * * @default - uses default aws/es KMS key. */ - readonly kmsKey?: kms.IKey; + readonly kmsKey?: kms.IKeyRef; } /** @@ -276,7 +276,7 @@ export interface CognitoOptions { * * @see https://docs.aws.amazon.com/opensearch-service/latest/developerguide/cognito-auth.html#cognito-auth-prereq */ - readonly role: iam.IRole; + readonly role: iam.IRoleRef; /** * The Amazon Cognito user pool ID that you want Amazon OpenSearch Service to use for OpenSearch Dashboards authentication. @@ -1988,7 +1988,7 @@ export class Domain extends DomainBase implements IDomain, ec2.IConnectable { encryptionAtRestOptions: { enabled: encryptionAtRestEnabled, kmsKeyId: encryptionAtRestEnabled - ? props.encryptionAtRest?.kmsKey?.keyId + ? props.encryptionAtRest?.kmsKey?.keyRef.keyId : undefined, }, nodeToNodeEncryptionOptions: { enabled: nodeToNodeEncryptionEnabled }, @@ -1996,7 +1996,7 @@ export class Domain extends DomainBase implements IDomain, ec2.IConnectable { cognitoOptions: props.cognitoDashboardsAuth ? { enabled: true, identityPoolId: props.cognitoDashboardsAuth?.identityPoolId, - roleArn: props.cognitoDashboardsAuth?.role.roleArn, + roleArn: props.cognitoDashboardsAuth?.role.roleRef.roleArn, userPoolId: props.cognitoDashboardsAuth?.userPoolId, } : undefined, vpcOptions: cfnVpcOptions, @@ -2188,7 +2188,7 @@ export class Domain extends DomainBase implements IDomain, ec2.IConnectable { // empircal evidence shows this is indeed required: https://github.com/aws/aws-cdk/issues/11412 this.accessPolicy.grantPrincipal.addToPrincipalPolicy(new iam.PolicyStatement({ actions: ['kms:List*', 'kms:Describe*', 'kms:CreateGrant'], - resources: [this.encryptionAtRestOptions.kmsKey.keyArn], + resources: [this.encryptionAtRestOptions.kmsKey.keyRef.keyArn], effect: iam.Effect.ALLOW, })); } diff --git a/packages/aws-cdk-lib/aws-rds/lib/aurora-cluster-instance.ts b/packages/aws-cdk-lib/aws-rds/lib/aurora-cluster-instance.ts index 8c34c13e0f465..a4f458043cfec 100644 --- a/packages/aws-cdk-lib/aws-rds/lib/aurora-cluster-instance.ts +++ b/packages/aws-cdk-lib/aws-rds/lib/aurora-cluster-instance.ts @@ -8,7 +8,7 @@ import { PerformanceInsightRetention } from './props'; import { CfnDBInstance } from './rds.generated'; import { ISubnetGroup } from './subnet-group'; import * as ec2 from '../../aws-ec2'; -import { IRole } from '../../aws-iam'; +import { IRoleRef } from '../../aws-iam'; import * as kms from '../../aws-kms'; import { IResource, Resource, Duration, RemovalPolicy, ArnFormat, FeatureFlags } from '../../core'; import { ValidationError } from '../../core/lib/errors'; @@ -33,7 +33,7 @@ export interface ClusterInstanceBindOptions { * * @default - A role is automatically created for you */ - readonly monitoringRole?: IRole; + readonly monitoringRole?: IRoleRef; /** * The removal policy on the cluster @@ -578,7 +578,7 @@ class AuroraClusterInstance extends Resource implements IAuroraClusterInstance { dbSubnetGroupName: props.isFromLegacyInstanceProps ? props.subnetGroup?.subnetGroupName : undefined, dbParameterGroupName: instanceParameterGroupConfig?.parameterGroupName, monitoringInterval: props.monitoringInterval && props.monitoringInterval.toSeconds(), - monitoringRoleArn: props.monitoringRole && props.monitoringRole.roleArn, + monitoringRoleArn: props.monitoringRole?.roleRef.roleArn, autoMinorVersionUpgrade: props.autoMinorVersionUpgrade, allowMajorVersionUpgrade: props.allowMajorVersionUpgrade, caCertificateIdentifier: props.caCertificate && props.caCertificate.toString(), diff --git a/packages/aws-cdk-lib/aws-rds/lib/cluster-engine.ts b/packages/aws-cdk-lib/aws-rds/lib/cluster-engine.ts index a4d69a77954d9..c1d2d06612bcd 100644 --- a/packages/aws-cdk-lib/aws-rds/lib/cluster-engine.ts +++ b/packages/aws-cdk-lib/aws-rds/lib/cluster-engine.ts @@ -16,14 +16,14 @@ export interface ClusterEngineBindOptions { * * @default - none */ - readonly s3ImportRole?: iam.IRole; + readonly s3ImportRole?: iam.IRoleRef; /** * The role used for S3 exporting. * * @default - none */ - readonly s3ExportRole?: iam.IRole; + readonly s3ExportRole?: iam.IRoleRef; /** * The customer-provided ParameterGroup. @@ -203,13 +203,13 @@ abstract class MySqlClusterEngineBase extends ClusterEngineBase { const s3ImportParam = this.combineImportAndExportRoles ? 'aws_default_s3_role' : 'aurora_load_from_s3_role'; - parameterGroup?.addParameter(s3ImportParam, options.s3ImportRole.roleArn); + parameterGroup?.addParameter(s3ImportParam, options.s3ImportRole.roleRef.roleArn); } if (options.s3ExportRole) { const s3ExportParam = this.combineImportAndExportRoles ? 'aws_default_s3_role' : 'aurora_select_into_s3_role'; - parameterGroup?.addParameter(s3ExportParam, options.s3ExportRole.roleArn); + parameterGroup?.addParameter(s3ExportParam, options.s3ExportRole.roleRef.roleArn); } return { diff --git a/packages/aws-cdk-lib/aws-rds/lib/cluster.ts b/packages/aws-cdk-lib/aws-rds/lib/cluster.ts index cd5189b25452f..699a5b89aaeb6 100644 --- a/packages/aws-cdk-lib/aws-rds/lib/cluster.ts +++ b/packages/aws-cdk-lib/aws-rds/lib/cluster.ts @@ -373,7 +373,7 @@ interface DatabaseClusterBaseProps { * * @default - if storageEncrypted is true then the default master key, no key otherwise */ - readonly storageEncryptionKey?: kms.IKey; + readonly storageEncryptionKey?: kms.IKeyRef; /** * The storage type to be associated with the DB cluster. @@ -961,7 +961,7 @@ abstract class DatabaseClusterNew extends DatabaseClusterBase { databaseName: props.defaultDatabaseName, enableCloudwatchLogsExports: props.cloudwatchLogsExports, // Encryption - kmsKeyId: props.storageEncryptionKey?.keyArn, + kmsKeyId: props.storageEncryptionKey?.keyRef.keyArn, storageEncrypted: props.storageEncryptionKey ? true : props.storageEncrypted, // Tags copyTagsToSnapshot: props.copyTagsToSnapshot ?? true, @@ -1836,7 +1836,7 @@ function validatePerformanceInsightsSettings( nodeId?: string; performanceInsightsEnabled?: boolean; performanceInsightRetention?: PerformanceInsightRetention; - performanceInsightEncryptionKey?: kms.IKey; + performanceInsightEncryptionKey?: kms.IKeyRef; }, ): void { const target = instance.nodeId ? `instance \'${instance.nodeId}\'` : '`instanceProps`'; @@ -1864,11 +1864,11 @@ function validatePerformanceInsightsSettings( // undefined or the same as the value at cluster level. if (cluster.performanceInsightEncryptionKey && instance.performanceInsightEncryptionKey) { const clusterKeyArn = cluster.performanceInsightEncryptionKey.keyArn; - const instanceKeyArn = instance.performanceInsightEncryptionKey.keyArn; + const instanceKeyArn = instance.performanceInsightEncryptionKey.keyRef.keyArn; const compared = Token.compareStrings(clusterKeyArn, instanceKeyArn); if (compared === TokenComparison.DIFFERENT) { - throw new ValidationError(`\`performanceInsightEncryptionKey\` for each instance must be the same as the one at cluster level, got ${target}: '${instance.performanceInsightEncryptionKey.keyArn}', cluster: '${cluster.performanceInsightEncryptionKey.keyArn}'`, cluster); + throw new ValidationError(`\`performanceInsightEncryptionKey\` for each instance must be the same as the one at cluster level, got ${target}: '${instance.performanceInsightEncryptionKey.keyRef.keyArn}', cluster: '${cluster.performanceInsightEncryptionKey.keyRef.keyArn}'`, cluster); } // Even if both of cluster and instance keys are unresolved, check if they are the same token. if (compared === TokenComparison.BOTH_UNRESOLVED && clusterKeyArn !== instanceKeyArn) { diff --git a/packages/aws-cdk-lib/aws-rds/lib/instance-engine.ts b/packages/aws-cdk-lib/aws-rds/lib/instance-engine.ts index 462729dd0d780..b72a263949712 100644 --- a/packages/aws-cdk-lib/aws-rds/lib/instance-engine.ts +++ b/packages/aws-cdk-lib/aws-rds/lib/instance-engine.ts @@ -29,14 +29,14 @@ export interface InstanceEngineBindOptions { * * @default - none */ - readonly s3ImportRole?: iam.IRole; + readonly s3ImportRole?: iam.IRoleRef; /** * The role used for S3 exporting. * * @default - none */ - readonly s3ExportRole?: iam.IRole; + readonly s3ExportRole?: iam.IRoleRef; /** * The option group of the database @@ -3043,7 +3043,7 @@ abstract class SqlServerInstanceEngineBase extends InstanceEngineBase { // https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/Appendix.SQLServer.Options.BackupRestore.html optionGroup.addConfiguration({ name: 'SQLSERVER_BACKUP_RESTORE', - settings: { IAM_ROLE_ARN: s3Role.roleArn }, + settings: { IAM_ROLE_ARN: s3Role.roleRef.roleArn }, }); } diff --git a/packages/aws-cdk-lib/aws-rds/lib/instance.ts b/packages/aws-cdk-lib/aws-rds/lib/instance.ts index 7266f15f8ae30..2b5151895cb97 100644 --- a/packages/aws-cdk-lib/aws-rds/lib/instance.ts +++ b/packages/aws-cdk-lib/aws-rds/lib/instance.ts @@ -597,7 +597,7 @@ export interface DatabaseInstanceNewProps { * * @default - A role is automatically created for you */ - readonly monitoringRole?: iam.IRole; + readonly monitoringRole?: iam.IRoleRef; /** * Whether to enable Performance Insights for the DB instance. @@ -620,7 +620,7 @@ export interface DatabaseInstanceNewProps { * * @default - default master key */ - readonly performanceInsightEncryptionKey?: kms.IKey; + readonly performanceInsightEncryptionKey?: kms.IKeyRef; /** * The database insights mode. @@ -709,7 +709,7 @@ export interface DatabaseInstanceNewProps { * * @default - The role will be created for you if `DatabaseInstanceNewProps#domain` is specified */ - readonly domainRole?: iam.IRole; + readonly domainRole?: iam.IRoleRef; /** * Existing subnet group for the instance. @@ -863,7 +863,7 @@ abstract class DatabaseInstanceNew extends DatabaseInstanceBase implements IData private readonly cloudwatchLogsRetentionRole?: iam.IRole; private readonly domainId?: string; - private readonly domainRole?: iam.IRole; + private readonly domainRole?: iam.IRoleRef; protected enableIamAuthentication?: boolean; @@ -976,11 +976,11 @@ abstract class DatabaseInstanceNew extends DatabaseInstanceBase implements IData enablePerformanceInsights: enablePerformanceInsights || props.enablePerformanceInsights, // fall back to undefined if not set, iops, monitoringInterval: props.monitoringInterval?.toSeconds(), - monitoringRoleArn: monitoringRole?.roleArn, + monitoringRoleArn: monitoringRole?.roleRef.roleArn, multiAz: props.multiAz, dbParameterGroupName: instanceParameterGroupConfig?.parameterGroupName, optionGroupName: props.optionGroup?.optionGroupName, - performanceInsightsKmsKeyId: props.performanceInsightEncryptionKey?.keyArn, + performanceInsightsKmsKeyId: props.performanceInsightEncryptionKey?.keyRef.keyArn, performanceInsightsRetentionPeriod: enablePerformanceInsights ? (props.performanceInsightRetention || PerformanceInsightRetention.DEFAULT) : undefined, @@ -995,7 +995,7 @@ abstract class DatabaseInstanceNew extends DatabaseInstanceBase implements IData vpcSecurityGroups: securityGroups.map(s => s.securityGroupId), maxAllocatedStorage: props.maxAllocatedStorage, domain: this.domainId, - domainIamRoleName: this.domainRole?.roleName, + domainIamRoleName: this.domainRole?.roleRef.roleName, networkType: props.networkType, caCertificateIdentifier: props.caCertificate ? props.caCertificate.toString() : undefined, applyImmediately: props.applyImmediately, @@ -1269,7 +1269,7 @@ export interface DatabaseInstanceProps extends DatabaseInstanceSourceProps { * * @default - default master key if storageEncrypted is true, no key otherwise */ - readonly storageEncryptionKey?: kms.IKey; + readonly storageEncryptionKey?: kms.IKeyRef; } /** @@ -1305,7 +1305,7 @@ export class DatabaseInstance extends DatabaseInstanceSource implements IDatabas const instance = new CfnDBInstance(this, 'Resource', { ...this.sourceCfnProps, characterSetName: props.characterSetName, - kmsKeyId: props.storageEncryptionKey && props.storageEncryptionKey.keyArn, + kmsKeyId: props.storageEncryptionKey && props.storageEncryptionKey.keyRef.keyArn, masterUsername: credentials.username, masterUserPassword: credentials.password?.unsafeUnwrap(), storageEncrypted: props.storageEncryptionKey ? true : props.storageEncrypted, @@ -1478,7 +1478,7 @@ export interface DatabaseInstanceReadReplicaProps extends DatabaseInstanceNewPro * * @default - default master key if storageEncrypted is true, no key otherwise */ - readonly storageEncryptionKey?: kms.IKey; + readonly storageEncryptionKey?: kms.IKeyRef; /** * The allocated storage size, specified in gibibytes (GiB). * @@ -1539,7 +1539,7 @@ export class DatabaseInstanceReadReplica extends DatabaseInstanceNew implements ...this.newCfnProps, // this must be ARN, not ID, because of https://github.com/terraform-providers/terraform-provider-aws/issues/528#issuecomment-391169012 sourceDbInstanceIdentifier: props.sourceDatabaseInstance.instanceArn, - kmsKeyId: props.storageEncryptionKey?.keyArn, + kmsKeyId: props.storageEncryptionKey?.keyRef.keyArn, storageEncrypted: props.storageEncryptionKey ? true : props.storageEncrypted, engine: shouldPassEngine ? engineType : undefined, allocatedStorage: props.allocatedStorage?.toString(), diff --git a/packages/aws-cdk-lib/aws-route53/lib/record-set.ts b/packages/aws-cdk-lib/aws-route53/lib/record-set.ts index 315998792f51e..f08260b795873 100644 --- a/packages/aws-cdk-lib/aws-route53/lib/record-set.ts +++ b/packages/aws-cdk-lib/aws-route53/lib/record-set.ts @@ -1033,7 +1033,7 @@ export interface CrossAccountZoneDelegationRecordProps { /** * The delegation role in the parent account */ - readonly delegationRole: iam.IRole; + readonly delegationRole: iam.IRoleRef; /** * The resource record cache time to live (TTL). @@ -1083,7 +1083,7 @@ export class CrossAccountZoneDelegationRecord extends Construct { const addToPrinciplePolicyResult = role.addToPrincipalPolicy(new iam.PolicyStatement({ effect: iam.Effect.ALLOW, actions: ['sts:AssumeRole'], - resources: [props.delegationRole.roleArn], + resources: [props.delegationRole.roleRef.roleArn], })); const customResource = new CustomResource(this, 'CrossAccountZoneDelegationCustomResource', { @@ -1091,7 +1091,7 @@ export class CrossAccountZoneDelegationRecord extends Construct { serviceToken: provider.serviceToken, removalPolicy: props.removalPolicy, properties: { - AssumeRoleArn: props.delegationRole.roleArn, + AssumeRoleArn: props.delegationRole.roleRef.roleArn, ParentZoneName: props.parentHostedZoneName, ParentZoneId: props.parentHostedZoneId, DelegatedZoneName: props.delegatedZone.zoneName, diff --git a/packages/aws-cdk-lib/aws-route53/lib/vpc-endpoint-service-domain-name.ts b/packages/aws-cdk-lib/aws-route53/lib/vpc-endpoint-service-domain-name.ts index b77241b68bbe6..a42255b276cbe 100644 --- a/packages/aws-cdk-lib/aws-route53/lib/vpc-endpoint-service-domain-name.ts +++ b/packages/aws-cdk-lib/aws-route53/lib/vpc-endpoint-service-domain-name.ts @@ -1,5 +1,5 @@ import { Construct } from 'constructs'; -import { IVpcEndpointService } from '../../aws-ec2'; +import { IVPCEndpointServiceRef } from '../../aws-ec2'; import { Fn, Names, Stack } from '../../core'; import { ValidationError } from '../../core/lib/errors'; import { md5hash } from '../../core/lib/helpers-internal'; @@ -14,7 +14,7 @@ export interface VpcEndpointServiceDomainNameProps { /** * The VPC Endpoint Service to configure Private DNS for */ - readonly endpointService: IVpcEndpointService; + readonly endpointService: IVPCEndpointServiceRef; /** * The domain name to use. @@ -37,7 +37,7 @@ export interface VpcEndpointServiceDomainNameProps { */ export class VpcEndpointServiceDomainName extends Construct { // Track all domain names created, so someone doesn't accidentally associate two domains with a single service - private static readonly endpointServices: IVpcEndpointService[] = []; + private static readonly endpointServices: IVPCEndpointServiceRef[] = []; // Track all domain names created, so someone doesn't accidentally associate two domains with a single service private static readonly endpointServicesMap: { [endpointService: string]: string} = {}; @@ -58,7 +58,7 @@ export class VpcEndpointServiceDomainName extends Construct { super(scope, id); const serviceUniqueId = Names.nodeUniqueId(props.endpointService.node); - const serviceId = props.endpointService.vpcEndpointServiceId; + const serviceId = props.endpointService.vpcEndpointServiceRef.serviceId; this.domainName = props.domainName; // Make sure a user doesn't accidentally add multiple domains diff --git a/packages/aws-cdk-lib/aws-s3-assets/lib/asset.ts b/packages/aws-cdk-lib/aws-s3-assets/lib/asset.ts index b0a604e3dcf6a..5e93ef978ad7f 100644 --- a/packages/aws-cdk-lib/aws-s3-assets/lib/asset.ts +++ b/packages/aws-cdk-lib/aws-s3-assets/lib/asset.ts @@ -54,7 +54,7 @@ export interface AssetOptions extends CopyOptions, cdk.FileCopyOptions, cdk.Asse * The ARN of the KMS key used to encrypt the handler code. * @default - the default server-side encryption with Amazon S3 managed keys(SSE-S3) key will be used. */ - readonly sourceKMSKey?: kms.IKey; + readonly sourceKMSKey?: kms.IKeyRef; /** * A display name for this asset diff --git a/packages/aws-cdk-lib/aws-s3-deployment/lib/bucket-deployment.ts b/packages/aws-cdk-lib/aws-s3-deployment/lib/bucket-deployment.ts index ea9a8c53bd420..dbc590a32556a 100644 --- a/packages/aws-cdk-lib/aws-s3-deployment/lib/bucket-deployment.ts +++ b/packages/aws-cdk-lib/aws-s3-deployment/lib/bucket-deployment.ts @@ -107,7 +107,7 @@ export interface BucketDeploymentProps { * * @default - No invalidation occurs */ - readonly distribution?: cloudfront.IDistribution; + readonly distribution?: cloudfront.IDistributionRef; /** * The file paths to invalidate in the CloudFront distribution. @@ -467,7 +467,7 @@ export class BucketDeployment extends Construct { Include: props.include, UserMetadata: props.metadata ? mapUserMetadata(props.metadata) : undefined, SystemMetadata: mapSystemMetadata(props), - DistributionId: props.distribution?.distributionId, + DistributionId: props.distribution?.distributionRef.distributionId, DistributionPaths: props.distributionPaths, SignContent: props.signContent, OutputObjectKeys: props.outputObjectKeys ?? true, @@ -957,8 +957,9 @@ export interface UserDefinedObjectMetadata { } function sourceConfigEqual(stack: cdk.Stack, a: SourceConfig, b: SourceConfig) { + const resolveName = (config: SourceConfig) => JSON.stringify(stack.resolve(config.bucket.bucketName)); return ( - JSON.stringify(stack.resolve(a.bucket.bucketName)) === JSON.stringify(stack.resolve(b.bucket.bucketName)) + resolveName(a) === resolveName(b) && a.zipObjectKey === b.zipObjectKey && a.markers === undefined && b.markers === undefined); } diff --git a/packages/aws-cdk-lib/aws-s3-notifications/lib/lambda.ts b/packages/aws-cdk-lib/aws-s3-notifications/lib/lambda.ts index 45a7a44773f63..62ce228fa3c6a 100644 --- a/packages/aws-cdk-lib/aws-s3-notifications/lib/lambda.ts +++ b/packages/aws-cdk-lib/aws-s3-notifications/lib/lambda.ts @@ -12,19 +12,19 @@ export class LambdaDestination implements s3.IBucketNotificationDestination { constructor(private readonly fn: lambda.IFunction) { } - public bind(scope: Construct, bucket: s3.IBucket): s3.BucketNotificationDestinationConfig { + public bind(scope: Construct, bucket: s3.IBucketRef): s3.BucketNotificationDestinationConfig { const permissionId = `AllowBucketNotificationsTo${Names.nodeUniqueId(this.fn.permissionsNode)}`; if (!(bucket instanceof Construct)) { throw new ValidationError(`LambdaDestination for function ${Names.nodeUniqueId(this.fn.permissionsNode)} can only be configured on a - bucket construct (Bucket ${bucket.bucketName})`, scope); + bucket construct (Bucket ${bucket.bucketRef.bucketName})`, scope); } if (bucket.node.tryFindChild(permissionId) === undefined) { this.fn.addPermission(permissionId, { sourceAccount: Stack.of(bucket).account, principal: new iam.ServicePrincipal('s3.amazonaws.com'), - sourceArn: bucket.bucketArn, + sourceArn: bucket.bucketRef.bucketArn, // Placing the permissions node in the same scope as the s3 bucket. // Otherwise, there is a circular dependency when the s3 bucket // and lambda functions declared in different stacks. diff --git a/packages/aws-cdk-lib/aws-s3-notifications/lib/sns.ts b/packages/aws-cdk-lib/aws-s3-notifications/lib/sns.ts index a1276c5d0caec..fb734d20f729d 100644 --- a/packages/aws-cdk-lib/aws-s3-notifications/lib/sns.ts +++ b/packages/aws-cdk-lib/aws-s3-notifications/lib/sns.ts @@ -12,13 +12,13 @@ export class SnsDestination implements s3.IBucketNotificationDestination { constructor(private readonly topic: sns.ITopic) { } - public bind(scope: Construct, bucket: s3.IBucket): s3.BucketNotificationDestinationConfig { + public bind(scope: Construct, bucket: s3.IBucketRef): s3.BucketNotificationDestinationConfig { this.topic.addToResourcePolicy(new iam.PolicyStatement({ principals: [new iam.ServicePrincipal('s3.amazonaws.com')], actions: ['sns:Publish'], resources: [this.topic.topicArn], conditions: { - ArnLike: { 'aws:SourceArn': bucket.bucketArn }, + ArnLike: { 'aws:SourceArn': bucket.bucketRef.bucketArn }, }, })); diff --git a/packages/aws-cdk-lib/aws-s3-notifications/lib/sqs.ts b/packages/aws-cdk-lib/aws-s3-notifications/lib/sqs.ts index c23901f4e100a..4e698e6c0b7fa 100644 --- a/packages/aws-cdk-lib/aws-s3-notifications/lib/sqs.ts +++ b/packages/aws-cdk-lib/aws-s3-notifications/lib/sqs.ts @@ -15,10 +15,10 @@ export class SqsDestination implements s3.IBucketNotificationDestination { * Allows using SQS queues as destinations for bucket notifications. * Use `bucket.onEvent(event, queue)` to subscribe. */ - public bind(_scope: Construct, bucket: s3.IBucket): s3.BucketNotificationDestinationConfig { + public bind(_scope: Construct, bucket: s3.IBucketRef): s3.BucketNotificationDestinationConfig { this.queue.grantSendMessages(new iam.ServicePrincipal('s3.amazonaws.com', { conditions: { - ArnLike: { 'aws:SourceArn': bucket.bucketArn }, + ArnLike: { 'aws:SourceArn': bucket.bucketRef.bucketArn }, }, })); diff --git a/packages/aws-cdk-lib/aws-s3/lib/bucket-policy.ts b/packages/aws-cdk-lib/aws-s3/lib/bucket-policy.ts index da61e88b52778..002dc781b3d1c 100644 --- a/packages/aws-cdk-lib/aws-s3/lib/bucket-policy.ts +++ b/packages/aws-cdk-lib/aws-s3/lib/bucket-policy.ts @@ -1,6 +1,6 @@ import { Construct } from 'constructs'; import { Bucket, IBucket } from './bucket'; -import { CfnBucket, CfnBucketPolicy } from './s3.generated'; +import { BucketPolicyReference, CfnBucket, CfnBucketPolicy, IBucketPolicyRef } from './s3.generated'; import { PolicyDocument } from '../../aws-iam'; import { RemovalPolicy, Resource, Token, Tokenization } from '../../core'; import { addConstructMetadata, MethodMetadata } from '../../core/lib/metadata-resource'; @@ -49,7 +49,7 @@ export interface BucketPolicyProps { * */ @propertyInjectable -export class BucketPolicy extends Resource { +export class BucketPolicy extends Resource implements IBucketPolicyRef { /** Uniquely identifies this class. */ public static readonly PROPERTY_INJECTION_ID: string = 'aws-cdk-lib.aws-s3.BucketPolicy'; @@ -88,11 +88,14 @@ export class BucketPolicy extends Resource { }(cfnBucketPolicy, id, { bucket, }); + // mark the Bucket as having this Policy bucket.policy = ret; return ret; } + public readonly bucketPolicyRef: BucketPolicyReference; + /** * A policy document containing permissions to add to the specified bucket. * For more information, see Access Policy Language Overview in the Amazon @@ -116,6 +119,7 @@ export class BucketPolicy extends Resource { bucket: this.bucket.bucketName, policyDocument: this.document, }); + this.bucketPolicyRef = this.resource.bucketPolicyRef; if (props.removalPolicy) { this.resource.applyRemovalPolicy(props.removalPolicy); diff --git a/packages/aws-cdk-lib/aws-s3/lib/bucket.ts b/packages/aws-cdk-lib/aws-s3/lib/bucket.ts index 30a2dc224a7b5..76482f0343498 100644 --- a/packages/aws-cdk-lib/aws-s3/lib/bucket.ts +++ b/packages/aws-cdk-lib/aws-s3/lib/bucket.ts @@ -5,18 +5,20 @@ import { IBucketNotificationDestination } from './destination'; import { BucketNotifications } from './notifications-resource'; import * as perms from './perms'; import { LifecycleRule, StorageClass } from './rule'; -import { CfnBucket } from './s3.generated'; +import { BucketReference, CfnBucket, IBucketRef } from './s3.generated'; import { parseBucketArn, parseBucketName } from './util'; import * as events from '../../aws-events'; import * as iam from '../../aws-iam'; import * as kms from '../../aws-kms'; import { + Annotations, CustomResource, Duration, FeatureFlags, Fn, IResource, Lazy, + PhysicalName, RemovalPolicy, Resource, ResourceProps, @@ -24,21 +26,21 @@ import { Tags, Token, Tokenization, - Annotations, - PhysicalName, } from '../../core'; import { UnscopedValidationError, ValidationError } from '../../core/lib/errors'; import { addConstructMetadata, MethodMetadata } from '../../core/lib/metadata-resource'; import { CfnReference } from '../../core/lib/private/cfn-reference'; import { propertyInjectable } from '../../core/lib/prop-injectable'; -import { AutoDeleteObjectsProvider } from '../../custom-resource-handlers/dist/aws-s3/auto-delete-objects-provider.generated'; +import { + AutoDeleteObjectsProvider, +} from '../../custom-resource-handlers/dist/aws-s3/auto-delete-objects-provider.generated'; import * as cxapi from '../../cx-api'; import * as regionInformation from '../../region-info'; const AUTO_DELETE_OBJECTS_RESOURCE_TYPE = 'Custom::S3AutoDeleteObjects'; const AUTO_DELETE_OBJECTS_TAG = 'aws-cdk:auto-delete-objects'; -export interface IBucket extends IResource { +export interface IBucket extends IResource, IBucketRef { /** * The ARN of the bucket. * @attribute @@ -1150,6 +1152,13 @@ export abstract class BucketBase extends Resource implements IBucket { return ret; } + + public get bucketRef(): BucketReference { + return { + bucketArn: this.bucketArn, + bucketName: this.bucketName, + }; + } } export interface BlockPublicAccessOptions { diff --git a/packages/aws-cdk-lib/aws-s3/lib/destination.ts b/packages/aws-cdk-lib/aws-s3/lib/destination.ts index 0c11c6bdc8b29..0e896aeb1dbd2 100644 --- a/packages/aws-cdk-lib/aws-s3/lib/destination.ts +++ b/packages/aws-cdk-lib/aws-s3/lib/destination.ts @@ -1,5 +1,5 @@ import { Construct, IDependable } from 'constructs'; -import { IBucket } from './bucket'; +import { IBucketRef } from './s3.generated'; /** * Implemented by constructs that can be used as bucket notification destinations. @@ -12,7 +12,7 @@ export interface IBucketNotificationDestination { * idempotency in each destination. * @param bucket The bucket object to bind to */ - bind(scope: Construct, bucket: IBucket): BucketNotificationDestinationConfig; + bind(scope: Construct, bucket: IBucketRef): BucketNotificationDestinationConfig; } /** diff --git a/packages/aws-cdk-lib/aws-secretsmanager/lib/secret.ts b/packages/aws-cdk-lib/aws-secretsmanager/lib/secret.ts index db0f084c90d6a..5e5426d13a414 100644 --- a/packages/aws-cdk-lib/aws-secretsmanager/lib/secret.ts +++ b/packages/aws-cdk-lib/aws-secretsmanager/lib/secret.ts @@ -724,7 +724,7 @@ export class Secret extends SecretBase { * @param encryptionKey The customer-managed encryption key to use for encrypting the secret value. */ @MethodMetadata() - public addReplicaRegion(region: string, encryptionKey?: kms.IKey): void { + public addReplicaRegion(region: string, encryptionKey?: kms.IKeyRef): void { const stack = Stack.of(this); if (!Token.isUnresolved(stack.region) && !Token.isUnresolved(region) && region === stack.region) { throw new ValidationError('Cannot add the region where this stack is deployed as a replica region.', this); @@ -732,7 +732,7 @@ export class Secret extends SecretBase { this.replicaRegions.push({ region, - kmsKeyId: encryptionKey?.keyArn, + kmsKeyId: encryptionKey?.keyRef.keyArn, }); } } diff --git a/packages/aws-cdk-lib/aws-servicecatalog/lib/constraints.ts b/packages/aws-cdk-lib/aws-servicecatalog/lib/constraints.ts index 4375e8b1de1a9..09a308930eff5 100644 --- a/packages/aws-cdk-lib/aws-servicecatalog/lib/constraints.ts +++ b/packages/aws-cdk-lib/aws-servicecatalog/lib/constraints.ts @@ -39,7 +39,7 @@ export interface StackSetsConstraintOptions extends CommonConstraintOptions { /** * IAM role used to administer the StackSets configuration. */ - readonly adminRole: iam.IRole; + readonly adminRole: iam.IRoleRef; /** * IAM role used to provision the products in the Stacks. diff --git a/packages/aws-cdk-lib/aws-servicecatalog/lib/private/association-manager.ts b/packages/aws-cdk-lib/aws-servicecatalog/lib/private/association-manager.ts index dd777922ffbee..1e4896f14847d 100644 --- a/packages/aws-cdk-lib/aws-servicecatalog/lib/private/association-manager.ts +++ b/packages/aws-cdk-lib/aws-servicecatalog/lib/private/association-manager.ts @@ -100,9 +100,9 @@ export class AssociationManager { } } - public static setLaunchRole(portfolio: IPortfolio, product: IProduct, launchRole: iam.IRole, options: CommonConstraintOptions): void { + public static setLaunchRole(portfolio: IPortfolio, product: IProduct, launchRole: iam.IRoleRef, options: CommonConstraintOptions): void { this.setLaunchRoleConstraint(portfolio, product, options, { - roleArn: launchRole.roleArn, + roleArn: launchRole.roleRef.roleArn, }); } @@ -128,7 +128,7 @@ export class AssociationManager { productId: product.productId, accountList: options.accounts, regionList: options.regions, - adminRole: options.adminRole.roleArn, + adminRole: options.adminRole.roleRef.roleArn, executionRole: options.executionRoleName, stackInstanceControl: options.allowStackSetInstanceOperations ? 'ALLOWED' : 'NOT_ALLOWED', }); diff --git a/packages/aws-cdk-lib/aws-sns/lib/topic.ts b/packages/aws-cdk-lib/aws-sns/lib/topic.ts index 261be3c006666..bfe7d736ba12a 100644 --- a/packages/aws-cdk-lib/aws-sns/lib/topic.ts +++ b/packages/aws-cdk-lib/aws-sns/lib/topic.ts @@ -1,7 +1,7 @@ import { Construct } from 'constructs'; import { CfnTopic } from './sns.generated'; import { ITopic, TopicBase } from './topic-base'; -import { IRole } from '../../aws-iam'; +import { IRoleRef } from '../../aws-iam'; import { IKey, Key } from '../../aws-kms'; import { ArnFormat, Lazy, Names, Stack, Token } from '../../core'; import { ValidationError } from '../../core/lib/errors'; @@ -147,14 +147,14 @@ export interface LoggingConfig { * * @default None */ - readonly failureFeedbackRole?: IRole; + readonly failureFeedbackRole?: IRoleRef; /** * The IAM role to be used when logging successful message deliveries in Amazon CloudWatch. * * @default None */ - readonly successFeedbackRole?: IRole; + readonly successFeedbackRole?: IRoleRef; /** * The percentage of successful message deliveries to be logged in Amazon CloudWatch. @@ -394,8 +394,8 @@ export class Topic extends TopicBase { } return { protocol: spec.protocol, - failureFeedbackRoleArn: spec.failureFeedbackRole?.roleArn, - successFeedbackRoleArn: spec.successFeedbackRole?.roleArn, + failureFeedbackRoleArn: spec.failureFeedbackRole?.roleRef.roleArn, + successFeedbackRoleArn: spec.successFeedbackRole?.roleRef.roleArn, successFeedbackSampleRate: spec.successFeedbackSampleRate?.toString(), }; }; diff --git a/packages/aws-cdk-lib/aws-stepfunctions-tasks/lib/bedrock/create-model-customization-job.ts b/packages/aws-cdk-lib/aws-stepfunctions-tasks/lib/bedrock/create-model-customization-job.ts index 7ba7506110a46..088486a9ab4fd 100644 --- a/packages/aws-cdk-lib/aws-stepfunctions-tasks/lib/bedrock/create-model-customization-job.ts +++ b/packages/aws-cdk-lib/aws-stepfunctions-tasks/lib/bedrock/create-model-customization-job.ts @@ -198,7 +198,7 @@ export interface BedrockCreateModelCustomizationJobProps extends sfn.TaskStateBa * * @default - use auto generated role */ - readonly role?: iam.IRole; + readonly role?: iam.IRoleRef; /** * The S3 bucket configuration where the training data is stored. @@ -240,7 +240,7 @@ export class BedrockCreateModelCustomizationJob extends sfn.TaskStateBase { protected readonly taskPolicies?: iam.PolicyStatement[]; private readonly integrationPattern: sfn.IntegrationPattern; - private _role: iam.IRole; + private _role: iam.IRoleRef; constructor(scope: Construct, id: string, private readonly props: BedrockCreateModelCustomizationJobProps) { super(scope, id, props); @@ -271,7 +271,7 @@ export class BedrockCreateModelCustomizationJob extends sfn.TaskStateBase { const poliyStatement = new iam.PolicyStatement({ actions: ['kms:Decrypt', 'kms:GenerateDataKey', 'kms:DescribeKey', 'kms:CreateGrant'], resources: ['*'], - principals: [new iam.ArnPrincipal(this._role.roleArn)], + principals: [new iam.ArnPrincipal(this._role.roleRef.roleArn)], conditions: { StringEquals: { 'kms:ViaService': [ @@ -292,7 +292,10 @@ export class BedrockCreateModelCustomizationJob extends sfn.TaskStateBase { * The IAM role for the bedrock create model customization job */ public get role(): iam.IRole { - return this._role; + if ('grant' in this._role) { + return this._role as iam.IRole; + } + throw new ValidationError(`Role is not an instance of IRole: ${this._role.constructor.name}`, this); } /** @@ -300,7 +303,7 @@ export class BedrockCreateModelCustomizationJob extends sfn.TaskStateBase { * * @see https://docs.aws.amazon.com/bedrock/latest/userguide/model-customization-iam-role.html */ - private renderBedrockCreateModelCustomizationJobRole(): iam.IRole { + private renderBedrockCreateModelCustomizationJobRole(): iam.IRoleRef { if (this.props.role) { return this.props.role; } @@ -483,7 +486,7 @@ export class BedrockCreateModelCustomizationJob extends sfn.TaskStateBase { }), new iam.PolicyStatement({ actions: ['iam:PassRole'], - resources: [this._role.roleArn], + resources: [this._role.roleRef.roleArn], }), ]; return policyStatements; @@ -528,7 +531,7 @@ export class BedrockCreateModelCustomizationJob extends sfn.TaskStateBase { OutputDataConfig: { S3Uri: this.props.outputData.bucket.s3UrlForObject(this.props.outputData.path), }, - RoleArn: this._role.roleArn, + RoleArn: this._role.roleRef.roleArn, TrainingDataConfig: { S3Uri: this.props.trainingData.bucket.s3UrlForObject(this.props.trainingData.path), }, diff --git a/packages/aws-cdk-lib/aws-stepfunctions-tasks/lib/sagemaker/base-types.ts b/packages/aws-cdk-lib/aws-stepfunctions-tasks/lib/sagemaker/base-types.ts index 0949e697a1308..c528e8924cb5d 100644 --- a/packages/aws-cdk-lib/aws-stepfunctions-tasks/lib/sagemaker/base-types.ts +++ b/packages/aws-cdk-lib/aws-stepfunctions-tasks/lib/sagemaker/base-types.ts @@ -604,7 +604,7 @@ export interface TransformOutput { * * @default - default KMS key for Amazon S3 for your role's account. */ - readonly encryptionKey?: kms.IKey; + readonly encryptionKey?: kms.IKeyRef; /** * S3 path where you want Amazon SageMaker to store the results of the transform job. @@ -633,7 +633,7 @@ export interface TransformResources { * * @default - None */ - readonly volumeEncryptionKey?: kms.IKey; + readonly volumeEncryptionKey?: kms.IKeyRef; } /** diff --git a/packages/aws-cdk-lib/aws-stepfunctions-tasks/lib/sagemaker/create-endpoint-config.ts b/packages/aws-cdk-lib/aws-stepfunctions-tasks/lib/sagemaker/create-endpoint-config.ts index 32f01039317fc..711d992869fbc 100644 --- a/packages/aws-cdk-lib/aws-stepfunctions-tasks/lib/sagemaker/create-endpoint-config.ts +++ b/packages/aws-cdk-lib/aws-stepfunctions-tasks/lib/sagemaker/create-endpoint-config.ts @@ -19,7 +19,7 @@ interface SageMakerCreateEndpointConfigOptions { * * @default - None */ - readonly kmsKey?: kms.IKey; + readonly kmsKey?: kms.IKeyRef; /** * An list of ProductionVariant objects, one for each model that you want to host at this endpoint. @@ -121,7 +121,7 @@ export class SageMakerCreateEndpointConfig extends sfn.TaskStateBase { return { EndpointConfigName: this.props.endpointConfigName, Tags: this.props.tags?.value, - KmsKeyId: this.props.kmsKey?.keyId, + KmsKeyId: this.props.kmsKey?.keyRef.keyId, ProductionVariants: this.props.productionVariants.map((variant) => ({ InitialInstanceCount: variant.initialInstanceCount ? variant.initialInstanceCount : 1, InstanceType: isJsonPathOrJsonataExpression(variant.instanceType.toString()) diff --git a/packages/aws-cdk-lib/aws-stepfunctions-tasks/lib/sagemaker/create-transform-job.ts b/packages/aws-cdk-lib/aws-stepfunctions-tasks/lib/sagemaker/create-transform-job.ts index 9e232f5578e24..7ec073986c51b 100644 --- a/packages/aws-cdk-lib/aws-stepfunctions-tasks/lib/sagemaker/create-transform-job.ts +++ b/packages/aws-cdk-lib/aws-stepfunctions-tasks/lib/sagemaker/create-transform-job.ts @@ -245,7 +245,7 @@ export class SageMakerCreateTransformJob extends sfn.TaskStateBase { return { TransformOutput: { S3OutputPath: output.s3OutputPath, - ...(output.encryptionKey ? { KmsKeyId: output.encryptionKey.keyArn } : {}), + ...(output.encryptionKey ? { KmsKeyId: output.encryptionKey.keyRef.keyArn } : {}), ...(output.accept ? { Accept: output.accept } : {}), ...(output.assembleWith ? { AssembleWith: output.assembleWith } : {}), }, @@ -258,7 +258,7 @@ export class SageMakerCreateTransformJob extends sfn.TaskStateBase { InstanceCount: resources.instanceCount, InstanceType: isJsonPathOrJsonataExpression(resources.instanceType.toString()) ? resources.instanceType.toString() : `ml.${resources.instanceType}`, - ...(resources.volumeEncryptionKey ? { VolumeKmsKeyId: resources.volumeEncryptionKey.keyArn } : {}), + ...(resources.volumeEncryptionKey ? { VolumeKmsKeyId: resources.volumeEncryptionKey.keyRef.keyArn } : {}), }, }; } diff --git a/packages/aws-cdk-lib/aws-stepfunctions-tasks/lib/stepfunctions/invoke-activity.ts b/packages/aws-cdk-lib/aws-stepfunctions-tasks/lib/stepfunctions/invoke-activity.ts index 64193f657b1a2..c0c7f8ea650b2 100644 --- a/packages/aws-cdk-lib/aws-stepfunctions-tasks/lib/stepfunctions/invoke-activity.ts +++ b/packages/aws-cdk-lib/aws-stepfunctions-tasks/lib/stepfunctions/invoke-activity.ts @@ -1,7 +1,7 @@ import { Construct } from 'constructs'; import * as iam from '../../../aws-iam'; -import { IKey } from '../../../aws-kms'; +import { IKeyRef } from '../../../aws-kms'; import * as sfn from '../../../aws-stepfunctions'; import { CustomerManagedEncryptionConfiguration } from '../../../aws-stepfunctions/lib/customer-managed-key-encryption-configuration'; @@ -99,13 +99,13 @@ export class StepFunctionsInvokeActivity extends sfn.TaskStateBase { } // IAM policy for the State Machine execution role to use the Activity KMS key when encrypting inputs - private createPolicyStatements(kmskey: IKey): iam.PolicyStatement[] { + private createPolicyStatements(kmskey: IKeyRef): iam.PolicyStatement[] { return [ new iam.PolicyStatement({ actions: [ 'kms:Decrypt', 'kms:GenerateDataKey', ], - resources: [kmskey.keyArn], + resources: [kmskey.keyRef.keyArn], conditions: { StringEquals: { 'kms:EncryptionContext:aws:states:activityArn': this.props.activity.activityArn, diff --git a/packages/aws-cdk-lib/aws-stepfunctions/lib/states/distributed-map/result-writer.ts b/packages/aws-cdk-lib/aws-stepfunctions/lib/states/distributed-map/result-writer.ts index 11be34d130357..0ba2b47db7d64 100644 --- a/packages/aws-cdk-lib/aws-stepfunctions/lib/states/distributed-map/result-writer.ts +++ b/packages/aws-cdk-lib/aws-stepfunctions/lib/states/distributed-map/result-writer.ts @@ -193,7 +193,7 @@ export class ResultWriter { */ public render(queryLanguage?: QueryLanguage): any { const argumentOrParameter = { - Bucket: this.bucket.bucketName, + Bucket: this.bucket.bucketRef.bucketName, ...(this.prefix && { Prefix: this.prefix }), }; @@ -211,7 +211,7 @@ export class ResultWriter { * Compile policy statements to provide relevent permissions to the state machine */ public providePolicyStatements(): iam.PolicyStatement[] { - return this.bucket?.bucketName ? buildS3PutObjectPolicyStatements(this.bucket.bucketName) : []; + return this.bucket?.bucketRef.bucketName ? buildS3PutObjectPolicyStatements(this.bucket.bucketRef.bucketName) : []; } } @@ -259,7 +259,7 @@ export class ResultWriterV2 { // Resource and Parameters are only applicable if the bucket is defined, otherwise they shouldn't be rendered. const shouldRenderResourceAndParameters = !!(this.bucket || this.bucketNamePath); const argumentOrParameter = shouldRenderResourceAndParameters ? { - ...(this.bucket && { Bucket: this.bucket.bucketName }), + ...(this.bucket && { Bucket: this.bucket.bucketRef.bucketName }), ...(this.bucketNamePath && { Bucket: this.bucketNamePath }), ...(this.prefix && { Prefix: this.prefix }), }: undefined; diff --git a/packages/aws-cdk-lib/aws-synthetics/lib/code.ts b/packages/aws-cdk-lib/aws-synthetics/lib/code.ts index e91de5816a3f8..988c011c6675f 100644 --- a/packages/aws-cdk-lib/aws-synthetics/lib/code.ts +++ b/packages/aws-cdk-lib/aws-synthetics/lib/code.ts @@ -45,7 +45,7 @@ export abstract class Code { * * @returns `S3Code` associated with the specified S3 object. */ - public static fromBucket(bucket: s3.IBucket, key: string, objectVersion?: string): S3Code { + public static fromBucket(bucket: s3.IBucketRef, key: string, objectVersion?: string): S3Code { return new S3Code(bucket, key, objectVersion); } @@ -194,14 +194,14 @@ export class InlineCode extends Code { * S3 bucket path to the code zip file */ export class S3Code extends Code { - public constructor(private bucket: s3.IBucket, private key: string, private objectVersion?: string) { + public constructor(private bucket: s3.IBucketRef, private key: string, private objectVersion?: string) { super(); } public bind(_scope: Construct, _handler: string, _family: RuntimeFamily, _runtimeName?: string): CodeConfig { return { s3Location: { - bucketName: this.bucket.bucketName, + bucketName: this.bucket.bucketRef.bucketName, objectKey: this.key, objectVersion: this.objectVersion, }, diff --git a/packages/aws-cdk-lib/custom-resources/lib/provider-framework/provider.ts b/packages/aws-cdk-lib/custom-resources/lib/provider-framework/provider.ts index 7396003c165e9..a4105977ed1b8 100644 --- a/packages/aws-cdk-lib/custom-resources/lib/provider-framework/provider.ts +++ b/packages/aws-cdk-lib/custom-resources/lib/provider-framework/provider.ts @@ -163,7 +163,7 @@ export interface ProviderProps { * * @default - AWS Lambda creates and uses an AWS managed customer master key (CMK) */ - readonly providerFunctionEnvEncryption?: kms.IKey; + readonly providerFunctionEnvEncryption?: kms.IKeyRef; /** * Defines what execution history events of the waiter state machine are logged and where they are logged. @@ -222,7 +222,7 @@ export class Provider extends Construct implements ICustomResourceProvider { private readonly vpcSubnets?: ec2.SubnetSelection; private readonly securityGroups?: ec2.ISecurityGroup[]; private readonly role?: iam.IRole; - private readonly providerFunctionEnvEncryption?: kms.IKey; + private readonly providerFunctionEnvEncryption?: kms.IKeyRef; private readonly frameworkLambdaLoggingLevel?: lambda.ApplicationLogLevel; constructor(scope: Construct, id: string, props: ProviderProps) { diff --git a/packages/aws-cdk-lib/pipelines/test/testhelpers/test-app.ts b/packages/aws-cdk-lib/pipelines/test/testhelpers/test-app.ts index 40fd1c2a5d5ac..bb8c4ccaa019d 100644 --- a/packages/aws-cdk-lib/pipelines/test/testhelpers/test-app.ts +++ b/packages/aws-cdk-lib/pipelines/test/testhelpers/test-app.ts @@ -71,7 +71,7 @@ export class AppWithOutput extends Stage { super(scope, id, props); const stack = new BucketStack(this, props.stackId ?? 'Stack'); - this.theOutput = new CfnOutput(stack, 'MyOutput', { value: stack.bucket.bucketName }); + this.theOutput = new CfnOutput(stack, 'MyOutput', { value: stack.bucket.bucketRef.bucketName }); } } @@ -122,7 +122,7 @@ export class ThreeStackApp extends Stage { * It contains a single Bucket. Such robust. Much uptime. */ export class BucketStack extends Stack { - public readonly bucket: s3.IBucket; + public readonly bucket: s3.IBucketRef; constructor(scope: Construct, id: string, props?: StackProps) { super(scope, id, props); @@ -165,7 +165,7 @@ export class StageWithStackOutput extends Stage { const stack = new BucketStack(this, 'Stack'); this.output = new CfnOutput(stack, 'BucketName', { - value: stack.bucket.bucketName, + value: stack.bucket.bucketRef.bucketName, }); } } diff --git a/packages/awslint/lib/rules/core-types.ts b/packages/awslint/lib/rules/core-types.ts index eb5de41f61e2c..1c0e00f7a5dbf 100644 --- a/packages/awslint/lib/rules/core-types.ts +++ b/packages/awslint/lib/rules/core-types.ts @@ -44,11 +44,7 @@ export class CoreTypes { return false; } - if (!c.name.startsWith('Cfn')) { - return false; - } - - return true; + return c.name.startsWith('Cfn'); } /** @@ -86,10 +82,11 @@ export class CoreTypes { } /** - * Return true if the given interface type is a CFN class or prop type + * Return true if the given interface type is a CFN class, prop type or interface */ public static isCfnType(interfaceType: reflect.Type) { return interfaceType.name.startsWith('Cfn') + || /^I\w+Ref/.test(interfaceType.name) || (interfaceType.namespace && interfaceType.namespace.startsWith('Cfn')) // aws_service.CfnTheResource.SubType || (interfaceType.namespace && interfaceType.namespace.split('.', 2).at(1)?.startsWith('Cfn')); diff --git a/packages/awslint/lib/rules/docs.ts b/packages/awslint/lib/rules/docs.ts index ed70eda0273b8..5de5306098019 100644 --- a/packages/awslint/lib/rules/docs.ts +++ b/packages/awslint/lib/rules/docs.ts @@ -84,6 +84,10 @@ docsLinter.add({ if (isModuleExperimental(e.ctx.assembly)) { return; } + if ((e.ctx.kind === 'type' && CoreTypes.isCfnType(e.ctx.documentable)) + || (e.ctx.kind === 'interface-property' && CoreTypes.isCfnType(e.ctx.containingType))) { + return; + } const sym = e.ctx.documentable; e.assert(sym.docs.docs.stability !== Stability.Experimental, e.ctx.errorKey); }, diff --git a/packages/awslint/lib/rules/resource.ts b/packages/awslint/lib/rules/resource.ts index 243319b0defec..f9a51e89cd30e 100644 --- a/packages/awslint/lib/rules/resource.ts +++ b/packages/awslint/lib/rules/resource.ts @@ -79,6 +79,9 @@ export class ResourceReflection { /** * Attribute properties are all the properties that begin with the type name (e.g. bucketXxx). + * + * We are adding a `bucketRef` property as well that we don't necessarily want to hold to the same linter + * rules as the other attributes, so we don't consider it an attribute. */ private findAttributeProperties(): Attribute[] { const result = new Array(); @@ -87,6 +90,9 @@ export class ResourceReflection { if (p.protected) { continue; // skip any protected properties } + if (p.name.endsWith('Ref')) { + continue; // skip any "Ref" properties, these are not attributes + } const basename = camelize(this.cfn.basename); diff --git a/tools/@aws-cdk/cdk-build-tools/bin/cdk-awslint b/tools/@aws-cdk/cdk-build-tools/bin/cdk-awslint index ea9cac3495e6b..c48646498be66 100755 --- a/tools/@aws-cdk/cdk-build-tools/bin/cdk-awslint +++ b/tools/@aws-cdk/cdk-build-tools/bin/cdk-awslint @@ -1,2 +1,4 @@ #!/usr/bin/env node + +// Run the original awslint require('awslint/bin/awslint.js'); \ No newline at end of file diff --git a/tools/@aws-cdk/spec2cdk/lib/cdk/cdk.ts b/tools/@aws-cdk/spec2cdk/lib/cdk/cdk.ts index 195a844ac2bc2..e1477cacd98cd 100644 --- a/tools/@aws-cdk/spec2cdk/lib/cdk/cdk.ts +++ b/tools/@aws-cdk/spec2cdk/lib/cdk/cdk.ts @@ -37,6 +37,7 @@ export class CdkCore extends ExternalModule { public readonly CfnTag = Type.fromName(this, 'CfnTag'); public readonly TagManager = $T(Type.fromName(this, 'TagManager')); public readonly TagType = $T(Type.fromName(this, 'TagType')); + public readonly Fn = $T(Type.fromName(this, 'Fn')); public readonly ITaggable = Type.fromName(this, 'ITaggable'); public readonly ITaggableV2 = Type.fromName(this, 'ITaggableV2'); public readonly IResolvable = Type.fromName(this, 'IResolvable'); diff --git a/tools/@aws-cdk/spec2cdk/lib/cdk/resource-class.ts b/tools/@aws-cdk/spec2cdk/lib/cdk/resource-class.ts index 7890e6efa5b0a..68c275885ef26 100644 --- a/tools/@aws-cdk/spec2cdk/lib/cdk/resource-class.ts +++ b/tools/@aws-cdk/spec2cdk/lib/cdk/resource-class.ts @@ -20,10 +20,11 @@ import { Stability, ObjectLiteral, Module, + InterfaceType, } from '@cdklabs/typewriter'; import { CDK_CORE, CONSTRUCTS } from './cdk'; import { CloudFormationMapping } from './cloudformation-mapping'; -import { ResourceDecider } from './resource-decider'; +import { ResourceDecider, shouldBuildReferenceInterface } from './resource-decider'; import { TypeConverter } from './type-converter'; import { classNameFromResource, @@ -45,6 +46,7 @@ const $this = $E(expr.this_()); export class ResourceClass extends ClassType { private readonly propsType: StructType; + private readonly refInterface?: InterfaceType; private readonly decider: ResourceDecider; private readonly converter: TypeConverter; private readonly module: Module; @@ -55,6 +57,20 @@ export class ResourceClass extends ClassType { private readonly resource: Resource, private readonly suffix?: string, ) { + let refInterface: InterfaceType | undefined; + if (shouldBuildReferenceInterface(resource)) { + // IBucketRef { bucketRef: BucketRef } + refInterface = new InterfaceType(scope, { + export: true, + name: `I${resource.name}${suffix ?? ''}Ref`, + extends: [CONSTRUCTS.IConstruct], + docs: { + summary: `Indicates that this resource can be referenced as a ${resource.name}.`, + stability: Stability.Experimental, + }, + }); + } + super(scope, { export: true, name: classNameFromResource(resource, suffix), @@ -67,9 +83,10 @@ export class ResourceClass extends ClassType { }), }, extends: CDK_CORE.CfnResource, - implements: [CDK_CORE.IInspectable, ...ResourceDecider.taggabilityInterfaces(resource)], + implements: [CDK_CORE.IInspectable, refInterface?.type, ...ResourceDecider.taggabilityInterfaces(resource)].filter(isDefined), }); + this.refInterface = refInterface; this.module = Module.of(this); this.propsType = new StructType(this.scope, { @@ -105,6 +122,8 @@ export class ResourceClass extends ClassType { cfnMapping.add(prop.cfnMapping); } + this.buildReferenceInterface(); + // Build the members of this class this.addProperty({ name: staticResourceTypeName(), @@ -153,6 +172,48 @@ export class ResourceClass extends ClassType { this.makeMustRenderStructs(); } + /** + * Build the reference interface for this resource + */ + private buildReferenceInterface() { + if (!shouldBuildReferenceInterface(this.resource)) { + return; + } + + // BucketRef { bucketName, bucketArn } + const refPropsStruct = new StructType(this.scope, { + export: true, + name: `${this.resource.name}${this.suffix ?? ''}Reference`, + docs: { + summary: `A reference to a ${this.resource.name} resource.`, + stability: Stability.External, + }, + }); + + // Build the shared interface + for (const { declaration } of this.decider.referenceProps ?? []) { + refPropsStruct.addProperty(declaration); + } + + const refProperty = this.refInterface!.addProperty({ + name: `${this.decider.camelResourceName}Ref`, + type: refPropsStruct.type, + immutable: true, + docs: { + summary: `A reference to a ${this.resource.name} resource.`, + }, + }); + + this.addProperty({ + name: refProperty.name, + type: refProperty.type, + getterBody: Block.with( + stmt.ret(expr.object(Object.fromEntries(this.decider.referenceProps.map(({ declaration, cfnValue }) => [declaration.name, cfnValue])))), + ), + immutable: true, + }); + } + private makeFromCloudFormationFactory() { const factory = this.addMethod({ name: '_fromCloudFormation', @@ -389,3 +450,10 @@ export class ResourceClass extends ClassType { } } } + +/** + * Type guard to filter out undefined values. + */ +function isDefined(x: T | undefined): x is T { + return x !== undefined; +} diff --git a/tools/@aws-cdk/spec2cdk/lib/cdk/resource-decider.ts b/tools/@aws-cdk/spec2cdk/lib/cdk/resource-decider.ts index 23906deef7805..a8db9a5c4c041 100644 --- a/tools/@aws-cdk/spec2cdk/lib/cdk/resource-decider.ts +++ b/tools/@aws-cdk/spec2cdk/lib/cdk/resource-decider.ts @@ -4,12 +4,28 @@ import { CDK_CORE } from './cdk'; import { PropertyMapping } from './cloudformation-mapping'; import { NON_RESOLVABLE_PROPERTY_NAMES, TaggabilityStyle, resourceTaggabilityStyle } from './tagging'; import { TypeConverter } from './type-converter'; -import { attributePropertyName, cloudFormationDocLink, propertyNameFromCloudFormation } from '../naming'; +import { attributePropertyName, camelcasedResourceName, cloudFormationDocLink, propertyNameFromCloudFormation, referencePropertyName } from '../naming'; import { splitDocumentation } from '../util'; // This convenience typewriter builder is used all over the place const $this = $E(expr.this_()); +interface ReferenceProp { + readonly declaration: PropertySpec; + readonly cfnValue: Expression; +} + +// We temporarily only do this for a few services, to experiment +const REFERENCE_PROP_SERVICES = [ + 'iam', + 'apigateway', + 'ec2', + 'cloudfront', + 'kms', + 's3', + 'lambda', +]; + /** * Decide how properties get mapped between model types, Typescript types, and CloudFormation */ @@ -25,16 +41,21 @@ export class ResourceDecider { private readonly taggability?: TaggabilityStyle; + public readonly referenceProps = new Array(); public readonly propsProperties = new Array(); public readonly classProperties = new Array(); public readonly classAttributeProperties = new Array(); + public readonly camelResourceName: string; constructor(private readonly resource: Resource, private readonly converter: TypeConverter) { + this.camelResourceName = camelcasedResourceName(resource); this.taggability = resourceTaggabilityStyle(this.resource); this.convertProperties(); this.convertAttributes(); + this.convertReferenceProps(); + this.propsProperties.sort((p1, p2) => p1.propertySpec.name.localeCompare(p2.propertySpec.name)); this.classProperties.sort((p1, p2) => p1.propertySpec.name.localeCompare(p2.propertySpec.name)); this.classAttributeProperties.sort((p1, p2) => p1.propertySpec.name.localeCompare(p2.propertySpec.name)); @@ -59,6 +80,70 @@ export class ResourceDecider { } } + /** + * Find an ARN property for this resource + * + * Returns `undefined` if no ARN property is found, or if the ARN property is already + * included in the primary identifier. + */ + private findArnProperty() { + const possibleArnNames = ['Arn', `${this.resource.name}Arn`]; + for (const name of possibleArnNames) { + const prop = this.resource.attributes[name]; + if (prop && !this.resource.primaryIdentifier?.includes(name)) { + return name; + } + } + return undefined; + } + + private convertReferenceProps() { + // Primary identifier. We assume all parts are strings. + const primaryIdentifier = this.resource.primaryIdentifier ?? []; + if (primaryIdentifier.length === 1) { + this.referenceProps.push({ + declaration: { + name: referencePropertyName(primaryIdentifier[0], this.resource.name), + type: Type.STRING, + immutable: true, + docs: { + summary: `The ${primaryIdentifier[0]} of the ${this.resource.name} resource.`, + }, + }, + cfnValue: $this.ref, + }); + } else if (primaryIdentifier.length > 1) { + for (const [i, cfnName] of enumerate(primaryIdentifier)) { + this.referenceProps.push({ + declaration: { + name: referencePropertyName(cfnName, this.resource.name), + type: Type.STRING, + immutable: true, + docs: { + summary: `The ${cfnName} of the ${this.resource.name} resource.`, + }, + }, + cfnValue: splitSelect('|', i, $this.ref), + }); + } + } + + const arnProp = this.findArnProperty(); + if (arnProp) { + this.referenceProps.push({ + declaration: { + name: referencePropertyName(arnProp, this.resource.name), + type: Type.STRING, + immutable: true, + docs: { + summary: `The ARN of the ${this.resource.name} resource.`, + }, + }, + cfnValue: $this[attributePropertyName(arnProp)], + }); + } + } + /** * Default mapping for a property */ @@ -329,12 +414,20 @@ export class ResourceDecider { return CDK_CORE.TagType.AUTOSCALING_GROUP; case 'map': return CDK_CORE.TagType.MAP; + default: + assertNever(variant); } - - throw new Error(`Unknown variant: ${this.resource.tagInformation?.variant}`); } } +/** + * Utility function to ensure exhaustive checks for never type. + */ +function assertNever(x: never): never { + // eslint-disable-next-line @cdklabs/no-throw-default-error + throw new Error(`Unexpected object: ${x}`); +} + export interface PropsProperty { readonly propertySpec: PropertySpec; readonly validateRequiredInConstructor: boolean; @@ -372,3 +465,15 @@ export function deprecationMessage(property: Property): string | undefined { return undefined; } + +function splitSelect(sep: string, n: number, base: Expression) { + return CDK_CORE.Fn.select(expr.lit(n), CDK_CORE.Fn.split(expr.lit(sep), base)); +} + +function enumerate(xs: A[]): Array<[number, A]> { + return xs.map((x, i) => [i, x]); +} + +export function shouldBuildReferenceInterface(resource: Resource) { + return true || REFERENCE_PROP_SERVICES.some(s => resource.cloudFormationType.toLowerCase().startsWith(`aws::${s}::`)); +} diff --git a/tools/@aws-cdk/spec2cdk/lib/naming/conventions.ts b/tools/@aws-cdk/spec2cdk/lib/naming/conventions.ts index be7a7f7336ba7..e290eb1ae1cdf 100644 --- a/tools/@aws-cdk/spec2cdk/lib/naming/conventions.ts +++ b/tools/@aws-cdk/spec2cdk/lib/naming/conventions.ts @@ -47,6 +47,10 @@ export function structNameFromTypeDefinition(def: TypeDefinition) { return `${def.name}Property`; } +export function camelcasedResourceName(res: Resource, suffix?: string) { + return `${camelcase(res.name)}${suffix ?? ''}`; +} + export function classNameFromResource(res: Resource, suffix?: string) { return `Cfn${res.name}${suffix ?? ''}`; } @@ -55,6 +59,10 @@ export function propStructNameFromResource(res: Resource, suffix?: string) { return `${classNameFromResource(res, suffix)}Props`; } +export function interfaceNameFromResource(res: Resource, suffix?: string) { + return `I${classNameFromResource(res, suffix)}`; +} + export function cfnProducerNameFromType(struct: TypeDeclaration) { return `convert${qualifiedName(struct)}ToCloudFormation`; } @@ -87,6 +95,21 @@ export function attributePropertyName(attrName: string) { return propertyNameFromCloudFormation(`attr${attrName.replace(/[^a-zA-Z0-9]/g, '')}`); } +/** + * Make sure the resource name is included in the property + */ +export function referencePropertyName(propName: string, resourceName: string) { + // Some primaryIdentifier components are structurally deep, like AWS::QuickSight::RefreshSchedule's + // 'schedule/scheduleId', or AWS::S3::StorageLens's `configuration/id`. Only return the last part. + propName = propName.split('/').pop() ?? propName; + + if (['arn', 'id', 'name', 'url'].includes(propName.toLowerCase())) { + return `${camelcase(resourceName)}${propName.charAt(0).toUpperCase()}${propName.slice(1)}`; + } + + return camelcase(propName); +} + /** * Generate a name for the given declaration so that we can generate helper symbols for it that won't class * diff --git a/tools/@aws-cdk/spec2cdk/test/__snapshots__/resources.test.ts.snap b/tools/@aws-cdk/spec2cdk/test/__snapshots__/resources.test.ts.snap new file mode 100644 index 0000000000000..b894cf0c9fdb7 --- /dev/null +++ b/tools/@aws-cdk/spec2cdk/test/__snapshots__/resources.test.ts.snap @@ -0,0 +1,1577 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`resource interface when primaryIdentifier is a property 1`] = ` +"/* eslint-disable prettier/prettier, @stylistic/max-len */ +import * as cdk from "aws-cdk-lib"; +import * as constructs from "constructs"; +import * as cfn_parse from "aws-cdk-lib/core/lib/helpers-internal"; +import * as cdk_errors from "aws-cdk-lib/core/lib/errors"; + +/** + * Indicates that this resource can be referenced as a Resource. + * + * @stability experimental + */ +export interface IResourceRef extends constructs.IConstruct { + /** + * A reference to a Resource resource. + */ + readonly resourceRef: ResourceReference; +} + +/** + * @cloudformationResource AWS::Some::Resource + * @stability external + * @see http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-some-resource.html + */ +export class CfnResource extends cdk.CfnResource implements cdk.IInspectable, IResourceRef { + /** + * The CloudFormation resource type name for this resource class. + */ + public static readonly CFN_RESOURCE_TYPE_NAME: string = "AWS::Some::Resource"; + + /** + * Build a CfnResource from CloudFormation properties + * + * A factory method that creates a new instance of this class from an object + * containing the CloudFormation properties of this resource. + * Used in the @aws-cdk/cloudformation-include module. + * + * @internal + */ + public static _fromCloudFormation(scope: constructs.Construct, id: string, resourceAttributes: any, options: cfn_parse.FromCloudFormationOptions): CfnResource { + resourceAttributes = resourceAttributes || {}; + const resourceProperties = options.parser.parseValue(resourceAttributes.Properties); + const propsResult = CfnResourcePropsFromCloudFormation(resourceProperties); + if (cdk.isResolvableObject(propsResult.value)) { + throw new cdk_errors.ValidationError("Unexpected IResolvable", scope); + } + const ret = new CfnResource(scope, id, propsResult.value); + for (const [propKey, propVal] of Object.entries(propsResult.extraProperties)) { + ret.addPropertyOverride(propKey, propVal); + } + options.parser.handleAttributes(ret, resourceAttributes, id); + return ret; + } + + /** + * The identifier of the resource. + */ + public id?: string; + + /** + * @param scope Scope in which this resource is defined + * @param id Construct identifier for this resource (unique in its scope) + * @param props Resource properties + */ + public constructor(scope: constructs.Construct, id: string, props: CfnResourceProps = {}) { + super(scope, id, { + "type": CfnResource.CFN_RESOURCE_TYPE_NAME, + "properties": props + }); + + this.id = props.id; + } + + public get resourceRef(): ResourceReference { + return { + "resourceId": this.ref + }; + } + + protected get cfnProperties(): Record { + return { + "id": this.id + }; + } + + /** + * Examines the CloudFormation resource and discloses attributes + * + * @param inspector tree inspector to collect and process attributes + */ + public inspect(inspector: cdk.TreeInspector): void { + inspector.addAttribute("aws:cdk:cloudformation:type", CfnResource.CFN_RESOURCE_TYPE_NAME); + inspector.addAttribute("aws:cdk:cloudformation:props", this.cfnProperties); + } + + protected renderProperties(props: Record): Record { + return convertCfnResourcePropsToCloudFormation(props); + } +} + +/** + * Properties for defining a \`CfnResource\` + * + * @struct + * @stability external + * @see http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-some-resource.html + */ +export interface CfnResourceProps { + /** + * The identifier of the resource. + * + * @see http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-some-resource.html#cfn-some-resource-id + */ + readonly id?: string; +} + +/** + * A reference to a Resource resource. + * + * @struct + * @stability external + */ +export interface ResourceReference { + /** + * The Id of the Resource resource. + */ + readonly resourceId: string; +} + +/** + * Determine whether the given properties match those of a \`CfnResourceProps\` + * + * @param properties - the TypeScript properties of a \`CfnResourceProps\` + * + * @returns the result of the validation. + */ +// @ts-ignore TS6133 +function CfnResourcePropsValidator(properties: any): cdk.ValidationResult { + if (!cdk.canInspect(properties)) return cdk.VALIDATION_SUCCESS; + const errors = new cdk.ValidationResults(); + if (!(properties && typeof properties == 'object' && !Array.isArray(properties))) { + errors.collect(new cdk.ValidationResult("Expected an object, but received: " + JSON.stringify(properties))); + } + errors.collect(cdk.propertyValidator("id", cdk.validateString)(properties.id)); + return errors.wrap("supplied properties not correct for \\"CfnResourceProps\\""); +} + +// @ts-ignore TS6133 +function convertCfnResourcePropsToCloudFormation(properties: any): any { + if (!cdk.canInspect(properties)) return properties; + CfnResourcePropsValidator(properties).assertSuccess(); + return { + "Id": cdk.stringToCloudFormation(properties.id) + }; +} + +// @ts-ignore TS6133 +function CfnResourcePropsFromCloudFormation(properties: any): cfn_parse.FromCloudFormationResult { + if (cdk.isResolvableObject(properties)) { + return new cfn_parse.FromCloudFormationResult(properties); + } + properties = ((properties == null) ? {} : properties); + if (!(properties && typeof properties == 'object' && !Array.isArray(properties))) { + return new cfn_parse.FromCloudFormationResult(properties); + } + const ret = new cfn_parse.FromCloudFormationPropertyObject(); + ret.addPropertyResult("id", "Id", (properties.Id != null ? cfn_parse.FromCloudFormation.getString(properties.Id) : undefined)); + ret.addUnrecognizedPropertiesAsExtra(properties); + return ret; +}" +`; + +exports[`resource interface when primaryIdentifier is an attribute 1`] = ` +"/* eslint-disable prettier/prettier, @stylistic/max-len */ +import * as cdk from "aws-cdk-lib"; +import * as constructs from "constructs"; +import * as cfn_parse from "aws-cdk-lib/core/lib/helpers-internal"; +import * as cdk_errors from "aws-cdk-lib/core/lib/errors"; + +/** + * Indicates that this resource can be referenced as a Resource. + * + * @stability experimental + */ +export interface IResourceRef extends constructs.IConstruct { + /** + * A reference to a Resource resource. + */ + readonly resourceRef: ResourceReference; +} + +/** + * @cloudformationResource AWS::Some::Resource + * @stability external + * @see http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-some-resource.html + */ +export class CfnResource extends cdk.CfnResource implements cdk.IInspectable, IResourceRef { + /** + * The CloudFormation resource type name for this resource class. + */ + public static readonly CFN_RESOURCE_TYPE_NAME: string = "AWS::Some::Resource"; + + /** + * Build a CfnResource from CloudFormation properties + * + * A factory method that creates a new instance of this class from an object + * containing the CloudFormation properties of this resource. + * Used in the @aws-cdk/cloudformation-include module. + * + * @internal + */ + public static _fromCloudFormation(scope: constructs.Construct, id: string, resourceAttributes: any, options: cfn_parse.FromCloudFormationOptions): CfnResource { + resourceAttributes = resourceAttributes || {}; + const resourceProperties = options.parser.parseValue(resourceAttributes.Properties); + const propsResult = CfnResourcePropsFromCloudFormation(resourceProperties); + if (cdk.isResolvableObject(propsResult.value)) { + throw new cdk_errors.ValidationError("Unexpected IResolvable", scope); + } + const ret = new CfnResource(scope, id, propsResult.value); + for (const [propKey, propVal] of Object.entries(propsResult.extraProperties)) { + ret.addPropertyOverride(propKey, propVal); + } + options.parser.handleAttributes(ret, resourceAttributes, id); + return ret; + } + + /** + * The identifier of the resource + * + * @cloudformationAttribute Id + */ + public readonly attrId: string; + + /** + * @param scope Scope in which this resource is defined + * @param id Construct identifier for this resource (unique in its scope) + * @param props Resource properties + */ + public constructor(scope: constructs.Construct, id: string, props: CfnResourceProps = {}) { + super(scope, id, { + "type": CfnResource.CFN_RESOURCE_TYPE_NAME, + "properties": props + }); + + this.attrId = cdk.Token.asString(this.getAtt("Id", cdk.ResolutionTypeHint.STRING)); + } + + public get resourceRef(): ResourceReference { + return { + "resourceId": this.ref + }; + } + + protected get cfnProperties(): Record { + return {}; + } + + /** + * Examines the CloudFormation resource and discloses attributes + * + * @param inspector tree inspector to collect and process attributes + */ + public inspect(inspector: cdk.TreeInspector): void { + inspector.addAttribute("aws:cdk:cloudformation:type", CfnResource.CFN_RESOURCE_TYPE_NAME); + inspector.addAttribute("aws:cdk:cloudformation:props", this.cfnProperties); + } + + protected renderProperties(props: Record): Record { + return convertCfnResourcePropsToCloudFormation(props); + } +} + +/** + * Properties for defining a \`CfnResource\` + * + * @struct + * @stability external + * @see http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-some-resource.html + */ +export interface CfnResourceProps { + +} + +/** + * A reference to a Resource resource. + * + * @struct + * @stability external + */ +export interface ResourceReference { + /** + * The Id of the Resource resource. + */ + readonly resourceId: string; +} + +/** + * Determine whether the given properties match those of a \`CfnResourceProps\` + * + * @param properties - the TypeScript properties of a \`CfnResourceProps\` + * + * @returns the result of the validation. + */ +// @ts-ignore TS6133 +function CfnResourcePropsValidator(properties: any): cdk.ValidationResult { + if (!cdk.canInspect(properties)) return cdk.VALIDATION_SUCCESS; + const errors = new cdk.ValidationResults(); + if (!(properties && typeof properties == 'object' && !Array.isArray(properties))) { + errors.collect(new cdk.ValidationResult("Expected an object, but received: " + JSON.stringify(properties))); + } + return errors.wrap("supplied properties not correct for \\"CfnResourceProps\\""); +} + +// @ts-ignore TS6133 +function convertCfnResourcePropsToCloudFormation(properties: any): any { + if (!cdk.canInspect(properties)) return properties; + CfnResourcePropsValidator(properties).assertSuccess(); + return {}; +} + +// @ts-ignore TS6133 +function CfnResourcePropsFromCloudFormation(properties: any): cfn_parse.FromCloudFormationResult { + if (cdk.isResolvableObject(properties)) { + return new cfn_parse.FromCloudFormationResult(properties); + } + properties = ((properties == null) ? {} : properties); + if (!(properties && typeof properties == 'object' && !Array.isArray(properties))) { + return new cfn_parse.FromCloudFormationResult(properties); + } + const ret = new cfn_parse.FromCloudFormationPropertyObject(); + ret.addUnrecognizedPropertiesAsExtra(properties); + return ret; +}" +`; + +exports[`resource interface with "Arn" 1`] = ` +"/* eslint-disable prettier/prettier, @stylistic/max-len */ +import * as cdk from "aws-cdk-lib"; +import * as constructs from "constructs"; +import * as cfn_parse from "aws-cdk-lib/core/lib/helpers-internal"; +import * as cdk_errors from "aws-cdk-lib/core/lib/errors"; + +/** + * Indicates that this resource can be referenced as a Something. + * + * @stability experimental + */ +export interface ISomethingRef extends constructs.IConstruct { + /** + * A reference to a Something resource. + */ + readonly somethingRef: SomethingReference; +} + +/** + * @cloudformationResource AWS::Some::Something + * @stability external + * @see http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-some-something.html + */ +export class CfnSomething extends cdk.CfnResource implements cdk.IInspectable, ISomethingRef { + /** + * The CloudFormation resource type name for this resource class. + */ + public static readonly CFN_RESOURCE_TYPE_NAME: string = "AWS::Some::Something"; + + /** + * Build a CfnSomething from CloudFormation properties + * + * A factory method that creates a new instance of this class from an object + * containing the CloudFormation properties of this resource. + * Used in the @aws-cdk/cloudformation-include module. + * + * @internal + */ + public static _fromCloudFormation(scope: constructs.Construct, id: string, resourceAttributes: any, options: cfn_parse.FromCloudFormationOptions): CfnSomething { + resourceAttributes = resourceAttributes || {}; + const resourceProperties = options.parser.parseValue(resourceAttributes.Properties); + const propsResult = CfnSomethingPropsFromCloudFormation(resourceProperties); + if (cdk.isResolvableObject(propsResult.value)) { + throw new cdk_errors.ValidationError("Unexpected IResolvable", scope); + } + const ret = new CfnSomething(scope, id, propsResult.value); + for (const [propKey, propVal] of Object.entries(propsResult.extraProperties)) { + ret.addPropertyOverride(propKey, propVal); + } + options.parser.handleAttributes(ret, resourceAttributes, id); + return ret; + } + + /** + * The identifier of the resource + * + * @cloudformationAttribute Id + */ + public readonly attrId: string; + + /** + * The arn for something + * + * @cloudformationAttribute SomethingArn + */ + public readonly attrSomethingArn: string; + + /** + * @param scope Scope in which this resource is defined + * @param id Construct identifier for this resource (unique in its scope) + * @param props Resource properties + */ + public constructor(scope: constructs.Construct, id: string, props: CfnSomethingProps = {}) { + super(scope, id, { + "type": CfnSomething.CFN_RESOURCE_TYPE_NAME, + "properties": props + }); + + this.attrId = cdk.Token.asString(this.getAtt("Id", cdk.ResolutionTypeHint.STRING)); + this.attrSomethingArn = cdk.Token.asString(this.getAtt("SomethingArn", cdk.ResolutionTypeHint.STRING)); + } + + public get somethingRef(): SomethingReference { + return { + "somethingId": this.ref, + "somethingArn": this.attrSomethingArn + }; + } + + protected get cfnProperties(): Record { + return {}; + } + + /** + * Examines the CloudFormation resource and discloses attributes + * + * @param inspector tree inspector to collect and process attributes + */ + public inspect(inspector: cdk.TreeInspector): void { + inspector.addAttribute("aws:cdk:cloudformation:type", CfnSomething.CFN_RESOURCE_TYPE_NAME); + inspector.addAttribute("aws:cdk:cloudformation:props", this.cfnProperties); + } + + protected renderProperties(props: Record): Record { + return convertCfnSomethingPropsToCloudFormation(props); + } +} + +/** + * Properties for defining a \`CfnSomething\` + * + * @struct + * @stability external + * @see http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-some-something.html + */ +export interface CfnSomethingProps { + +} + +/** + * A reference to a Something resource. + * + * @struct + * @stability external + */ +export interface SomethingReference { + /** + * The Id of the Something resource. + */ + readonly somethingId: string; + + /** + * The ARN of the Something resource. + */ + readonly somethingArn: string; +} + +/** + * Determine whether the given properties match those of a \`CfnSomethingProps\` + * + * @param properties - the TypeScript properties of a \`CfnSomethingProps\` + * + * @returns the result of the validation. + */ +// @ts-ignore TS6133 +function CfnSomethingPropsValidator(properties: any): cdk.ValidationResult { + if (!cdk.canInspect(properties)) return cdk.VALIDATION_SUCCESS; + const errors = new cdk.ValidationResults(); + if (!(properties && typeof properties == 'object' && !Array.isArray(properties))) { + errors.collect(new cdk.ValidationResult("Expected an object, but received: " + JSON.stringify(properties))); + } + return errors.wrap("supplied properties not correct for \\"CfnSomethingProps\\""); +} + +// @ts-ignore TS6133 +function convertCfnSomethingPropsToCloudFormation(properties: any): any { + if (!cdk.canInspect(properties)) return properties; + CfnSomethingPropsValidator(properties).assertSuccess(); + return {}; +} + +// @ts-ignore TS6133 +function CfnSomethingPropsFromCloudFormation(properties: any): cfn_parse.FromCloudFormationResult { + if (cdk.isResolvableObject(properties)) { + return new cfn_parse.FromCloudFormationResult(properties); + } + properties = ((properties == null) ? {} : properties); + if (!(properties && typeof properties == 'object' && !Array.isArray(properties))) { + return new cfn_parse.FromCloudFormationResult(properties); + } + const ret = new cfn_parse.FromCloudFormationPropertyObject(); + ret.addUnrecognizedPropertiesAsExtra(properties); + return ret; +}" +`; + +exports[`resource interface with "Arn" 1`] = ` +"/* eslint-disable prettier/prettier, @stylistic/max-len */ +import * as cdk from "aws-cdk-lib"; +import * as constructs from "constructs"; +import * as cfn_parse from "aws-cdk-lib/core/lib/helpers-internal"; +import * as cdk_errors from "aws-cdk-lib/core/lib/errors"; + +/** + * Indicates that this resource can be referenced as a Resource. + * + * @stability experimental + */ +export interface IResourceRef extends constructs.IConstruct { + /** + * A reference to a Resource resource. + */ + readonly resourceRef: ResourceReference; +} + +/** + * @cloudformationResource AWS::Some::Resource + * @stability external + * @see http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-some-resource.html + */ +export class CfnResource extends cdk.CfnResource implements cdk.IInspectable, IResourceRef { + /** + * The CloudFormation resource type name for this resource class. + */ + public static readonly CFN_RESOURCE_TYPE_NAME: string = "AWS::Some::Resource"; + + /** + * Build a CfnResource from CloudFormation properties + * + * A factory method that creates a new instance of this class from an object + * containing the CloudFormation properties of this resource. + * Used in the @aws-cdk/cloudformation-include module. + * + * @internal + */ + public static _fromCloudFormation(scope: constructs.Construct, id: string, resourceAttributes: any, options: cfn_parse.FromCloudFormationOptions): CfnResource { + resourceAttributes = resourceAttributes || {}; + const resourceProperties = options.parser.parseValue(resourceAttributes.Properties); + const propsResult = CfnResourcePropsFromCloudFormation(resourceProperties); + if (cdk.isResolvableObject(propsResult.value)) { + throw new cdk_errors.ValidationError("Unexpected IResolvable", scope); + } + const ret = new CfnResource(scope, id, propsResult.value); + for (const [propKey, propVal] of Object.entries(propsResult.extraProperties)) { + ret.addPropertyOverride(propKey, propVal); + } + options.parser.handleAttributes(ret, resourceAttributes, id); + return ret; + } + + /** + * The arn for the resource + * + * @cloudformationAttribute Arn + */ + public readonly attrArn: string; + + /** + * The identifier of the resource + * + * @cloudformationAttribute Id + */ + public readonly attrId: string; + + /** + * @param scope Scope in which this resource is defined + * @param id Construct identifier for this resource (unique in its scope) + * @param props Resource properties + */ + public constructor(scope: constructs.Construct, id: string, props: CfnResourceProps = {}) { + super(scope, id, { + "type": CfnResource.CFN_RESOURCE_TYPE_NAME, + "properties": props + }); + + this.attrArn = cdk.Token.asString(this.getAtt("Arn", cdk.ResolutionTypeHint.STRING)); + this.attrId = cdk.Token.asString(this.getAtt("Id", cdk.ResolutionTypeHint.STRING)); + } + + public get resourceRef(): ResourceReference { + return { + "resourceId": this.ref, + "resourceArn": this.attrArn + }; + } + + protected get cfnProperties(): Record { + return {}; + } + + /** + * Examines the CloudFormation resource and discloses attributes + * + * @param inspector tree inspector to collect and process attributes + */ + public inspect(inspector: cdk.TreeInspector): void { + inspector.addAttribute("aws:cdk:cloudformation:type", CfnResource.CFN_RESOURCE_TYPE_NAME); + inspector.addAttribute("aws:cdk:cloudformation:props", this.cfnProperties); + } + + protected renderProperties(props: Record): Record { + return convertCfnResourcePropsToCloudFormation(props); + } +} + +/** + * Properties for defining a \`CfnResource\` + * + * @struct + * @stability external + * @see http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-some-resource.html + */ +export interface CfnResourceProps { + +} + +/** + * A reference to a Resource resource. + * + * @struct + * @stability external + */ +export interface ResourceReference { + /** + * The Id of the Resource resource. + */ + readonly resourceId: string; + + /** + * The ARN of the Resource resource. + */ + readonly resourceArn: string; +} + +/** + * Determine whether the given properties match those of a \`CfnResourceProps\` + * + * @param properties - the TypeScript properties of a \`CfnResourceProps\` + * + * @returns the result of the validation. + */ +// @ts-ignore TS6133 +function CfnResourcePropsValidator(properties: any): cdk.ValidationResult { + if (!cdk.canInspect(properties)) return cdk.VALIDATION_SUCCESS; + const errors = new cdk.ValidationResults(); + if (!(properties && typeof properties == 'object' && !Array.isArray(properties))) { + errors.collect(new cdk.ValidationResult("Expected an object, but received: " + JSON.stringify(properties))); + } + return errors.wrap("supplied properties not correct for \\"CfnResourceProps\\""); +} + +// @ts-ignore TS6133 +function convertCfnResourcePropsToCloudFormation(properties: any): any { + if (!cdk.canInspect(properties)) return properties; + CfnResourcePropsValidator(properties).assertSuccess(); + return {}; +} + +// @ts-ignore TS6133 +function CfnResourcePropsFromCloudFormation(properties: any): cfn_parse.FromCloudFormationResult { + if (cdk.isResolvableObject(properties)) { + return new cfn_parse.FromCloudFormationResult(properties); + } + properties = ((properties == null) ? {} : properties); + if (!(properties && typeof properties == 'object' && !Array.isArray(properties))) { + return new cfn_parse.FromCloudFormationResult(properties); + } + const ret = new cfn_parse.FromCloudFormationPropertyObject(); + ret.addUnrecognizedPropertiesAsExtra(properties); + return ret; +}" +`; + +exports[`resource interface with Arn as a property and not a primaryIdentifier 1`] = ` +"/* eslint-disable prettier/prettier, @stylistic/max-len */ +import * as cdk from "aws-cdk-lib"; +import * as constructs from "constructs"; +import * as cfn_parse from "aws-cdk-lib/core/lib/helpers-internal"; +import * as cdk_errors from "aws-cdk-lib/core/lib/errors"; + +/** + * Indicates that this resource can be referenced as a Resource. + * + * @stability experimental + */ +export interface IResourceRef extends constructs.IConstruct { + /** + * A reference to a Resource resource. + */ + readonly resourceRef: ResourceReference; +} + +/** + * @cloudformationResource AWS::Some::Resource + * @stability external + * @see http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-some-resource.html + */ +export class CfnResource extends cdk.CfnResource implements cdk.IInspectable, IResourceRef { + /** + * The CloudFormation resource type name for this resource class. + */ + public static readonly CFN_RESOURCE_TYPE_NAME: string = "AWS::Some::Resource"; + + /** + * Build a CfnResource from CloudFormation properties + * + * A factory method that creates a new instance of this class from an object + * containing the CloudFormation properties of this resource. + * Used in the @aws-cdk/cloudformation-include module. + * + * @internal + */ + public static _fromCloudFormation(scope: constructs.Construct, id: string, resourceAttributes: any, options: cfn_parse.FromCloudFormationOptions): CfnResource { + resourceAttributes = resourceAttributes || {}; + const resourceProperties = options.parser.parseValue(resourceAttributes.Properties); + const propsResult = CfnResourcePropsFromCloudFormation(resourceProperties); + if (cdk.isResolvableObject(propsResult.value)) { + throw new cdk_errors.ValidationError("Unexpected IResolvable", scope); + } + const ret = new CfnResource(scope, id, propsResult.value); + for (const [propKey, propVal] of Object.entries(propsResult.extraProperties)) { + ret.addPropertyOverride(propKey, propVal); + } + options.parser.handleAttributes(ret, resourceAttributes, id); + return ret; + } + + /** + * The identifier of the resource + * + * @cloudformationAttribute Id + */ + public readonly attrId: string; + + /** + * The arn for the resource. + */ + public arn?: string; + + /** + * @param scope Scope in which this resource is defined + * @param id Construct identifier for this resource (unique in its scope) + * @param props Resource properties + */ + public constructor(scope: constructs.Construct, id: string, props: CfnResourceProps = {}) { + super(scope, id, { + "type": CfnResource.CFN_RESOURCE_TYPE_NAME, + "properties": props + }); + + this.attrId = cdk.Token.asString(this.getAtt("Id", cdk.ResolutionTypeHint.STRING)); + this.arn = props.arn; + } + + public get resourceRef(): ResourceReference { + return { + "resourceId": this.ref + }; + } + + protected get cfnProperties(): Record { + return { + "arn": this.arn + }; + } + + /** + * Examines the CloudFormation resource and discloses attributes + * + * @param inspector tree inspector to collect and process attributes + */ + public inspect(inspector: cdk.TreeInspector): void { + inspector.addAttribute("aws:cdk:cloudformation:type", CfnResource.CFN_RESOURCE_TYPE_NAME); + inspector.addAttribute("aws:cdk:cloudformation:props", this.cfnProperties); + } + + protected renderProperties(props: Record): Record { + return convertCfnResourcePropsToCloudFormation(props); + } +} + +/** + * Properties for defining a \`CfnResource\` + * + * @struct + * @stability external + * @see http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-some-resource.html + */ +export interface CfnResourceProps { + /** + * The arn for the resource. + * + * @see http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-some-resource.html#cfn-some-resource-arn + */ + readonly arn?: string; +} + +/** + * A reference to a Resource resource. + * + * @struct + * @stability external + */ +export interface ResourceReference { + /** + * The Id of the Resource resource. + */ + readonly resourceId: string; +} + +/** + * Determine whether the given properties match those of a \`CfnResourceProps\` + * + * @param properties - the TypeScript properties of a \`CfnResourceProps\` + * + * @returns the result of the validation. + */ +// @ts-ignore TS6133 +function CfnResourcePropsValidator(properties: any): cdk.ValidationResult { + if (!cdk.canInspect(properties)) return cdk.VALIDATION_SUCCESS; + const errors = new cdk.ValidationResults(); + if (!(properties && typeof properties == 'object' && !Array.isArray(properties))) { + errors.collect(new cdk.ValidationResult("Expected an object, but received: " + JSON.stringify(properties))); + } + errors.collect(cdk.propertyValidator("arn", cdk.validateString)(properties.arn)); + return errors.wrap("supplied properties not correct for \\"CfnResourceProps\\""); +} + +// @ts-ignore TS6133 +function convertCfnResourcePropsToCloudFormation(properties: any): any { + if (!cdk.canInspect(properties)) return properties; + CfnResourcePropsValidator(properties).assertSuccess(); + return { + "Arn": cdk.stringToCloudFormation(properties.arn) + }; +} + +// @ts-ignore TS6133 +function CfnResourcePropsFromCloudFormation(properties: any): cfn_parse.FromCloudFormationResult { + if (cdk.isResolvableObject(properties)) { + return new cfn_parse.FromCloudFormationResult(properties); + } + properties = ((properties == null) ? {} : properties); + if (!(properties && typeof properties == 'object' && !Array.isArray(properties))) { + return new cfn_parse.FromCloudFormationResult(properties); + } + const ret = new cfn_parse.FromCloudFormationPropertyObject(); + ret.addPropertyResult("arn", "Arn", (properties.Arn != null ? cfn_parse.FromCloudFormation.getString(properties.Arn) : undefined)); + ret.addUnrecognizedPropertiesAsExtra(properties); + return ret; +}" +`; + +exports[`resource interface with Arn as primaryIdentifier 1`] = ` +"/* eslint-disable prettier/prettier, @stylistic/max-len */ +import * as cdk from "aws-cdk-lib"; +import * as constructs from "constructs"; +import * as cfn_parse from "aws-cdk-lib/core/lib/helpers-internal"; +import * as cdk_errors from "aws-cdk-lib/core/lib/errors"; + +/** + * Indicates that this resource can be referenced as a Resource. + * + * @stability experimental + */ +export interface IResourceRef extends constructs.IConstruct { + /** + * A reference to a Resource resource. + */ + readonly resourceRef: ResourceReference; +} + +/** + * @cloudformationResource AWS::Some::Resource + * @stability external + * @see http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-some-resource.html + */ +export class CfnResource extends cdk.CfnResource implements cdk.IInspectable, IResourceRef { + /** + * The CloudFormation resource type name for this resource class. + */ + public static readonly CFN_RESOURCE_TYPE_NAME: string = "AWS::Some::Resource"; + + /** + * Build a CfnResource from CloudFormation properties + * + * A factory method that creates a new instance of this class from an object + * containing the CloudFormation properties of this resource. + * Used in the @aws-cdk/cloudformation-include module. + * + * @internal + */ + public static _fromCloudFormation(scope: constructs.Construct, id: string, resourceAttributes: any, options: cfn_parse.FromCloudFormationOptions): CfnResource { + resourceAttributes = resourceAttributes || {}; + const resourceProperties = options.parser.parseValue(resourceAttributes.Properties); + const propsResult = CfnResourcePropsFromCloudFormation(resourceProperties); + if (cdk.isResolvableObject(propsResult.value)) { + throw new cdk_errors.ValidationError("Unexpected IResolvable", scope); + } + const ret = new CfnResource(scope, id, propsResult.value); + for (const [propKey, propVal] of Object.entries(propsResult.extraProperties)) { + ret.addPropertyOverride(propKey, propVal); + } + options.parser.handleAttributes(ret, resourceAttributes, id); + return ret; + } + + /** + * The arn of the resource + * + * @cloudformationAttribute Arn + */ + public readonly attrArn: string; + + /** + * @param scope Scope in which this resource is defined + * @param id Construct identifier for this resource (unique in its scope) + * @param props Resource properties + */ + public constructor(scope: constructs.Construct, id: string, props: CfnResourceProps = {}) { + super(scope, id, { + "type": CfnResource.CFN_RESOURCE_TYPE_NAME, + "properties": props + }); + + this.attrArn = cdk.Token.asString(this.getAtt("Arn", cdk.ResolutionTypeHint.STRING)); + } + + public get resourceRef(): ResourceReference { + return { + "resourceArn": this.ref + }; + } + + protected get cfnProperties(): Record { + return {}; + } + + /** + * Examines the CloudFormation resource and discloses attributes + * + * @param inspector tree inspector to collect and process attributes + */ + public inspect(inspector: cdk.TreeInspector): void { + inspector.addAttribute("aws:cdk:cloudformation:type", CfnResource.CFN_RESOURCE_TYPE_NAME); + inspector.addAttribute("aws:cdk:cloudformation:props", this.cfnProperties); + } + + protected renderProperties(props: Record): Record { + return convertCfnResourcePropsToCloudFormation(props); + } +} + +/** + * Properties for defining a \`CfnResource\` + * + * @struct + * @stability external + * @see http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-some-resource.html + */ +export interface CfnResourceProps { + +} + +/** + * A reference to a Resource resource. + * + * @struct + * @stability external + */ +export interface ResourceReference { + /** + * The Arn of the Resource resource. + */ + readonly resourceArn: string; +} + +/** + * Determine whether the given properties match those of a \`CfnResourceProps\` + * + * @param properties - the TypeScript properties of a \`CfnResourceProps\` + * + * @returns the result of the validation. + */ +// @ts-ignore TS6133 +function CfnResourcePropsValidator(properties: any): cdk.ValidationResult { + if (!cdk.canInspect(properties)) return cdk.VALIDATION_SUCCESS; + const errors = new cdk.ValidationResults(); + if (!(properties && typeof properties == 'object' && !Array.isArray(properties))) { + errors.collect(new cdk.ValidationResult("Expected an object, but received: " + JSON.stringify(properties))); + } + return errors.wrap("supplied properties not correct for \\"CfnResourceProps\\""); +} + +// @ts-ignore TS6133 +function convertCfnResourcePropsToCloudFormation(properties: any): any { + if (!cdk.canInspect(properties)) return properties; + CfnResourcePropsValidator(properties).assertSuccess(); + return {}; +} + +// @ts-ignore TS6133 +function CfnResourcePropsFromCloudFormation(properties: any): cfn_parse.FromCloudFormationResult { + if (cdk.isResolvableObject(properties)) { + return new cfn_parse.FromCloudFormationResult(properties); + } + properties = ((properties == null) ? {} : properties); + if (!(properties && typeof properties == 'object' && !Array.isArray(properties))) { + return new cfn_parse.FromCloudFormationResult(properties); + } + const ret = new cfn_parse.FromCloudFormationPropertyObject(); + ret.addUnrecognizedPropertiesAsExtra(properties); + return ret; +}" +`; + +exports[`resource interface with multiple primaryIdentifiers 1`] = ` +"/* eslint-disable prettier/prettier, @stylistic/max-len */ +import * as cdk from "aws-cdk-lib"; +import * as constructs from "constructs"; +import * as cfn_parse from "aws-cdk-lib/core/lib/helpers-internal"; +import * as cdk_errors from "aws-cdk-lib/core/lib/errors"; + +/** + * Indicates that this resource can be referenced as a Resource. + * + * @stability experimental + */ +export interface IResourceRef extends constructs.IConstruct { + /** + * A reference to a Resource resource. + */ + readonly resourceRef: ResourceReference; +} + +/** + * @cloudformationResource AWS::Some::Resource + * @stability external + * @see http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-some-resource.html + */ +export class CfnResource extends cdk.CfnResource implements cdk.IInspectable, IResourceRef { + /** + * The CloudFormation resource type name for this resource class. + */ + public static readonly CFN_RESOURCE_TYPE_NAME: string = "AWS::Some::Resource"; + + /** + * Build a CfnResource from CloudFormation properties + * + * A factory method that creates a new instance of this class from an object + * containing the CloudFormation properties of this resource. + * Used in the @aws-cdk/cloudformation-include module. + * + * @internal + */ + public static _fromCloudFormation(scope: constructs.Construct, id: string, resourceAttributes: any, options: cfn_parse.FromCloudFormationOptions): CfnResource { + resourceAttributes = resourceAttributes || {}; + const resourceProperties = options.parser.parseValue(resourceAttributes.Properties); + const propsResult = CfnResourcePropsFromCloudFormation(resourceProperties); + if (cdk.isResolvableObject(propsResult.value)) { + throw new cdk_errors.ValidationError("Unexpected IResolvable", scope); + } + const ret = new CfnResource(scope, id, propsResult.value); + for (const [propKey, propVal] of Object.entries(propsResult.extraProperties)) { + ret.addPropertyOverride(propKey, propVal); + } + options.parser.handleAttributes(ret, resourceAttributes, id); + return ret; + } + + /** + * Another identifier of the resource + * + * @cloudformationAttribute Another + */ + public readonly attrAnother: string; + + /** + * The identifier of the resource + * + * @cloudformationAttribute Id + */ + public readonly attrId: string; + + /** + * @param scope Scope in which this resource is defined + * @param id Construct identifier for this resource (unique in its scope) + * @param props Resource properties + */ + public constructor(scope: constructs.Construct, id: string, props: CfnResourceProps = {}) { + super(scope, id, { + "type": CfnResource.CFN_RESOURCE_TYPE_NAME, + "properties": props + }); + + this.attrAnother = cdk.Token.asString(this.getAtt("Another", cdk.ResolutionTypeHint.STRING)); + this.attrId = cdk.Token.asString(this.getAtt("Id", cdk.ResolutionTypeHint.STRING)); + } + + public get resourceRef(): ResourceReference { + return { + "resourceId": cdk.Fn.select(0, cdk.Fn.split("|", this.ref)), + "another": cdk.Fn.select(1, cdk.Fn.split("|", this.ref)) + }; + } + + protected get cfnProperties(): Record { + return {}; + } + + /** + * Examines the CloudFormation resource and discloses attributes + * + * @param inspector tree inspector to collect and process attributes + */ + public inspect(inspector: cdk.TreeInspector): void { + inspector.addAttribute("aws:cdk:cloudformation:type", CfnResource.CFN_RESOURCE_TYPE_NAME); + inspector.addAttribute("aws:cdk:cloudformation:props", this.cfnProperties); + } + + protected renderProperties(props: Record): Record { + return convertCfnResourcePropsToCloudFormation(props); + } +} + +/** + * Properties for defining a \`CfnResource\` + * + * @struct + * @stability external + * @see http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-some-resource.html + */ +export interface CfnResourceProps { + +} + +/** + * A reference to a Resource resource. + * + * @struct + * @stability external + */ +export interface ResourceReference { + /** + * The Id of the Resource resource. + */ + readonly resourceId: string; + + /** + * The Another of the Resource resource. + */ + readonly another: string; +} + +/** + * Determine whether the given properties match those of a \`CfnResourceProps\` + * + * @param properties - the TypeScript properties of a \`CfnResourceProps\` + * + * @returns the result of the validation. + */ +// @ts-ignore TS6133 +function CfnResourcePropsValidator(properties: any): cdk.ValidationResult { + if (!cdk.canInspect(properties)) return cdk.VALIDATION_SUCCESS; + const errors = new cdk.ValidationResults(); + if (!(properties && typeof properties == 'object' && !Array.isArray(properties))) { + errors.collect(new cdk.ValidationResult("Expected an object, but received: " + JSON.stringify(properties))); + } + return errors.wrap("supplied properties not correct for \\"CfnResourceProps\\""); +} + +// @ts-ignore TS6133 +function convertCfnResourcePropsToCloudFormation(properties: any): any { + if (!cdk.canInspect(properties)) return properties; + CfnResourcePropsValidator(properties).assertSuccess(); + return {}; +} + +// @ts-ignore TS6133 +function CfnResourcePropsFromCloudFormation(properties: any): cfn_parse.FromCloudFormationResult { + if (cdk.isResolvableObject(properties)) { + return new cfn_parse.FromCloudFormationResult(properties); + } + properties = ((properties == null) ? {} : properties); + if (!(properties && typeof properties == 'object' && !Array.isArray(properties))) { + return new cfn_parse.FromCloudFormationResult(properties); + } + const ret = new cfn_parse.FromCloudFormationPropertyObject(); + ret.addUnrecognizedPropertiesAsExtra(properties); + return ret; +}" +`; + +exports[`resource with multiple primaryIdentifiers as properties 1`] = ` +"/* eslint-disable prettier/prettier, @stylistic/max-len */ +import * as cdk from "aws-cdk-lib"; +import * as constructs from "constructs"; +import * as cfn_parse from "aws-cdk-lib/core/lib/helpers-internal"; +import * as cdk_errors from "aws-cdk-lib/core/lib/errors"; + +/** + * Indicates that this resource can be referenced as a Resource. + * + * @stability experimental + */ +export interface IResourceRef extends constructs.IConstruct { + /** + * A reference to a Resource resource. + */ + readonly resourceRef: ResourceReference; +} + +/** + * @cloudformationResource AWS::Some::Resource + * @stability external + * @see http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-some-resource.html + */ +export class CfnResource extends cdk.CfnResource implements cdk.IInspectable, IResourceRef { + /** + * The CloudFormation resource type name for this resource class. + */ + public static readonly CFN_RESOURCE_TYPE_NAME: string = "AWS::Some::Resource"; + + /** + * Build a CfnResource from CloudFormation properties + * + * A factory method that creates a new instance of this class from an object + * containing the CloudFormation properties of this resource. + * Used in the @aws-cdk/cloudformation-include module. + * + * @internal + */ + public static _fromCloudFormation(scope: constructs.Construct, id: string, resourceAttributes: any, options: cfn_parse.FromCloudFormationOptions): CfnResource { + resourceAttributes = resourceAttributes || {}; + const resourceProperties = options.parser.parseValue(resourceAttributes.Properties); + const propsResult = CfnResourcePropsFromCloudFormation(resourceProperties); + if (cdk.isResolvableObject(propsResult.value)) { + throw new cdk_errors.ValidationError("Unexpected IResolvable", scope); + } + const ret = new CfnResource(scope, id, propsResult.value); + for (const [propKey, propVal] of Object.entries(propsResult.extraProperties)) { + ret.addPropertyOverride(propKey, propVal); + } + options.parser.handleAttributes(ret, resourceAttributes, id); + return ret; + } + + /** + * Another identifier of the resource. + */ + public anotherId?: string; + + /** + * The identifier of the resource. + */ + public id?: string; + + /** + * @param scope Scope in which this resource is defined + * @param id Construct identifier for this resource (unique in its scope) + * @param props Resource properties + */ + public constructor(scope: constructs.Construct, id: string, props: CfnResourceProps = {}) { + super(scope, id, { + "type": CfnResource.CFN_RESOURCE_TYPE_NAME, + "properties": props + }); + + this.anotherId = props.anotherId; + this.id = props.id; + } + + public get resourceRef(): ResourceReference { + return { + "resourceId": cdk.Fn.select(0, cdk.Fn.split("|", this.ref)), + "anotherId": cdk.Fn.select(1, cdk.Fn.split("|", this.ref)) + }; + } + + protected get cfnProperties(): Record { + return { + "anotherId": this.anotherId, + "id": this.id + }; + } + + /** + * Examines the CloudFormation resource and discloses attributes + * + * @param inspector tree inspector to collect and process attributes + */ + public inspect(inspector: cdk.TreeInspector): void { + inspector.addAttribute("aws:cdk:cloudformation:type", CfnResource.CFN_RESOURCE_TYPE_NAME); + inspector.addAttribute("aws:cdk:cloudformation:props", this.cfnProperties); + } + + protected renderProperties(props: Record): Record { + return convertCfnResourcePropsToCloudFormation(props); + } +} + +/** + * Properties for defining a \`CfnResource\` + * + * @struct + * @stability external + * @see http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-some-resource.html + */ +export interface CfnResourceProps { + /** + * Another identifier of the resource. + * + * @see http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-some-resource.html#cfn-some-resource-anotherid + */ + readonly anotherId?: string; + + /** + * The identifier of the resource. + * + * @see http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-some-resource.html#cfn-some-resource-id + */ + readonly id?: string; +} + +/** + * A reference to a Resource resource. + * + * @struct + * @stability external + */ +export interface ResourceReference { + /** + * The Id of the Resource resource. + */ + readonly resourceId: string; + + /** + * The AnotherId of the Resource resource. + */ + readonly anotherId: string; +} + +/** + * Determine whether the given properties match those of a \`CfnResourceProps\` + * + * @param properties - the TypeScript properties of a \`CfnResourceProps\` + * + * @returns the result of the validation. + */ +// @ts-ignore TS6133 +function CfnResourcePropsValidator(properties: any): cdk.ValidationResult { + if (!cdk.canInspect(properties)) return cdk.VALIDATION_SUCCESS; + const errors = new cdk.ValidationResults(); + if (!(properties && typeof properties == 'object' && !Array.isArray(properties))) { + errors.collect(new cdk.ValidationResult("Expected an object, but received: " + JSON.stringify(properties))); + } + errors.collect(cdk.propertyValidator("anotherId", cdk.validateString)(properties.anotherId)); + errors.collect(cdk.propertyValidator("id", cdk.validateString)(properties.id)); + return errors.wrap("supplied properties not correct for \\"CfnResourceProps\\""); +} + +// @ts-ignore TS6133 +function convertCfnResourcePropsToCloudFormation(properties: any): any { + if (!cdk.canInspect(properties)) return properties; + CfnResourcePropsValidator(properties).assertSuccess(); + return { + "AnotherId": cdk.stringToCloudFormation(properties.anotherId), + "Id": cdk.stringToCloudFormation(properties.id) + }; +} + +// @ts-ignore TS6133 +function CfnResourcePropsFromCloudFormation(properties: any): cfn_parse.FromCloudFormationResult { + if (cdk.isResolvableObject(properties)) { + return new cfn_parse.FromCloudFormationResult(properties); + } + properties = ((properties == null) ? {} : properties); + if (!(properties && typeof properties == 'object' && !Array.isArray(properties))) { + return new cfn_parse.FromCloudFormationResult(properties); + } + const ret = new cfn_parse.FromCloudFormationPropertyObject(); + ret.addPropertyResult("anotherId", "AnotherId", (properties.AnotherId != null ? cfn_parse.FromCloudFormation.getString(properties.AnotherId) : undefined)); + ret.addPropertyResult("id", "Id", (properties.Id != null ? cfn_parse.FromCloudFormation.getString(properties.Id) : undefined)); + ret.addUnrecognizedPropertiesAsExtra(properties); + return ret; +}" +`; + +exports[`resource with optional primary identifier gets property from ref 1`] = ` +"/* eslint-disable prettier/prettier, @stylistic/max-len */ +import * as cdk from "aws-cdk-lib"; +import * as constructs from "constructs"; +import * as cfn_parse from "aws-cdk-lib/core/lib/helpers-internal"; +import * as cdk_errors from "aws-cdk-lib/core/lib/errors"; + +/** + * Indicates that this resource can be referenced as a Resource. + * + * @stability experimental + */ +export interface IResourceRef extends constructs.IConstruct { + /** + * A reference to a Resource resource. + */ + readonly resourceRef: ResourceReference; +} + +/** + * @cloudformationResource AWS::Some::Resource + * @stability external + * @see http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-some-resource.html + */ +export class CfnResource extends cdk.CfnResource implements cdk.IInspectable, IResourceRef { + /** + * The CloudFormation resource type name for this resource class. + */ + public static readonly CFN_RESOURCE_TYPE_NAME: string = "AWS::Some::Resource"; + + /** + * Build a CfnResource from CloudFormation properties + * + * A factory method that creates a new instance of this class from an object + * containing the CloudFormation properties of this resource. + * Used in the @aws-cdk/cloudformation-include module. + * + * @internal + */ + public static _fromCloudFormation(scope: constructs.Construct, id: string, resourceAttributes: any, options: cfn_parse.FromCloudFormationOptions): CfnResource { + resourceAttributes = resourceAttributes || {}; + const resourceProperties = options.parser.parseValue(resourceAttributes.Properties); + const propsResult = CfnResourcePropsFromCloudFormation(resourceProperties); + if (cdk.isResolvableObject(propsResult.value)) { + throw new cdk_errors.ValidationError("Unexpected IResolvable", scope); + } + const ret = new CfnResource(scope, id, propsResult.value); + for (const [propKey, propVal] of Object.entries(propsResult.extraProperties)) { + ret.addPropertyOverride(propKey, propVal); + } + options.parser.handleAttributes(ret, resourceAttributes, id); + return ret; + } + + /** + * The identifier of the resource. + */ + public id?: string; + + /** + * @param scope Scope in which this resource is defined + * @param id Construct identifier for this resource (unique in its scope) + * @param props Resource properties + */ + public constructor(scope: constructs.Construct, id: string, props: CfnResourceProps = {}) { + super(scope, id, { + "type": CfnResource.CFN_RESOURCE_TYPE_NAME, + "properties": props + }); + + this.id = props.id; + } + + public get resourceRef(): ResourceReference { + return { + "resourceId": this.ref + }; + } + + protected get cfnProperties(): Record { + return { + "id": this.id + }; + } + + /** + * Examines the CloudFormation resource and discloses attributes + * + * @param inspector tree inspector to collect and process attributes + */ + public inspect(inspector: cdk.TreeInspector): void { + inspector.addAttribute("aws:cdk:cloudformation:type", CfnResource.CFN_RESOURCE_TYPE_NAME); + inspector.addAttribute("aws:cdk:cloudformation:props", this.cfnProperties); + } + + protected renderProperties(props: Record): Record { + return convertCfnResourcePropsToCloudFormation(props); + } +} + +/** + * Properties for defining a \`CfnResource\` + * + * @struct + * @stability external + * @see http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-some-resource.html + */ +export interface CfnResourceProps { + /** + * The identifier of the resource. + * + * @see http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-some-resource.html#cfn-some-resource-id + */ + readonly id?: string; +} + +/** + * A reference to a Resource resource. + * + * @struct + * @stability external + */ +export interface ResourceReference { + /** + * The Id of the Resource resource. + */ + readonly resourceId: string; +} + +/** + * Determine whether the given properties match those of a \`CfnResourceProps\` + * + * @param properties - the TypeScript properties of a \`CfnResourceProps\` + * + * @returns the result of the validation. + */ +// @ts-ignore TS6133 +function CfnResourcePropsValidator(properties: any): cdk.ValidationResult { + if (!cdk.canInspect(properties)) return cdk.VALIDATION_SUCCESS; + const errors = new cdk.ValidationResults(); + if (!(properties && typeof properties == 'object' && !Array.isArray(properties))) { + errors.collect(new cdk.ValidationResult("Expected an object, but received: " + JSON.stringify(properties))); + } + errors.collect(cdk.propertyValidator("id", cdk.validateString)(properties.id)); + return errors.wrap("supplied properties not correct for \\"CfnResourceProps\\""); +} + +// @ts-ignore TS6133 +function convertCfnResourcePropsToCloudFormation(properties: any): any { + if (!cdk.canInspect(properties)) return properties; + CfnResourcePropsValidator(properties).assertSuccess(); + return { + "Id": cdk.stringToCloudFormation(properties.id) + }; +} + +// @ts-ignore TS6133 +function CfnResourcePropsFromCloudFormation(properties: any): cfn_parse.FromCloudFormationResult { + if (cdk.isResolvableObject(properties)) { + return new cfn_parse.FromCloudFormationResult(properties); + } + properties = ((properties == null) ? {} : properties); + if (!(properties && typeof properties == 'object' && !Array.isArray(properties))) { + return new cfn_parse.FromCloudFormationResult(properties); + } + const ret = new cfn_parse.FromCloudFormationPropertyObject(); + ret.addPropertyResult("id", "Id", (properties.Id != null ? cfn_parse.FromCloudFormation.getString(properties.Id) : undefined)); + ret.addUnrecognizedPropertiesAsExtra(properties); + return ret; +}" +`; diff --git a/tools/@aws-cdk/spec2cdk/test/conventions.test.ts b/tools/@aws-cdk/spec2cdk/test/conventions.test.ts new file mode 100644 index 0000000000000..8ddb28d7a69c4 --- /dev/null +++ b/tools/@aws-cdk/spec2cdk/test/conventions.test.ts @@ -0,0 +1,13 @@ +import { camelcasedResourceName } from '../lib/naming'; + +test('test the ref-naminification of OIDCProvider', () => { + const name = camelcasedResourceName({ + name: 'OIDCProvider', + cloudFormationType: 'AWS::IAM::OIDCProvider', + attributes: {}, + properties: {}, + $id: '', + }); + + expect(name).toEqual('oidcProvider'); +}); diff --git a/tools/@aws-cdk/spec2cdk/test/resources.test.ts b/tools/@aws-cdk/spec2cdk/test/resources.test.ts new file mode 100644 index 0000000000000..ca0d00b771f75 --- /dev/null +++ b/tools/@aws-cdk/spec2cdk/test/resources.test.ts @@ -0,0 +1,273 @@ +import { Service, SpecDatabase, emptyDatabase } from '@aws-cdk/service-spec-types'; +import { TypeScriptRenderer } from '@cdklabs/typewriter'; +import { AstBuilder } from '../lib/cdk/ast'; + +const renderer = new TypeScriptRenderer(); +let db: SpecDatabase; +let service: Service; + +beforeEach(async () => { + db = emptyDatabase(); + + service = db.allocate('service', { + name: 'aws-some', + shortName: 'some', + capitalized: 'Some', + cloudFormationNamespace: 'AWS::Some', + }); +}); + +test('resource interface when primaryIdentifier is a property', () => { + // GIVEN + const resource = db.allocate('resource', { + name: 'Resource', + primaryIdentifier: ['Id'], + attributes: {}, + properties: { + Id: { + type: { type: 'string' }, + documentation: 'The identifier of the resource', + }, + }, + cloudFormationType: 'AWS::Some::Resource', + }); + db.link('hasResource', service, resource); + + // THEN + const foundResource = db.lookup('resource', 'cloudFormationType', 'equals', 'AWS::Some::Resource').only(); + + const ast = AstBuilder.forResource(foundResource, { db }); + + const rendered = renderer.render(ast.module); + + expect(rendered).toMatchSnapshot(); +}); + +test('resource with optional primary identifier gets property from ref', () => { + // GIVEN + const resource = db.allocate('resource', { + name: 'Resource', + primaryIdentifier: ['Id'], + attributes: {}, + properties: { + Id: { + type: { type: 'string' }, + documentation: 'The identifier of the resource', + }, + }, + cloudFormationType: 'AWS::Some::Resource', + }); + db.link('hasResource', service, resource); + + // THEN + const foundResource = db.lookup('resource', 'cloudFormationType', 'equals', 'AWS::Some::Resource').only(); + + const ast = AstBuilder.forResource(foundResource, { db }); + + const rendered = renderer.render(ast.module); + + expect(rendered).toMatchSnapshot(); +}); + +test('resource with multiple primaryIdentifiers as properties', () => { + // GIVEN + const resource = db.allocate('resource', { + name: 'Resource', + primaryIdentifier: ['Id', 'AnotherId'], + attributes: {}, + properties: { + Id: { + type: { type: 'string' }, + documentation: 'The identifier of the resource', + }, + AnotherId: { + type: { type: 'string' }, + documentation: 'Another identifier of the resource', + }, + }, + cloudFormationType: 'AWS::Some::Resource', + }); + db.link('hasResource', service, resource); + + // THEN + const foundResource = db.lookup('resource', 'cloudFormationType', 'equals', 'AWS::Some::Resource').only(); + + const ast = AstBuilder.forResource(foundResource, { db }); + + const rendered = renderer.render(ast.module); + + expect(rendered).toMatchSnapshot(); +}); + +test('resource interface when primaryIdentifier is an attribute', () => { + // GIVEN + const resource = db.allocate('resource', { + name: 'Resource', + primaryIdentifier: ['Id'], + properties: {}, + attributes: { + Id: { + type: { type: 'string' }, + documentation: 'The identifier of the resource', + }, + }, + cloudFormationType: 'AWS::Some::Resource', + }); + db.link('hasResource', service, resource); + + // THEN + const foundResource = db.lookup('resource', 'cloudFormationType', 'equals', 'AWS::Some::Resource').only(); + + const ast = AstBuilder.forResource(foundResource, { db }); + + const rendered = renderer.render(ast.module); + + expect(rendered).toMatchSnapshot(); +}); + +test('resource interface with multiple primaryIdentifiers', () => { + // GIVEN + const resource = db.allocate('resource', { + name: 'Resource', + primaryIdentifier: ['Id', 'Another'], + properties: {}, + attributes: { + Id: { + type: { type: 'string' }, + documentation: 'The identifier of the resource', + }, + Another: { + type: { type: 'string' }, + documentation: 'Another identifier of the resource', + }, + }, + cloudFormationType: 'AWS::Some::Resource', + }); + db.link('hasResource', service, resource); + + // THEN + const foundResource = db.lookup('resource', 'cloudFormationType', 'equals', 'AWS::Some::Resource').only(); + + const ast = AstBuilder.forResource(foundResource, { db }); + + const rendered = renderer.render(ast.module); + + expect(rendered).toMatchSnapshot(); +}); + +test('resource interface with "Arn"', () => { + // GIVEN + const resource = db.allocate('resource', { + name: 'Resource', + primaryIdentifier: ['Id'], + properties: {}, + attributes: { + Id: { + type: { type: 'string' }, + documentation: 'The identifier of the resource', + }, + Arn: { + type: { type: 'string' }, + documentation: 'The arn for the resource', + }, + }, + cloudFormationType: 'AWS::Some::Resource', + }); + db.link('hasResource', service, resource); + + // THEN + const foundResource = db.lookup('resource', 'cloudFormationType', 'equals', 'AWS::Some::Resource').only(); + + const ast = AstBuilder.forResource(foundResource, { db }); + + const rendered = renderer.render(ast.module); + + expect(rendered).toMatchSnapshot(); +}); + +test('resource interface with "Arn"', () => { + // GIVEN + const resource = db.allocate('resource', { + name: 'Something', + primaryIdentifier: ['Id'], + properties: {}, + attributes: { + Id: { + type: { type: 'string' }, + documentation: 'The identifier of the resource', + }, + SomethingArn: { + type: { type: 'string' }, + documentation: 'The arn for something', + }, + }, + cloudFormationType: 'AWS::Some::Something', + }); + db.link('hasResource', service, resource); + + // THEN + const foundResource = db.lookup('resource', 'cloudFormationType', 'equals', 'AWS::Some::Something').only(); + + const ast = AstBuilder.forResource(foundResource, { db }); + + const rendered = renderer.render(ast.module); + + expect(rendered).toMatchSnapshot(); +}); + +test('resource interface with Arn as a property and not a primaryIdentifier', () => { + // GIVEN + const resource = db.allocate('resource', { + name: 'Resource', + primaryIdentifier: ['Id'], + properties: { + Arn: { + type: { type: 'string' }, + documentation: 'The arn for the resource', + }, + }, + attributes: { + Id: { + type: { type: 'string' }, + documentation: 'The identifier of the resource', + }, + }, + cloudFormationType: 'AWS::Some::Resource', + }); + db.link('hasResource', service, resource); + + // THEN + const foundResource = db.lookup('resource', 'cloudFormationType', 'equals', 'AWS::Some::Resource').only(); + + const ast = AstBuilder.forResource(foundResource, { db }); + + const rendered = renderer.render(ast.module); + + expect(rendered).toMatchSnapshot(); +}); + +test('resource interface with Arn as primaryIdentifier', () => { + // GIVEN + const resource = db.allocate('resource', { + name: 'Resource', + primaryIdentifier: ['Arn'], + properties: {}, + attributes: { + Arn: { + type: { type: 'string' }, + documentation: 'The arn of the resource', + }, + }, + cloudFormationType: 'AWS::Some::Resource', + }); + db.link('hasResource', service, resource); + + // THEN + const foundResource = db.lookup('resource', 'cloudFormationType', 'equals', 'AWS::Some::Resource').only(); + + const ast = AstBuilder.forResource(foundResource, { db }); + + const rendered = renderer.render(ast.module); + + expect(rendered).toMatchSnapshot(); +});