はじめに

Terraform 管理外で作成したAWSリソースを Terraform の管理下に置きたい。そんな時は terraform import を使用します。
今回は AWS WAF を例に、terraform importの動き、インポートしたリソースの更新について説明します。

terraform import の詳細については下記公式ドキュメントをご参照ください。
Terraform 公式ドキュメント

使用する環境

Default Action が Block、日本からのアクセスを許可するルールが設定された Web ACL があります。
この Web ACL を terraform import で Terraform 管理下に置いた後、 Terraform で更新します。

terraform import

terraform import 実行し、既存リソースを Terraform 管理下に入れます。
Web ACL を指定して terraform import を実行します。

terraform import 実行

$ terraform import aws_wafv2_web_acl.webacl xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/saeki-test-202309/CLOUDFRONT
aws_wafv2_web_acl.webacl: Importing from ID "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/saeki-test-202309/CLOUDFRONT"...
--- 省略 ---
aws_wafv2_web_acl.webacl: Import prepared!
  Prepared aws_wafv2_web_acl for import
aws_wafv2_web_acl.webacl: Refreshing state... [id=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx]
data.aws_caller_identity.self: Read complete after 1s [id=xxxxxxxxxxxx]

Import successful!

The resources that were imported are shown above. These resources are now in
your Terraform state and will henceforth be managed by Terraform.

terraform.tfstate の中身を確認し、インポートされた内容を確認します。
Web ACL のリソース情報が取り込まれており、Default Action が Block、日本からの接続を Allow するルールが入っています。

{
  "version": 4,
  "terraform_version": "1.5.7",
  "serial": 1,
  "lineage": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
  "outputs": {}
  },
  "resources": [
--- 省略 ---
    {
      "mode": "managed",
      "type": "aws_wafv2_web_acl",
      "name": "webacl",
      "provider": "provider["registry.terraform.io/hashicorp/aws"]",
      "instances": [
        {
          "schema_version": 0,
          "attributes": {
            "arn": "arn:aws:wafv2:us-east-1:xxxxxxxxxxxx:global/webacl/saeki-test-202309/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
            "association_config": [],
            "capacity": 1,
            "captcha_config": [],
            "custom_response_body": [],
            "default_action": [
              {
                "allow": [],
                "block": [
                  {
                    "custom_response": []
                  }
                ]
              }
            ],
            "description": "",
            "id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
            "lock_token": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
            "name": "saeki-test-202309",
            "rule": [
              {
                "action": [
                  {
                    "allow": [
                      {
                        "custom_request_handling": []
                      }
                    ],
                    "block": [],
                    "captcha": [],
                    "challenge": [],
                    "count": []
                  }
                ],
                "captcha_config": [],
                "name": "Allow-JP",
                "override_action": [],
                "priority": 0,
                "rule_label": [],
                "statement": [
                  {
                    "and_statement": [],
                    "byte_match_statement": [],
                    "geo_match_statement": [
                      {
                        "country_codes": [
                          "JP"
                        ],
                        "forwarded_ip_config": []
                      }
                    ],
                    "ip_set_reference_statement": [],
                    "label_match_statement": [],
                    "managed_rule_group_statement": [],
                    "not_statement": [],
                    "or_statement": [],
                    "rate_based_statement": [],
                    "regex_match_statement": [],
                    "regex_pattern_set_reference_statement": [],
                    "rule_group_reference_statement": [],
                    "size_constraint_statement": [],
                    "sqli_match_statement": [],
                    "xss_match_statement": []
                  }
                ],
                "visibility_config": [
                  {
                    "cloudwatch_metrics_enabled": true,
                    "metric_name": "Allow-JP",
                    "sampled_requests_enabled": true
                  }
                ]
              }
            ],
            "scope": "CLOUDFRONT",
            "tags": {},
            "tags_all": {},
            "token_domains": [],
            "visibility_config": [
              {
                "cloudwatch_metrics_enabled": true,
                "metric_name": "saeki-test-202309",
                "sampled_requests_enabled": true
              }
            ]
          },
          "sensitive_attributes": [],
          "private": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX=="
        }
      ]
    }
  ],
  "check_results": null
}

Web ACL の更新

インポートした Web ACL を更新していきます。

tf コード

更新後の Web ACL のリソース情報を記述します。
Default Action を Allow にし、日本以外からの接続を Block するルールを設定します。

resource "aws_wafv2_web_acl" "webacl" {
  name  = "saeki-test-202309"
  scope = "CLOUDFRONT"

  default_action {
    allow {}
  }

  rule {
    name = "limit-jp"
    priority = 0
    action {
      block {}
    }
    statement {
      not_statement {
        statement {
          geo_match_statement {
            country_codes = ["JP"]
          }
        }
      }
    }
    visibility_config {
      cloudwatch_metrics_enabled = true
      metric_name = "limit-jp"
      sampled_requests_enabled = true
    }
  }


  visibility_config {
    cloudwatch_metrics_enabled = true
    metric_name                = "saeki-test-202309"
    sampled_requests_enabled   = true
  }
}

デプロイ

$ terraform plan
Terraform will perform the following actions:

  # aws_wafv2_web_acl.webacl will be updated in-place
--- 省略 ---
Plan: 0 to add, 1 to change, 0 to destroy.

$ terraform apply
--- 省略 ---
Apply complete! Resources: 0 added, 1 changed, 0 destroyed.

更新後のリソース

Default ActionがAllowになり、日本以外からの接続をBlockするルールが設定されました。

おわりに

terrafrom import を使うことで、既存リソースをTerraform(tfstate)に取り込むことができます。
tfstate に取り込むだけですので、対象リソースのコードは別途書く必要がありますが、その後は Terraform で管理することが可能になります。

Terraform のバージョン1.5では、import ブロックというものが追加されています。
import ブロックを利用し、インポート対象のリソースのコードを自動作成してくれることができるようです。

次回はそちらを試してみようと思います。