継続的ブログ

主にweb系の技術について書いています

TerraformでSourceをECRにしたCodePipelineを作成する

SourceにECRをしたものを設定する機会があったので書いておきます。
SourceにGitHubを指定しているサンプルはよく見かけますが、ECRを指定しているものはあまりなかったので参考になれば。

CodePipeline

resource "aws_codepipeline" "example" {
  name     = "example"
  role_arn = aws_iam_role.codepipeline.arn

  artifact_store {
    location = aws_s3_bucket.artifact.bucket
    type     = "S3"
  }

  stage {
    name = "Source"

    action {
      name             = "Source"
      category         = "Source"
      owner            = "AWS"
      provider         = "ECR"
      version          = "1"
      output_artifacts = ["source"]

      configuration = {
        RepositoryName = "example"
        ImageTag       = "stage"
      }
    }
  }

  stage {
    name = "Build"

    action {
      name             = "Build"
      category         = "Build"
      owner            = "AWS"
      provider         = "CodeBuild"
      version          = "1"
      input_artifacts  = ["source"]
      output_artifacts = ["build"]

      configuration = {
        ProjectName = aws_codebuild_project.example.id
      }
    }
  }

  stage {
    name = "Deploy"

    action {
      name            = "Deploy"
      category        = "Deploy"
      owner           = "AWS"
      provider        = "ECS"
      input_artifacts = ["build"]
      version         = "1"

      configuration = {
        ClusterName = aws_ecs_cluster.example.arn
        ServiceName = aws_ecs_service.ecample.name
        FileName    = "imagedefinitions.json"
      }
    }
  }
}

Sourceの部分は provider = "ECR" を設定して、configurationにトリガーしたいECRのリポジトリ名、イメージタグ名を設定します。(ImageTagはオプショナル)
ここでは example リポジトリの stage タグのプッシュをトリガーにする設定をしています。

buildspec.yml

version: 0.2

phases:
  install:
    runtime-versions:
      python: 3.7
  build:
    commands:
      - echo Build started on `date`
      - REPOSITORY_URI=$(cat imageDetail.json | python -c "import sys, json; print(json.load(sys.stdin)['ImageURI'].split('@')[0])")
      - IMAGE_TAG=$(cat imageDetail.json | python -c "import sys, json; print(json.load(sys.stdin)['ImageTags'][0])")
      - echo $REPOSITORY_URI:$IMAGE_TAG
  post_build:
    commands:
      - echo Build completed on `date`
      - echo Writing image definitions file...
      - printf '[{"name":"web","imageUri":"%s"}]' $REPOSITORY_URI:$IMAGE_TAG > imagedefinitions.json

artifacts:
    files: imagedefinitions.json

output として imageDetail.json が生成されるので、そこから imagedefinitions.json を作成します。
ECS Blue/Greenデプロイを選択する場合は、imageDetail.json そのままで大丈夫かと思いますが、今回は標準デプロイを選択したので imagedefinitions.json を作成しています。

stackoverflow.com

これで設定完了!ECRにプッシュすればCodePipelineが起動する!

と思ったのですが、Terraform等から作成する場合はCloudWatch Events等は自前で用意しなきゃいけないんですね...(マネージメントコンソールから作成すれば勝手に作ってくれる)

docs.aws.amazon.com

CloudWatch Events & CloudTrail

data "aws_iam_policy_document" "cloudwatch_events" {
  statement {
    effect    = "Allow"
    resources = ["*"]

    actions = [
      "codepipeline:StartPipelineExecution"
    ]
  }
}

data "aws_iam_policy_document" "cloudwatch_events_assume_role" {
  statement {
    actions = ["sts:AssumeRole"]

    principals {
      type        = "Service"
      identifiers = ["events.amazonaws.com"]
    }
  }
}

resource "aws_iam_role" "cloudwatch_events" {
  name               = "codepipeline-cloudwatch-events"
  assume_role_policy = data.aws_iam_policy_document.cloudwatch_events_assume_role.json
}

resource "aws_iam_policy" "cloudwatch_events" {
  name   = "codepipeline-cloudwatch-events"
  policy = data.aws_iam_policy_document.cloudwatch_events.json
}

resource "aws_iam_role_policy_attachment" "cloudwatch_events" {
  role       = aws_iam_role.cloudwatch_events.name
  policy_arn = aws_iam_policy.cloudwatch_events.arn
}

resource "aws_cloudwatch_event_rule" "ecr" {
  name        = "codepipeline-ecr-event-rule"
  description = "Amazon CloudWatch Events rule to automatically start your pipeline when a change occurs in the Amazon ECR image tag."

  event_pattern = <<-JSON
  {
    "source": [
      "aws.ecr"
    ],
    "detail-type": [
      "AWS API Call via CloudTrail"
    ],
    "detail": {
      "eventSource": [
        "ecr.amazonaws.com"
      ],
      "eventName": [
        "PutImage"
      ],
      "requestParameters": {
        "repositoryName": [
          "example"
        ],
        "imageTag": [
          "stage"
        ]
      }
    }
  }
  JSON

  depends_on = ["aws_codepipeline.example"]
}

resource "aws_cloudwatch_event_target" "ecr" {
  rule      = aws_cloudwatch_event_rule.ecr.name
  target_id = aws_cloudwatch_event_rule.ecr.name
  arn       = aws_codepipeline.example.arn
  role_arn  = aws_iam_role.cloudwatch_events.arn
}
data "aws_caller_identity" "current" {}

resource "aws_cloudtrail" "example" {
  name           = "example"
  s3_bucket_name = aws_s3_bucket.example.id

  event_selector {
    read_write_type           = "All"
    include_management_events = true

    data_resource {
      type   = "AWS::S3::Object"
      values = ["arn:aws:s3:::"]
    }
  }
}

resource "aws_s3_bucket" "example" {
  bucket = "cloudtrail"
  acl    = "private"

  policy = <<-POLICY
  {
    "Version": "2012-10-17",
    "Statement": [
      {
        "Sid": "AWSCloudTrailAclCheck",
        "Effect": "Allow",
        "Principal": {
          "Service": "cloudtrail.amazonaws.com"
        },
        "Action": "s3:GetBucketAcl",
        "Resource": "arn:aws:s3:::cloudtrail"
      },
      {
        "Sid": "AWSCloudTrailWrite",
        "Effect": "Allow",
        "Principal": {
          "Service": "cloudtrail.amazonaws.com"
        },
        "Action": "s3:PutObject",
        "Resource": "arn:aws:s3:::cloudtrail/AWSLogs/${data.aws_caller_identity.current.account_id}/*",
        "Condition": {
          "StringEquals": {
            "s3:x-amz-acl": "bucket-owner-full-control"
          }
        }
      }
    ]
  }
  POLICY
}

CloudWatch Events の設定だけではうまくいかなくて、↓を参考に CloudTrail も設定したらうまくいきました!

https://forums.aws.amazon.com/thread.jspa?threadID=306306&tstart=0


今後もしかしたら CloudWatch Events の作成までしてくれる可能性もあるかも?

github.com


CodePipelineのことは書いてないですが、この本かなり良かったです!

Terraform - Up & Running: Writing Infrastructure As Code

Terraform - Up & Running: Writing Infrastructure As Code

Second Edition は v0.12 に対応していて、まだ 発売前 ですが O'Reilly の Safari Books Online では読めます!

www.oreilly.com

自分も Safari Books Online で読んだのですが、 Free Trial の 10日で読める分量だと思うのでもし興味があれば是非!