Terraform Cloudを使って複数環境(本番/STG)にAWSリソースをデプロイしてみる

2023.02.06

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

「STGと本番のAWSアカウントにTerraform Cloudでリソースをデプロイするにはどんな設定をすればいいんだろう?」

実際のシステム運用では、本番とSTGなど複数の環境を作ることが多いと思います。

このブログが、Terraform Cloudで複数環境を設定する際の参考になると嬉しいです。

構成図

本番環境とSTG環境のAWSアカウントがそれぞれ存在して、Terraformのコード上で本番とSTGでstateが分かれているとします。

Terraform Cloud上ではシステム用にProjectを作り、その中に本番環境とSTG環境のWorkspaceを作る構成です。

デプロイフロー

今回は上記のデプロイフローを想定して、Terraform Cloudを設定していきます。

  1. mainブランチにPull Request
  2. Terraform Cloud上で本番とSTG環境のPlan実行
  3. Pull Requestのマージ時に、STG環境にApply実行
  4. Terraform Cloudのコンソールから手動承認後、本番環境にApply実行

STG環境は自動適用で、本番環境はSTG環境の状態を確認した上で手動承認して、Terraformを適用するという流れです。

やってみた

コードを見てみる

使用するコードは以下リポジトリの、infra/配下になります。

msato0731/multi-env-tfc-demo

AWSリソース作成のTerraformコードは、EC2を作成するだけの内容です。

infra/environments/stg/main.tf

provider "aws" {
  region = "ap-northeast-1"
  default_tags {
    tags = {
      Env       = "stg"
      Terraform = "true"
      SystemName = "tfc-demo"
    }
  }
}
terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 4.52.0"
    }
  }
}

module "vpc" {
  source  = "terraform-aws-modules/vpc/aws"

  name = "stg-vpc"
  cidr = "10.10.0.0/16"

  azs                       = ["ap-northeast-1a", "ap-northeast-1c"]
  private_subnets           = ["10.10.1.0/24", "10.10.2.0/24"]
  public_subnets            = ["10.10.101.0/24", "10.10.102.0/24"]
}

module "ec2" {
  source = "../../modules/ec2"
  subnet_id = module.vpc.private_subnets[0]
  instance_name = "tfc-demo-stg"
}

infra/module/ec2/main.tf

data aws_ssm_parameter amzn2_ami {
  name = "/aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2"
}

resource "aws_network_interface" "this" {
  subnet_id   = var.subnet_id
}

resource "aws_instance" "this" {
  ami           = data.aws_ssm_parameter.amzn2_ami.value
  instance_type = var.instance_type

  network_interface {
    network_interface_id = aws_network_interface.this.id
    device_index         = 0
  }

  tags = {
    Name = var.instance_name
  }
}

Projectの作成

まずは、今回作成するデモシステム用にProjectを作成します。

Project名は以下とします。

  • Project名: multi-env-demo

Projectの分け方も色々考え方があると思いますが、今回はシステムごとに分けるようなイメージで作っていきます。

作成したProject内にSTG環境と本番環境のWorkspaceを作成していきます。

STG Workspaceの作成

Workspaceはstateファイルごとに作成します。

今回はデモ用でリソース数が少ないので、本番とSTGといった単位でstateを分けています。 そのため、Workspaceも本番とSTGで合計2つ作成します。

まずは、STGのWorkspaceを作ります。

先ほど作成したProjectを選択して、Workspaceを作成します。

  • Workspace名: multi-env-demo-stg

デモ用のGithubリポジトリと接続するように設定していきます。

ProjectとWorkspace名を設定して、Create Workspaceを選択します。

STG環境 Workspaceの設定

Workspaceを作成できたところで、必要な設定をしていきます。

multi-env-demo-stg > Settingsに遷移します。

以下を設定します。

  • Apply Method: Auto Apply
  • Terraform Working Directory: infra/environments/stg
  • VCS branch: main

Apply Methodterraform applyの適用に関する設定です。 デフォルトはManula applyとなっており、手動承認が必要な形です。

