【保存版】terraform destroyの決定的な使い方ガイド!リスクゼロで実行する7つの手順

terraform destroyとは?初心者でもわかる基礎知識

terraform destroyコマンドの基本的な機能と目的

Terraform destroyは、Terraformで作成したインフラストラクチャを完全に削除するためのコマンドです。このコマンドは、terraform applyの逆の動作を行い、管理されているすべてのリソースを安全に解体します。

基本的な使用方法:

# 基本的なdestroyコマンド
terraform destroy

# 実行計画の確認のみ
terraform plan -destroy

# 自動承認付きのdestroy
terraform destroy -auto-approve

主な特徴:

  1. べき等性の保証
  • 複数回実行しても同じ結果になることを保証
  • 既に削除されているリソースは無視される
  1. 依存関係の自動解決
  • リソース間の依存関係を考慮して適切な順序で削除
  • 例:ECSクラスタを削除する前に、タスクとサービスを先に削除
  1. ステート管理との連携
  • terraform.tfstateファイルを参照して現在の状態を把握
  • 削除完了後、ステートファイルも適切に更新

destroy実行時の内部動作の仕組み

terraform destroyコマンドが実行される際の内部動作を理解することで、より安全な運用が可能になります。

実行フロー:

  1. 計画フェーズ
   graph TB
       A[設定ファイル読み込み] --> B[現在の状態確認]
       B --> C[削除計画の作成]
       C --> D[依存関係グラフの構築]
       D --> E[削除順序の決定]
  1. 実行フェーズ
  • リソースの依存関係グラフを逆順に走査
  • 各リソースのdestroyメソッドを呼び出し
  • 削除結果をステートファイルに反映

重要な内部処理:

フェーズ処理内容目的
前処理プロバイダーの初期化API接続の準備
計画作成依存関係の解析安全な削除順序の決定
リソース削除APIコール実行実際のリソース削除
後処理ステート更新状態の整合性維持

エラーハンドリング機能:

  • タイムアウト処理
  • リトライメカニズム
  • ロールバックのサポート
  • エラーメッセージの詳細な記録

destroyコマンドは、これらの機能を組み合わせることで、複雑なインフラストラクチャでも安全な削除を実現します。次のセクションでは、実行前に確認すべき重要なポイントについて詳しく説明します。

terraform destroyを実行する前に必ず確認すべき7つのポイント

実行計画の確認方法とリスク評価の手順

terraform destroyを実行する前の計画確認は、事故を防ぐための最も重要なステップです。以下の手順に従って、慎重に確認を行いましょう。

1. 実行計画の確認手順:

# 削除計画の詳細な確認
terraform plan -destroy -detailed-exitcode

# 特定のリソースのみの削除計画確認
terraform plan -destroy -target="aws_instance.example"

# 変数値を指定して計画確認
terraform plan -destroy -var="environment=staging"

リスク評価チェックリスト:

  • [ ] 対象環境(本番/検証/開発)の確認
  • [ ] 削除対象リソースの完全な列挙
  • [ ] 依存サービスへの影響調査
  • [ ] データ損失のリスク評価
  • [ ] 復旧時間の見積もり

依存関係の把握と削除順序の確認方法

リソース間の依存関係を正確に把握することで、予期しない削除の連鎖を防ぐことができます。

依存関係の可視化:

# グラフの生成
terraform graph | dot -Tsvg > dependency.svg

# 詳細な依存関係の出力
terraform state list
terraform state show aws_vpc.main

重要な確認項目:

確認項目確認方法注意点
直接的な依存関係terraform graph削除順序の確認
暗黙の依存関係リソース設定確認クロススタック参照
外部依存関係手動調査他チームの利用状況
データ依存関係バックアップ確認永続データの有無

バックアップとロールバック戦略の準備

安全なdestroyの実行には、適切なバックアップとロールバック戦略が不可欠です。

バックアップ手順:

  1. ステートファイルのバックアップ
   # ステートファイルのバックアップ作成
   cp terraform.tfstate terraform.tfstate.backup

   # S3へのバックアップ
   aws s3 cp terraform.tfstate s3://backup-bucket/$(date +%Y%m%d)/
  1. 重要データのスナップショット
   # EC2スナップショットの作成例
   resource "aws_ebs_snapshot" "backup" {
     volume_id = aws_ebs_volume.main.id
     description = "Pre-destroy backup"
     tags = {
       Name = "pre-destroy-backup"
       Timestamp = timestamp()
     }
   }

