Last active
August 9, 2023 11:06
-
-
Save ottokruse/41694de3831d6bfe9080743b352aa2fe to your computer and use it in GitHub Desktop.
Script to publish CDK assets (e.g. Lambda function code) to S3 and generate parameter files, so you can combine cdk synth with CloudFormation deployments. This is essentially the equivalent of 'sam package' but then for CDK. Tested to work for Lambda and S3-deployments
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env ts-node | |
// This script uploads your assets to the CDK staging bucket in S3 (just as cdk deploy would) | |
// and writes out two files: | |
// - parameters.ini to use in CLI deployments (see instructions below) | |
// - parameters.json to use in AWS CodePipeline for CloudFormation deployments | |
// | |
// Installation instructions: | |
// - Save this script cdk-package.ts to the root of your CDK repo (i.e. next to cdk.json) and make it executable | |
// - Install script dependencies: npm install jsonpath aws-sdk adm-zip @types/jsonpath @types/adm-zip | |
// | |
// Run instructions for CLI deployments: | |
// 1) cdk synth > template.yml | |
// 2) AWS_REGION=eu-west-1 ./cdk-package.ts | |
// 3) aws cloudformation deploy \ | |
// --stack-name <stack-name> \ | |
// --template-file template.yml \ | |
// --parameter-overrides $(cat parameters.ini) \ | |
// --capabilities CAPABILITY_IAM | |
import { promises as fs } from "fs"; | |
import { join } from "path"; | |
import * as jp from "jsonpath"; | |
import { S3, CloudFormation } from "aws-sdk"; | |
import * as Zip from "adm-zip"; | |
const CFNCLIENT = new CloudFormation(); | |
const S3CLIENT = new S3(); | |
async function main() { | |
// Determine toolkit stack name, that stack will have an Output revealing the staging bucket | |
const { toolkitStackName = 'CDKToolkit' } = JSON.parse( | |
await fs.readFile(join(__dirname, "cdk.json"), "utf8") | |
); | |
const { Stacks } = await CFNCLIENT.describeStacks({ | |
StackName: toolkitStackName, | |
}).promise(); | |
if (!Stacks || !Stacks.length) { | |
throw new Error(`Toolkit stack ${toolkitStackName} does not exist, please run: cdk bootstrap`); | |
} | |
const stagingBucket = Stacks[0].Outputs!.find( | |
(out) => out.OutputKey === "BucketName" | |
)!.OutputValue!; | |
console.log(`Will use staging bucket ${stagingBucket}`); | |
// Parse manifest file, locate assets and upload them to S3 staging bucket | |
const manifest = JSON.parse( | |
await fs.readFile(join(__dirname, "cdk.out", "manifest.json"), "utf8") | |
); | |
const assets = jp.query(manifest, `$..[?(@.type=='aws:cdk:asset')]`); | |
const publishPromises = assets.map(async (asset) => { | |
let assetBody: Buffer; | |
let s3key: string; | |
if (!asset.data.path.endsWith('.zip')) { | |
const lambdaZip = new Zip(); | |
lambdaZip.addLocalFolder(join(__dirname, "cdk.out", asset.data.path)); | |
s3key = `assets/${asset.data.id}.zip`; | |
assetBody = lambdaZip.toBuffer(); | |
} else { | |
assetBody = await fs.readFile(join(__dirname, "cdk.out", asset.data.path)); | |
s3key = `assets/${asset.data.id}`; | |
} | |
await S3CLIENT.putObject({ | |
Key: s3key, | |
Bucket: stagingBucket, | |
Body: assetBody, | |
}).promise(); | |
console.log(`Uploaded asset ${asset.data.id} (${asset.data.path})`); | |
return { | |
[asset.data.s3BucketParameter]: stagingBucket, | |
[asset.data.s3KeyParameter]: `${s3key}||`, | |
[asset.data.artifactHashParameter]: asset.data.sourceHash, | |
}; | |
}); | |
// Write out parameter files for deployments | |
const cfnParameters = await Promise.all(publishPromises); | |
const cfnParametersFlat = cfnParameters.reduce( | |
(reduced, params) => Object.assign(reduced, params), | |
{} | |
); | |
await fs.writeFile( | |
"parameters.ini", | |
Object.entries(cfnParametersFlat) | |
.map(([k, v]) => `${k}=${v}`) | |
.join("\n") | |
); | |
await fs.writeFile( | |
"parameters.json", | |
JSON.stringify(cfnParametersFlat, null, 2) | |
); | |
} | |
main().catch((err) => { | |
console.error(err); | |
process.exit(1); | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment