🏗️

Managed SSLからCertificate Managerに移行する

2025/02/20に公開

はじめに

実務で Managed SSL から Certificate Manager に乗り換える機会がありました。その時に Terraform の予期しないエラーで苦労したので Managed SSL から Certificate Manager への移行手順をまとめてみました。example.comabc.example.netの2つのドメイン証明書を移行する例を使って説明していきます。

[Step0]移行前の状態

はじめにStep0として移行前の状態を確認します。
example.comabc.example.netの2つのドメインの証明書が Managed SSL に管理されていて、それら証明書がロードバランサと関連付けられています。

# Managed SSLで管理している証明書
resource "google_compute_managed_ssl_certificate" "domains" {
  name = "ssl-cert"
  managed {
    domains = [
      "example.com",
      "abc.example.net",
    ]
  }
}
# ロードバランサ
resource "google_compute_target_https_proxy" "lb" {
  name = "https-proxy"

  url_map = google_compute_url_map.lb.id
  ssl_policy = google_compute_ssl_policy.policy.name

  # Managed SSLとの関連づけ
  ssl_certificates = [
    google_compute_managed_ssl_certificate.domains
  ]
}
resource "google_compute_url_map" "lb" {
  ...省略
}
# SSLポリシー
resource "google_compute_ssl_policy" "policy" {
  name            = "ssl-policy"
  profile         = "MODERN"
  min_tls_version = "TLS_1_2"
}

[Step1]新しい証明書とCertificate Mapをデプロイする

こちらの記事を参考に以下4つのリソースをデプロイします。

  • google_certificate_manager_dns_authorization
  • google_certificate_manager_certificate
  • google_certificate_manager_certificate_map
  • google_certificate_manager_certificate_map_entry

追加する Terraform のコードは以下です。

locals {
  domains = [
    "example.com",
    "abc.example.net",
  ]
}
# DNS認証
resource "google_certificate_manager_dns_authorization" "authorizations" {
  for_each = toset(local.domains)

  name   = "dns-authz-${replace(each.key, ".", "-")}"
  domain = each.key
}
# 証明書
resource "google_certificate_manager_certificate" "certificates" {
  for_each = toset(local.domains)

  name = "cert-manager-${replace(each.key, ".", "-")}"
  managed {
    domains = [
      each.key
    ]
    dns_authorizations = [
      google_certificate_manager_dns_authorization.authorizations[each.key].id
    ]
  }
}
# Certificate Map
resource "google_certificate_manager_certificate_map" "map" {
  name = "cert-map"
}
# マップエントリー
resource "google_certificate_manager_certificate_map_entry" "map_entries" {
  for_each = toset(local.domains)

  name     = "map-entry-${replace(each.key, ".", "-")}"
  map      = google_certificate_manager_certificate_map.map.name
  hostname = each.key

  certificates = [
    google_certificate_manager_certificate.certificates[each.key].id
  ]
}

上記コードを追加したらterraform applyをします。applyが終わったら DNS に CNAME のレコードを追加して証明書を有効化してください。

[Step2]ロードバランサとCertificate Mapを関連づける

DNS 設定が終わって Certificate Manager の証明書がアクティベートされたのが確認できたらロードバランサと Certificate Map を関連付けます。

# Certificate Map(Step1で追加されたリソース)
resource "google_certificate_manager_certificate_map" "map" {
  name = "cert-map"
}
# ロードバランサ(修正する)
resource "google_compute_target_https_proxy" "lb" {
  name = "https-proxy"

  url_map = google_compute_url_map.lb.id
  ssl_policy = google_compute_ssl_policy.lb.name

  # 削除予定(一旦残す)
  ssl_certificates = [
    google_compute_managed_ssl_certificate.domains
  ]

  # Certificate Mapとロードバランサの関連付け(追加)
  certificate_map = "//certificatemanager.googleapis.com/${google_certificate_manager_certificate_map.map.id}"
}

ssl_certificates の設定を残して certificate_map を追加するのがポイントです。この状態でterraform applyを実行します。一時的にロードバランサに Managed SSL と Certificate Manager の両方の証明書が関連付けられます(両方の証明書を関連付けないとデプロイ時に conditionNotMet エラーが発生します)。

[Step3]ロードバランサからManaged SSLの参照を外す

Step2までデプロイができたらgoogle_compute_target_https_proxyから ssl_certificates を削除して Managed SSL の参照を外します。

resource "google_compute_target_https_proxy" "lb" {
  name = "https-proxy"

  url_map = google_compute_url_map.lb.id
  ssl_policy = google_compute_ssl_policy.lb.name

  # 削除
  #ssl_certificates = [
  #  google_compute_managed_ssl_certificate.domains
  #]

  # Certificate Mapとロードバランサの関連付け
  certificate_map = "//certificatemanager.googleapis.com/${google_certificate_manager_certificate_map.map.id}"
}

この状態でterraform applyを実行します。これでロードバランサと Managed SSL の関連が外れます。

[Step4]Managed SSLを削除する

ここまでできたらあとは使わなくなった Managed SSL 関連のリソースを削除して終わりです。google_compute_managed_ssl_certificateのコードを削除してterraform applyします。

# 削除
#resource "google_compute_managed_ssl_certificate" "domains" {
#  name = "ssl-cert"
#  managed {
#    domains = [
#      "example.com",
#      "abc.example.net",
#    ]
#  }
#}

以上で Managed SSL から Certificate Manager への移行は完了です。

補足: 移行時に発生したエラー

なにも考えずに以下のようにgoogle_compute_target_https_proxyから ssl_certificates を削除して certificate_map を追加すると

resource "google_compute_target_https_proxy" "lb" {
  name = "https-proxy"

  url_map = google_compute_url_map.lb.id
  ssl_policy = google_compute_ssl_policy.lb.name

  # Managed SSLとの関連を削除する
  #ssl_certificates = [
  #  google_compute_managed_ssl_certificate.domains
  #]

  # Certificate Mapとロードバランサの関連付け(追加)
  certificate_map = "//certificatemanager.googleapis.com/${google_certificate_manager_certificate_map.map.id}"
}

terraform apply 時に以下のようなエラーが発生します。

Error updating TargetHttpsProxy "projects/{プロジェクト名}/global/targetHttpsProxies/{リソース名}":
googleapi:
Error 412: Certificate Map or at least 1 SSL certificate must be specified for setting SSL certificates in TargetHttpsProxy.,
conditionNotMet

ちゃんと証明書を設定してるのになんで?という内容のエラーメッセージです。上記エラーについて Issue が上がっていて、そこを読むと

一旦両方の証明書をロードバランサに紐づけてデプロイしたあとに、古い方の証明書の紐付けを削除して再度デプロイしろとありました(証明書の安全な移行を考えれば当然なのかもしれませんがTerraformのエラーメッセージは不親切に感じました)。というわけで conditionNotMet エラーを回避して証明書の移行をするには今回紹介した手順にそって段階的にデプロイをする必要があるということでした。

関連記事

Discussion