Site cover image

Site icon imageViserhaut Tech Blog

This blog is a blog that records the daily learning of an infrastructure engineer. It aims to "Viser Haut" or strive for the highest.

AWS VaultとDockerを使ったTerraform実行環境の構築

概要

Terraformを安全かつ効率的に実行するために、AWS Vaultで認証情報を管理し、Dockerを利用してTerraformを実行する環境を構築します。この方法は、開発環境を整備する際に役立ち、ローカル環境に認証情報を直接保存するリスクを軽減できます。


この記事で伝えたいこと

  • AWS Vaultを用いた安全なAWS認証情報の管理方法
  • Dockerを活用したTerraformの実行環境構築手順
  • 上記を組み合わせた、効率的でセキュアなインフラ運用方法

解決したい課題

  1. AWS認証情報の安全な管理が難しい

    プロジェクトの認証情報をローカル環境で管理すると、漏洩や誤用のリスクが高まります。

  2. Terraformの環境セットアップの手間

    バージョン管理や依存ライブラリの管理が煩雑で、環境の再現性が損なわれがちです。


課題の原因

  1. 認証情報を直接ローカル環境に保存

    ~/.aws/credentialsファイルに認証情報を保存するのは便利ですが、セキュリティリスクが伴います。

  2. Terraformの依存環境に関する問題

    Terraformのバージョンがプロジェクトによって異なるため、環境間での整合性を取るのが難しい。


課題を解決する技術、手法

技術、手法の概要
  • AWS Vault

    AWS認証情報をセキュアに保存し、必要な時だけ安全に引き出す仕組みを提供します。

  • Docker

    Terraformをコンテナ内で実行することで、環境の一貫性を確保し、依存関係の管理が容易になります。


技術、手法の効果
  1. AWS Vault
    • 認証情報を暗号化して保存するため、セキュリティが向上。
    • 認証情報の有効期限を管理できるため、誤用のリスクを軽減。
  2. Docker
    • 一度セットアップした環境を使い回せるため、環境構築の手間を削減。
    • Terraformのバージョンや依存関係を簡単に管理できる。

構築手順
1. IAMユーザーの作成

AWS Management Consoleで必要な権限を持つIAMユーザー(例:iac-user)を作成し、多要素認証を有効化、アクセスキーを発行します。なお、IAMユーザには最終的に動作確認用としてS3Readonlyの権限を付与します。

2. AWS CLIのインストール

以下のコマンドを実行してAWS CLIをインストールします。

curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
unzip awscliv2.zip
sudo ./aws/install
3. AWS Vaultのインストール

AWS Vaultをインストールして設定します。

AWS_VAULT_VERSION="v7.2.0"
wget -O aws-vault "https://github.com/99designs/aws-vault/releases/download/${AWS_VAULT_VERSION}/aws-vault-linux-amd64"
sudo mv aws-vault /usr/local/bin/ && sudo chmod +x /usr/local/bin/aws-vault
4. パスワード管理ツールの設定

passを利用してAWS Vaultのバックエンドを設定します。

sudo apt-get update && sudo apt-get install -y pass gnupg
gpg --gen-key
pass init "YOUR_PUBLIC_KEY"
5. AWS Vaultプロファイルの作成

AWS VaultにIAMユーザーのプロファイルを追加して、動作確認を行います。

vi ~/.bashrc
# aws vault
export AWS_VAULT_BACKEND=pass
export AWS_VAULT_PASS_PREFIX=aws-vault
export AWS_SESSION_TOKEN_TTL=3h
export GPG_TTY=$(tty)

source ~/.bashrc

aws-vault add "iac-user"
aws-vault list

aws-vault exec iac-user -- aws sts get-caller-identity
aws-vault exec iac-user -- aws s3 ls
6. Docker Composeの設定

Terraformを実行するDocker Composeの構成を用意します。

compose.yaml

services:
  terraform:
    image: hashicorp/terraform:1.9.8
    volumes:
      - .:/terraform
    working_dir: /terraform
    environment:
      - AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID}
      - AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY}
      - AWS_SESSION_TOKEN=${AWS_SESSION_TOKEN}
7. Terraformの初期設定

Terraformの設定ファイルを作成します。

main.tf

terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }
}

provider "aws" {
  region = "ap-northeast-1"
}

# Create an S3 bucket
resource "aws_s3_bucket" "example_bucket" {
  bucket = "iac-test-bucket-12345"

  # タグの設定
  tags = {
    Name        = "ExampleBucket"
    Environment = "Development"
  }
}

