TerraformのtfstateをAzure Blob Storageで管理する

AzureのリソースをTerraformを使って構築する際に、tfstateの管理をどうしようかと思っていたところ、AWSのS3でtfstateを管理するようにAzure Blob Storageでほぼ同じことができそうだったので試してみた。

tfstateとは

Terraformで管理しているリソースの状態を表すファイル qiita.com

デフォルトだとローカルに保存されるが、複数人で開発する時などに困る。 基本的にはAWSのS3だったり、今だとTerraform Cloudでも管理出来たりなど、 tfstate自体はリモートで管理することが多い。 tfstateには各リソースの機密情報も含まれてたりするので、GitHubでは管理するのは良くない。 dev.classmethod.jp

tfstateを管理するリソースグループ、Blob Storageを作成

今回はAzure CLIを使ってtfstateを管理する各リソースを作成していく

Azure CLI のインストール | Microsoft Docs

Azureにログインする

$ az login

サブスクリプションの指定行う(複数サクブスクリプションを持っている場合)

$ az account set --subscription "<サブスクリプション名>"

ちなみにaz account listコマンドでサブスクリプションの状態が確認できます

$ az account list --output table

ログインができたら、リソースグループを作成する

$ RESOURCE_GROUP_NAME=tfstate
$ az group create --name $RESOURCE_GROUP_NAME --location japaneast

ストレージアカウントを作成する

$ STORAGE_ACCOUNT_NAME=tfstate$RANDOM
$ az storage account create --resource-group $RESOURCE_GROUP_NAME --name $STORAGE_ACCOUNT_NAME --sku Standard_LRS

ストレージアカウントのアカウントキーを取得する(Blobコンテナ作成時に必要となる)

$ ACCOUNT_KEY=$(az storage account keys list --resource-group $RESOURCE_GROUP_NAME --account-name $STORAGE_ACCOUNT_NAME --query [0].value -o tsv)

ちなみに当方の環境だと以下のようなエラーがでた

zsh: no matches found: [0]

原因、回避策はこちらが参考になった qiita.com

Blobコンテナの作成

$ CONTAINER_NAME=tfstate
$ az storage container create --name $CONTAINER_NAME --account-name $STORAGE_ACCOUNT_NAME --account-key $ACCOUNT_KEY

Azure Storage Explorer等でBlobコンテナが作成されていることを確認できればOK f:id:rnakamine:20200906141532p:plain

backendの設定

まずはmain.tfファイルを作成し、providerブロックを追加。ここではazurermを指定する

provider "azurerm" {
  version = "~>2.0"
  features {}
}

次にterraformブロックを追加する。 backendを以下のように設定することでtfstateを上記で作成したBlob Storageで管理することができる

terraform {
  backend "azurerm" {
    resource_group_name  = "tfstate"
    storage_account_name = "tfstate26723"
    container_name       = "tfstate"
    key                  = "terraform.tfstate"
  }
}

keyではtfstateのファイル名を指定している

次にterraform initコマンドを実行して初期化を行い、リソース作成に必要なバイナリ等をダウンロードする。 この時にbackendの設定も行われる。

$ terraform init

Initializing the backend...

Successfully configured the backend "azurerm"! Terraform will automatically
use this backend unless the backend configuration changes.

Initializing provider plugins...
- Checking for available provider plugins...
- Downloading plugin for provider "azurerm" (hashicorp/azurerm) 2.26.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 has been successfully initialized!といった文言が出力される。

ちなみにダウンロードされたバイナリ等は.terraformディレクトリ以下に保存される。 今回だとこんな感じ

.terraform
├── plugins
│   └── darwin_amd64
│       ├── lock.json
│       └── terraform-provider-azurerm_v2.26.0_x5
└── terraform.tfstate

tfstateの状態を確認

サンプルとして適当にリソースグループを作成して、その状態をBlob Storageに作成されたtfstateで確認する。

先ほど作成したmain.tfに以下のリソースを追加する

resource "azurerm_resource_group" "example" {
  name     = "example"
  location = "japaneast"
}

terraform applyコマンドを実行しリソースの作成を行う。

$ terraform apply

Acquiring state lock. This may take a few moments...

An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # azurerm_resource_group.example will be created
  + resource "azurerm_resource_group" "example" {
      + id       = (known after apply)
      + location = "japaneast"
      + name     = "example"
    }

Plan: 1 to add, 0 to change, 0 to destroy.

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes

azurerm_resource_group.example: Creating...
azurerm_resource_group.example: Creation complete after 1s [id=/subscriptions/<サブスクリプションID>/resourceGroups/example]

Apply complete! Resources: 1 added, 0 changed, 0 destroyed.

リソースの作成が始まると上記で作成したBlob Storageの中にterraform.tfstateというファイルが作成される。これがリソースの状態を管理しているファイルとなっている。リソースの作成中はこのファイルに対してロックがかかっており、他の人の操作による同時に状態が変更されるのを防止してくれる

f:id:rnakamine:20200906153711p:plain

www.terraform.io

terraform.tfstate中身を見てみるとこんな感じ

{
  "version": 4,
  "terraform_version": "0.12.26",
  "serial": 1,
  "lineage": "efe53c3f-7ddb-81e4-2d5b-24ce11859842",
  "outputs": {},
  "resources": [
    {
      "mode": "managed",
      "type": "azurerm_resource_group",
      "name": "example",
      "provider": "provider.azurerm",
      "instances": [
        {
          "schema_version": 0,
          "attributes": {
            "id": "/subscriptions/<サブスクリプションID>/resourceGroups/example",
            "location": "japaneast",
            "name": "example",
            "tags": null,
            "timeouts": null
          },
          "private": "*******************************"
        }
      ]
    }
  ]
}

リソースを削除した場合の状態も確認してみる

terraform destoryコマンドでリソースの削除を行い、再度Blob Srogaeの中にあるterraform.tfstateの中身を確認

{
  "version": 4,
  "terraform_version": "0.12.26",
  "serial": 6,
  "lineage": "efe53c3f-7ddb-81e4-2d5b-24ce11859842",
  "outputs": {},
  "resources": []
}

tfstateでリソースが削除できているのを確認できた

まとめ

今回はtfstateをAzure Blob Storageで管理するようにしてみたが、Terraform Cloudを使うとtfstateのバージョン管理ができたり他にもいろいろ機能がありそうだったので、そちらの方も調査&検証してみたい。