ロールバック戦略:

  1. 即時ロールバック手順の準備
  • terraform.tfstateの復元手順
  • 重要リソースの再作成スクリプト
  • 依存サービスの復旧手順
  1. 復旧時間目標(RTO)の設定
  • 許容できるダウンタイムの定義
  • 必要なリソースの確保
  • チーム間の連携体制確認
  1. 検証環境での予行演習
  • 削除とロールバックの実践
  • タイムライン記録
  • 問題点の洗い出し

これらの確認ポイントを慎重にチェックすることで、terraform destroyの実行リスクを最小限に抑えることができます。次のセクションでは、これらの確認を踏まえた上での具体的な実行手順について説明します。

実践!terraform destroyの安全な実行手順

ステップバイステップで学ぶdestroyコマンドの使い方

terraform destroyを安全に実行するための具体的な手順を、実践的な例を交えて解説します。

1. 事前準備フェーズ

# 最新の状態を取得
terraform init
terraform get -update

# 現在のステート確認
terraform show

# 実行計画の確認
terraform plan -destroy -out=destroy.plan

2. 安全確認フェーズ

# プラン内容の詳細確認
terraform show destroy.plan

# 影響範囲の確認
terraform state list

# 依存関係の確認
terraform graph | dot -Tsvg > destroy-plan.svg

3. 実行フェーズ

# プランに基づいた実行
terraform apply destroy.plan

# または直接実行(非推奨)
terraform destroy -auto-approve

各フェーズでの確認項目:

フェーズ確認項目合格基準
事前準備設定ファイルの整合性エラーなし
安全確認リソース依存関係想定通りの順序
実行削除進捗エラーなし
完了確認リソース存在確認全て削除済み

-targetオプションを使用した部分的な削除の方法

特定のリソースのみを安全に削除する方法について説明します。

基本的な使用方法:

# 特定のリソースの削除
terraform destroy -target="aws_instance.example"

# 複数リソースの削除
terraform destroy \
  -target="aws_instance.web" \
  -target="aws_eip.web_ip"

高度な使用例:

# モジュール内のリソース削除
terraform destroy -target="module.vpc.aws_subnet.public[0]"

# タグベースの削除(スクリプト例)
#!/bin/bash
for resource in $(terraform state list | grep "environment=dev"); do
  terraform destroy -target="$resource" -auto-approve
done

自動化スクリプトでの使用方法とベストプラクティス

CI/CDパイプラインやシェルスクリプトでterraform destroyを使用する際のベストプラクティスを紹介します。

1. 安全な自動化スクリプトの例:

#!/bin/bash
set -e

# 環境変数の検証
if [ -z "$TF_WORKSPACE" ]; then
    echo "Error: TF_WORKSPACE is not set"
    exit 1
fi

# 事前チェック関数
function pre_destroy_check() {
    local workspace=$1

    # プロダクション環境の保護
    if [[ "$workspace" == "production" ]]; then
        echo "Error: Cannot destroy production environment"
        exit 1
    }

    # ステートファイルの存在確認
    if [ ! -f "terraform.tfstate" ]; then
        echo "Error: State file not found"
        exit 1
    }
}

# メイン処理
pre_destroy_check "$TF_WORKSPACE"

# バックアップ作成
timestamp=$(date +%Y%m%d_%H%M%S)
terraform state pull > "tfstate_backup_${timestamp}.json"

# 実行計画の作成と確認
terraform plan -destroy -out=destroy.plan

# 承認プロセス(オプション)
read -p "Continue with destroy? (y/n) " -n 1 -r
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
    exit 1
fi

# 実行
terraform apply destroy.plan

ベストプラクティス:

  1. 環境分離
   # 環境別の変数定義
   variable "environment" {
     type = string
     validation {
       condition = contains(["dev", "staging", "prod"], var.environment)
       error_message = "Environment must be dev, staging, or prod."
     }
   }
  1. リソースタグ付け
   # 共通タグの定義
   locals {
     common_tags = {
       Environment = var.environment
       ManagedBy   = "terraform"
       Team        = "infrastructure"
     }
   }
  1. ロギングと監査
   # CloudWatchログの設定
   resource "aws_cloudwatch_log_group" "terraform_audit" {
     name              = "/terraform/destroy-audit"
     retention_in_days = 30

     tags = local.common_tags
   }

