Closed9

[Terraform] Multi Providerで特定のリソースだけ別リージョンに作成したい

samuraikunsamuraikun

ユースケース

他のリソースは、東京リージョンで構築したいがCloudFront用のSSL証明書は、us-east-1(バージニア)リージョンのものしか使えないので、ACM Certificate関連のリソースだけバージニアリージョンでTerraformで作成したい

samuraikunsamuraikun

ディレクトリ構成

ひとまず説明用にルートディレクトリにルートモジュール用のmain.tfだけあるようなシンプルな構成。

※実際は、ステージング用本番用にそれぞれモジュールを分けて、各環境用のモジュール内の main.tf でTerraformを実行するのがよくある方法かと思われます。

.
├── main.tf
└── modules
    └── certificates
        ├── main.tf
        ├── output.tf
        └── variables.tf
samuraikunsamuraikun

ユースケースを実現するためのアプローチ方針

  1. ルートモジュールに複数リージョンのawsプロバイダーを定義
  2. ルートモジュール内のACMのモジュール呼び出し部分でプロバイダーを指定?
  3. ACMモジュール内でリソース定義部分でプロバイダーを指定
samuraikunsamuraikun

ベースとなるTerraform実装

ここから、Multi Providerで別リージョンでCloudFront用のSSL証明書をバージニアリージョンに作れるようにする

ルートモジュールの main.tf

./main.tf
terraform {
  required_version = ">= 1.0.9"
  backend "s3" {
    bucket = "sample-bucket-2022-01-04"
    key    = "terraform.tfstate"
    region = "ap-northeast-1"
  }
  required_providers {
    aws = {
      source = "hashicorp/aws"
    }
  }
}

provider "aws" {
  region  = var.region.name
}

provider "aws" {
  region = "us-east-1"
  alias  = "virginia"
}

module "certificate" {
  source = "./modules/certificates"
}

ACMのモジュール

./modules/certificates/main.tf
resource "aws_acm_certificate" "acm_for_cloudfront" {
  provider          = aws.virginia
  domain_name       = "*.test.com"
  validation_method = "DNS"

  lifecycle {
    create_before_destroy = true
  }
}

resource "aws_route53_record" "cert_validation" {
  depends_on = [
    aws_acm_certificate.acm_for_cloudfront
  ]

  for_each = {
    for dvo in aws_acm_certificate.acm_for_cloudfront.domain_validation_options : dvo.domain_name => {
      name   = dvo.resource_record_name
      record = dvo.resource_record_value
      type   = dvo.resource_record_type
    }
  }

  allow_overwrite = true
  name            = each.value.name
  records         = [each.value.record]
  ttl             = 60
  type            = each.value.type
  zone_id         = var.route53_hosted_zone_id
}

resource "aws_acm_certificate_validation" "acm_for_cloudfront" {
  provider                = aws.virginia
  certificate_arn         = aws_acm_certificate.acm_for_cloudfront.arn
  validation_record_fqdns = [for record in aws_route53_record.cert_validation : record.fqdn]
}
samuraikunsamuraikun

ここまでのTerraform実装で、terraform init, terraform plan

terraform init は成功

$ terraform init
Initializing modules...

Initializing the backend...

Initializing provider plugins...
- Reusing previous version of hashicorp/aws from the dependency lock file
- Using previously-installed hashicorp/aws v3.57.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.

terraform plan は失敗

$ terraform plan
╷
│ Error: Provider configuration not present
│
│ To work with module.acm_certificate.aws_acm_certificate_validation.collet_com_for_cloudfront its original provider configuration at
│ module.acm_certificate.provider["registry.terraform.io/hashicorp/aws"].virginia is required, but it has been removed. This occurs when a provider configuration is removed
│ while objects created by that provider still exist in the state. Re-add the provider configuration to destroy
│ module.acm_certificate.aws_acm_certificate_validation.collet_com_for_cloudfront, after which you can remove the provider configuration again.
╵
╷
│ Error: Provider configuration not present
│
│ To work with module.acm_certificate.aws_acm_certificate.acm_for_cloudfront its original provider configuration at
│ module.acm_certificate.provider["registry.terraform.io/hashicorp/aws"].virginia is required, but it has been removed. This occurs when a provider configuration is removed
│ while objects created by that provider still exist in the state. Re-add the provider configuration to destroy
│ module.acm_certificate.aws_acm_certificate.collet_com_for_cloudfront, after which you can remove the provider configuration again.
╵
samuraikunsamuraikun