resource "aws_s3_bucket_acl" "example_bucket_acl" {
  bucket = aws_s3_bucket.example_bucket.id
  acl    = "private"
}
8. Terraformコマンドの実行

以下のコマンドを実行して動作確認を行います。planは成功し、applyでは権限エラーとなることを確認します。

$ docker compose run --rm terraform init
Initializing the backend...
Initializing provider plugins...
- Reusing previous version of hashicorp/aws from the dependency lock file
- Using previously-installed hashicorp/aws v5.78.0

Terraform has been successfully initialized!

You may now begin working with Terraform. Try running "terraform plan" to see
any changes that are required for your infrastructure. All Terraform commands
should now work.

If you ever set or change modules or backend configuration for Terraform,
rerun this command to reinitialize your working directory. If you forget, other
commands will detect it and remind you to do so if necessary.
$ docker compose run --rm terraform plan

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:

  # aws_s3_bucket.example_bucket will be created
  + resource "aws_s3_bucket" "example_bucket" {
      + acceleration_status         = (known after apply)
      + acl                         = (known after apply)
      + arn                         = (known after apply)
      + bucket                      = "iac-test-bucket-12345"
      + 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" = "Development"
          + "Name"        = "ExampleBucket"
        }
      + tags_all                    = {
          + "Environment" = "Development"
          + "Name"        = "ExampleBucket"
        }
      + website_domain              = (known after apply)
      + website_endpoint            = (known after apply)

      + cors_rule (known after apply)

      + grant (known after apply)

      + lifecycle_rule (known after apply)

      + logging (known after apply)

      + object_lock_configuration (known after apply)

      + replication_configuration (known after apply)

      + server_side_encryption_configuration (known after apply)

      + versioning (known after apply)

      + website (known after apply)
    }

  # aws_s3_bucket_acl.example_bucket_acl will be created
  + resource "aws_s3_bucket_acl" "example_bucket_acl" {
      + acl    = "private"
      + bucket = (known after apply)
      + id     = (known after apply)

      + access_control_policy (known after apply)
    }

Plan: 2 to add, 0 to change, 0 to destroy.

───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────

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.
$ docker compose run --rm terraform apply

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:

  # aws_s3_bucket.example_bucket will be created
  + resource "aws_s3_bucket" "example_bucket" {
      + acceleration_status         = (known after apply)
      + acl                         = (known after apply)
      + arn                         = (known after apply)
      + bucket                      = "iac-test-bucket-12345"
      + 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" = "Development"
          + "Name"        = "ExampleBucket"
        }
      + tags_all                    = {
          + "Environment" = "Development"
          + "Name"        = "ExampleBucket"
        }
      + website_domain              = (known after apply)
      + website_endpoint            = (known after apply)

      + cors_rule (known after apply)

      + grant (known after apply)

      + lifecycle_rule (known after apply)

      + logging (known after apply)

      + object_lock_configuration (known after apply)

      + replication_configuration (known after apply)

      + server_side_encryption_configuration (known after apply)

      + versioning (known after apply)

      + website (known after apply)
    }

  # aws_s3_bucket_acl.example_bucket_acl will be created
  + resource "aws_s3_bucket_acl" "example_bucket_acl" {
      + acl    = "private"
      + bucket = (known after apply)
      + id     = (known after apply)

      + access_control_policy (known after apply)
    }

Plan: 2 to add, 0 to change, 0 to destroy.

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes

aws_s3_bucket.example_bucket: Creating...
╷
│ Error: creating S3 Bucket (iac-test-bucket-12345): operation error S3: CreateBucket, https response error StatusCode: 403, RequestID: D7MPEW41G5ZMCZGC, HostID: DNjvSBf+qaRJWYw7GBg0QbA5Yu4dFbzAFG1OP7u3c1vnSfR13jzdU5bghHm+mlwviBhQGPzePuM=, api error AccessDenied: User: arn:aws:iam::846373975592:user/iac-user is not authorized to perform: s3:CreateBucket on resource: "arn:aws:s3:::iac-test-bucket-12345" because no identity-based policy allows the s3:CreateBucket action
│ 
│   with aws_s3_bucket.example_bucket,
│   on main.tf line 18, in resource "aws_s3_bucket" "example_bucket":
│   18: resource "aws_s3_bucket" "example_bucket" {
│ 
╵ 

この記事を通して、安全で効率的なTerraform実行環境を構築できるようになります。AWS VaultとDockerを活用することで、セキュリティと生産性を両立したインフラ運用を実現しましょう。