これらの手順とベストプラクティスを組み合わせることで、安全かつ効率的なterraform destroyの実行が可能になります。次のセクションでは、実行時に発生する可能性のあるトラブルとその対処方法について説明します。

terraform destroyのトラブルシューティング完全ガイド

よくあるエラーとその解決方法

terraform destroyの実行時に遭遇する可能性のある主要なエラーとその解決方法を解説します。

1. 認証関連のエラー

Error: error configuring Terraform AWS Provider: no valid credential sources found

解決手順:

# AWSクレデンシャルの確認
aws configure list

# 環境変数での設定
export AWS_ACCESS_KEY_ID="your_access_key"
export AWS_SECRET_ACCESS_KEY="your_secret_key"
export AWS_DEFAULT_REGION="ap-northeast-1"

2. 依存関係エラー

Error: error deleting S3 Bucket (example-bucket): BucketNotEmpty: 
status code: 409

解決手順:

# バケット内のオブジェクト確認
aws s3 ls s3://example-bucket --recursive

# 強制削除(注意して使用)
aws s3 rm s3://example-bucket --recursive
terraform destroy -target="aws_s3_bucket.example"

一般的なエラーと解決策:

エラータイプ主な原因解決アプローチ予防策
認証エラークレデンシャル不正認証情報の再設定定期的な認証確認
依存関係エラーリソース順序違反依存関係の手動解決グラフ確認の徹底
タイムアウトAPI制限/ネットワークリトライ/タイムアウト延長十分な時間確保
権限エラーIAM権限不足必要権限の付与事前権限確認

スタックしたdestroyの対処方法

destroyが途中で停止した場合の対処方法について説明します。

1. プロセス分析

# 実行状態の確認
terraform show

# ロックファイルの確認
ls -la .terraform.tfstate.lock.info

# ロック強制解除(慎重に使用)
terraform force-unlock LOCK_ID

2. 段階的な削除戦略

# 問題のリソースを特定
terraform state list

# 個別リソースの削除
terraform destroy -target="問題のリソース"

# ステート削除(最終手段)
terraform state rm "問題のリソース"

トラブルシューティングフローチャート:

graph TD
    A[Destroy失敗] --> B{エラー種別判定}
    B -->|認証エラー| C[認証情報確認]
    B -->|依存関係エラー| D[依存リソース確認]
    B -->|タイムアウト| E[再実行/時間延長]
    B -->|権限エラー| F[IAM権限確認]
    C --> G[クレデンシャル再設定]
    D --> H[手動依存解決]
    E --> I[パラメータ調整]
    F --> J[権限追加]

削除が失敗した場合のリカバリー手順

削除失敗時の安全なリカバリー手順を説明します。

1. 状態評価

# 現在の状態確認
terraform show

# プランの再作成
terraform plan -destroy -refresh-only

2. システマチックな復旧手順

#!/bin/bash
# リカバリースクリプトの例
set -e

function check_state() {
    terraform show > current_state.txt
    if grep -q "Error" current_state.txt; then
        echo "State contains errors"
        return 1
    fi
    return 0
}

function attempt_recovery() {
    local resource=$1

    echo "Attempting recovery for $resource"

    # バックアップ作成
    terraform state pull > "state_backup_$(date +%s).tfstate"

    # リソース個別削除試行
    if terraform destroy -target="$resource" -auto-approve; then
        echo "Successfully recovered $resource"
        return 0
    else
        echo "Recovery failed for $resource"
        return 1
    fi
}

# メイン処理
failed_resources=$(terraform show | grep "Error" | cut -d'"' -f2)

for resource in $failed_resources; do
    if ! attempt_recovery "$resource"; then
        echo "Manual intervention required for $resource"
    fi
done

リカバリーチェックリスト:

  1. 初期評価
  • エラーメッセージの記録
  • 影響範囲の特定
  • 依存リソースの確認
  1. バックアップ作成
   # ステートファイルバックアップ
   cp terraform.tfstate terraform.tfstate.$(date +%s).backup

   # 設定ファイルバックアップ
   cp -r . ../terraform-backup-$(date +%s)
  1. 段階的リカバリー
  • 問題の少ないリソースから対応
  • 依存関係を考慮した順序
  • 各ステップでの状態確認
  1. 検証と文書化
   # 状態の検証
   terraform validate
   terraform plan -destroy

   # 実行ログの保存
   terraform destroy -auto-approve 2>&1 | tee destroy_recovery.log

