Terraform × Azure: 基本編

f:id:dombri:20200725180932j:plain
こんにちは。お久しぶりです。

最近Twitter諸々のアイコンを変えました。

以前のアイコンはPEANUTSに出てきそうなボブ茶髪の人でした。覚えていらっしゃいますでしょうか・・・
「誰だかわからなくなる」リスクはありますが、気分転換になっていいですね。

本日の内容は以下です。


今回の記事の経緯

お仕事ではAzureをメインで利用しています。

運用が始まって数ヶ月。自動化したくてしょうがない手作業が山ほどあるのに、それを自動化するタスクの優先順位が一向に上がらないまま、日々のタスクに追われています。

このままだともう一生手作業のままでは・・・それは辛い・・・SRE名乗れない・・・

という危機感から重い腰を上げて、この連休の間にTerraformの基本を学んでみることにしました。
「ここでTerraformやらせてください!やりたいんです!」って言える状態にしておきたい。


前提条件

  • Microsoft Azure のアカウントがあること
  • Cloud Shell (bash) が使用できること


実際の手順

Azureのクイックスタートをベースに進めていきます。

この手順ではリソースグループを作成するTerraform構成ファイルを作成するそうです。人畜無害なリソースグループ。安心設計。

これより先の手順はCloudShellで行います。AzureCLIでもできるみたいです。


1. サービスプリンシパルの準備

※すでにある場合は読み飛ばしてください

Azure サービスをデプロイまたは使用する自動化ツール (Terraform など) のアクセス許可は、常に制限されている必要があります。 完全な特権を持つユーザーとしてアプリケーションをログインさせる代わりに、Azure にはサービス プリンシパルが用意されています。

https://docs.microsoft.com/ja-jp/azure/developer/terraform/getting-started-cloud-shell より

サービスプリンシパルにAzureサービスへのアクセス権限を持たせることによって、Terraformのリソース作成を制御します。SPか・・・
※と、ここでちょっとモヤッた部分を汲んでくださる資料がありました。2年前から多少変化があるかもしれませんが。引用する資料、真壁さん発なことが多い。いつも助けられています。

www.slideshare.net


1) サービスプリンシパル (SP) の作成

今回は 共同作成者 ロールのSPを作成します。

# サブスクリプション情報を表示
$ az account list --query "[].{name:name, subscriptionId:id}"
[
  {
    "name": "subscription_name",
    "subscriptionId": "********************"
  }
]

# 特定のサブスクリプションへの操作設定
$ az account set --subscription="<subscription_id>"

# SPの作成
$ az ad sp create-for-rbac --role="Contributor" --scopes="/subscriptions/<subscription_id>"

Creating a role assignment under the scope of "/subscriptions/******************:"
{
  "appId": "**************",
  "displayName": "*****",
  "name": "***********",
  "password": "***********",
  "tenant": "*********"
}

注意:ここで表示されたパスワードは必ず控えておくこと。後から参照することはできない。


2) SPを使用してログインする

先ほど作成したSPでログインしなおします。

$ az login --service-principal -u <service_principal_name> -p "<service_principal_password>" --tenant "<service_principal_tenant>"

Cloud Shell is automatically authenticated under the initial account signed-in with. Run 'az login' only if you need to use a different account
[
  {
    "cloudName": "AzureCloud",
    "homeTenantId": "**********",
    "id": "*********",
    "isDefault": true,
    "managedByTenants": [],
    "name": "***",
    "state": "Enabled",
    "tenantId": "******",
    "user": {
      "name": "<s_p_name>",
      "type": "servicePrincipal"
    }
  }
]

わお。userのnameから、SPでのログインになっていることを確認できました。 CloudShellでこんなことできるの知らなかった。


2. Terraformの構成

1) Terraformのインストール

ありがたいことに、Cloud ShellにはTerraformがインストールされているので環境の用意は不要です!


2) Terraform構成ファイルを作成

いよいよTerraformらしく(?)なってきました。

リソースグループを作成する、Terraform構成ファイルを作成していきます。


①Terraform用のディレクトリを作成

$ cd clouddrive
$ mkdir terraTest
$ cd terraTest


②Terraform構成ファイルを作成

上記で作成したディレクトリ配下に、以下のファイルを作成します。

書いてあることは、

  • Azureに対してのTerraform構成ファイル で、
  • リソースグループ terraTest-rg を、
  • 東日本リージョン に作成するよ

ということです。

