はじめに
Terraform Cloud(TFC)のOpenID Connect (OIDC) 認証を使えば、シークレットを保持せずにAzureへデプロイが可能です。
しかし、複数サブスクリプションをまたぐHub & Spoke構成(VNetピアリングなど)の構築には、複数のプロバイダーおよびOIDC設定が必要になります。
本記事では、Azureプラットフォームに対するTFCのマルチOIDC構成を利用し、Hub/Spoke間のVNetピアリングを構築する手順を解説します。
参考ドキュメント:
Multiple Configurations - Azure Dynamic Provider Credentials | Terraform | HashiCorp Developer
前提条件:対向VNetへの権限について
1. Hub/SpokeのVNetをデプロイするサービスプリンシパル準備
Hub/Spokeそれぞれをデプロイするためのサービスプリンシパルを事前に作成する必要があります。
OIDCを利用するため、作成後資格情報の登録も必要です。
サービスプリンシパル関連の設定は以下を参考にできます。
HCP Terraform WorkspaceでAzure Provider認証にOIDCを利用する
2. 必要なRole付与
複数サブスクリプション間でVNetピアリングを構築する場合、それぞれの認証情報(Service Principal等)に対して、Azure側で事前に以下のRBAC(ロールベースアクセス制御)を設定しておく必要があります。
- Hub用の認証情報: 対向となる「Spoke側VNet」に対するRead(読み取り)以上の権限(Network Contributorなど)
- Spoke用の認証情報: 対向となる「Hub側VNet」に対するRead(読み取り)以上の権限(Network Contributorなど)
※互いのVNetを参照・紐づけするために、クロスでの権限付与が必須となります。
※もちろん自サブスクリプションに対する権限も必要になります。
1. TFCの環境変数設定
デフォルトのプロバイダー(Hub用)と、エイリアスプロバイダー(Spoke用)のそれぞれに対して設定を行います。エイリアスを使用する場合、環境変数名の末尾に _<TAG> (今回は _SPOKE)を付与します。
設定する環境変数(Environment Variables)
| Key | Value | 備考 |
|---|---|---|
TFC_AZURE_PROVIDER_AUTH |
true |
Hub用(デフォルト) |
TFC_AZURE_RUN_CLIENT_ID |
(Hub用のClient ID) |
Hub用(デフォルト) |
TFC_AZURE_PROVIDER_AUTH_SPOKE |
true |
Spoke用(SPOKEエイリアス) |
TFC_AZURE_RUN_CLIENT_ID_SPOKE |
(Spoke用のClient ID) |
Spoke用(SPOKEエイリアス) |
2. Terraformコードの実装
実際にTerraformで実装します。
複数OIDCに関連する設定を実施にしているのは、2.1 ~ 2.2です。
2.1 変数の定義
無くてもよさそうですが必須です。
TFCが動的に生成する認証情報を受け取るため、特定の名前 (tfc_azure_dynamic_credentials) のvariable を必ず定義する必要があります。この名前は予約されており、変更できません。
# variables.tf
### -----------------------------------
# HCP Terraform仕様によるvariable
# 名前変更不可
variable "tfc_azure_dynamic_credentials" {
description = "Object containing Azure dynamic credentials configuration"
type = object({
default = object({
client_id_file_path = string
oidc_token_file_path = string
})
aliases = map(object({
client_id_file_path = string
oidc_token_file_path = string
}))
})
}
### -----------------------------------
# 通常のvariable設定
variable "tenant_id" {
type = string
}
variable "hub_subscription_id" {
type = string
}
variable "spoke_subscription_id" {
type = string
}
2.2 プロバイダーの設定
use_cli = false と use_oidc = true を明示的に設定し、先ほど定義した tfc_azure_dynamic_credentials からトークンを読み込みます。
ここでの tfc_azure_dynamic_credentials.default... や tfc_azure_dynamic_credentials.aliases["<TAG>"]... といった記述は、TFCのDynamic Provider Credentialsを利用する際の「決まり文句(固定の仕様)」です。 独自の名前に変更することはできず、必ずこの記述方法に沿ってファイルパスをプロバイダーに渡す必要があります。
# providers.tf
terraform {
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = "~> 4.0"
}
}
}
# デフォルトプロバイダー (Hub用)
provider "azurerm" {
tenant_id = var.tenant_id
subscription_id = var.hub_subscription_id
use_cli = false
# 複数構成を使用するため明示的にtrueに設定
use_oidc = true
# ※ここが決まり文句
client_id_file_path = var.tfc_azure_dynamic_credentials.default.client_id_file_path
oidc_token_file_path = var.tfc_azure_dynamic_credentials.default.oidc_token_file_path
features {}
}
# エイリアスプロバイダー (Spoke用)
provider "azurerm" {
alias = "spoke"
tenant_id = var.tenant_id
subscription_id = var.spoke_subscription_id
use_cli = false
use_oidc = true
# 環境変数の末尾を _SPOKE としたため、キーは "SPOKE" となる。これも決まり文句。
client_id_file_path = var.tfc_azure_dynamic_credentials.aliases["SPOKE"].client_id_file_path
oidc_token_file_path = var.tfc_azure_dynamic_credentials.aliases["SPOKE"].oidc_token_file_path
features {
resource_group {
prevent_deletion_if_contains_resources = false
}
}
}
ここまででOIDCの設定が完了です。
2.3 VNetとVNetピアリングの作成例
実際に呼び出し側の設定をします。
ここでは例として、異なるサブスクリプションにVNetを作成し、VNetピアリングします。
ポイントはSpoke側のリソースには明示的に provider = azurerm.spoke を指定することです。
# main.tf
# ---------------------------------------------------------
# Hub側リソース (デフォルトプロバイダー)
# ---------------------------------------------------------
resource "azurerm_resource_group" "hub" {
name = "rg-hub-vnet"
location = "Japaneast"
}
resource "azurerm_virtual_network" "hub" {
name = "vnet-hub"
address_space = ["10.0.0.0/16"]
location = azurerm_resource_group.hub.location
resource_group_name = azurerm_resource_group.hub.name
}
# ---------------------------------------------------------
# Spoke側リソース (エイリアスプロバイダー)
# ---------------------------------------------------------
resource "azurerm_resource_group" "spoke" {
provider = azurerm.spoke
name = "rg-spoke-vnet"
location = "Japaneast"
}
resource "azurerm_virtual_network" "spoke" {
provider = azurerm.spoke
name = "vnet-spoke"
address_space = ["10.1.0.0/16"]
location = azurerm_resource_group.spoke.location
resource_group_name = azurerm_resource_group.spoke.name
}
# ---------------------------------------------------------
# VNetピアリングの設定
# ---------------------------------------------------------
# Hub -> Spoke のピアリング
resource "azurerm_virtual_network_peering" "hub_to_spoke" {
name = "peer-hub-to-spoke"
resource_group_name = azurerm_resource_group.hub.name
virtual_network_name = azurerm_virtual_network.hub.name
remote_virtual_network_id = azurerm_virtual_network.spoke.id
}
# Spoke -> Hub のピアリング
resource "azurerm_virtual_network_peering" "spoke_to_hub" {
provider = azurerm.spoke
name = "peer-spoke-to-hub"
resource_group_name = azurerm_resource_group.spoke.name
virtual_network_name = azurerm_virtual_network.spoke.name
remote_virtual_network_id = azurerm_virtual_network.hub.id
}
これで1つのWorkspace内で複数のOIDCを利用できるようになります。
注意点としては、TFC_AZURE_PROVIDER_AUTHのような環境変数を使う必要があることです。
azurerm providerのargumentを確認すると環境変数ではなく、provider block内にterraformの変数として直接渡してもできそうですが、環境変数の利用が必須でした。
(provider内の引数をうまく認識せずエラーになった)
さいごに
HCP Terraformの仕様によって、一部理解が難しい部分があるものの、一度設定してしまえば簡単に使いまわせそうです。
この記事が参考になれば幸いです。
最後までお読みいただきありがとうございました。