Wiz テックブログ

Wizは、最新のIoTやICTサービスをお客様に届ける「ITの総合商社」です。

TerraformでCodeDeploy+CodePipeline (GitHub Ver2)を実装する

こんにちは。バックエンドエンジニアの高橋です。

あるプロジェクトで実装したCodeDeployとCodePipelineを使ったデプロイをTerraform化したので、その実装例を紹介したいと思います。

バランサーとEC2

f:id:wiz_tak:20210212143033j:plain

今回はバランサーに複数のウェブサーバがぶら下がっていてそこにインプレースデプロイする構成とします。

デプロイフロー

f:id:wiz_tak:20210212114735j:plain CodePipelineソースアクションのGitHubバージョンですが、1が非推奨の為バージョン2で実装します。

※ Terraformでは、バージョン2のGitHub接続の為のCodeStartConnectionが一部使えないので注意(後述)

Terraformで実装

前提

VPC、EC2、ALBといったリソースは既に構築済みである事とします。

また各環境とモジュールのそれぞれに必要な変数が設定されているものとします。

ディレクトリ構成

.
├── envs
│   ├── develop
│   │   ├── backend.tf
│   │   ├── deploy
│   │   │   ├── backend.tf
│   │   │   ├── main.tf
│   │   │   └── variables.tf
│   │   ├── main.tf
│   │   └── variables.tf
└── modules
    └── deploy
        ├── codedeploy
        │   ├── main.tf
        │   └── output.tf
        ├── codepipeline
        │   └── main.tf
        └── provider
            └── main.tf

CodeDeploy

必要なリソースのインポート

$ terraform import module.codedeploy.aws_vpc.main_vpc vpc-0******
$ terraform import module.codedeploy.aws_lb_target_group.alb_target_group arn:aws:******
$ terraform import module.codedeploy.aws_iam_role.ec2_deploy_role RoleNameSample

①インポートしたリソースの定義

// VPC
resource "aws_vpc" "main_vpc" {
  ~~
}

// ALB Target Group
resource "aws_lb_target_group" "alb_target_group" {
  ~~
}

// EC2にアタッチしたロール
resource "aws_iam_role" "ec2_attach_role" {
  ~~
}

②デプロイアプリケーションとデプロイグループの作成

// アプリケーション作成
resource "aws_codedeploy_app" "deploy_application" {
  compute_platform = "Server"
  name             = "${var.project}-deploy"
}

// デプロイグループ作成
resource "aws_codedeploy_deployment_group" "deploy_group" {
  app_name               = aws_codedeploy_app.deploy_application.name
  deployment_group_name  = "${var.stage}-deploy-group"
  service_role_arn       = aws_iam_role.ec2_attach_role.arn
  deployment_config_name = "CodeDeployDefault.OneAtATime"

  // デプロイ対象のEC2タグ
  ec2_tag_set {
    ec2_tag_filter {
      key   = "Deploy"
      type  = "KEY_AND_VALUE"
      value = var.stage
    }
  }

  // 失敗時のロールバック
  auto_rollback_configuration {
    enabled = true
    events  = ["DEPLOYMENT_FAILURE"]
  }

  // ロードバランサーの有効化
  deployment_style {
    deployment_option = "WITH_TRAFFIC_CONTROL"
    deployment_type   = "IN_PLACE"
  }

  // ターゲットグループ
  load_balancer_info {
    target_group_info {
      name = aws_lb_target_group.alb_target_group.name
    }
  }
}

CodePipeline

①IAMロール作成とポリシーアタッチ

ポリシーは長いので割愛します。

ActionにはCodeDeployやS3など必要なリソースを適宜セットしてください。

// IAMロール作成
resource "aws_iam_role" "codepipeline_role" {
  name = "${var.project}-${var.stage}-pipeline-role"

  assume_role_policy = <<EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Service": "codepipeline.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}
EOF
}

// ポリシーの作成
resource "aws_iam_role_policy" "codepipeline_policy" {
  name = "${var.project}-${var.stage}-pipeline-policy"
  role = aws_iam_role.codepipeline_role.id

  policy = <<EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Action": [
        "iam:PassRole"
      ],
      "Resource": "*",
      "Effect": "Allow",
      "Condition": {
        "StringEqualsIfExists": {
          "iam:PassedToService": [
            "cloudformation.amazonaws.com",
            "elasticbeanstalk.amazonaws.com",
            "ec2.amazonaws.com",
            "ecs-tasks.amazonaws.com"
          ]
        }
      }
    },
    ~~
}
EOF
}

②S3バケット作成

アーティファクトストアとなるS3バケットを作成します。

resource "aws_s3_bucket" "codepipeline_bucket" {
  bucket        = "${var.project}-${var.stage}-codepipeline-${var.region}"
  acl           = "private"
  force_destroy = false
}

③パイプライン作成

ソース/デプロイステージのみを作ります。

(ビルドステージは今回はスキップします)

~~
variable "codestar_connection_arn" {}
~~


resource "aws_codepipeline" "codepipeline" {
  name     = "${var.project}-${var.stage}-pipeline"
  role_arn = aws_iam_role.codesart_connection.arn

  // アーティファクトストア
  artifact_store {
    location = aws_s3_bucket.codepipeline_bucket.bucket
    type     = "S3"
  }

  // ソースステージ
  stage {
    name = "Source"

    action {
      name             = "Source"
      category         = "Source"
      owner            = "AWS"
      provider         = "CodeStarSourceConnection"
      version          = "1"
      output_artifacts = ["SourceArtifact"]

      configuration = {
        ConnectionArn    = var.codestar_connection_arn
        FullRepositoryId = var.repository
        BranchName       = var.repository_branch
      }
    }
  }

  // デプロイステージ
  stage {
    name = "Deploy"

    action {
      name            = "Deploy"
      category        = "Deploy"
      owner           = "AWS"
      provider        = "CodeDeploy"
      input_artifacts = ["SourceArtifact"]
      version         = "1"

      configuration = {
        ApplicationName     = var.codedeploy_application_name
        DeploymentGroupName = var.codedeploy_deploy_group_name
      }
    }
  }
}

aws_codestarconnections_connectionリソースについて

Terraformで保留状態になっており使用出来ません。

(2021年2月12日現在)

その為、事前にAWSコンソールのCodePipelineから適当にアプリケーションを作成し、発行されたarnを変数定義する必要があります。

f:id:wiz_tak:20210212135004p:plain

入出力アーティファクトについて

パイプラインの各ステージで必要な output_artifacts/input_artifacts の箇所は実装する際に少し迷いそうですが、

以下の図の様なワークフローを理解すると分かり易いのかなと思います。

f:id:wiz_tak:20210212135658j:plain

まとめ

Terraformで言語化すると作成するリソースにはどのリソースやコンポーネントが必要なのかといった全体像の把握が改めて出来る点が良いなと思っています。

ただ一部保留や未対応であったりと100%がTerraformで対応出来る訳ではない点が残念です。

最後に

Wizではエンジニアを募集しております。 興味のある方、ぜひご覧下さい。

【フロントエンドエンジニア】

場所にとらわれず自社メディア成長に貢献したいフロントエンドエンジニア募集! - 株式会社WizのWebエンジニアの求人 - Wantedly

【バックエンドエンジニア】

勤務地自宅を叶える!バックエンドエンジニアとして事業を成長させたい方募集 - 株式会社WizのWebエンジニアの求人 - Wantedly