今まで逃げてきたTerraformを使ったインフラ構築を行うため、Dockerを使ってTerraformの実行環境を作ったので備忘録として残しておく。
マジでインフラの領域の知識がなさすぎて、解説とかは全然できないが、とりあえずS3バケットを作成できただけでも進歩。
環境情報
・macOS Sequoia
・Docker
・VSCode
前提
Dockerがすでに導入されており、docker-compose.ymlの記述がある程度理解でき、Dockerを触ったことがある方を前提としています。
Lambda作成の権限がついているAWSユーザのアクセスキーIDとシークレットキーを控えていること。(環境変数に設定する際に使用)
AWSユーザの作成がわからない人はググるかAIに聞いてくれればわかりやすい手順がたくさん出てきます。
以下のプロジェクトを例で手順を書いていきます。
プロジェクト構成
.
├── docker-compose.yml
└── terraform
├── environments
│ └── dev
│ ├── dev.tfvars
│ ├── main.tf
│ ├── outputs.tf
│ ├── provider.tf
│ ├── terraform.tfstate
│ └── variables.tf
├── lambda
│ └── index.js
└── modules
├── compute
│ ├── main.tf
│ ├── outputs.tf
│ └── variables.tf
├── data
│ ├── main.tf
│ ├── outputs.tf
│ └── variables.tf
├── lambda
│ ├── lambda_function_dev.zip
│ ├── main.tf
│ ├── outputs.tf
│ └── variables.tf
└── network
├── main.tf
├── outputs.tf
└── variables.tf
手順1
.envファイルを作成し、環境変数を設定
AWS_ACCESS_KEY_ID=[AWSユーザ作成した際のアクセスキー]
AWS_SECRET_ACCESS_KEY=[AWSユーザ作成した際のシークレットアクセスキー]
手順2
docker-composeファイルの作成
version: "3.8"
services:
terraform-trial:
image: hashicorp/terraform:1.7.5
environment:
- AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID}
- AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY}
- AWS_REGION=${AWS_REGION:-ap-northeast-1}
volumes:
- ./trial/terraform:/terraform
working_dir: /terraform
entrypoint: ash
tty: true
手順3
docker-compose up -dコマンド実施、実施後コンテナにアタッチ。
手順3まで実施できれば最低限テラフォームを書く環境は整った。
docker-compose up -d
手順4
ディレクトリ作成
ディレクトリの構成はなんか前にツイッターで見かけた構成が綺麗だったからそれを参考に作成
mkdir -p ./environments/dev ./modules/compute ./modules/data ./modules/network
手順5
tfファイルを作成
dev.tfvarsのbucket_name_prefixがバケット名になります。
バケット名の後ろにmain.tfの「resource “random_id” “suffix” {」で定義されているランダムな文字列がくっつきます。
/environments/dev/
# 開発環境の具体的な設定値を定義するファイル
# terraform apply -var-file=dev.tfvars コマンドで使用される
region = "ap-northeast-1" # デプロイするAWSリージョン(東京)
environment = "dev" # 開発環境を示す識別子
# S3バケット設定
bucket_name_prefix = "trial-s3Bucket" # アプリケーションデータを示すバケット名
enable_versioning = true # データの変更履歴を保持する機能を有効化
enable_lifecycle_rules = true # コスト最適化のためのデータ自動移行・削除ポリシーを有効化
expiration_days = 1095 # データを3年間保持し、その後自動削除(コンプライアンス要件)
# 開発環境のインフラリソース定義ファイル
# 必要なモジュールを呼び出してdev環境のリソースを作成
# S3バケットモジュールを追加
# 開発環境用のデータ保存バケットをセットアップ
module "data" {
source = "../../modules/data"
environment = var.environment # 「dev」環境として設定
bucket_name_prefix = var.bucket_name_prefix # バケット名のプレフィックス設定
enable_versioning = var.enable_versioning # バージョニング機能の有効/無効を設定
enable_lifecycle_rules = var.enable_lifecycle_rules # ライフサイクルポリシーの有効/無効を設定
expiration_days = var.expiration_days # データ保持期間を設定
}
# 他のモジュールも必要に応じて追加
# 例: コンピューティングリソース、データベース、ネットワークなど
# 開発環境の出力値定義ファイル
# 作成したリソースの重要な情報を出力し、他のシステムやユーザーが参照できるようにする
# 既存の出力...
# S3バケット出力
# データストレージモジュールから作成されたバケットの情報を出力
output "s3_bucket_id" {
description = "作成されたS3バケットのID"
value = module.data.bucket_id
# バケットを参照するための基本的な識別子(バケット名)
}
output "s3_bucket_arn" {
description = "S3バケットのARN"
value = module.data.bucket_arn
# IAMポリシーやリソースポリシーで使用するための完全修飾リソース名
}
output "s3_bucket_domain_name" {
description = "S3バケットのドメイン名"
value = module.data.bucket_domain_name
# バケットへのアクセスURLを構築するためのドメイン名
}
# AWSプロバイダと必要なバージョン設定のためのファイル
# Terraformがどのプロバイダ(AWS)を使用し、どのように認証するかを定義
provider "aws" {
region = var.region # 環境変数ファイルで指定されたリージョンを使用
}
terraform {
# 必要なプロバイダとバージョン制約の定義
# プロジェクトの安定性のため特定のバージョン範囲に制限
required_providers {
aws = {
source = "hashicorp/aws" # AWSリソースを管理するためのプロバイダ
version = "~> 4.0" # 4.xの最新バージョンを使用
}
archive = {
source = "hashicorp/archive" # ZIPファイルなどのアーカイブを作成するためのプロバイダ
version = "~> 2.0" # 2.xの最新バージョンを使用
}
}
# 必要に応じてバックエンドの設定を追加
# Terraformの状態ファイルをS3に保存するための設定(現在はコメントアウト)
# backend "s3" {
# bucket = "my-terraform-state"
# key = "dev/terraform.tfstate"
# region = "ap-northeast-1"
# }
}
variable "region" {
description = "AWSのリージョン"
type = string
}
variable "environment" {
description = "環境名"
type = string
default = "dev"
}
# S3バケット関連の変数
variable "bucket_name_prefix" {
description = "S3バケット名のプレフィックス"
type = string
# バケットの用途を示す名前の接頭辞
}
variable "enable_versioning" {
description = "バケットのバージョニングを有効にするかどうか"
type = bool
default = true
# データ保全のためのバージョン管理機能
}
variable "enable_lifecycle_rules" {
description = "ライフサイクルルールを有効にするかどうか"
type = bool
default = false
# ストレージコスト最適化のための自動データ管理ポリシー
}
variable "expiration_days" {
description = "オブジェクトの有効期限(日数)"
type = number
default = 730 # 2年
# コンプライアンスやコスト管理のためのデータ保持期間設定
}
/modules/data/
# S3バケットモジュール
# このモジュールは環境ごとのデータ保存用のS3バケットとその設定を管理します
# S3バケットの作成
# アプリケーションデータを保存するためのメインストレージとして機能
resource "aws_s3_bucket" "data_bucket" {
bucket = "${var.bucket_name_prefix}-${var.environment}-${random_id.suffix.hex}"
tags = {
Name = "${var.bucket_name_prefix}-${var.environment}"
Environment = var.environment
}
}
# ランダムなサフィックスを生成してバケット名の一意性を確保
# S3バケット名はAWS全体で一意である必要があるため、衝突を防ぐためのランダム文字列を生成
resource "random_id" "suffix" {
byte_length = 4
}
# バケットのパブリックアクセスをブロック
# セキュリティのため、意図しないパブリックアクセスを防止する設定
resource "aws_s3_bucket_public_access_block" "data_bucket_access" {
bucket = aws_s3_bucket.data_bucket.id
block_public_acls = true
block_public_policy = true
ignore_public_acls = true
restrict_public_buckets = true
}
# バケットのバージョニング設定
# データ保護のため、オブジェクトの以前のバージョンを保持する機能
# 誤削除や上書きからの復旧が可能になります
resource "aws_s3_bucket_versioning" "data_bucket_versioning" {
bucket = aws_s3_bucket.data_bucket.id
versioning_configuration {
status = var.enable_versioning ? "Enabled" : "Disabled"
}
}
# ライフサイクルルールの設定(オプション)
# ストレージコスト最適化のための自動データ管理ポリシー
# 時間経過とともにデータをより低コストなストレージクラスに移行し、最終的に削除します
resource "aws_s3_bucket_lifecycle_configuration" "data_bucket_lifecycle" {
bucket = aws_s3_bucket.data_bucket.id
rule {
id = "archive-and-delete"
status = var.enable_lifecycle_rules ? "Enabled" : "Disabled"
transition {
days = 90
storage_class = "STANDARD_IA" # 90日後にアクセス頻度の低いストレージに移行
}
transition {
days = 365
storage_class = "GLACIER" # 1年後にさらに低コストの長期保存用ストレージに移行
}
expiration {
days = var.expiration_days # 設定した日数後にオブジェクトを完全に削除
}
}
}
# データストレージモジュールの出力値定義ファイル
# 作成したS3バケットの情報を他のリソースやモジュールで参照できるようにするための出力値
output "bucket_id" {
description = "作成されたS3バケットのID"
value = aws_s3_bucket.data_bucket.id
# バケットを参照するために使用する基本的な識別子
}
output "bucket_arn" {
description = "S3バケットのARN"
value = aws_s3_bucket.data_bucket.arn
# IAMポリシーやリソースポリシーで参照するためのAmazon Resource Name
}
output "bucket_domain_name" {
description = "S3バケットのドメイン名"
value = aws_s3_bucket.data_bucket.bucket_domain_name
# バケットにHTTPリクエストを送信する際のエンドポイントURLを構築するために使用
}
output "bucket_regional_domain_name" {
description = "S3バケットのリージョナルドメイン名"
value = aws_s3_bucket.data_bucket.bucket_regional_domain_name
# 特定リージョンに最適化されたエンドポイントURLを構築するために使用
}
# データストレージモジュールの変数定義ファイル
# S3バケットの設定を外部から制御するためのパラメータを定義
variable "environment" {
description = "デプロイする環境(dev、staging、prod)"
type = string
# 環境ごとに異なるバケットを作成するための識別子
}
variable "bucket_name_prefix" {
description = "S3バケット名のプレフィックス"
type = string
# バケットの用途を示す名前の接頭辞。最終的なバケット名は prefix-env-randomsuffix 形式になる
}
variable "enable_versioning" {
description = "バケットのバージョニングを有効にするかどうか"
type = bool
default = true
# データの変更履歴を保持する機能。誤削除や上書きからの復旧に役立つ
}
variable "enable_lifecycle_rules" {
description = "ライフサイクルルールを有効にするかどうか"
type = bool
default = false
# ストレージコスト最適化のための自動データ移行・削除ポリシーを有効化する設定
}
variable "expiration_days" {
description = "オブジェクトの有効期限(日数)"
type = number
default = 730 # 2年
# データ保持期間を設定。コンプライアンス要件やコスト管理に関連
}
手順6
terraform initコマンド実行
cd environments/dev
terraform init
実行後、「Terraform has been successfully initialized!」が出ればOK!
手順7
terraform planコマンド実施
terraform plan -var-file=dev.tfvars
実行結果
S3バケット関連が「+」になっていればOK
Terraform used the selected providers to generate the following
execution plan. Resource actions are indicated with the following
symbols:
+ create
Terraform will perform the following actions:
# module.data.aws_s3_bucket.data_bucket will be created
+ resource "aws_s3_bucket" "data_bucket" {
+ acceleration_status = (known after apply)
+ acl = (known after apply)
+ arn = (known after apply)
+ bucket = (known after apply)
+ bucket_domain_name = (known after apply)
+ bucket_prefix = (known after apply)
+ bucket_regional_domain_name = (known after apply)
+ force_destroy = false
+ hosted_zone_id = (known after apply)
+ id = (known after apply)
+ object_lock_enabled = (known after apply)
+ policy = (known after apply)
+ region = (known after apply)
+ request_payer = (known after apply)
+ tags = {
+ "Environment" = "dev"
+ "Name" = "trial-S3bucket-dev"
}
+ tags_all = {
+ "Environment" = "dev"
+ "Name" = "trial-S3bucket-dev"
}
+ website_domain = (known after apply)
+ website_endpoint = (known after apply)
}
# module.data.aws_s3_bucket_lifecycle_configuration.data_bucket_lifecycle will be created
+ resource "aws_s3_bucket_lifecycle_configuration" "data_bucket_lifecycle" {
+ bucket = (known after apply)
+ id = (known after apply)
+ rule {
+ id = "archive-and-delete"
+ status = "Enabled"
+ expiration {
+ days = 1095
+ expired_object_delete_marker = (known after apply)
}
+ transition {
+ days = 365
+ storage_class = "GLACIER"
}
+ transition {
+ days = 90
+ storage_class = "STANDARD_IA"
}
}
}
# module.data.aws_s3_bucket_public_access_block.data_bucket_access will be created
+ resource "aws_s3_bucket_public_access_block" "data_bucket_access" {
+ block_public_acls = true
+ block_public_policy = true
+ bucket = (known after apply)
+ id = (known after apply)
+ ignore_public_acls = true
+ restrict_public_buckets = true
}
# module.data.aws_s3_bucket_versioning.data_bucket_versioning will be created
+ resource "aws_s3_bucket_versioning" "data_bucket_versioning" {
+ bucket = (known after apply)
+ id = (known after apply)
+ versioning_configuration {
+ mfa_delete = (known after apply)
+ status = "Enabled"
}
}
# module.data.random_id.suffix will be created
+ resource "random_id" "suffix" {
+ b64_std = (known after apply)
+ b64_url = (known after apply)
+ byte_length = 4
+ dec = (known after apply)
+ hex = (known after apply)
+ id = (known after apply)
}
Plan: 5 to add, 0 to change, 0 to destroy.
Changes to Outputs:
+ s3_bucket_arn = (known after apply)
+ s3_bucket_domain_name = (known after apply)
+ s3_bucket_id = (known after apply)
─────────────────────────────────────────────────────────────────────
Note: You didn't use the -out option to save this plan, so Terraform
can't guarantee to take exactly these actions if you run "terraform
apply" now.
手順8
terraform applyコマンド実施
terraform apply -var-file=dev.tfvars
「Do you want to perform these actions?
Terraform will perform the actions described above.
Only ‘yes’ will be accepted to approve.」
と聞かれるので「yes」を入力し、「Apply complete!」が表示されればS3バケット作成完了。
手順9
作成されているかの確認
マネジメントコンソールからS3バケットを確認し、バケットが作成されていればOK

おまけ
作成したS3バケットを消したい時はenvironments/dev配下のtfファイルからS3バケットに関する記載を消すなりコメントアウトすればOK
/environments/dev
region = "ap-northeast-1" # デプロイするAWSリージョン(東京)
# AWSプロバイダと必要なバージョン設定のためのファイル
# Terraformがどのプロバイダ(AWS)を使用し、どのように認証するかを定義
provider "aws" {
region = var.region # 環境変数ファイルで指定されたリージョンを使用
}
terraform {
# 必要なプロバイダとバージョン制約の定義
# プロジェクトの安定性のため特定のバージョン範囲に制限
required_providers {
aws = {
source = "hashicorp/aws" # AWSリソースを管理するためのプロバイダ
version = "~> 4.0" # 4.xの最新バージョンを使用
}
archive = {
source = "hashicorp/archive" # ZIPファイルなどのアーカイブを作成するためのプロバイダ
version = "~> 2.0" # 2.xの最新バージョンを使用
}
}
}
# 開発環境の変数定義ファイル
# dev環境に特化したパラメータを設定するための変数を定義
variable "region" {
description = "AWSのリージョン"
type = string
# リソースをデプロイするAWSのリージョン(地理的なデータセンターの場所)
}
terraform plan -var-file=dev.tfvarsを実施。
手順7で+になっていた部分が-になればOK
Terraform used the selected providers to generate the following
execution plan. Resource actions are indicated with the following
symbols:
- destroy
Terraform will perform the following actions:
# module.data.aws_s3_bucket.data_bucket will be destroyed
# (because aws_s3_bucket.data_bucket is not in configuration)
- resource "aws_s3_bucket" "data_bucket" {
- arn = "arn:aws:s3:::trial-s3bucket-dev-b8b4ed89" -> null
- bucket = "trial-s3bucket-dev-b8b4ed89" -> null
- bucket_domain_name = "trial-s3bucket-dev-b8b4ed89.s3.amazonaws.com" -> null
- bucket_regional_domain_name = "trial-s3bucket-dev-b8b4ed89.s3.ap-northeast-1.amazonaws.com" -> null
- force_destroy = false -> null
- hosted_zone_id = "Z2M4EHUR26P7ZW" -> null
- id = "trial-s3bucket-dev-b8b4ed89" -> null
- object_lock_enabled = false -> null
- region = "ap-northeast-1" -> null
- request_payer = "BucketOwner" -> null
- tags = {
- "Environment" = "dev"
- "Name" = "trial-s3bucket-dev"
} -> null
- tags_all = {
- "Environment" = "dev"
- "Name" = "trial-s3bucket-dev"
} -> null
- grant {
- id = "9e7dece75611f03cd60f299df8085b17c2dbeba2b45d683c315bb7db6d20e33c" -> null
- permissions = [
- "FULL_CONTROL",
] -> null
- type = "CanonicalUser" -> null
}
- lifecycle_rule {
- abort_incomplete_multipart_upload_days = 0 -> null
- enabled = true -> null
- id = "archive-and-delete" -> null
- tags = {} -> null
- expiration {
- days = 1095 -> null
- expired_object_delete_marker = false -> null
}
- transition {
- days = 365 -> null
- storage_class = "GLACIER" -> null
}
- transition {
- days = 90 -> null
- storage_class = "STANDARD_IA" -> null
}
}
- server_side_encryption_configuration {
- rule {
- bucket_key_enabled = false -> null
- apply_server_side_encryption_by_default {
- sse_algorithm = "AES256" -> null
}
}
}
- versioning {
- enabled = true -> null
- mfa_delete = false -> null
}
}
# module.data.aws_s3_bucket_lifecycle_configuration.data_bucket_lifecycle will be destroyed
# (because aws_s3_bucket_lifecycle_configuration.data_bucket_lifecycle is not in configuration)
- resource "aws_s3_bucket_lifecycle_configuration" "data_bucket_lifecycle" {
- bucket = "trial-s3bucket-dev-b8b4ed89" -> null
- id = "trial-s3bucket-dev-b8b4ed89" -> null
- rule {
- id = "archive-and-delete" -> null
- status = "Enabled" -> null
- expiration {
- days = 1095 -> null
- expired_object_delete_marker = false -> null
}
- filter {
}
- transition {
- days = 365 -> null
- storage_class = "GLACIER" -> null
}
- transition {
- days = 90 -> null
- storage_class = "STANDARD_IA" -> null
}
}
}
# module.data.aws_s3_bucket_public_access_block.data_bucket_access will be destroyed
# (because aws_s3_bucket_public_access_block.data_bucket_access is not in configuration)
- resource "aws_s3_bucket_public_access_block" "data_bucket_access" {
- block_public_acls = true -> null
- block_public_policy = true -> null
- bucket = "trial-s3bucket-dev-b8b4ed89" -> null
- id = "trial-s3bucket-dev-b8b4ed89" -> null
- ignore_public_acls = true -> null
- restrict_public_buckets = true -> null
}
# module.data.aws_s3_bucket_versioning.data_bucket_versioning will be destroyed
# (because aws_s3_bucket_versioning.data_bucket_versioning is not in configuration)
- resource "aws_s3_bucket_versioning" "data_bucket_versioning" {
- bucket = "trial-s3bucket-dev-b8b4ed89" -> null
- id = "trial-s3bucket-dev-b8b4ed89" -> null
- versioning_configuration {
- status = "Enabled" -> null
}
}
# module.data.random_id.suffix will be destroyed
# (because random_id.suffix is not in configuration)
- resource "random_id" "suffix" {
- b64_std = "uLTtiQ==" -> null
- b64_url = "uLTtiQ" -> null
- byte_length = 4 -> null
- dec = "3098865033" -> null
- hex = "b8b4ed89" -> null
- id = "uLTtiQ" -> null
}
Plan: 0 to add, 0 to change, 5 to destroy.
Changes to Outputs:
- s3_bucket_arn = "arn:aws:s3:::trial-s3bucket-dev-b8b4ed89" -> null
- s3_bucket_domain_name = "trial-s3bucket-dev-b8b4ed89.s3.amazonaws.com" -> null
- s3_bucket_id = "trial-s3bucket-dev-b8b4ed89" -> null
─────────────────────────────────────────────────────────────────────
Note: You didn't use the -out option to save this plan, so Terraform
can't guarantee to take exactly these actions if you run "terraform
apply" now.
/terraform/environments/dev #
terraform apply -var-file=dev.tfvarsを実施。
「Apply complete! Resources: 0 added, 0 changed, 5 destroyed.」が出ているのを確認後、S3バケットを確認し、対象のものが消えてればOK
今後はマネジメントコンソールはなるべく使わずにTerraformを使って環境構築していくように頑張る。
ほぼほぼAIに聞きながら環境構築したせいで参考サイトがほぼない。。。
参考サイト(ツイート)