DockerでTerraform実行環境作成

備忘録
この記事は約36分で読めます。

今まで逃げてきたTerraformを使ったインフラ構築を行うため、Dockerを使ってTerraformの実行環境を作ったので備忘録として残しておく。

マジでインフラの領域の知識がなさすぎて、解説とかは全然できないが、とりあえずS3バケットを作成できただけでも進歩。

環境情報

・macOS Sequoia

・Docker

・VSCode

前提

Dockerがすでに導入されており、docker-compose.ymlの記述がある程度理解でき、Dockerを触ったことがある方を前提としています。

Lambda作成の権限がついているAWSユーザのアクセスキーIDとシークレットキーを控えていること。(環境変数に設定する際に使用)

AWSユーザの作成がわからない人はググるかAIに聞いてくれればわかりやすい手順がたくさん出てきます。

以下のプロジェクトを例で手順を書いていきます。

GitHub - aitea1/allTest at docker-terraform
色々なテスト用. Contribute to aitea1/allTest development by creating an account on GitHub.

プロジェクト構成

Tree
.
├── 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ファイルを作成し、環境変数を設定

.env
AWS_ACCESS_KEY_ID=[AWSユーザ作成した際のアクセスキー]
AWS_SECRET_ACCESS_KEY=[AWSユーザ作成した際のシークレットアクセスキー]

手順2

docker-composeファイルの作成

Bash
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まで実施できれば最低限テラフォームを書く環境は整った。

Bash
docker-compose up -d

手順4

ディレクトリ作成

ディレクトリの構成はなんか前にツイッターで見かけた構成が綺麗だったからそれを参考に作成

Bash
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/

dev.tfvars
# 開発環境の具体的な設定値を定義するファイル
# 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年間保持し、その後自動削除(コンプライアンス要件)
main.tf
# 開発環境のインフラリソース定義ファイル
# 必要なモジュールを呼び出して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     # データ保持期間を設定
}

# 他のモジュールも必要に応じて追加
# 例: コンピューティングリソース、データベース、ネットワークなど
outputs.tf
# 開発環境の出力値定義ファイル
# 作成したリソースの重要な情報を出力し、他のシステムやユーザーが参照できるようにする

# 既存の出力...

# 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を構築するためのドメイン名
}
provider.tf
# 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"
  # }
}
variables.tf
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/

main.tf
# 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  # 設定した日数後にオブジェクトを完全に削除
    }
  }
}
outputs.tf
# データストレージモジュールの出力値定義ファイル
# 作成した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を構築するために使用
}
variables.tf
# データストレージモジュールの変数定義ファイル
# 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コマンド実行

Bash
cd environments/dev
terraform init

実行後、「Terraform has been successfully initialized!」が出ればOK!

手順7

terraform planコマンド実施

Bash
terraform plan -var-file=dev.tfvars

実行結果

S3バケット関連が「+」になっていればOK

Bash
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コマンド実施

Bash
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

dev.tfvars
region        = "ap-northeast-1"  # デプロイするAWSリージョン(東京)
provider.tf
# 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の最新バージョンを使用
    }
  }
}
variables.tf
# 開発環境の変数定義ファイル
# dev環境に特化したパラメータを設定するための変数を定義

variable "region" {
  description = "AWSのリージョン"
  type        = string
  # リソースをデプロイするAWSのリージョン(地理的なデータセンターの場所)
}

terraform plan -var-file=dev.tfvarsを実施。

手順7で+になっていた部分が-になればOK

Bash
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に聞きながら環境構築したせいで参考サイトがほぼない。。。

参考サイト(ツイート)

タイトルとURLをコピーしました