Terragrunt は Terraform のラッパーツールです。Terraform の機能を拡張し使いやすくします。Terragrunt が解決する Terraform の主な課題は以下の通りです。
- Terraform だと複数環境ごとの terraform 定義の管理が冗長になる
- Terraform だと State ファイルの分割・管理に手間がかかる
- Terraform だと State ファイルを格納するリモートストレージ(例:S3)を手動で作成しないといけない
0. Terraform と Terragrunt をインストールする #
- Install | Terraform | HashiCorp Developer
- Install | Terragrunt
Homebrew でインストールします。
brew tap hashicorp/tap
brew install hashicorp/tap/terraform
brew install terragrunt
terraform
と terragrunt
コマンドを実行してインストールできたことを確認します。
terraform --version
> Terraform v1.13.1
terragrunt --version
> terragrunt version 0.86.2
1. EC2 を起動する Terragrunt を書いてみる #
EC2 インスタンスを起動する Terragrunt の例です。以下の要件を満たすように作成します。
- ap-northeast-1 リージョンの既存の subnet の中に Amazon Linux の t3.micro インスタンスを起動します。
- EC2 インスタンス起動時の処理(ユーザデータスクリプト)で “Hello, World!” と書かれたテキストファイルを作成します。
- 起動した EC2 インスタンスに Session Manager(SSM)で接続できるようにします。(https://docs.aws.amazon.com/systems-manager/latest/userguide/session-manager.html)
なお、AWS CLI では SSO(Single Sign-On)を利用して AWS にログインすることを前提としています。
ディレクトリ構成 #
my-terragrunt/
├ .gitignore
├ my_userdata.sh
├ terragrunt.hcl
└ main.tf
.gitignore
#
.terragrunt-cache/
.terragrunt-stack/
my_userdata.sh
#
ユーザデータスクリプトです。EC2 インスタンスの起動時に実行されるよう、後述の Terraform(main.tf
) で指定しています。
https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/user-data.html
EC2 インスタンス内の /home/ec2-user/hello.txt
に “Hello, World!” というテキストを作成します。
#!/bin/bash
echo "Hello, World!" > /home/ec2-user/hello.txt
chown ec2-user:ec2-user /home/ec2-user/hello.txt
chmod 0644 /home/ec2-user/hello.txt
terragrunt.hcl
#
Terragrunt の設定ファイルです。ステートファイルの保存先や Terraform を実行する際の引数(inputs)などを指定しています。
locals {
aws_profile = "my_profile" # 自分の環境に合わせて書き換える - AWS SSO でログインする際のプロファイル名
aws_remote_state_s3_region = "ap-northeast-1"
aws_remote_state_s3_bucket_name = "my-remote-state-bucket"
aws_remote_state_s3_file_key = "terraform.tfstate"
aws_ec2_region = "ap-northeast-1"
aws_ec2_vpc_id = "vpc-xxxxxxxxxxxxxxxxx" # 自分の環境に合わせて書き換える - ap-northeast-1 内の既存の VPC ID
aws_ec2_subnet_id = "subnet-xxxxxxxxxxxxxxxxx" # 自分の環境に合わせて書き換える - ap-northeast-1 内の既存の subnet ID
aws_ec2_ami_id = "ami-0228232d282f16465" # Amazon Linux 2023 x86_64
aws_ec2_instance_type = "t3.micro"
aws_ec2_name = "my-ec2"
}
terraform {
source = "./"
extra_arguments "aws_profile" {
commands = [
"init",
"apply",
"refresh",
"import",
"plan",
"taint",
"untaint"
]
env_vars = {
AWS_PROFILE = "${local.aws_profile}"
}
}
}
remote_state {
backend = "s3"
generate = {
path = "backend.tf"
if_exists = "overwrite_terragrunt"
}
config = {
encrypt = true
region = local.aws_remote_state_s3_region
bucket = local.aws_remote_state_s3_bucket_name
key = local.aws_remote_state_s3_file_key
}
}
inputs = {
ec2_region = local.aws_ec2_region
ec2_vpc_id = local.aws_ec2_vpc_id
ec2_subnet_id = local.aws_ec2_subnet_id
ec2_ami_id = local.aws_ec2_ami_id
ec2_instance_type = local.aws_ec2_instance_type
ec2_name = local.aws_ec2_name
}
main.tf
#
Terraform の定義ファイルです。variable
で Terragrunt の inputs
で指定した値を受け取り処理を行います。
# ------------------------------------------------------------------------------
# Terraform Configuration
# ------------------------------------------------------------------------------
terraform {
required_version = ">= 0.13"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}
# ------------------------------------------------------------------------------
# Variables
# ------------------------------------------------------------------------------
variable "ec2_region" {
type = string
}
variable "ec2_vpc_id" {
type = string
}
variable "ec2_subnet_id" {
type = string
}
variable "ec2_ami_id" {
type = string
}
variable "ec2_instance_type" {
type = string
}
variable "ec2_name" {
type = string
}
# ------------------------------------------------------------------------------
# Provider Configuration
# ------------------------------------------------------------------------------
provider "aws" {
region = var.ec2_region
}
# ------------------------------------------------------------------------------
# Resources - IAM
# ------------------------------------------------------------------------------
resource "aws_iam_role" "this" {
name = "my-ssm-role"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Action = "sts:AssumeRole"
Effect = "Allow"
Principal = {
Service = "ec2.amazonaws.com"
}
}
]
})
}
resource "aws_iam_role_policy_attachment" "this" {
role = aws_iam_role.this.name
policy_arn = "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore"
}
resource "aws_iam_instance_profile" "this" {
name = "my-ssm-profile"
role = aws_iam_role.this.name
}
# ------------------------------------------------------------------------------
# Resources - Security Group
# ------------------------------------------------------------------------------
resource "aws_security_group" "this" {
name = "my-egress-security-group"
description = "Security group for allow any outbound traffic"
vpc_id = var.ec2_vpc_id
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
tags = {
Name = "my-egress-security-group"
}
}
# ------------------------------------------------------------------------------
# Resources - EC2
# ------------------------------------------------------------------------------
resource "aws_instance" "this" {
ami = var.ec2_ami_id
instance_type = var.ec2_instance_type
subnet_id = var.ec2_subnet_id
vpc_security_group_ids = [aws_security_group.this.id]
associate_public_ip_address = false
iam_instance_profile = aws_iam_instance_profile.this.name
tags = {
Name = var.ec2_name
}
user_data = base64encode(file("${path.module}/my_userdata.sh"))
user_data_replace_on_change = true
}
2. Terragrunt を実行してみる #
プロビジョニングの実行 #
aws sso login --profile my_profile
terragrunt plan
terragrunt apply -auto-approve
デプロビジョニングの実行 #
aws sso login --profile my_profile
export AWS_PROFILE=my_profile
export AWS_SDK_LOAD_CONFIG=1
terragrunt destroy