なむゆだよ

なむゆなのだよ

Azure DevOpsでTerratest使ってTerraformスクリプトのテスト自動化をしましょーよ!の会

f:id:nam_yu_sql:20210518081229j:plain

この記事は後ほどcloud.config tech blogにもマルチポストします。

はじめに

最近はTerraformも回しています、なむゆです。
IaCを実現するツールであるTerraformですが、そのコードを編集した際にちゃんと思った通りに動くか確認する方法としては、そのコードを実際に動かして作成された環境を確認するのが一般的かと思います。
しかし、そうすると毎回terraform planしてterraform initし、環境が作成されて動作確認もできたら手動でその環境を削除しなければならず、かなり手間と時間がかかります。
ここを効率化しようとしたときに使えそうなツールをいろいろ探していた時に見つけた方法として、今回はTerratestを用いたAzure DevOpsパイプラインを作る方法を共有したいと思います。

Terratestとは

Terratestは、Gruntwork.ioが開発している、インフラストラクチャー向けのGo言語のテストライブラリです。
terra---という名前のとおり、terraformをメインでサポートしていますが、他にPackerやDocker、Kubernetesの構築のテストも行えるようです。
特に、terraformをサポートしているため、terraformがサポートしているGCPAWS、そしてAzureのインフラもテストすることができます。

TerratestをAzure Pipelineで実行出来たら何がうれしいの?

そんなインフラのテストライブラリであるTerraformですが、CI(継続的インテグレーション)の仕組みとしてAzure Pipelineに組み込めるとさらに強力になります。
Azureの環境構築にTerraformを使っていた場合、そのTerraformスクリプトGithub等で管理するかと思うのですが、更新したときにはそのコードをマージしても動作することを保証するために動作確認を取るかと思います。
その際にTerratestを使ったパイプラインを用意することで、例えばGithubにPRが作成されたタイミングでTerratestによるテストを自動的に実行し、動作確認を取って結果が正常かどうかを確認することができます。
手動で動作確認を取ろうとすると環境作成の手順や作成したリソースの確認の手間がかかったり作業した箇所以外の影響に気付かずデグレが起きたりする可能性がありますが、テストとして動作確認の自動化の仕組みを作っておくことでこれらを解決できます。

実際にTerratestのテストパイプラインを作ってみる

ここではTerratestのサンプルスクリプトを使いながら、Azure DevOpsのパイプラインとしてAzureにリソースをデプロイするTerraformスクリプトのテストを行う例を示したいと思います。

スクリプトを用意

今回使用するTerraformとテスト用のgolangスクリプトはTerratestのExamplesのページにあるAzureの例のものになります。
4つあるので、これらをAzure DevOpsのReposなり、権限のあるGithubリポジトリなりに作成しておきます。
気を付ける必要があるのはこれらを配置するディレクトリ構成です。
terraform_azure_example_test.goからの相対パスの記述の都合上以下のような構成で配置するようにしてください。

├─examples  
│  └─azure  
│      └─terraform-azure-example  
│              main.tf  
│              outputs.tf  
│              README.md  
│              variables.tf  
│  
└─test  
    └─azure  
            terraform_azure_example_test.go  

それに加えて、パイプライン用の以下のyamlスクリプトリポジトリ内の適当な場所に作成しておきます。
テスト用のgoスクリプトがあるディレクトリのパスだけ実際のディレクトリに合わせて変更しましょう。

pr: main  
trigger: none  
  
variables:  
  - group: AZURE_CREDENTIALS  
  
steps:  
  - pwsh: |  
      az login --service-principal -u $(ARM_CLIENT_ID) -p $(ARM_CLIENT_SECRET) --tenant $(ARM_TENANT_ID)  
      az account set --subscription $(ARM_SUBSCRIPTION_ID)  
      cd $(Build.SourcesDirectory)/[ここにはソースのルートディレクトリからテスト用のgoスクリプトがあるディレクトリまでのパスが入ります]  
      go mod init "terratest-ex"  
      go get -v -u github.com/gruntwork-io/terratest  
      go test -v -timeout 30m  
    env:  
      ARM_CLIENT_ID: $(ARM_CLIENT_ID)  
      ARM_CLIENT_SECRET: $(ARM_CLIENT_SECRET)  
      ARM_SUBSCRIPTION_ID: $(ARM_SUBSCRIPTION_ID)  
      ARM_TENANT_ID: $(ARM_TENANT_ID)  

今回使用するスクリプトとしては上記のもので全部になります。
次はこれらのスクリプトを動かすための下準備をしていきます。

Azureのサービスプリンシパルを用意

この作業には、お手持ちのAzure サブスクリプションに対して権限を割り当てられたサービスプリンシパルが必要になります。
所有者権限があれば十分なので、サービスプリンシパルを作成し、サブスクリプションに対して権限を割り当てておきます。
具体的な方法についてはこちらのドキュメントを参考にすると分かりやすいかと思います。
この時、いくつかのパラメータをメモしておくようにしてください。
パイプライン実行時にterraformの実行対象のサブスクリプションへのログイン処理に必要になるためです。
必要なものとしては、「割り当てた対象のサブスクリプションのID」「作成したサービスプリンシパルのクライアントID」「サービスプリンシパルを作成したActiveDirectoryのテナントID」、「作成したサービスプリンスパルにログインするためのシークレット」の4つになります。
サービスプリンシパルのシークレットの作成については、上記のドキュメント内の「新しいアプリケーション シークレットを作成する」の項にやり方が書かれています。

Variable Groupを用意

パイプラインで使用するVariable Groupを用意します。
Azure DevOps内のPipelines -> Library -> 「+ Variable Group」のボタンから、VariableGroupを新規作成します。
Azureのサービスプリンシパルを用意する際に取得した4つのパラメータをそれぞれの名前で追加していきます。

ARM_CLIENT_ID: 作成したサービスプリンシパルのクライアントID  
ARM_CLIENT_SECRET: 作成したサービスプリンスパルにログインするためのシークレット  
ARM_SUBSCRIPTION_ID: 割り当てた対象のサブスクリプションのID  
ARM_TENANT_ID: サービスプリンシパルを作成したActiveDirectoryのテナントID  

これら4つの変数を追加出来たら、Variable group nameに「AZURE_CREDENTIALS」と名前を付けて保存します。

パイプラインを用意

これにて下準備は完了したので、パイプラインを用意します。
パイプラインの作成方法については過去記事に例があるのでそちらを参考にしてみてください。

実行

最後に作成したパイプラインを実行します。
このパイプラインは6分程度で完了するかと思います。
処理の途中でAzureを開くと、リソースが作成されていることが確認できるかと思います。
そして、テストが終わるとそのテストのために作成されたリソースが順次削除されていき、最終的にはテストの実行前の状態に戻ることが確認できるかと思います。

おわりに

今回はAzure DevOpsでTerratestを用いたTerraformのコードのテストを実行する方法を共有しました。
これによってTerratestでのTerraformスクリプトのテストをAzure Pipeline上の自動化されたタスクとして行えるようになります。
Terraformコードが本当に動くか確認するために実際にterraformを実行して目視で動作確認するスクリプトて、テストに書いてあることはより確実にチェックでき、変更による他の部分の予期しない影響も感知することができます。
また、今回は行いませんでしたが、terraformスクリプトをapplyするのではなくplanをした際の結果を用いてテストを行うこともでき、そちらの方法だと1回のテストにかかる時間が少ない分より頻繁にテストを実行することができます。

Terraformコードの動作確認の効率化をしたい際に検討してみるのもいいかと思います。

参考