これらのトラブルシューティング手順を理解し、実践することで、ほとんどの問題に対処することが可能になります。次のセクションでは、terraform destroyを活用したコスト最適化戦略について説明します。

terraform destroyを活用したコスト最適化戦略

開発環境の自動シャットダウンの実装方法

開発環境のコスト最適化のため、不要な時間帯のリソースを自動的に削除する方法を解説します。

1. 時間帯別の環境管理システム

# variables.tf
variable "environment" {
  type    = string
  default = "development"
}

variable "working_hours" {
  type = object({
    start = number
    end   = number
  })
  default = {
    start = 9  # 9:00
    end   = 18 # 18:00
  }
}

# main.tf
locals {
  is_working_hours = (
    can(timestamp()) && 
    tonumber(formatdate("H", timestamp())) >= var.working_hours.start && 
    tonumber(formatdate("H", timestamp())) < var.working_hours.end
  )
}

# リソース定義例
resource "aws_instance" "dev_instance" {
  count = local.is_working_hours ? 1 : 0

  ami           = "ami-0c55b159cbfafe1f0"
  instance_type = "t3.micro"

  tags = {
    Name        = "dev-instance"
    Environment = var.environment
    AutoDestroy = "true"
  }
}

自動化スクリプトの実装:

#!/bin/bash
# schedule-destroy.sh

# タイムゾーンの設定
export TZ=Asia/Tokyo

# 現在の時刻を取得
current_hour=$(date +%H)

# 稼働時間外の場合、destroyを実行
if [ $current_hour -lt 9 ] || [ $current_hour -ge 18 ]; then
    echo "Outside working hours - destroying development environment"
    terraform destroy -auto-approve -var="environment=development"
else
    echo "Within working hours - environment will be maintained"
fi

cronでの設定例:

# crontab -e
0 18 * * 1-5 /path/to/schedule-destroy.sh >> /var/log/terraform-destroy.log 2>&1
0 9 * * 1-5 /path/to/terraform-apply.sh >> /var/log/terraform-apply.log 2>&1

使用していないリソースの特定と削除の自動化

未使用リソースを特定し、自動的に削除する仕組みの実装方法を説明します。

1. リソース使用状況の監視スクリプト

import boto3
import json
from datetime import datetime, timedelta

def find_unused_resources():
    # CloudWatchクライアントの初期化
    cloudwatch = boto3.client('cloudwatch')
    ec2 = boto3.client('ec2')

    # 未使用EC2インスタンスの検出
    instances = ec2.describe_instances()
    unused_instances = []

    for reservation in instances['Reservations']:
        for instance in reservation['Instances']:
            instance_id = instance['InstanceId']

            # CPU使用率の確認
            response = cloudwatch.get_metric_statistics(
                Namespace='AWS/EC2',
                MetricName='CPUUtilization',
                Dimensions=[{'Name': 'InstanceId', 'Value': instance_id}],
                StartTime=datetime.utcnow() - timedelta(days=7),
                EndTime=datetime.utcnow(),
                Period=3600,
                Statistics=['Average']
            )

            # 7日間のCPU使用率が10%未満の場合
            if not response['Datapoints'] or \
               max(point['Average'] for point in response['Datapoints']) < 10:
                unused_instances.append(instance_id)

    return unused_instances

def generate_terraform_destroy():
    unused = find_unused_resources()

    # Terraformコマンドの生成
    for instance_id in unused:
        print(f"terraform destroy -target=aws_instance.{instance_id} -auto-approve")

if __name__ == "__main__":
    generate_terraform_destroy()

2. 自動削除のワークフロー実装

# .github/workflows/cleanup.yml
name: Cleanup Unused Resources

on:
  schedule:
    - cron: '0 0 * * 0'  # 毎週日曜日に実行
  workflow_dispatch:      # 手動実行も可能

jobs:
  cleanup:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2

      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v1
        with:
          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          aws-region: ap-northeast-1

      - name: Setup Terraform
        uses: hashicorp/setup-terraform@v1

      - name: Find and Destroy Unused Resources
        run: |
          python3 scripts/find_unused_resources.py > destroy_commands.sh
          chmod +x destroy_commands.sh
          ./destroy_commands.sh

      - name: Notify Results
        if: always()
        uses: actions/github-script@v6
        with:
          script: |
            const script = require('./.github/scripts/notify.js')
            await script({github, context})

