🤖

Terraformで作成したリソースの対象リージョンを変更する

2024/05/26に公開

Terraformで作成したリソースの対象リージョンを変更したい。
例えば東京リージョンにlambda関数を作成したけど、後からバージニアリージョンに変更したいケース。このような修正は、CloudFrontのlambda@edge用にlambda関数を作成するとき、最初は東京リージョンに作成したけどlambda@edgeはグローバルリソースとしてバージニアリージョンに作成しないといけないことに気付いたので修正するときに発生する。もちろんlambda@edgeに限らず、グローバルリソースであれば同様の問題は発生するし、性能や可用性の観点から別リージョンにリソースを構築したいケースも存在する。

導入: 東京リージョンへのリソース作成

terraform-provider-aws では作成するリソースはプロバイダの設定により制御する。
例えば東京リージョンにダミーのlambda関数を構築するterraformコードは以下の通り。region = "ap-northeast-1" の設定によりすべてのリソースが東京リージョンに生成される。

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

resource "aws_lambda_function" "this" {
  function_name = "sample"
  filename      = data.archive_file.this.output_path
  role          = aws_iam_role.this.arn
  handler       = "lambda.handler"
  runtime       = "nodejs20.x"
}

data "archive_file" "this" {
  type        = "zip"
  output_path = "${path.module}/lambda.zip"
  source {
    content  = "dummy"
    filename = "lambda.js"
  }
}

resource "aws_iam_role" "this" {
  name               = "sample"
  assume_role_policy = data.aws_iam_policy_document.assume_lambda.json
}

data "aws_iam_policy_document" "assume_lambda" {
  statement {
    actions = ["sts:AssumeRole"]

    principals {
      type        = "Service"
      identifiers = ["lambda.amazonaws.com"]
    }
  }
}

リージョンを変更する

上記のコードをterraform init & terraform applyしてlambda関数を構築した後で、lambda関数をバージニアリージョンに作り直したい。そのために以下のような差分を適用する。バージニアリージョンに設定したawsプロバイダを構築して、lambda関数にはこのプロバイダを利用することを明示的に指定する。

--- a/environments/change-aws-region/main.tf
+++ b/environments/change-aws-region/main.tf
@@ -2,7 +2,14 @@ provider "aws" {
   region = "ap-northeast-1"
 }
 
+provider "aws" {
+  alias  = "virginia"
+  region = "us-east-1"
+}
+
 resource "aws_lambda_function" "this" {
+  provider = aws.virginia
+
   function_name = "sample"
   filename      = data.archive_file.this.output_path
   role          = aws_iam_role.this.arn

しかし、この変更でterraform planすると既存リソースの再構築ではなく新規構築のみが行われる。
実際にapplyしてもバージニアリージョンにlambda関数が構築されるが東京リージョンに作成したlambda関数に影響は無い。terraform applyの実行結果やtfstate上では東京リージョンに作成したlambda関数は存在していないかのように見えるが、AWS上では東京リージョンにlambda関数が作成されたまま放置される。

$ terraform plan
data.archive_file.this: Reading...
data.archive_file.this: Read complete after 0s [id=53fbeb5371ebe932c4a98e9e29d09805a923f5a8]
data.aws_iam_policy_document.assume_lambda: Reading...
data.aws_iam_policy_document.assume_lambda: Read complete after 0s [id=2690255455]
aws_iam_role.this: Refreshing state... [id=sample]
aws_lambda_function.this: Refreshing state... [id=sample]

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_lambda_function.this will be created
  + resource "aws_lambda_function" "this" {
      + architectures                  = (known after apply)
      + arn                            = (known after apply)
      + filename                       = "./lambda.zip"
      + function_name                  = "sample"
      + handler                        = "lambda.handler"
      + id                             = (known after apply)
      + invoke_arn                     = (known after apply)
      + last_modified                  = (known after apply)
      + memory_size                    = 128
      + package_type                   = "Zip"
      + publish                        = false
      + qualified_arn                  = (known after apply)
      + qualified_invoke_arn           = (known after apply)
      + reserved_concurrent_executions = -1
      + role                           = "arn:aws:iam::123456789012:role/sample"
      + runtime                        = "nodejs20.x"
      + signing_job_arn                = (known after apply)
      + signing_profile_version_arn    = (known after apply)
      + skip_destroy                   = false
      + source_code_hash               = (known after apply)
      + source_code_size               = (known after apply)
      + tags_all                       = (known after apply)
      + timeout                        = 3
      + version                        = (known after apply)
    }

Plan: 1 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.

これは terraformの制約によるものであり、既存のリソースを異なるリージョンに構築し直すような仕組みは備えていない。providerの設定変更がどのような影響を与えるのかterraformが制御することが難しいため。このため、リージョンを変更する操作をterraformはサポートしていない。

https://github.com/hashicorp/terraform/issues/3454

対策としては一度リソースを削除してから再構築するしかなさそう。別リージョンへの構築なので、構築してから既存リソースを削除でもよいかもしれない(S3バケットのようにグローバルで一意なリソースしか作成できない場合は先に削除する必要がある)。

参考

  • [2023-10-27] terraform でリソースのリージョン変更時の嵌りどころ

    • 同じような問題に遭遇して対応した内容を日本語でまとめてくれている人は既にいた。
    • 自分のたどりついた結論と同じく、一度削除してから再構築するしかなさそう。
  • resource level region configuration

    • リージョンの制御をリソースレベルでも指定できるようにしたいという提案のIssue
    • このような仕組みであればリージョン指定の変更がどのような影響を与えるのか明確になるし、複数リージョンにリソースを構築する場合にその数だけプロバイダを定義しなくてよいので嬉しい。
    • ただし、特に議論や実装が進んでいるわけではないので、このような仕組みが実装されるかどうかはわからない。

Discussion