概要
TerraformのプロジェクトをCI/CDする情報があまりなかったので、アカウント管理用のプロジェクトを題材に試してみました。
管理対象のアカウントはAWSのIAMユーザーとGitHub Organizationのメンバーです。
AWSの準備
Terraform実行ユーザの作成
Terraformを実行するIAMユーザーを作成し、適切なIAMポリシーを割り当てます。
権限の範囲を気にしないのであれば、公式のIAMFullAccessとS3FullAccessのポリシーを割り当てれば十分です。ユーザー作成後はアクセスキー(アクセスキーIDとシークレットアクセスキー)を発行しておきます。
tfstateを保存するS3バケットの作成
CircleCI上でTerraformを動かす場合、tfstateを永続化する必要があります。
今回はS3にtfstateを保存し、実行時に共有する構成にします。
このtfstateを読み書きするために上記のS3FullAccessのポリシーが必要になります。
GitHubの準備
GitHubの準備はメンバーの追加削除ができる権限を持つPersonal Access Tokenを発行しておきます。
権限はadmin:orgのフルコントロールが必要になります。
アカウント管理のTerraform化
variables.tfに変数を、main.tfに構成の定義を記載します。
実際の運用ではAWSのIAMユーザーは追加削除だけでなく、IAMポリシーの付与やグループへの追加も行うのでもう少し複雑になります。
# IAMユーザ一覧
variable "iam_users" {
type = "list"
default = [
"iam_user1",
"iam_user2",
"iam_user3",
]
}
# GitHub Organizationのメンバー一覧
variable "github_members" {
type = "list"
default = [
"github_account1",
"github_account2",
"github_account3",
]
}
# tfstateの設定
terraform {
backend "s3" {
encrypt = "true"
region = "ap-northeast-1"
bucket = "yukihira1992-tfstates"
key = "account-management/terraform.tfstate"
acl = "bucket-owner-full-control"
}
}
# AWS Providerの設定
provider "aws" {
region = "ap-northeast-1"
}
# IAMユーザの設定
resource "aws_iam_user" "iam_users" {
count = "${length(var.iam_users)}"
name = "${element(var.iam_users, count.index)}"
path = "/"
force_destroy = false
}
# GitHub Providerの設定
provider "github" {
organization = "your_organizaion"
}
# GitHub Organizationのメンバー設定
resource "github_membership" "this" {
count = "${length(var.github_members)}"
username = "${element(var.github_members, count.index)}"
role = "member"
}
CircleCIの設定
設定ファイルの作成
version: 2
jobs:
test:
working_directory: ~/account-management
docker:
- image: hashicorp/terraform:light
steps:
- checkout
- run:
name: Init terraform
command: terraform init ~/account-management
- run:
name: Validate terraform
command: terraform validate ~/account-management
- run:
name: Plan terraform
command: terraform plan ~/account-management
deploy:
working_directory: ~/account-management
docker:
- image: hashicorp/terraform:light
steps:
- checkout
- run:
name: Init terraform
command: terraform init ~/account-management
- run:
name: Apply terraform
command: terraform apply -auto-approve ~/account-management
workflows:
version: 2
test-and-deploy:
jobs:
- test
- hold:
type: approval
requires:
- test
filters:
branches:
only: master
- deploy:
requires:
- hold
filters:
branches:
only: master
設定ファイルのポイントは次のとおりです。
Docker Image
CircleCI公式のTerraform用Docker Imageは無いのでHashicorpが用意しているhashicorp/terraformを採用しています。
Terraformのテスト
簡単ではありますが、terraform validateによる構文エラーのチェックとterraform planによる実行計画の確認を持ってしてテストとしています。(実行計画は人間がチェックする必要があります。)
terraform applyの確認回避
terraform applyは反映前に人間による確認を必要としているため -auto-approve オプションで確認を回避しています。
$ terraform apply -auto-approve
Pull Requestマージ前のテストとマージ後のデプロイ
testジョブはすべてのブランチで実行されるため、testジョブの結果をPull Requestマージの条件にすることができます。
一方でdeployジョブはmasterブランチに限定しているため、Pull Requestがmasterブランチにマージされた後でのみ実行されます。また、deployジョブの前にholdジョブで処理を止め人間の確認を待ちます。
アクセス権限の設定
CircleCI上でAWSのAPIとGitHubのAPIを操作できるようアクセス権限を設定します。
アクセス権限はCircleCIのプロジェクトに環境変数として設定します。
必要な環境変数は次の4つになります。
- AWS_DEFAULT_REGION : AWSのリージョン
- AWS_ACCESS_KEY_ID : Terraform用IAMユーザーのアクセスキーID
- AWS_SECRET_ACCESS_KEY : Terraform用IAMユーザーのシークレットアクセスキー
- GITHUB_TOKEN : GitHub OrganizationのPersonal Access Token
ブランチ保護設定
GitHubの場合になりますが、Terraformリポジトリのmasterブランチの保護設定を有効にします。マージ前のチェックの設定を次のように設定します。test-and-deployは一度CircleCI上でWorkflowを動かすと選択できるようになります。
動作確認
新しいブランチをGitHubにpushするとtestジョブが単独で実行されます。
続いてPull Requestをmasterブランチにマージするとtest-and-deplyのすべてのジョブが実行され、holdジョブで確認待ちになります。
holdジョブをクリックすると許可の確認をされるのでApproveをクリックします。
最後に人間がholdジョブを承認するとdeployジョブが動き構成変更が反映されます。
まとめ
アカウント管理をTerraformで行いCI/CDを組み合わせることでテストとデプロイを自動化してみました。
Terraformのような構成管理ツールにCI/CDを導入すると機密情報はCIサービスにのみ設定すればよく、個々の開発者が機密情報を保持する必要がなくなるのは大きなメリットだと思いました。次はTerraform planやapplyの結果をGitHub側から簡単に見れるようにできたらと思います。