コスト最適化のベストプラクティス:

戦略実装方法期待効果
時間帯制御スケジュール実行非稼働時間のコスト削減
未使用検出メトリクス監視無駄なリソースの削除
自動化CI/CDパイプライン運用コストの削減
レポート定期メール通知可視化とトラッキング

コスト監視と通知の実装:

# cost_monitoring.tf
resource "aws_budgets_budget" "cost_alert" {
  name              = "development-cost-alert"
  budget_type       = "COST"
  limit_amount      = "1000"
  limit_unit        = "USD"
  time_period_start = "2024-01-01_00:00"
  time_unit         = "MONTHLY"

  notification {
    comparison_operator = "GREATER_THAN"
    threshold          = 80
    threshold_type     = "PERCENTAGE"
    notification_type  = "ACTUAL"

    subscriber_email_addresses = ["team@example.com"]
  }
}

これらの戦略を組み合わせることで、効果的なコスト最適化が実現できます。次のセクションでは、実践的なterraform destroy活用事例について説明します。

実践的なterraform destroy活用事例

大規模環境での安全な削除戦略

大規模なインフラストラクチャでterraform destroyを安全に実行するための戦略と実践例を紹介します。

1. 段階的な削除戦略の実装

# environments/production/main.tf
locals {
  deletion_stages = {
    stage1 = ["aws_lb.front", "aws_autoscaling_group.web"]
    stage2 = ["aws_ecs_service.api", "aws_ecs_cluster.main"]
    stage3 = ["aws_rds_cluster.main", "aws_elasticache_cluster.cache"]
    stage4 = ["aws_vpc.main", "aws_subnet.private", "aws_subnet.public"]
  }
}

# 段階的削除の実装
resource "null_resource" "staged_destroy" {
  for_each = local.deletion_stages

  provisioner "local-exec" {
    command = <<-EOF
      echo "Starting deletion stage: ${each.key}"
      for resource in ${join(" ", each.value)}; do
        terraform destroy -target=$resource -auto-approve
        if [ $? -ne 0 ]; then
          echo "Error in ${each.key} during $resource deletion"
          exit 1
        fi
      done
    EOF
  }
}

大規模環境での削除チェックリスト:

  1. 事前評価
   # リソース数の確認
   terraform state list | wc -l

   # 推定削除時間の計算
   terraform plan -destroy -detailed-exitcode
  1. 影響範囲の分析
   # impact_analysis.tf
   data "external" "resource_dependencies" {
     program = ["python", "${path.module}/scripts/analyze_dependencies.py"]
   }

   output "deletion_impact" {
     value = data.external.resource_dependencies.result
   }
  1. 段階的な実行計画
   graph TD
       A[フロントエンド層] --> B[アプリケーション層]
       B --> C[データベース層]
       C --> D[ネットワーク層]
       D --> E[IAMとセキュリティ]

マルチアカウント環境での運用テクニック

複数のAWSアカウントにまたがる環境でのterraform destroy運用方法を解説します。

1. アカウント間の依存関係管理

# provider設定
provider "aws" {
  alias  = "prod"
  region = "ap-northeast-1"
  assume_role {
    role_arn = "arn:aws:iam::PROD-ACCOUNT-ID:role/TerraformExecutionRole"
  }
}

provider "aws" {
  alias  = "dev"
  region = "ap-northeast-1"
  assume_role {
    role_arn = "arn:aws:iam::DEV-ACCOUNT-ID:role/TerraformExecutionRole"
  }
}

# クロスアカウントリソースの管理
module "cross_account_resources" {
  source = "./modules/cross-account"

  providers = {
    aws.prod = aws.prod
    aws.dev  = aws.dev
  }
}

2. マルチアカウント削除の自動化

# multi_account_destroy.py
import boto3
import subprocess

def destroy_resources(account_id, role_name):
    # アカウントの認証情報を取得
    sts = boto3.client('sts')
    assumed_role = sts.assume_role(
        RoleArn=f'arn:aws:iam::{account_id}:role/{role_name}',
        RoleSessionName='TerraformDestroy'
    )

    # 環境変数の設定
    os.environ['AWS_ACCESS_KEY_ID'] = assumed_role['Credentials']['AccessKeyId']
    os.environ['AWS_SECRET_ACCESS_KEY'] = assumed_role['Credentials']['SecretAccessKey']
    os.environ['AWS_SESSION_TOKEN'] = assumed_role['Credentials']['SessionToken']

    # Terraformの実行
    subprocess.run([
        'terraform',
        'destroy',
        '-auto-approve',
        f'-var="account_id={account_id}"'
    ])

