Young Leaves

Azure Blob Storage + Azure Key Vault でTerraform のtfstate ファイルを管理する

チームなど複数人でTerraform の管理を行う場合、リソースの状態管理を行うtfstate ファイルをどこに保存するか悩みますよね?今回はAzure Blob Storage とAzure Key Vault を利用し、tfstate ファイルを安全に保存する方法について説明します。

実施環境

Terraform

1.6.6

Azure CLI

2.55.0

前提条件

  • Azure アカウントを持っていること
  • Azure リソースを作成するためのサービスプリンシパル作成などの環境構築が完了していること
  • Azure CLI のコマンドはAzure Cloud Shell のBash から実行とする

Terraform の状態管理について

Terraform ではインフラの管理を行うために状態管理用のtfstate ファイルを作成し、その中にリソースの構成やメタデータなどを書き込み管理を行います。tfstate ファイルはterraform apply 実行時に新規作成か更新を行い、tfstate ファイルからどのリソースに変更が加えられているかなどを確認しプロビジョニングを行います。デフォルトでは「terraform.tfstate」というファイル名でローカルPC 上に保存されます。また、このtfstate ファイルは設定によりファイル名を指定することができるため、環境ごとやリソースに応じたファイル名をつけることもできます。

しかし、個人でTerraform の開発を行う場合であればローカルPC 上でも良いですが、チーム内で複数の人がファイルの修正を行うとtfstate ファイルの不一致が発生することがあります。不整合が発生した状態ではTerraform でのプロビジョニングができなくなるだけではなく、場合によってはTerraform でプロビジョニングした構成を破壊する危険もあります。そのため、複数人でTerraform を利用する場合は共通のtfstate ファイルを利用するようにします。ただし、tfstate ファイルではシークレットなどの機密情報がプレーンテキストで書き込まれるため、GitHub などのバージョン管理ツールで保存を行うのは行わないようにしましょう。

tfstate ファイルはtf ファイル内にbackend 設定を追加することで、ローカルPC 上からリモートのオブジェクトストレージやTerraform Cloud などに配置することができます。リモート上の配置であればbackend 設定のみで良いですが、他の人が状態変更中にファイルのロックをしたい場合、場合によっては追加でリソースを作成する必要もあります。例えば、AWS S3 で今回利用するAzure Blob ストレージでは、リモート上のtfstate ファイルの格納に加え、tfstate ファイルを使用中のロックも同時に実施してくれます。そのため、今回はAzure Blob ストレージを使ったtfstate ファイルの管理を実施します。

また、今回は行いませんがtfstate 用のBlob ストレージをTerraform で管理したい場合はtfstate 用のBlob ストレージは別で管理することが推奨されています。以下はAWS S3 用のドキュメントですが、外部で管理することの記載があります。

Backend Type: s3 | Terraform | HashiCorp Developer

今回の構成

今回は、tfstate ファイルをAzure Blob ストレージに保存しつつ、ストレージアカウントのアクセスキーの保存用としてAzure Key Vault を利用します。こちらの2つを作成後、Terraform ファイルでAzure 上にリソースグループを作成するファイルを実行し、tfstate ファイルがBlob ストレージ内に配置されたことを確認するところまでをやります。

また、今回利用するTerraform のコードはGitHub 上にもあります。

https://github.com/kdkwakaba/terraform-sample/blob/main/manage-tfstate-with-blob-storage/global/blob/main.tf

変数設定、リソースグループの作成

初めにリソース作成用の変数設定とストレージアカウント用のリソースグループを作成します。ストレージアカウント名は全世界で一意の必要があるため、利用できない場合は他のストレージアカウント名を利用してください。

# Azureリソース用の変数を設定する
RESOURCE_GROUP_NAME="tfstatedemorg"
LOCATION="japaneast"
STORAGE_ACCOUNT_NAME="tfstatedemost"
CONTAINER_NAME="tfstate"
KEY_VAULT_NAME="tfstatedemokv"

# ストレージアカウント用のリソースグループを作成する
az group create -n $RESOURCE_GROUP_NAME -l $LOCATION

ストレージアカウントとBlob コンテナーの作成、アクセスキーを取得

リソースグループ作成後、tfstate ファイルを格納するストレージアカウントとBlob コンテナーを作成します。今回はストレージアカウントにIP アドレスの制限をかけていますが、セキュリティを更に強化したい場合は必要に応じてRBAC などでアクセス可能なユーザーを制限するなど実施してください。

# ストレージアカウントを作成する
az storage account create \
    -n $STORAGE_ACCOUNT_NAME \
    -g $RESOURCE_GROUP_NAME \
    -l $LOCATION \
    --sku Standard_LRS \
    --encryption-services blob \
    --public-network-access Enabled

