AutomationでAzure ADユーザとリソースグループを大量作成＆削除

定期的にAzureのハンズオン勉強会をやっていて、一時的なADユーザとADユーザ専用のリソースグループを参加者人数分作ってるんですが、さすがに毎回作って消してはめんどくさいので自動化しましたという話しです。

# やりたいこと
## 勉強会開始前
Azure Automationを使って、毎月勉強会開催前にAzure ADユーザ20人分と、Azure ADユーザ用のリソースグループ20個を作ります。
それぞれのユーザは自分のリソースグループしか見えないようにします。
完了したらSlackに通知します。

## 勉強会終了後
Azure Automationで、事前に作ったADユーザとリソースグループを削除します。
完了したらSlackに通知します。

# Automationアカウント作成

* まずはAutomationアカウントを作りましょう。手順は公式参照。
[Automation アカウントを作成する](https://docs.microsoft.com/ja-jp/azure/automation/automation-quickstart-create-account)  

* アカウントを作ったらモジュールをアップデートします。
これをやっておかないと「not recognized as the name of a cmdlet」エラーが発生する可能性があります。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/180335/8e956046-6249-30a2-3184-331dfe46be60.png)

# AutomationアカウントにUser Account Administrator権限付与
Azure ADユーザを作成するので、Automationアカウントに「User Account Administrator」権限が必要です。
権限付与しておかないと、New-AzureRmADUser実行時に「Insufficient privileges to complete the operation」エラーが発生します。

* 管理者権限でPowerShellを開きます。
※執筆時点ではCloudShellではうまくいきません。ローカルのAzure PowerShellを使いましょう。

* AzureADモジュールをインストールします。

```
Install-Module -Name AzureAD
```

* Azure ADに接続します。

```
Connect-AzureAD
```

* ディレクトリロールの一覧を出力します。DisplayName「User Account Administrator」のObjectIDを控えておきます。

```
Get-AzureADDirectoryRole
```

* DisplayName「User Account Administrator」が存在しない場合は、以下の手順で有効にします。DisplayName「User Account Administrator」のObjectIDを控えておきます。

```
$roleTemplate = Get-AzureADDirectoryRoleTemplate | ? { $_.DisplayName -eq "User Account Administrator" }
Enable-AzureADDirectoryRole -RoleTemplateId $roleTemplate.ObjectId
Get-AzureADDirectoryRole
```

* AutomationアカウントのオブジェクトIDを控えておきます。

```
Get-AzureADServicePrincipal -All $true | Select-String "[YOUR AUTOMATION ACCOUNT NAME]"
```

* Automationアカウントに権限を付与します。

```
Add-AzureADDirectoryRoleMember -ObjectId [USER ACCOUNT ADMINISTRATOR OBJECT ID] -RefObjectId [AUTOMATION ACCOUNT OBJECT ID]
```

# Automationアカウントをサブスクリプションの所有者に登録
Azure ADのRBAC操作が必要なため、Automationアカウントをサブスクリプションの所有者に登録します。
所有者にしておかないと、New-AzureRmRoleAssignment実行時に「The client 'XX' with object id 'XX' does not have authorization to perform action」エラーが発生します。

* サブスクリプションのIAMから権限追加します。  
![image.png](https://qiita-image-store.s3.amazonaws.com/0/180335/b5833cb8-6809-4089-aeff-ed024fc7b618.png)


# Runbook作成
* Runbookの種類は「PowerShell」で作成します。

```
Param(
    # before(ハンズオン開始前) or after(ハンズオン開始後)を指定します
    [string]$param,
    # 作成するADユーザのパスワードを指定します
    [string]$password
)

$Conn = Get-AutomationConnection -Name AzureRunAsConnection
Add-AzureRMAccount -ServicePrincipal -Tenant $Conn.TenantID -ApplicationID $Conn.ApplicationID -CertificateThumbprint $Conn.CertificateThumbprint

switch($param) {
    before {
        for ($i=1; $i -lt 21; $i++) {
            $num = $i.ToString("00")
            # userXXという名前のADユーザを作ります。この例では20人分作っています
            $SecureStringPassword = ConvertTo-SecureString -String $password -AsPlainText -Force
            New-AzureRmADUser -DisplayName user$num -UserPrincipalName user$num@[YOUR AD TENANT].onmicrosoft.com -Password $SecureStringPassword -MailNickName user$num
            # handsonXXという名前のリソースグループを作ります
            New-AzureRmResourceGroup -Name handson$num -Location japaneast
            # handsonXXのIAMにuserXXを所有者権限で付与します
            New-AzureRmRoleAssignment -SignInName user$num@[YOUR AD TENANT].onmicrosoft.com -RoleDefinitionName "owner" -ResourceGroupName handson$num
        }
        $msg = "Azureハンズオンの環境を準備したよ"
    }
    after {
        for ($i=1; $i -lt 21; $i++) {
            $num = $i.ToString("00")
            # 作成したADユーザとリソースグループを削除します
            Remove-AzureRmADUser -UserPrincipalName user$num@[YOUR AD TENANT].onmicrosoft.com -Force
            Remove-AzureRmResourceGroup -Name handson$num -Force
        }
        $msg = "Azureハンズオンで使った環境をキレイにしたよ"
    }
    default {
        $msg = "ERROR: Please specify an argument"
    }
}

$enc = [System.Text.Encoding]::GetEncoding('ISO-8859-1')
$utf8Bytes = [System.Text.Encoding]::UTF8.GetBytes($msg)
$payload = @{
        text = $enc.GetString($utf8Bytes);
        username = "Azure Automation";
        icon_emoji = ":azure:"
        channel = "[YOUR CHANNEL NAME]"
}

# Slackに完了を通知します
Invoke-RestMethod -Uri "[YOUR INCOMING WEBHOOK URL]" -Method Post -Body (ConvertTo-Json $payload)
```

# 実行
あとは実行してうまく動作すればOKです！
必要に応じてAutomationスケジュール設定しましょう。