Created
May 24, 2022 04:11
-
-
Save sisyphushappy/ba38731cde9655c7db04c4711cce5458 to your computer and use it in GitHub Desktop.
Build a docker image asset, deploy it to ECR, and update a file in a remote GitHub repo with the docker image asset hash
This file contains hidden or 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
| import {Duration, Stack, StackProps} from "aws-cdk-lib"; | |
| import {Construct} from "constructs"; | |
| import {Repository} from "aws-cdk-lib/aws-ecr"; | |
| import {Function, LayerVersion, Runtime} from "aws-cdk-lib/aws-lambda"; | |
| import {ArnPrincipal, Effect, ManagedPolicy, PolicyStatement} from "aws-cdk-lib/aws-iam"; | |
| import {PythonFunction} from "@aws-cdk/aws-lambda-python-alpha"; | |
| import {DockerImageAsset} from "aws-cdk-lib/aws-ecr-assets"; | |
| import * as path from "path"; | |
| import * as ecrdeploy from 'cdk-ecr-deployment'; | |
| import {Secret} from "aws-cdk-lib/aws-secretsmanager"; | |
| import {AwsCustomResource, AwsCustomResourcePolicy, PhysicalResourceId} from "aws-cdk-lib/custom-resources"; | |
| export class DockerStack extends Stack { | |
| private ecrRepository: Repository; | |
| private dockerImageAsset: DockerImageAsset; | |
| private ecrDeployment: ecrdeploy.ECRDeployment; | |
| private updateGitRepoTagFileFunction : Function; | |
| private deploymentTriggerFunction: AwsCustomResource; | |
| constructor(scope: Construct, id: string, props?: StackProps) { | |
| super(scope, id, props); | |
| // create an image repository | |
| this.createImageRepository(); | |
| // create a docker image asset | |
| this.createDockerImageAsset(); | |
| // copy the docker image asset to ecr | |
| this.createEcrDeployment(); | |
| // create a python lambda that clones a target Github repo, updates a file | |
| // with the hash of the docker image asset, and pushes back to the remote | |
| this.createPythonFunction(); | |
| // trigger the python lambda | |
| this.createTriggerFunction(); | |
| } | |
| createImageRepository() { | |
| // Create the repository | |
| this.ecrRepository = new Repository(this, 'ExampleImageRepository', { | |
| repositoryName: 'exaample-image-asset', | |
| lifecycleRules: [{ | |
| description: "Keeps a maximum number of images to minimize storage", | |
| maxImageCount: 10 | |
| }] | |
| }); | |
| const exampleAccountPrincipal = new ArnPrincipal(`arn:aws:iam::<example-aws-account>:root`); | |
| this.ecrRepository.addToResourcePolicy(new PolicyStatement({ | |
| sid: "AllowCrossAccountPull", | |
| effect: Effect.ALLOW, | |
| actions: [ | |
| "ecr:GetDownloadUrlForLayer", | |
| "ecr:BatchCheckLayerAvailability", | |
| "ecr:BatchGetImage" | |
| ], | |
| principals: [ exampleAccountPrincipal ] | |
| })) | |
| this.ecrRepository.grantPull(exampleAccountPrincipal); | |
| } | |
| createDockerImageAsset() { | |
| this.dockerImageAsset = new DockerImageAsset(this, 'DockerImageAsset', { | |
| directory: path.join(__dirname, '../example-app-source-root'), | |
| buildArgs: {}, | |
| invalidation: { | |
| buildArgs: false, | |
| }, | |
| }); | |
| } | |
| createEcrDeployment() { | |
| const dockerImageAssetHash = this.dockerImageAsset.assetHash; | |
| const destinationImageName = `${this.ecrRepository.repositoryUri}:${dockerImageAssetHash}`; | |
| this.ecrDeployment = new ecrdeploy.ECRDeployment(this, 'EcrDeployment', { | |
| src: new ecrdeploy.DockerImageName(this.dockerImageAsset.imageUri), | |
| dest: new ecrdeploy.DockerImageName(destinationImageName), | |
| }); | |
| } | |
| createPythonFunction() { | |
| const githubAccessTokenSecret = Secret.fromSecretNameV2(this, 'GithubAccessTokenSecret', 'github-token-id'); | |
| this.updateGitRepoTagFileFunction = new PythonFunction(this, 'UpdateTagFileFunction', { | |
| entry: path.join(__dirname, '..', 'resources', 'update_github_remote_tag_file_handler'), | |
| runtime: Runtime.PYTHON_3_9, | |
| environment: { | |
| // required environment variables for python script | |
| 'AUTHOR_NAME': 'Alice Smith', | |
| 'AUTHOR_EMAIL': '[email protected]', | |
| 'TAG_FILE_NAME': 'EXAMPLE_TAG_FILE_NAME', | |
| 'TAG_VALUE': this.dockerImageAsset.assetHash, | |
| 'GITHUB_ACCOUNT_NAME': 'example-account-name', | |
| 'GITHUB_REPO_NAME': 'example-cdk-pipeline', | |
| 'GITHUB_USERNAME': 'example-username', | |
| 'GITHUB_ACCESS_TOKEN_SECRET_ARN': githubAccessTokenSecret.secretArn, | |
| 'REGION': 'us-east-1' | |
| }, | |
| timeout: Duration.minutes(15), | |
| // lambda layer adds git to path (see: https://github.com/lambci/git-lambda-layer) | |
| layers: [ LayerVersion.fromLayerVersionArn(this, 'GitLambdaLayer', 'arn:aws:lambda:us-east-2:553035198032:layer:git-lambda2:8') ], | |
| }); | |
| this.updateGitRepoTagFileFunction.role!.addManagedPolicy(ManagedPolicy.fromAwsManagedPolicyName("SecretsManagerReadWrite")); | |
| } | |
| createTriggerFunction() { | |
| this.deploymentTriggerFunction = new AwsCustomResource(this, 'DeploymentTriggerFunctoin', { | |
| policy: AwsCustomResourcePolicy.fromStatements([new PolicyStatement({ | |
| actions: ['lambda:InvokeFunction'], | |
| effect: Effect.ALLOW, | |
| resources: [this.updateGitRepoTagFileFunction.functionArn] | |
| })]), | |
| timeout: Duration.minutes(15), | |
| onCreate: { | |
| service: 'Lambda', | |
| action: 'invoke', | |
| parameters: { | |
| FunctionName: this.updateGitRepoTagFileFunction.functionName, | |
| InvocationType: 'Event' | |
| }, | |
| physicalResourceId: PhysicalResourceId.of(Date.now().toString()), | |
| }, | |
| onUpdate: { | |
| service: 'Lambda', | |
| action: 'invoke', | |
| parameters: { | |
| FunctionName: this.updateGitRepoTagFileFunction.functionName, | |
| InvocationType: 'Event' | |
| }, | |
| physicalResourceId: PhysicalResourceId.of(Date.now().toString()), | |
| } | |
| }); | |
| deploymentTriggerFunction.node.addDependency(this.ecrDeployment); | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment