Azure環境のTerraform管理をはじめる

過去に何回かAzure環境(私の用途だとAzureADだけど)をTerraform管理するため、構築してきたけど 毎回やり方を忘れて数時間無駄にするのでメモ。

概要

  1. TerraformでAzure環境をゴニョゴニョするために「Azure サービスプリンシパル」なるものを作る。
  2. AWSで言う「プログラムによるアクセス」のIAMユーザーを作るのと同義な気がする。しらんけど。
  3. stateファイルを管理するために「Azure Storage」も用意する。
  4. 1で作った「Azureサービスプリンシパル」に権限を与える。
  5. tfファイルの作成
  6. tfファイルでリソース作成してみる

1. Azureサービスプリンシパルを作る

  1. Azureポータルにログイン。
  2. Cloud Shellを開く
    • f:id:undersooon:20200910095247p:plain
  3. Bashを選択して、下記コマンドを実行。(サブスクリプションIDの確認)
    • az account list --query "[].{name:name, subscriptionId:id, tenantId:tenantId}"
    • 複数表示されたら、下記で使用するサブスクリプションIDを指定する。
      • az account set --subscription="<<上記のsubscriptionIdの値>>"
  4. 確認したサブスクリプションIDを変数に入れとく。
    • SUBSCRIPTION_ID=<<上記のsubscriptionIdの値>>
  5. サービスプリンシパル名の定義。(任意の値です。わかりやすくTerraformとかにするといいかも)
    • 例: SUBSCRIPTION_NAME="Terraform"
  6. 下記コマンドで、サービスプリンシパルを作成。
    • az ad sp create-for-rbac --role="Contributor" --scopes="/subscriptions/${SUBSCRIPTION_ID}" --name "${SUBSCRIPTION_NAME}"
      • 表示される値はすべてあとで使うのでメモしておく。 ※特にpasswordはあとで確認不可なので必ずメモする。

2. stateファイル管理用のAzure Storage」も用意

  1. ストレージアカウントの構成スクリプトをそのまま実行する。
    • storage_account_name, container_name, access_key はあとで必要になるので必ずメモしておく。

(念の為、上記URLのスクリプト内容を転記)

#!/bin/bash

RESOURCE_GROUP_NAME=tstate
STORAGE_ACCOUNT_NAME=tstate$RANDOM
CONTAINER_NAME=tstate

# Create resource group
az group create --name $RESOURCE_GROUP_NAME --location eastus

# Create storage account
az storage account create --resource-group $RESOURCE_GROUP_NAME --name $STORAGE_ACCOUNT_NAME --sku Standard_LRS --encryption-services blob

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

# Create blob container
az storage container create --name $CONTAINER_NAME --account-name $STORAGE_ACCOUNT_NAME --account-key $ACCOUNT_KEY

echo "storage_account_name: $STORAGE_ACCOUNT_NAME"
echo "container_name: $CONTAINER_NAME"
echo "access_key: $ACCOUNT_KEY"

3. 「Azureサービスプリンシパル」に権限を与える

  1. Azureポータルの「Azure Active Directory」 - 「アプリの登録」から作成したサービスプリンシパル名のアプリを探して開く。
  2. APIのアクセス許可」から必要な権限を付与する。
    • ご参考までに:当方は、AzureADユーザー、グループ管理、エンタープライズアプリケーション(SSO用アプリ)しか用が無かったので下記権限にしてます。
      • f:id:undersooon:20200910100503p:plain
  3. 「管理者の同意」を与える。
    • 同画面の真ん中らへんに下記ボタンがあるはずなので忘れずに実行。※わかりづらいUIなのでよくさがしてください。
      • f:id:undersooon:20200910100712p:plain
  4. 「Azure Active Directory」 - 「ロールと管理者」から、必要なロールを与える。
    • グローバル管理者が一番手っ取り早いですが、ユースケースで判断してください。(たとえば、ユーザー管理、グループ管理したいだけなら「ユーザー管理者」ロールで十分)

4. tfファイルの作成

上記までで、TerraformによるAzure環境の管理ができるようになったので下記のようなtfファイルを作って管理する。

main.tf(azureadプロバイダを使う宣言と、stateファイルの保管場所の指定)

provider "azuread" {
  version = "0.7"
}

terraform {
  backend "azurerm" {
    resource_group_name  = "tstate(手順2でリソースグループ名を変えてた場合はその名前にする)"
    storage_account_name = "<<手順2のstorage_account_name>>"
    container_name       = "<<手順2のcontainer_name>>"
  }
}

※MSのサイトではbackendにkeyも指定しているが、それは危険なのでterraform initするときに指定するようにする。(後述)

enterpriseApp.tf(エンタープライズアプリケーションの管理)

resource "azuread_application" "appName" {
  name     = "appName"
  homepage = "https://account.activedirectory.windowsazure.com:444/applications/default.aspx?metadata=customappsso|ISV9.1|primary|z"

  app_role {
    allowed_member_types = [
      "User"
    ]
    description  = "User"
    display_name = "User"
    is_enabled   = true
  }

  app_role {
    allowed_member_types = [
      "User",
    ]
    description  = "msiam_access"
    display_name = "msiam_access"
    is_enabled   = true
  }
}

resource "azuread_service_principal" "appName" {
  depends_on                   = ["azuread_application.appName"]
  app_role_assignment_required = "true"
  application_id               = "${azuread_application.appName.application_id}"
  tags = [
    "8adf8e6e-67b2-4cf2-a259-e3dc5476c621",
    "WindowsAzureActiveDirectoryCustomSingleSignOnApplication",
    "WindowsAzureActiveDirectoryGalleryApplicationNonPrimaryV1",
    "WindowsAzureActiveDirectoryIntegratedApp",
  ]
}

※1 azuread_service_principalについて補足1
エンタープライズアプリケーションの種類はtags欄で決まる模様。上記は「ギャラリー以外のアプリケーション」を作成する例。
※2 azuread_service_principalについて補足2
depends_onを設定しないとazuread_applicationとの整合性がとれなくなってつむ。

azureuser.tf(ユーザーやグループを管理するtf)

# AzureADからObject IDを取得
data "azuread_user" "user1" {
  user_principal_name = "user1@hogehoge.com"
}

# グループ作成
resource "azuread_group" "group1" {
  name = "group1"

# AzureADのグループにメンバー追加
resource "azuread_group_member" "group1" {
  group_object_id  = azuread_group.group1.id
  member_object_id = data.azuread_user.user1.id
}

5. TerraformでAzureリソースを作成してみる

手順4のtfファイルが存在するディレクトリで下記を実行。

export ARM_SUBSCRIPTION_ID="<<手順1のsubscriptionId>>"
export ARM_CLIENT_ID="<<手順1のappId>>"
export ARM_CLIENT_SECRET="<<手順1のpassword>>"
export ARM_TENANT_ID="<<手順1のtennant>>"

terraform init -backend-config="key=<<手順2のaccess_key>>"

無事、successとなったら

terraform plan で作成されるリソースの確認。 terraform applyでリソース作成して終了!

GitHub ActionsやCircle CI等でCIしたいときは上記値を環境変数とかに埋め込めばできます。

参考にさせていただいたURL

docs.microsoft.com

docs.microsoft.com

qiita.com