Reputation: 359
When creating a WebAcl in CDK and letting CDK generate the name, I want to use that generated name as a variable in CDK, i.e. when generating the WebAcl like this (no explicit name set in the properties) ...
const webAcl = new CfnWebACL(this, "webAcl", {
defaultAction: {
allow: {},
},
scope: myScope,
visibilityConfig: {
cloudWatchMetricsEnabled: true,
metricName: "webACL",
sampledRequestsEnabled: true,
}
});
... the webAcl will have a generated Name like webAcl-7xtQ0oTU473X
after deployment (the id with an appended hash). The problem is that I do not know how to reference this name as a variable in CDK.
I found the following possibilities to get some type of ids/names but none of them resolve to webAcl-7xtQ0oTU473X
after deployment (some, however, include the value I want):
webAcl.name // is undefined in CDK
webAcl.logicalId // resolves to 'webAcl' (no hash)
webAcl.attrId // resolves to the id
webAcl.attrLabelNamespace // resolves to 'awswaf:<accountNumber>:webacl:webACL-7xtQ0oTU473X:'
webAcl.attrArn // resolves to the full Arn
Names.uniqueId(webAcl) // resolves to '<stackName><nestedStackName>webACL<someOtherHash>'
Is there some other way to get the desired value as a variable?
Upvotes: 0
Views: 1419
Reputation: 3780
L1 constructs are exactly the resources defined by AWS CloudFormation—no more, no less. You must provide the resource's required configuration yourself.
From the Developer Guide
The library does not define any values for us but uses only the values we have provided. So, for example, if we do not set any value for the Name
, then there is no value in the synthesized template.
Below is our resource definition from the synthesized template.
Resources:
webAcl:
Type: AWS::WAFv2::WebACL
Properties:
DefaultAction:
Allow: {}
Scope: my-scope
VisibilityConfig:
CloudWatchMetricsEnabled: true
MetricName: webACL
SampledRequestsEnabled: true
The resource definition does not have the Name
property. CloudFormation generates the property when it deploys the template.
There is no way to access properties we have not set. Okay, without hacks.
In the CloudFormation template, we can use the resource's return values. Please, find the list in the CloudFormation user guide for the WebACL. Every resource has this section.
We can access the values in the CDK script as tokens.
console.log({
ref: webAcl.ref,
attrArn: webAcl.attrArn,
attrCapacity: webAcl.attrCapacity,
attrId: webAcl.attrId,
attrLabelNamespace: webAcl.attrLabelNamespace
})
And these are the only values generated by the CloudFormation during the deployment, which we can access in our script. The CDK library maps them nicely to the CloudFormation GetAtt
, or Ref
function calls in the synthesized template.
For example, we get this for the webAcl.attrCapacity
. I have no idea why it looks weird in the console log.
Fn::GetAtt:
- webAcl
- Capacity
We can use intrinsic functions to extract our value from the resource's values.
Fn.select(3, Fn.split(':', webAcl.attrLabelNamespace))
In the synthesized template, we get the chain of function calls.
Fn::Select:
- 3
- Fn::Split:
- ":"
- Fn::GetAtt:
- webAcl
- LabelNamespace
This looks fragile to me, but it might work. Please keep in mind that you can not apply any JavaScript operations to this value but use it as a construct property only. Because the CDK substitutes it with function calls in the template.
I use the following code to test the answer.
import { App, Fn, Stack } from 'aws-cdk-lib'
import { env } from 'process'
import { CfnBucket } from 'aws-cdk-lib/aws-s3'
import { CfnWebACL } from 'aws-cdk-lib/aws-wafv2'
function createStack (scope, id, props) {
const stack = new Stack(scope, id, props)
const webAcl = new CfnWebACL(stack, 'webAcl', {
defaultAction: {
allow: {},
},
scope: 'my-scope',
visibilityConfig: {
cloudWatchMetricsEnabled: true,
metricName: 'webACL',
sampledRequestsEnabled: true,
}
})
new CfnBucket(stack, 'bucket', {
bucketName: Fn.select(3, Fn.split(':', webAcl.attrLabelNamespace))
})
console.log({
ref: webAcl.ref,
attrArn: webAcl.attrArn,
attrCapacity: webAcl.attrCapacity,
attrId: webAcl.attrId,
attrLabelNamespace: webAcl.attrLabelNamespace
})
return stack
}
const app = new App()
createStack(app, 'WebAclName', {
env: { account: env.CDK_DEFAULT_ACCOUNT, region: env.CDK_DEFAULT_REGION }
})
Upvotes: 4