I'm modifying the code from this workshop to do cross-account rather than cross-region. https://cdk-advanced.workshop.aws/start.html
The first thing I did was install and configure cdk-assume-role-credential-plugin and bootstrapped.
In the workshop they use a AwsCustomResource to get around the cross-region limitation of StringParameter.valueFromLookup.
static valueFromLookup(scope, parameterName) Requires that the stack this scope is defined in will have explicit account/region information. Otherwise, it will fail during synthesis.Using an AwsCustomResource allows them to override the region from stack.
const targetKeyLookupCR = new cr.AwsCustomResource(this, 'TargetKeyLookup', {
onUpdate: { // will also be called for a CREATE event
service: 'SSM',
action: 'getParameter',
parameters: {
Name: props.targetKeyIdSsmParameterName
},
region: props.targetRegion,
physicalResourceId: cr.PhysicalResourceId.of(Date.now().toString())
},
policy: cr.AwsCustomResourcePolicy.fromSdkCalls({resources: [parameterArn]})
});
Unfortunately you cannot override account of stack in a similar fashion. Adding an assumedRole does not help either. It always looks for parameter in stack's account.
I tried to use SecretsManager & AwsCustomResource to get around that cross-account limitation:
const targetKeyLookupCR = new cr.AwsCustomResource(this, 'TargetKeyLookup', {
onUpdate: {
service: 'SecretsManager',
action: 'getSecretValue',
parameters: {
SecretId: props.targetKeyIdSsmParameterName
},
assumedRoleArn: 'arn:aws:iam::111111111111:role/MultiRegionS3CrrKmsCmkXacct',
region: props.targetRegion,
physicalResourceId: cr.PhysicalResourceId.of(Date.now().toString())
},
policy: cr.AwsCustomResourcePolicy.fromSdkCalls({resources: [parameterArn]})
});
const secretString = targetKeyLookupCR.getResponseField(dataPath);
The code above returns a string that looks like this.
"{"password":"/];asdf(5;{ASDF=L%UuVxasDFHg`(:l","MyKeyID":"arn:aws:kms:us-west-1: 111111111111:key/1111a22b-3c44-5ddd-66e7-f8f9999a1111"}"
I can see it in CloudWatch:
2021-08-14T04:34:15.641Z c5ae47f7-1e4a-43a3-a475-d0d16ea7e1be INFO Responding {"Status":"SUCCESS","Reason":"OK",... "SecretString":"{"password":"/];asdf(5;{ASDF=L%UuVxasDFHg`(:l","MyKeyID":"arn:aws:kms:us-west-1: 111111111111:key/1111a22b-3c44-5ddd-66e7-f8f9999a1111}"...}
In the workshop they use the response directly:
role.addToPolicy(new iam.PolicyStatement({
resources: [targetKeyLookupCR.getResponseField('Parameter.Value')],
actions: ['kms:Encrypt'] }));
To get the Value of MyKeyID, I've tried...
JSON.parse and splitting the string. No matter what I try ends up with an error like 'Unexpected token $ in JSON at position 0' or 'TypeError: Cannot read property 'split' of undefined'
This led me to https://docs.aws.amazon.com/cdk/latest/guide/tokens.html#tokens_json, which I definitely thought was going to be the answer! It was not. More 'Unexpected token $ in JSON at position 0' errors.
const stack = cdk.Stack.of(this);
const secretString = targetKeyLookupCR.getResponseField('SecretString');
const jsonString = stack.toJsonString(secretString);
const jsonObj = JSON.parse(jsonString);
Update: From this https://docs.aws.amazon.com/cdk/api/latest/docs/core-readme.html#cfnjson I got a different error that indicates it has the right string, but just can't parse it properly. Unexpected token p in JSON at position 3 at JSON.parse ().
const secretString = targetKeyLookupCR.getResponseField('SecretString');
const cfnJson = new CfnJson(this, 'cfn-json', {
value: secretString,
});
// @ts-ignore
const myKeyId = cfnJson.MyKeyID || '*';
This is because CfnJson adds quotes around string:
"Value": {
"Fn::Join": [
"",
[
"\"",
{
"Fn::GetAtt": [
"MySourceTargetKeyLookupACF65EAE",
"SecretString"
]
},
"\""
]
]
}
I'm not sure how to proceed. How would I get the ARN from secretString as plain text?