# ストレージアカウントのパブリックネットワーク設定を変更する
az storage account update \
    --resource-group $RESOURCE_GROUP_NAME \
    -n $STORAGE_ACCOUNT_NAME \
    --default-action Deny

# ストレージアカウントのIPアドレス制限を行う
# 今回は単体のIPアドレスを指定しているが、複数人で利用する場合はIPアドレス帯を指定する
az storage account network-rule add \
    --account-name $STORAGE_ACCOUNT_NAME \
    -g $RESOURCE_GROUP_NAME \
    --action Allow \
    --ip-address <利用するIPアドレス>

# tfstate用のBlobコンテナーを作成する
# tfstateファイルのバージョン管理を行えるようにバージョニングを有効化する
az storage container create \
    -n $CONTAINER_NAME \
    --account-name $STORAGE_ACCOUNT_NAME

Blob コンテナーまで作成後、Terraform からBlob コンテナーにアクセスするため、ストレージアカウントのアクセスキーを取得します。

# ストレージアカウントのアクセスキーを取得する
ACCOUNT_KEY=$(az storage account keys list \
    -g $RESOURCE_GROUP_NAME \
    -n $STORAGE_ACCOUNT_NAME \
    --query '[0].value' -o tsv)

Key Vault の作成、アクセスキーの格納

ストレージアカウント、Blob コンテナーの準備ができたら、アクセスキー用のKey Vault を作成しそこからアクセスキーを取得するようにします。検証などで

# アクセスキー用のKey Vaultを作成する
az keyvault create \
    -n $KEY_VAULT_NAME \
    -g $RESOURCE_GROUP_NAME \
    --location $LOCATION

# Key Vaultにストレージアカウントのアクセスキーのシークレットを作成する
az keyvault secret set \
    --vault-name $KEY_VAULT_NAME \
    --name "StorageAccountAccessKey" --value $ACCOUNT_KEY

Key Vault にアクセスキーを格納したら、Key Vault を利用しストレージアカウントのアクセスキーを環境変数に格納します。常に格納したい場合は .bashrc など起動時に実行できるところに格納しておきます。

# Key Vaultからストレージアカウントのアクセスキーを取得し環境変数に設定する
export ARM_ACCESS_KEY=$(az keyvault secret show \
    --name "StorageAccountAccessKey" \
    --vault-name $KEY_VAULT_NAME \
    --query value -o tsv)

Terraform のbackend を設定

Terraform でAzure Blob コンテナーにtfstate ファイルを保存するため、backend の設定を行います。今回はリソースグループを作成するTerraform ファイルでbackend の設定を追加します。

terraform {
  required_version = ">= 1.0.0, < 2.0.0"

  required_providers {
    azurerm = {
      source  = "hashicorp/azurerm"
      version = "~>3.0"
    }
  }
  backend "azurerm" {
      resource_group_name  = "tfstatedemorg"
      storage_account_name = "tfstatedemost"
      container_name       = "tfstate"
      key                  = "rg/terraform.tfstate"
  }
}

provider "azurerm" {
  features {}
}

resource "azurerm_resource_group" "tfstatedemo" {
  name     = "testrg"
  location = "Japan East"
}

backend で設定する各項目の内容は以下のとおりです。

resource_group_name

Storage Account のあるリソースグループ名を設定する。

storage_account_name

tfstate ファイルを格納するStorage Account 名を設定する。

container_name

tfstate ファイルを格納するBlob コンテナーを設定する。

key

tfstate ファイルのパスおよびファイル名を設定する。例えば「dir/terraform.tfstate」と設定すると、Blob コンテナー配下のdir ディレクトリにterraform.tfstate ファイルを作成する。tfstate のファイル名も指定できるため、環境ごとにprod-terraform.tfstate などと分けることも可能。

動作確認

Terraform ファイルを作成したら動作確認を実施します。

# Terraformを初期化する
terraform init

# Terraformのリソースを承認する
terraform apply

Terraform の承認後、リソースグループが作成されたことを確認します。

# リソースグループを確認する
# リソースグループの内容が表示されることを確認する
az group show -n testrg

リソースグループの確認後、Azure Portal 上からBlob Storage 内にtfstate ファイルが存在することを確認します。

また、Terraform 実行時にはtfstate ファイルがロックされていることも確認します。

リソースのクリーンアップ

動作確認後、リソースグループを削除しリソースのクリーンアップをします。リソースをそのまま残しておきたい場合はこの手順はスキップしてください。

# リソースグループを削除する
az group delete -n tfstatedemorg

まとめ

  • Terraform ではtfstate ファイルを利用し、各リソースの変更などの状態管理を行っている。
  • 複数人のチームで開発を行う場合、ローカルPC 上ではなく共通の場所にtfstate ファイルを保存する。
  • リモート上にtfstate ファイルを保存する場合、backend 設定に格納場所とファイル名を指定し格納する。

参考資料