・terraTest.tf

provider "azurerm" {
  # The "feature" block is required for AzureRM provider 2.x.
  # If you are using version 1.x, the "features" block is not allowed.
  version = "~>2.0"
  features {}
}
resource "azurerm_resource_group" "rg" {
        name = "terraTest-rg"
        location = "japaneast"
}
  • provider ブロックでAzureプロバイダー(azurerm) を使用することを指定

  • azurerm プロバイダー ブロック内には、versionfeatures が設定

    • 使用方法はバージョン固有
  • resource リソース宣言は、azurerm_resource_group のリソースの種類に対するもの
    • azure_resource_group に必要な引数は name location


3) 実行プランの作成

実行プラン とやらを作成します。

①Terraformの初期化

$ terraform init

Initializing the backend...

Initializing provider plugins...
- Checking for available provider plugins...
- Downloading plugin for provider "azurerm" (hashicorp/azurerm) 2.20.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.
  • リソースグループの作成に必要なモジュールがDLされる

ちなみに、構成ファイルのないディレクトリで terraform init を実行すると失敗しました。


②アクションのプレビュー

期待した通りに実行されるかを確かめることができます。

$ terraform plan

Refreshing Terraform state in-memory prior to plan...
The refreshed state will be used to calculate this plan, but will not be
persisted to local or remote state storage.

------------------------------------------------------------------------

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.rg will be created
  + resource "azurerm_resource_group" "rg" {
      + id       = (known after apply)
      + location = "japaneast"
      + name     = "terraTest-rg"
    }

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

------------------------------------------------------------------------

Note: You didn't specify an "-out" parameter to save this plan, so Terraform
can't guarantee that exactly these actions will be performed if
"terraform apply" is subsequently run.
  • -out パラメータを使用すると、後々のためにこのプランを保存しておくことができる


③実行プランの適用

$ terraform apply

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.rg will be created
  + resource "azurerm_resource_group" "rg" {
      + id       = (known after apply)
      + location = "japaneast"
      + name     = "terraTest-rg"
    }

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.rg: Creating...
azurerm_resource_group.rg: Creation complete after 1s [id=/subscriptions/*****/resourceGroups/terraTest-rg]

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

ちゃんとリソースグループができているか確認します。

# リソースグループの確認
$ az group show -n "terraTest-rg"

{
  "id": "/subscriptions/****/resourceGroups/terraTest-rg",
  "location": "japaneast",
  "managedBy": null,
  "name": "terraTest-rg",
  "properties": {
    "provisioningState": "Succeeded"
  },
  "tags": {},
  "type": "Microsoft.Resources/resourceGroups"
}

やったー!できてるできてる。


4) 実行プランの保存

ここまでの動作は対話形式でしたが、複雑な操作の場合は実行プランをファイル形式で保存しておくと使いやすいです。

learn.hashicorp.com


# Terraformの初期化
$ terraform init

# -out で出力するファイル名を指定
$ terraform plan -out terraTest.tfplan

# 上記ファイルを指定し、実行
$ terraform apply terraTest.tfplan

Apply complete! Resources: 0 added, 0 changed, 0 destroyed.
  • 途中の対話形式の質問はなく処理が完了した

※既にあるリソースグループ(名)なので、新しいものは作成されない


3. クリーンアップ

なんと、Terraformで作成したリソースはTerraformで削除できる、と。試してみます。


$ terraform destroy

# 削除の確認
$ az group show -n "terraTest-rg"

Resource group 'terraTest-rg' could not be found.
  • destroy で、現在の実行プランを破棄する


まとめ

なんで今までやってなかったんだろう・・・と後悔するくらい、Terraformは想像していたよりわかりやすい仕組みででした。

  1. リソースを作成するためのユーザ(サービスプリンシパル)を用意する
  2. なんのリソースが欲しいか、どこに作りたいかを決める
  3. terraform init を実行して、構成ファイルを作成する
    1. ここでAzure用プラグインをDL
  4. terraform plan で結果を事前にチェック
  5. . ここで実行プランを保存しておくと使いまわせる
  6. terraform applyでリソースを作成


どうやって汎用化するのか?
連続した構築処理の書き方は?
ファイル管理の方法は?
毎回CloudShell面倒なんだけど?
等これだけではわからないことだらけですが、まずはスモールスタートでチームに共有してみよう!
来週が待ち遠しいです。でも連休は終わって欲しくないです。

本日はここまで。