I have been trying to create a deployment pipeline for Lambda function using CodePipeline. During development I noticed there is weird issue which does not allow me to use Fn::GetArtifactAtt in the parameterOverrides (1), because default JSON serializer forces to use Fn::Join which does not accept Fn::GetArtifactAtt.
I see it was noticed in #566, but for 0.22.0 (build 644ebf5) version it does not work for me.
import iam = require('@aws-cdk/aws-iam');
import codepipeline = require('@aws-cdk/aws-codepipeline');
import cloudformation = require('@aws-cdk/aws-cloudformation');
import codepipelineapi = require('@aws-cdk/aws-codepipeline-api');
const addDeployPrepareAction = (
id: string,
buildArtifact: codepipelineapi.Artifact,
props: {
stage: codepipeline.Stage,
stackName: string,
region: string,
changeSetName: string,
role: iam.Role
}
) => {
const { stackName, stage, region, changeSetName, role } = props
new cloudformation.PipelineCreateReplaceChangeSetAction(stage, id, {
stackName,
stage,
region,
role,
changeSetName,
runOrder: 1,
adminPermissions: false,
capabilities: cloudformation.CloudFormationCapabilities.NamedIAM,
parameterOverrides: {
'LambdaCodeZip': buildArtifact.objectKey,
'LambdaCodeBucket': buildArtifact.bucketName,
},
})
}
Expected (one of the possible solution):
ParameterOverrides: !Sub |
{
"LambdaCodeZip" : { "Fn::GetArtifactAtt" : [ "BuildOutput", "ObjectKey" ] },
"LambdaCodeBucket": { "Fn::GetArtifactAtt" : [ "BuildOutput", "BucketName" ] }
}
Actual:
ParameterOverrides:
Fn::Join:
- ""
- - '{"LambdaCodeZip":"'
- Fn::GetArtifactAtt:
- BuildOutput
- ObjectKey
- '","LambdaCodeBucket":"'
- Fn::GetArtifactAtt:
- BuildOutput
- BucketName
- '"}'
Error:
aws cloudformation validate-template --template-body file://pipeline.json
An error occurred (ValidationError) when calling the ValidateTemplate operation: Template Error: Encountered unsupported function: Fn::GetArtifactAtt Supported functions are: [Fn::Base64, Fn::GetAtt, Fn::GetAZs, Fn::ImportValue, Fn::Join, Fn::Split, Fn::FindInMap, Fn::Select, Ref, Fn::Equals, Fn::If, Fn::Not, Condition, Fn::And, Fn::Or, Fn::Contains, Fn::EachMemberEquals, Fn::EachMemberIn, Fn::ValueOf, Fn::ValueOfAll, Fn::RefAll, Fn::Sub, Fn::Cidr]
The problem here seems to be that {Fn::GetArtifactAtt} is modeled as an actual CloudFormation intrinsic, whereas it's supposed to be just opaque JSON in the ParameterOverrides property?
Expected (one of the possible solution):
ParameterOverrides: !Sub |
What does the !Sub still add here? Does it do anything?
cc @skinny85
The problem here seems to be that
{Fn::GetArtifactAtt}is modeled as an actual CloudFormation intrinsic, whereas it's supposed to be just opaque JSON in theParameterOverridesproperty?
Yes, I think so. I guess I will encounter the same problem with Fn::GetParam in ParameterOverrides. Please read more about these two dedicated functions: https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/continuous-delivery-codepipeline-parameter-override-functions.html
Expected (one of the possible solution):
ParameterOverrides: !Sub |What does the
!Substill add here? Does it do anything?
Fn::Sub with some Parameter or using any other function (Fn::GetAtt, Fn::Ref...) inside the ParameterOverrides:ParameterOverrides: !Sub |
{
"LambdaCodeZip" : { "Fn::GetArtifactAtt" : [ "BuildOutput", "ObjectKey" ] },
"LambdaCodeBucket": { "Fn::GetArtifactAtt" : [ "BuildOutput", "BucketName" ] },
"AssetsBucketName": "${AssetsBucket}",
"AssetsBucketDomainName": "${AssetsBucket.DomainName}",
"GitHubRepositoryName": "${GitHubRepositoryName}"
}
For an example I provided in first post !Sub is not required to work, but you have to keep the | pipe character to inform you are starting a multi-line block.
YAML:
ParameterOverrides: |
{
"LambdaCodeZip" : { "Fn::GetArtifactAtt" : [ "BuildOutput", "ObjectKey" ] },
"LambdaCodeBucket": { "Fn::GetArtifactAtt" : [ "BuildOutput", "BucketName" ] }
}
JSON:
{
"ParameterOverrides": "{\n \"LambdaCodeZip\" : { \"Fn::GetArtifactAtt\" : [ \"BuildOutput\", \"ObjectKey\" ] },\n \"LambdaCodeBucket\": { \"Fn::GetArtifactAtt\" : [ \"BuildOutput\", \"BucketName\" ] }\n}\n",
}
Edit: It does not work.
As a workaround we may mock it in the following way:
parameterOverrides: {
'LambdaCodeZip': `{ "Fn::GetArtifactAtt" : [ "${buildArtifact.name}", "ObjectKey" ] }`,
'LambdaCodeBucket': `{ "Fn::GetArtifactAtt" : [ "${buildArtifact.name}", "BucketName" ] }`,
'GitHubRepositoryName': cdk.Fn.sub('${GitHubRepositoryName}'),
},
Output YAML:
ParameterOverrides:
Fn::Join:
- ""
- - '{"LambdaCodeZip":"{ \"Fn::GetArtifactAtt\" : [
\"BuildOutput\", \"ObjectKey\" ]
}","LambdaCodeBucket":"{ \"Fn::GetArtifactAtt\" : [
\"BuildOutput\", \"BucketName\" ]
}","GitHubRepositoryName":"'
- Fn::Sub: ${GitHubRepositoryName}
- '"}'
Output JSON:
{
"ParameterOverrides": {
"Fn::Join": [
"",
[
"{\"LambdaCodeZip\":\"{ \\\"Fn::GetArtifactAtt\\\" : [ \\\"BuildOutput\\\", \\\"ObjectKey\\\" ] }\",\"LambdaCodeBucket\":\"{ \\\"Fn::GetArtifactAtt\\\" : [ \\\"BuildOutput\\\", \\\"BucketName\\\" ] }\",\"GitHubRepositoryName\":\"",
{
"Fn::Sub": "${GitHubRepositoryName}"
},
"\"}"
]
]
},
}
@piotrkubisa thanks for reporting this one, I'm looking into it.
Quick question: did your workaround from this comment work? I does deploy through CFN, however the Pipeline with those Parameter Overrides set fails updating its Stack for me, because of escaping (CFN thinks { "Fn::GetArtifactAtt" : [ "BuildOutput", "BucketName" ] is literally the Bucket name).
Sadly, no it does not work :cry: You are right it will provide { "Fn::GetArtifactAtt" : [ "BuildOutput", "BucketName" ] } as a string value as a parameter.
I figured out what the issue is. I submitted a PR with the fix here.
Thanks!
NP, thanks for reporting the issue!
This seems to be still be happening when not used in the Cfn parameterOverrides property. Given this:
new PipelineProject(scope, `Project`, {
// ...
environmentVariables: {
WEB_API_LOC: {
type: BuildEnvironmentVariableType.PLAINTEXT,
value: JSON.stringify(props.webApiLambdaCode.assign(webApiOutput.s3Location))
}
}
// ...
})
You still get this output:
ProjectD3596EDB:
Type: AWS::CodeBuild::Project
Properties:
Artifacts:
Type: CODEPIPELINE
Environment:
ComputeType: BUILD_GENERAL1_SMALL
EnvironmentVariables:
- Name: WEB_API_LOC
Type: PLAINTEXT
Value:
Fn::Join:
- ""
- - '{"ApiApiFuncLambdaSourceBucketNameParameterB6671E0E":"'
- Fn::GetArtifactAtt:
- WebApiOut
- BucketName
- '","ApiApiFuncLambdaSourceObjectKeyParameter1B2544EC":"'
- Fn::GetArtifactAtt:
- WebApiOut
- ObjectKey
- '"}'
That could be a miss from our side, but I'm 99% sure this won't work anyway (the only place Fn::GetArtifactAtt can be used is inside a CloudFormation CodePipeline action, they are meaningless in a CodeBuild project, and will never get substituted by anything).
I believe what you're looking for is codepipeline.Artifacts.
@skinny85 Thanks for the response. My end goal is to work around this issue about passing more than a few parameters via parameterOverrides, and my thought was to write out the artifact attributes to a templateConfiguration file. It makes sense that Fn::GetArtifactAtt wouldn't be substituted if it came from there though. Can I reference artifact attributes another way?
A couple of things about that:
LambdaSourceBucketNameParameter and LambdaSourceObjectKeyParameter, are just the defaults. You can create your own:lambda.Code.fromCfnParameters({
bucketNameParam: new CfnParameter(this, 'A'),
objectKeyParam: new CfnParameter(this, 'B'),
});
const sourceOutput = new codepipeline.Artifact('S');
Hope this helps!
Adam
I think specifying my own CfnParameters will be good enough for this use case. Thanks! 馃帀
For anyone finding this in the future, I finally got a response from AWS Support regarding the issue:
I've queried the CodePipeline team and searched though the current development workflows and couldn't find any current activity related to increasing the limit for parameters passed to a CloudFormation stack or any alternative method for this action, so we have issued a feature request based on your request for our development team.
I'm not able to provide an estimated time for this feature to be available, but you can follow the release on new features through the CloudFormation (1) and CodePipeline (2) official pages to check when the new feature will be available. Additionally when I receive any update from the feature request ticket I will send a new reply on your case, even if it was closed.
(1) CloudFormation Release History
URL: https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/ReleaseHistory.html
(2) CodePipeline History
URL: https://docs.aws.amazon.com/codepipeline/latest/userguide/history.html
So for now, it looks like the CfnParameter workaround is the best option.
Most helpful comment
I figured out what the issue is. I submitted a PR with the fix here.