# アカウントリストでの実行
accounts = [
    {'id': '111111111111', 'role': 'TerraformRole'},
    {'id': '222222222222', 'role': 'TerraformRole'}
]

for account in accounts:
    destroy_resources(account['id'], account['role'])

マルチアカウント運用のベストプラクティス:

項目実装方法重要ポイント
認証管理IAMロール継承最小権限の原則
依存関係グラフ分析アカウント間の順序
ログ管理CloudWatch統合集中管理
バックアップクロスアカウント復旧可能性確保

実践的な運用シナリオ:

  1. 環境のクリーンアップ
   # cleanup.sh
   #!/bin/bash

   # 環境変数の設定
   export TF_WORKSPACE="cleanup"

   # アカウントごとの処理
   for account in $(cat accounts.txt); do
     echo "Processing account: $account"

     # アカウント別の設定読み込み
     source "./config/$account.env"

     # terraform実行
     terraform init
     terraform destroy -auto-approve \
       -var="account_id=$account" \
       -var="environment=$ENVIRONMENT"
   done
  1. 定期的なメンテナンス
   # maintenance-workflow.yml
   name: Regular Maintenance

   on:
     schedule:
       - cron: '0 0 * * 0'  # 毎週日曜日

   jobs:
     cleanup:
       runs-on: ubuntu-latest
       strategy:
         matrix:
           account: ['dev', 'staging', 'prod']

       steps:
         - uses: actions/checkout@v2

         - name: Configure AWS Credentials
           uses: aws-actions/configure-aws-credentials@v1
           with:
             role-to-assume: arn:aws:iam::${{ secrets.AWS_ACCOUNT_ID }}:role/GithubActionsRole
             aws-region: ap-northeast-1

         - name: Execute Terraform Destroy
           run: |
             cd environments/${{ matrix.account }}
             terraform init
             terraform destroy -auto-approve

これらの実践例を参考に、組織の規模と要件に合わせた適切な運用方法を選択することで、安全かつ効率的なリソース管理が可能になります。次のセクションでは、terraform destroyのセキュリティベストプラクティスについて説明します。

terraform destroyのセキュリティベストプラクティス

アクセス制御と監査ログの設定方法

terraform destroyコマンドの実行に関する適切なアクセス制御と監査の実装方法を解説します。

1. IAMポリシーの実装

# terraform_iam.tf
resource "aws_iam_policy" "terraform_destroy" {
  name        = "terraform-destroy-policy"
  description = "Policy for terraform destroy execution"

  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Effect = "Allow"
        Action = [
          "terraform:Destroy"
        ]
        Resource = "*"
        Condition = {
          StringEquals = {
            "aws:RequestTag/Environment": ["development", "staging"]
          }
          StringNotEquals = {
            "aws:RequestTag/Environment": "production"
          }
        }
      },
      {
        Effect = "Deny"
        Action = [
          "terraform:Destroy"
        ]
        Resource = "*"
        Condition = {
          StringEquals = {
            "aws:RequestTag/Protected": "true"
          }
        }
      }
    ]
  })
}

# 監査ログ設定
resource "aws_cloudwatch_log_group" "terraform_audit" {
  name              = "/terraform/destroy-audit"
  retention_in_days = 365

  tags = {
    Environment = var.environment
    Service     = "terraform-audit"
  }
}

resource "aws_cloudwatch_metric_alarm" "destroy_alert" {
  alarm_name          = "terraform-destroy-execution"
  comparison_operator = "GreaterThanThreshold"
  evaluation_periods  = "1"
  metric_name         = "DestroyExecutionCount"
  namespace           = "Custom/Terraform"
  period             = "300"
  statistic          = "Sum"
  threshold          = "0"
  alarm_description  = "This metric monitors terraform destroy executions"
  alarm_actions      = [aws_sns_topic.security_alerts.arn]
}

2. 監査ログの設定

# audit_logging.sh
#!/bin/bash

