Open branthebuidler opened 3 years ago
Hey @avivsugarman,
unfortunately, I'm not sure what you're trying to do here is supported by CDK Pipelines. The Fn::GetArtifactAtt
expression can only be used inside the parameterOverrides
property of a CloudFormation Stack deployment CodePipeline Action, but CDK Pipelines does not allow you to set the parameters on the Stack level - it operates on the Stage level.
Paging in @rix0rrr to answer the question, as he knows much more about CDK Pipelines than I do.
Relabeling this as a feature request because this doesn't look possible to me either. Let's see what @rix0rrr says
Since you know the build number you need in the build stage, couldn't you just use the build number directly in your CDK app? The generated templates will then contain that value at deploy time.
@rix0rrr Yes, that is the workaround we went with, and it is a reasonable solution for this specific use case even though ideally we would base our build ID off of the commit hash and not an arbitrary build time ID.
I think this should still be kept as an open feature request, as the ability to share pipeline data with an application stage (either via env vars or parameter overrides or some other way) will probably prove useful in other use cases.
This issue has not received any attention in 1 year. If you want to keep this issue open, please leave a comment below and auto-close will be canceled.
Hello, is there any update on this issue? This seems to be a common problem - wanting to export variable generated in CodeBuild Action to the next stage. If this feature request is not in the works, could someone please clearly post the workaround in the AWS docs or here?
In my case, I generate an artifact in CodeBuild Action and I want to access that artifact's name during the Cloudformation stack deployment, which is part of next stage in the pipeline. What would be the workaround for that? Static naming of the artifact doesn't work because Cloudformation doesn't detect any changes and doesn't produce a change-set. Any guidance would be much appreciated.
Found a workaround - used AwsCustomResource
to fetch artifact information i needed in the Cloudformation stack and used it to update the lambda function.
CDK+Codepipeline really needs to solve this issue to help the engineers.
Found a workaround - used
AwsCustomResource
to fetch artifact information i needed in the Cloudformation stack and used it to update the lambda function. CDK+Codepipeline really needs to solve this issue to help the engineers.
@vinibartling mind sharing the CR you used as a workaround? I agree its not ideal, but it may provide a good starting point for a contributor who wants to take on this issue
Sure @pradoz, here's the workaround I have in place.
It is a 3 step process, with some conventions in place:
During Source build stage in the pipeline, when I package my lambda, I upload it to a common artifacts S3 versioned bucket with the same name everytime. For example: foo-service --> foo-service.zip. Since the bucket is versioned, every new upload updates the version of this file. In my CDK codebase, the "FOO" stack that needs this artifact is aware of this artifact-filename (convention) and S3 bucket name.
In DEV stage, I have a custom resource to pull the versionId of foo-service.zip lambda package from S3 bucket and also upload this versionId to SSM, so I can use the same version in PROD stage. I use static fromBucket(bucket: s3.IBucket, key: string, objectVersion?: string): S3Code
from Code
('aws-cdk-lib/aws-lambda') for generating LambdaFunction.
const lambdaAttr = {
functionNameSuffix: 'foo-service',
customResourceName: 'foo-service-get-version-id',
artifactFilename: 'foo-service.zip'
};
getVersionId(lambdaAttr: LambdaAttributes, ssmRole: Role) {
const paramName = `${lambdaAttr.functionNameSuffix}-version`;
if (this.stage == Stage.DEV) {
const customResource = this.generateCustomResourceToReadS3(
lambdaAttr.customResourceName,
lambdaAttr.artifactFilename,
);
const versionId = customResource.getResponseField('VersionId');
// store versionId in ssm
const ssmParameter = new StringParameter(
this.stack,
`${lambdaAttr.functionNameSuffix}-version`,
{
parameterName: paramName,
stringValue: versionId,
},
);
ssmParameter.grantRead(ssmRole);
return versionId;
}
// read from parameter from ssm to ensure version was written correctly
return this.generateCustomResourceToReadSSM(
`${lambdaAttr.functionNameSuffix}-read-version`,
paramName,
);
}
generateCustomResourceToReadS3(id: string, key: string) {
return new cr.AwsCustomResource(this.stack, id, {
onUpdate: {
// will also be called for a CREATE event
service: 'S3',
action: 'headObject',
parameters: {
Bucket: ARTIFACT_BUCKET_NAME,
Key: key,
},
physicalResourceId: cr.PhysicalResourceId.of(Date.now().toString()), // Update physical id to always fetch the latest version
},
policy: cr.AwsCustomResourcePolicy.fromStatements([
new PolicyStatement({
effect: Effect.ALLOW,
actions: ['s3:*'],
resources: ['*'],
}),
]),
removalPolicy: cdk.RemovalPolicy.DESTROY,
});
}
// Access for PROD to read From DEV account's SSM
public grantReadAccessToSSM() {
return new Role(this.stack, `${this.appPrefix}-ssmReadAccessToProd`, {
assumedBy: new AccountPrincipal(PROD),
roleName: PROD_ASSUMED_ROLENAME_TO_READ_SSM(this.appPrefix),
});
}
generateCustomResourceToReadSSM(id: string, paramName: string) {
const resource = new cr.AwsCustomResource(this.stack, id, {
onUpdate: {
service: 'SSM',
action: 'getParameter',
parameters: {
Name: paramName,
},
physicalResourceId: cr.PhysicalResourceId.of(Date.now().toString()), // Update physical id to always fetch the latest version
assumedRoleArn: PROD_ASSUMED_ROLEARN(this.appPrefix),
},
policy: cr.AwsCustomResourcePolicy.fromStatements([
new PolicyStatement({
effect: Effect.ALLOW,
actions: ['ssm:*'],
resources: ['*'],
}),
new PolicyStatement({
effect: Effect.ALLOW,
actions: ['sts:*'],
resources: ['*'],
}),
]),
});
return resource.getResponseField('Parameter.Value');
}
Hope this helps. Please feel free to reach out with anything else I can help.
I have a Codepipelines based project (v. 1.114.0).
Use case:
I need to deploy an application stack to multiple environments, and want to input a build number that is the output of the build stage in the pipeline. The build number references the docker build in ECR that I want the application stack to use. To this end, I am attempting to pass the build number parameter generated at build time in a CodeBuild stage to the ApplicationStage deployment in Cloudformation.
Code:
I am seeing errors in CFN when attempting to deploy the application stack:
I have already tried to pass this information using Codepipelines variables that I exported from CodeBuild, but with no luck (CFN is unable to resolve variable references cross stage it seems). Additionally, the cdk.Stage construct does not support parameter overrides as far as I can tell.
Any recommendations for how to pull this off? It seems like there is not support for this use case.
P.S. Would prefer not to use Parameter Store as I have multiple deployment stages and want to ensure the specific build output is passed to the correct stage at the correct time. While this is likely doable with the Parameter Store, it introduces unnecessary complexity.