ルートモジュールを以下のように修正して、init, planを再実行

./main.tf
provider "aws" {
  region  = var.region.name
}

provider "aws" {
  region = "us-east-1"
  alias  = "virginia"
}

module "certificate" {
  source = "../../modules/certificates"

+ providers = {
+   aws = aws
+   aws.virginia = aws.virginia
+ }
+}

terraform init

Warningが発生

$ terraform init
Initializing modules...

Initializing the backend...

Initializing provider plugins...
- Reusing previous version of hashicorp/aws from the dependency lock file
- Using previously-installed hashicorp/aws v3.57.0

╷
│ Warning: Provider aws is undefined
│
│   on main.tf line 58, in module "acm_certificate":58:     aws = aws
│
│ Module module.acm_certificate does not declare a provider named aws.
│ If you wish to specify a provider configuration for the module, add an entry for aws in the required_providers block within the module.
╵

╷
│ Warning: Provider aws.virginia is undefined
│
│   on main.tf line 59, in module "acm_certificate":59:     aws.virginia = aws.virginia
│
│ Module module.acm_certificate does not declare a provider named aws.virginia.
│ If you wish to specify a provider configuration for the module, add an entry for aws.virginia in the required_providers block within the module.
╵

Terraform has been successfully initialized!

terraform plan

失敗した

$ terraform plan
╷
│ Warning: Provider aws is undefined
│
│   on main.tf line 58, in module "acm_certificate":58:     aws = aws
│
│ Module module.acm_certificate does not declare a provider named aws.
│ If you wish to specify a provider configuration for the module, add an entry for aws in the required_providers block within the module.
╵
╷
│ Warning: Provider aws.virginia is undefined
│
│   on main.tf line 59, in module "acm_certificate":59:     aws.virginia = aws.virginia
│
│ Module module.acm_certificate does not declare a provider named aws.virginia.
│ If you wish to specify a provider configuration for the module, add an entry for aws.virginia in the required_providers block within the module.
╵
╷
│ Error: Provider configuration not present
│
│ To work with module.maintenance.aws_acm_certificate.maintenance (orphan) its original provider configuration at
│ module.maintenance.provider["registry.terraform.io/hashicorp/aws"].virginia is required, but it has been removed. This occurs when a provider configuration is removed while
│ objects created by that provider still exist in the state. Re-add the provider configuration to destroy module.maintenance.aws_acm_certificate.maintenance (orphan), after
│ which you can remove the provider configuration again.
╵
╷
│ Error: Provider configuration not present
│
│ To work with module.maintenance.aws_acm_certificate_validation.maintenance (orphan) its original provider configuration at
│ module.maintenance.provider["registry.terraform.io/hashicorp/aws"].virginia is required, but it has been removed. This occurs when a provider configuration is removed while
│ objects created by that provider still exist in the state. Re-add the provider configuration to destroy module.maintenance.aws_acm_certificate_validation.maintenance (orphan),
│ after which you can remove the provider configuration again.
╵
samuraikunsamuraikun

子モジュールに別でprovider指定すると、子モジュールをリファクタリングで分解したときにterraform planが失敗する
こうなると、子モジュールのprovider定義をルートモジュールに移動させて再度terraform applyする必要があった

ルートモジュールで別リージョン用のproviderを定義しつつ、子モジュールの方では terraform init でWarningが出るので、required_providersを追加する必要がある

terraform {
  required_version = ">= 1.0.9"
  required_providers {
    aws = {
      source = "hashicorp/aws"
      configuration_aliases = [ aws, aws.virginia ]
    }
  }
}
samuraikunsamuraikun

このスクラップはそもそも子モジュールにすでにproviderが定義されていたという前提が抜け落ちていた。。。
ということで一旦Close

このスクラップは2022/01/04にクローズされました