# ログ出力関数
log_terraform_action() {
    local action=$1
    local user=$2
    local resources=$3

    # CloudWatchへのログ送信
    aws logs put-log-events \
        --log-group-name "/terraform/destroy-audit" \
        --log-stream-name "$(date +%Y-%m-%d)" \
        --log-events timestamp=$(date +%s%3N),message="{\"action\":\"$action\",\"user\":\"$user\",\"resources\":\"$resources\"}"
}

# Terraformラッパースクリプト
terraform destroy "$@" 2>&1 | tee >(log_terraform_action "destroy" "${AWS_USER}" "$(terraform show -json | jq -r '.planned_values.root_module.resources[].address')")

CI/CDパイプラインでの安全な実装方法

CI/CDパイプラインでterraform destroyを安全に実装する方法を説明します。

1. GitHubActions実装例

# .github/workflows/terraform-destroy.yml
name: Terraform Destroy

on:
  workflow_dispatch:
    inputs:
      environment:
        description: 'Environment to destroy'
        required: true
        type: choice
        options:
        - development
        - staging
      approval_token:
        description: 'Approval token'
        required: true

permissions:
  id-token: write
  contents: read

jobs:
  security-check:
    runs-on: ubuntu-latest
    steps:
      - name: Verify approval token
        run: |
          if [[ "${{ github.event.inputs.approval_token }}" != "$(cat .github/approval_tokens/${{ github.event.inputs.environment }})" ]]; then
            echo "Invalid approval token"
            exit 1
          fi

  destroy-plan:
    needs: security-check
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2

      - name: Configure AWS Credentials
        uses: aws-actions/configure-aws-credentials@v1
        with:
          role-to-assume: arn:aws:iam::${{ secrets.AWS_ACCOUNT_ID }}:role/GithubActionsRole
          aws-region: ap-northeast-1

      - name: Terraform Plan Destroy
        run: |
          terraform init
          terraform plan -destroy -out=destroy.plan

      - name: Upload Plan
        uses: actions/upload-artifact@v2
        with:
          name: destroy-plan
          path: destroy.plan

  approve-destroy:
    needs: destroy-plan
    runs-on: ubuntu-latest
    environment:
      name: ${{ github.event.inputs.environment }}
    steps:
      - name: Download Plan
        uses: actions/download-artifact@v2
        with:
          name: destroy-plan

      - name: Apply Destroy
        run: terraform apply destroy.plan

2. セキュリティチェックの実装

# security_checks.py
import boto3
import json

def check_protected_resources():
    """保護されたリソースのチェック"""
    client = boto3.client('resourcegroupstaggingapi')

    response = client.get_resources(
        TagFilters=[
            {
                'Key': 'Protected',
                'Values': ['true']
            }
        ]
    )

    return [r['ResourceARN'] for r in response['ResourceTagMappingList']]

def validate_destroy_plan(plan_file):
    """destroyプランの検証"""
    with open(plan_file, 'r') as f:
        plan = json.load(f)

    protected_resources = check_protected_resources()

    for resource in plan.get('resource_changes', []):
        if resource['address'] in protected_resources:
            raise Exception(f"Protected resource found: {resource['address']}")

if __name__ == "__main__":
    validate_destroy_plan('destroy.plan.json')

セキュリティベストプラクティスまとめ:

カテゴリ実装項目重要ポイント
アクセス制御IAMポリシー最小権限の原則
監査CloudWatchログ完全な監査証跡
承認フロー多段階承認意図しない実行の防止
保護機能リソースタグ重要リソースの保護

セキュリティチェックリストの実装:

# security_checks.tf
locals {
  security_checks = {
    production_protection = {
      condition = var.environment == "production"
      error_message = "Direct destroy in production is not allowed"
    }
    backup_verification = {
      condition = var.backup_confirmed
      error_message = "Backup confirmation required"
    }
    approval_validation = {
      condition = var.approval_token != ""
      error_message = "Valid approval token required"
    }
  }
}

resource "null_resource" "security_validation" {
  count = var.enable_security_checks ? 1 : 0

  lifecycle {
    precondition {
      condition     = !contains([for check in local.security_checks : check.condition], false)
      error_message = join("\n", [for check in local.security_checks : check.error_message if !check.condition])
    }
  }
}

これらのセキュリティ実装により、terraform destroyの安全な実行が確保されます。適切なアクセス制御、監査ログ、承認フロー、保護機能を組み合わせることで、意図しない削除や不正なアクセスを防ぐことができます。