Azureでterraform testのmockを試してみる

Azure

Terraform 1.7にテストのモック機能が追加されました。テストの機能は1.6で追加されています。その時のテストでは実際に「plan」や「apply」を実行していました。そのため、リソースによっては時間がかかるものもあります。しかし、今回追加されたモックを利用することでテスト時間が短縮されます。さらに、実際にリソースが作成されることはないので、Azure環境を気にすることなく、より気軽にテストを行うことができます。

今回は、AzureのStorage Accountを例にモック機能を試してみます。

前提

以下のバージョンを使用しています。

terraform1.7.2
azurerm3.90.0

dataをモック

まずはどのようにモック使用するかを「data」を例に見てみます。以下のように既存のStorage Account情報を取得しています。

// main.tf
terraform {
  required_providers {
    azurerm = {
      source  = "hashicorp/azurerm"
      version = ">=3.90.0"
    }
  }
  required_version = ">=1.7.0"
  backend "azurerm" {
    resource_group_name  = "xxx"
    storage_account_name = "xxx"
    container_name       = "xxx"
    key                  = "terraform.tfstate"
  }
}
provider "azurerm" {
  features {}
}

data "azurerm_storage_account" "this" {
  name                = "stdbwunity"
  resource_group_name = "rg"
}

テストは以下の通り記載します。9行目までがモックの設定です。

// test.tftest.hcl
mock_provider "azurerm" {}

override_data {
  target = data.azurerm_storage_account.this
  values = {
    account_kind = "StorageV2"
    account_tier = "Standard"
  }
}


run "storage_data_test" {

    command = apply
    assert {
        condition = data.azurerm_storage_account.this.account_kind == "StorageV2"
        error_message = "kind should be StorageV2"
    }

    assert {
        condition = data.azurerm_storage_account.this.account_tier == "Standard"
        error_message = "tier should be Standard"
    }

}

まずはモックなしでテストを実行をします。テストファイルの1行目から9行目をいったんコメントアウトします。すると、terraformがAzure環境へアクセスを行い、指定したStorage Accountの情報を取得します。既存のStorage Accountは「StorageV2」かつ「Standard」ですので、テストは成功します。

続いてはモックを有効にします。ただし、この時3行目から9行目の「override_data」はコメントアウトしたままで、1行目の「mock_provider」のみ有効にします。その結果、以下のようにテストに失敗しました。

モックを利用する場合、実際にプロバイダー経由でAzureにアクセスは行いません。代わりにterraformで自動でランダムな文字列を生成しています。

この動作は「override」することができます。テストファイルの3行目から9行目のコメントアウトを解除して実行すると、指定した値でterraformで値を設定してくれるためテストは成功します。「override_data」の「target」にモックを行いたい対象のdataを指定します。「values」にはStorage Accountのプロパティと上書きしたい値を指定します。

モックを利用すると、Azureまで確認することなくterraformがその場で値を生成してくれます。そのためテスト実行時間は短くなります。モック有り無しで3回ずつ実行してみた結果を以下に記載します。

モック有無 [s]1回目2回目3回目
なし10.8510.7610.04
あり2.953.083.03

moduleをモック

続いてはmoduleをモックしてみます。まずは以下のようにStorage Accountを作成するmoduleを用意します。

// modules/storage/storage.tf

resource "azurerm_storage_account" "module_storage" {
  name                     = var.storage_name
  location                 = var.location
  resource_group_name      = var.rg_name
  account_tier             = var.tier
  account_replication_type = var.replication
}

output "kind" {
  value = azurerm_storage_account.module_storage.account_kind
}
output "tier" {
  value = azurerm_storage_account.module_storage.account_tier
}
output "url" {
  value = azurerm_storage_account.module_storage.primary_blob_endpoint
}
output "host" {
  value = azurerm_storage_account.module_storage.primary_blob_host
}

呼び出し側は以下の通りです。

// main.tf

resource "azurerm_resource_group" "this" {
  name     = "rg-terraform-test"
  location = var.location
  tags     = local.tags
}

module "storage" {
  source       = "./modules/storage"
  rg_name      = azurerm_resource_group.this.name
  storage_name = "stterraformfromparent"
  tier         = "Standard"
  replication  = "LRS"
}

moduleのモックは「override_module」を利用します。そして「outputs」にオブジェクト形式でmoduleからのアウトプットを上書きする値を設定します。

//test.tftest.hcl

mock_provider "azurerm" {}

override_module {
  target = module.storage
  outputs = {
    kind = "StorageV2"
    tier = "Premium"
  }
}

run "storage_mock_test" {

    command = plan
    assert {
        condition = module.storage.kind == "StorageV2"
        error_message = "kind should be StorageV2"
    }

    assert {
        condition = module.storage.tier == "Standard"
        error_message = "tier should be Standard"
    }
}

あえて失敗するテストを記載しています。その結果、以下の通り期待通り失敗しました。

ちなみに、モックで値(output)を上書きしないと、dataをモックした時と異なりランダムな文字列が設定されるわけではなく、値は「null」となりました。

もう少し実践的な例

モックの動作は確認できました。しかし、今まで見てきた内容ですと、ただのlintとあまり変わらない気がします。そこでもう少しあり得そうなテストをやってみたいと思います。Storage Accountを作成し、そのblob endpointを取得します。そしてendpointからホスト名を取得する処理を実装します。

Storage Accoun作成後に得られるblobエンドポイントは以下のようなものです。

url = "https://stterraformfromparent.blob.core.windows.net/"

ここから先頭の「https://」と末尾の「/」を削除します。やり方は複数あると思いますが、例えば以下の方法で実現できます。

resource "azurerm_resource_group" "second" {
  name     = "rg-test-second"
  location = var.location
  tags = {
    host = trimsuffix(trimprefix(module.storage.url, "https://"), "/")
  }
}

本来は、取得できたホスト名をAzure Functionsの環境変数とかに設定することになるかと思います。ただし、今回はAzure Functionsのterraformコードを記載するのが面倒であったため、適当にリソースグループのタグに設定してみました。

そして、タグに期待する通りのホスト名が設定されるかをモックを利用してテストしてみます。先ほどのStorage Accountを作成するmoduleを使っています。

mock_provider "azurerm" {}

override_module {
  target = module.storage
  outputs = {
    url = "https://test.blob.core.windows.net/"
  }
}

run "storage_host_test" {

    command = apply
    assert {
        condition = azurerm_resource_group.second.tags.host == "test.blob.core.windows.net"
        error_message = "host should be test.blob.core.windows.net"
    }
}

Storage Accountのエンドポイントは「plan」時にはわかりません。「apply」した後にわかります(実際はStorage Account名.blob.core.windows.netですので事前にわかります)。しかし、モックを使うことで、実際にAzureでリソースを作成(apply)しなくても簡単に動作をテストできます。

ちなみに、Storage Account作成後「azurerm_storage_account.module_storage.primary_blob_host」よりホスト名を直接取得できるので、本来はこんなことをする必要はありません。(いい例が思いつきませんでした)

最後に

terraformで関数を利用している個所では、モックを利用してユニットテストを作成すると便利そうだなと感じました。

以下に参考となるterraformの記事リンクを載せますので、興味がある方は見てみてください。

コメント