今回は、STG環境はPull RequestをMergeした際(デフォルトブランチへのPush時)にApplyを実行したいので、Auto Applyにします。

Terraform Working Directoryは、Terraformを実行するディレクトリです。 ここで設定したディレクトリで、terraform関連のコマンドが実行されます。

今回はディレクトリで環境を分けているため、STG環境のディレクトリであるinfra/environments/stgを設定しました。

本番環境 Workspaceの作成

STGと同様に本番 Workspaceを作成します。

STGと異なる点は、Workspace名のみです。

  • Workspace名: multi-env-demo-prod

本番環境 Workspaceの設定

STGと同様にWorkspaceの設定を行います。

  • Apply Method: Manual apply
  • Terraform Working Directory: infra/environments/prod
  • VCS branch: main

Apply MethodManual applyとして、Terraform Cloud上での手動承認を必要な形にします。

Manual applyにしておくと、Plan後に以下のように承認を求められるようになります。

Terraform CloudでAWSを使用するための準備

Terraform CloudがAWSリソースを管理できるように、Terraform Cloud用の本番環境とSTG環境Workspace用にそれぞれIAMロールを作成します。

IAMロール作成の詳細な手順は以下をご確認ください。

IAMロールには以下のポリシーをアタッチしてください。

Terraform Cloud用のIAMロールポリシー

{
    "Statement": [
        {
            "Action": [
                "ec2:*",
                "ssm:*"
            ],
            "Effect": "Allow",
            "Resource": "*"
        }
    ],
    "Version": "2012-10-17"
}

信頼関係は以下のように設定します。

Terraform Cloud用のIAMロール 信頼関係

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "Federated": "arn:aws:iam::[アカウントID]:oidc-provider/app.terraform.io"
            },
            "Action": "sts:AssumeRoleWithWebIdentity",
            "Condition": {
                "StringEquals": {
                    "app.terraform.io:aud": "aws.workload.identity"
                },
                "StringLike": {
                    "app.terraform.io:sub": "organization:[Organization名]:project:multi-env-demo:workspace:multi-env-demo-stg:run_phase:*"
                }
            }
        }
    ]
}

※本番環境用IAMロールは、Workspace名multi-env-demo-stgの部分はmulti-env-demo-prdにします。

IAMロールを作成したら、STGと本番のWorkspaceの環境変数に以下を設定します。

  • TFC_AWS_PROVIDER_AUTH: true
  • TFC_AWS_RUN_ROLE_ARN: IAMロールのARN

動作確認

Pull Request時に各WorkspaceでPlan

Pull Request時に各WorkspaceでPlanが実行されることを確認します。

infra/envirionments/prodinfra/environments/stgのtfファイルを少し変更してPull Requestを出してみます。

以下のように本番とSTGのWorkspaceでPlanが走っていることがGithub上からも確認できます。

Detailの部分をクリックするとTerraform Cloudに飛んで詳細を確認できます。

Pull Requestマージ時にSTG WorkspaceのApply

次にPull Requestマージ時にSTG WorkspaceのApplyが実行されることを確認します。

マージしました。Terraform Cloudを見てみましょう。

Workspaceのステータスとみると、STG WorkspaceがApplyedになっておりApplyが実行されいることがわかります。 また、この時点では本番 Workspaceの方はApplyが実行されていません。

マージ後手動で本番WorkspaceのApply

最後にSTGで動作確認が問題なかったことを想定して、本番に変更を適用しましょう。

Terraform Cloudの本番 Workspaceで手動承認を行います。 Confirm & Applyを選択します。

無事Applyが成功しました。

おわりに

Terraform Cloudを使った複数環境へのAWSリソース デプロイの例でした。

今回は本番もSTGも同一ブランチを使った例を紹介しましたが、本番とSTGでブランチを分けている場合もあると思います。

その際は、STG環境 WorkspaceのVCS Settingの部分でSTG環境用のブランチを指定すれば良いです。

他にも、Runのトリガーでは特定のフォルダ配下の変更時などを設定することができ、柔軟にデプロイフローを作成可能です。

以上、AWS事業本部の佐藤(@chari7311)でした。