I had the same problem at work and sumbled across this already asked question that does not have a proper answer, so I'll give instructions of how I did.
Baseline: you cannot do this with cloudformation, cloudformation is used to create infrastructure and automations. Although, you can accomplish this with codebuild and you can use cloudformation to create a codebuild project. This repository does that for a practical example.
What you would do is: create a cloudformation template that creates a codebuild project and in your codebuild create a buildspec.yml (file that specifies the build) that will push your image to ECR.
The codebuild project would look like this:
CodeBuildProject:
Type: AWS::CodeBuild::Project
Properties:
Artifacts:
Type: CODEPIPELINE
Description: "Codebuild project to push flask api image to ecr"
Environment:
ComputeType:
!FindInMap [CodeBuildComputeTypeMap, !Ref GithubBranch, type]
EnvironmentVariables:
- Name: AWS_DEFAULT_REGION
Value: !Ref AWS::Region
- Name: AWS_ACCOUNT_ID
Value: !Ref "AWS::AccountId"
- Name: AWS_ECR_REPOSITORY_URI
Value: !Sub ${AWS::AccountId}.dkr.ecr.${AWS::Region}.amazonaws.com/${EcrRepository}
- Name: IMAGE_REPO_NAME
Value: !Ref GithubRepository
- Name: IMAGE_TAG
Value: "latest"
Image: "aws/codebuild/standard:5.0"
PrivilegedMode: true
Type: "LINUX_CONTAINER"
ServiceRole: !GetAtt CodeBuildRole.Arn
Source:
Type: "CODEPIPELINE"
BuildSpec: buildspec.yml
EcrRepository:
Type: AWS::ECR::Repository
Properties:
RepositoryName: !Ref GithubRepository
CodeBuildRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service:
- codebuild.amazonaws.com
Action:
- "sts:AssumeRole"
Policies:
- PolicyName: "PushImageToEcr"
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- ecr:BatchGetImage
- ecr:BatchCheckLayerAvailability
- ecr:CompleteLayerUpload
- ecr:GetDownloadUrlForLayer
- ecr:InitiateLayerUpload
- ecr:PutImage
- ecr:UploadLayerPart
- ecr:GetAuthorizationToken
Resource: "*"
- PolicyName: "CodeBuildLogsRole"
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- logs:CreateLogGroup
- logs:CreateLogStream
- logs:PutLogEvents
Resource:
- !Sub "arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/codebuild/*"
- PolicyName: "GetAndPutArtifacts"
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: "Allow"
Action:
- s3:GetObject
- s3:PutObject
- s3:ListBucket
Resource:
- !GetAtt ArtifactBucket.Arn
- !Sub ${ArtifactBucket.Arn}/*
ArtifactBucket:
Type: AWS::S3::Bucket
This should go in the Resources section of the cloudformation and will create the codebuild project, the ecr repository, the codebuild service role and the s3 bucket for the artifacts.
Then you need a buildspec.yml template to push your image, this would look like this:
version: 0.2
phases:
pre_build:
commands:
- echo Logging in to Amazon ECR...
- aws ecr get-login-password --region $AWS_DEFAULT_REGION | docker login --username AWS --password-stdin $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com
build:
commands:
- echo Build started on `date`
- echo Building the Docker image...
- cd app/
- docker build -t $IMAGE_REPO_NAME:$IMAGE_TAG .
- docker tag $IMAGE_REPO_NAME:$IMAGE_TAG $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$IMAGE_REPO_NAME:$IMAGE_TAG
post_build:
commands:
- echo Build completed on `date`
- echo Pushing the Docker image...
- docker push $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$IMAGE_REPO_NAME:$IMAGE_TAG