Terragrunt は、Terraform を使用してインフラストラクチャを管理するための作業を簡素化するための Terraform Wrapper として知られるオープンソースのツールです。Terragrunt は Terraform が提供する機能を拡張し、モジュールのコード再利用、柔軟な構成、再利用性の向上を可能にします。特に、Terraform を使用して複数の環境やアカウントを管理する際に Terragrunt は非常に便利です。
$ brew install tfenv # Installs Terraform (version specified in .terraform-version)
$ brew install tgenv # Installs Terragrunt (version specified in .terragrunt-version)
### フォルダ構成
このフォルダ構成は、他の記事を参考に作成されました。この構造の利点は、各環境ごとに異なる変数を指定できるため、コードの再利用性が向上することです。
$ tree .
.
├── README.md
├── docs
│ └── graph-dependencies.png
├── envs
│ ├── prod
│ │ ├── ResourceGroupA
│ │ │ └── terragrunt.hcl
│ │ ├── ResourceGroupB
│ │ │ └── terragrunt.hcl
│ │ └── env.hcl
│ └── terragrunt.hcl
└── modules
├── ResourceGroupA
│ └── xx.tf
└── ResourceGroupB
└── xx.tf
このファイルには、バックエンドとして AWS S3 バケットに*.tfstate ファイルを保存するための設定が含まれており、各環境に独自の*.tfstate ファイルを S3 バケット内に保存することができます。また、AWS リソースを作成するために Terraform が使用するプロバイダの定義も含まれています。設定では、各 AWS リージョンごとに異なるプロバイダ設定が指定されています。
*.tfstate ファイルを使用してリソースの状態を管理することで、複数の人がプロジェクトに取り組んでいても競合を回避することができます。
# Configuration for storing *.tfstate files for each environment
remote_state {
backend = "s3"
config = {
bucket = "tfstate-xxxxxxxxxxxxxx"
# Stored in `stg/modA.tfstate`
key = "${path_relative_to_include()}.tfstate"
region = "ap-northeast-1"
encrypt = true
}
generate = {
path = "backend.tf"
if_exists = "overwrite"
}
}
generate "provider" {
path = "provider.tf"
if_exists = "overwrite_terragrunt"
contents = <<EOF
terraform {
required_version = ">= 1.3.7"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 4.53.0"
}
}
}
# Tokyo region
provider "aws" {
region = "ap-northeast-1"
alias = "tokyo"
}
# Virginia region
provider "aws" {
region = "us-east-1"
alias = "virginia"
}
# ... Other regions
EOF
}
このファイルは、Terragrunt の環境固有のファイルであり、prod 環境の設定が含まれています。これらの変数は、他の Terragrunt ファイルで使用され、環境固有の設定を定義するために使用されます。具体的には、envs/prod/ResourceGroupA の hcl ファイルで呼び出され、Terraform コードの module/ResourceGroupA にパラメータとして渡されます。
locals {
ENV = "prod"
PROJECT_NAME = "test"
ACCOUNT_ID = "123456789"
VPC_CIDE = "10.0.0.0/24"
}
「ResourceGroupA」と「ResourceGroupB」の HCL ファイルの例を示しましょう。
最初は依存関係がないと仮定し、A というクリーンなスレートのシナリオを考えます。B は A に依存するリソースグループとします。
vim envs/prod/ResourceGroupA/terragrunt.hcl
locals {
ENV = read_terragrunt_config(find_in_parent_folders("env.hcl"))
}
# Include definition of all environments (envs/terragrunt.hcl)
include {
path = find_in_parent_folders()
}
terraform {
# Reference the module
source = "../../../modules//ResourceGroupA"
}
# Specify the input values for the module
inputs = {
ENV = local.ENV.locals.ENV
PROJECT_NAME = local.ENV.locals.PROJECT_NAME
ACCOUNT_ID = local.ENV.locals.ACCOUNT_ID
}
vim envs/prod/ResourceGroupB/terragrunt.hcl
locals {
ENV = read_terragrunt_config(find_in_parent_folders("env.hcl"))
}
# Include definition of all environments (envs/terragrunt.hcl)
include {
path = find_in_parent_folders()
}
terraform {
# Reference the module
source = "../../../modules//ResourceGroupB"
}
# Specify the input values for the module
inputs = {
ENV = local.ENV.locals.ENV
PROJECT_NAME = local.ENV.locals.PROJECT_NAME
ACCOUNT_ID = local.ENV.locals.ACCOUNT_ID
VPC_ID = local.ENV.locals.VPC_ID
}
variable "ENV" {
description = "Environment"
type = string
}
variable "PROJECT_NAME" {
description = "Project Name"
type = string
}
variable "vpc_id" {
description = "The ID of the VPC"
type = string
}
output "vpc_id" {
value = aws_vpc.main.id
}
cd envs/prod
terragrunt run-all hclfmt
terragrunt run-all fmt
cd envs/prod
terragrunt validate-all
cd envs/prod
terragrunt run-all plan
cd envs/prod
terragrunt run-all apply
brew install graphviz
cd envs/terragrunt-prod
terragrunt graph-dependencies | dot -Tpng > graph-dependencies.png
name: Terragrunt Actions
on:
push:
branches:
- master
env:
TERRAGRUNT_CACHE_DIR: ${{ github.workspace }}/tool
jobs:
build:
runs-on: ubuntu-latest
env:
TARGET_ENV: ''
steps:
- name: Set Production
run: |
mkdir -p ${GITHUB_WORKSPACE}/deploy
echo 'export TARGET_ENV="prod"' >> ${GITHUB_WORKSPACE}/deploy/.env
if: github.ref == 'refs/heads/master'
env:
GITHUB_WORKSPACE: ${{ github.workspace }}
- name: Set Staging
run: |
mkdir -p ${GITHUB_WORKSPACE}/deploy
echo 'export TARGET_ENV="stg"' >> ${GITHUB_WORKSPACE}/deploy/.env
if: github.ref == 'refs/heads/staging'
env:
GITHUB_WORKSPACE: ${{ github.workspace }}
- name: Terragrunt Plan
env:
TERRAGRUNT_DOWNLOAD: "https://github.com/gruntwork-io/terragrunt/releases/download/v0.34.0/terragrunt_linux_amd64"
run: |
source ${GITHUB_WORKSPACE}/deploy/.env
sudo apt-get update && sudo apt-get install -y curl unzip git
curl -Lo /tmp/terragrunt.zip ${TERRAGRUNT_DOWNLOAD}
sudo unzip -d /usr/local/bin /tmp/terragrunt.zip
cd ${GITHUB_WORKSPACE}/envs/${TARGET_ENV}
terragrunt run-all plan
if: github.ref == 'refs/heads/master' || github.ref == 'refs/heads/staging'
env:
GITHUB_WORKSPACE: ${{ github.workspace }}
TERRAGRUNT_CACHE_DIR: ${{ env.TERRAGRUNT_CACHE_DIR }}
- name: Terragrunt Apply
if: github.ref == 'refs/heads/master'
env:
TERRAGRUNT_DOWNLOAD: "https://github.com/gruntwork-io/terragrunt/releases/download/v0.34.0/terragrunt_linux_amd64"
run: |
source ${GITHUB_WORKSPACE}/deploy/.env
sudo apt-get update && sudo apt-get install -y curl unzip git
curl -Lo /tmp/terragrunt.zip ${TERRAGRUNT_DOWNLOAD}
sudo unzip -d /usr/local/bin /tmp/terragrunt.zip
cd ${GITHUB_WORKSPACE}/envs/${TARGET_ENV}
terragrunt run-all apply --terragrunt-non-interactive
env:
GITHUB_WORKSPACE: ${{ github.workspace }}
TERRAGRUNT_CACHE_DIR: ${{ env.TERRAGRUNT_CACHE_DIR }}
image: hashicorp/terraform:1.3.7
definitions:
caches:
terragrunt: ${BITBUCKET_CLONE_DIR}/tool
steps:
- step: &set-production
name: Set Production
script:
- mkdir -p ${BITBUCKET_CLONE_DIR}/deploy
- echo 'export TARGET_ENV="prod"' >> ${BITBUCKET_CLONE_DIR}/deploy/.env
artifacts:
- deploy/**
- step: &set-staging
name: Set Staging
script:
- mkdir -p ${BITBUCKET_CLONE_DIR}/deploy
- echo 'export TARGET_ENV="stg"' >> ${BITBUCKET_CLONE_DIR}/deploy/.env
artifacts:
- deploy/**
- step: &terragrunt-plan
name: terragrunt Plan
caches:
- terragrunt
script:
- source ${BITBUCKET_CLONE_DIR}/deploy/.env
- apk update && apk add bash curl groff jq less unzip git
- if [ ! -d ${BITBUCKET_CLONE_DIR}/tool/tfenv ]; then git clone https://github.com/tfutils/tfenv.git ${BITBUCKET_CLONE_DIR}/tool/tfenv; fi
- if [ ! -d ${BITBUCKET_CLONE_DIR}/tool/tgenv ]; then git clone https://github.com/cunymatthieu/tgenv.git ${BITBUCKET_CLONE_DIR}/tool/tgenv; fi
- export PATH=${BITBUCKET_CLONE_DIR}/tool/tfenv/bin:$PATH
- export PATH=${BITBUCKET_CLONE_DIR}/tool/tgenv/bin:$PATH
- cd ${BITBUCKET_CLONE_DIR}/envs/${TARGET_ENV}
- tfenv install && tgenv install
- terragrunt run-all plan
- step: &terragrunt-apply
name: terragrunt Apply
trigger: manual
caches:
- terragrunt
script:
- source ${BITBUCKET_CLONE_DIR}/deploy/.env
- apk update && apk add bash curl groff jq less unzip git
- if [ ! -d ${BITBUCKET_CLONE_DIR}/tool/tfenv ]; then git clone https://github.com/tfutils/tfenv.git ${BITBUCKET_CLONE_DIR}/tool/tfenv; fi
- if [ ! -d ${BITBUCKET_CLONE_DIR}/tool/tgenv ]; then git clone https://github.com/cunymatthieu/tgenv.git ${BITBUCKET_CLONE_DIR}/tool/tgenv; fi
- export PATH=${BITBUCKET_CLONE_DIR}/tool/tfenv/bin:$PATH
- export PATH=${BITBUCKET_CLONE_DIR}/tool/tgenv/bin:$PATH
- cd ${BITBUCKET_CLONE_DIR}/envs/${TARGET_ENV}
- tfenv install && tgenv install
- terragrunt run-all apply --terragrunt-non-interactive
pipelines:
default:
- step: *set-production
- step: *terragrunt-plan
branches:
master:
- step: *set-production
- step: *terragrunt-plan
- step:
<<: *terragrunt-apply
deployment: production