Introduction
※ 20180519 以前書いた方法が仕様変更によってできなくなっているという報告がありましたので修正しましたPowerShell を使用して Azure サブスクリプションにログインする場合、通常はユーザーIDとパスワードを使用します。
Login-AzureRMAccount
パラメタを何も指定しない場合、ログインダイアログボックスが表示され、手動でユーザーIDとパスワードを入力します。

-Credential パラメタを使用すればダイアログボックスの表示を省略することも可能です。
$UserID = "userid@company.com"
$UserPass = ConvertTo-SecureString "password" -AsPlainText -Force
$cred = New-Object System.Management.Automation.PsCredential $UserID,$UserPass
Login-AzureRmAccount -Credential $cred
ただ、この方法では多要素認証(MFA, Multi Factor Authentication) に対応できません。ユーザーIDに多要素認証が設定されている場合、以下のようなエラーが出力されます。
Login-AzureRmAccount : AADSTS50079: The user is required to use multi-factor authentication.
Trace ID: 7c047e1d-4da8-4c7a-8e0e-b7bdcf1a2200
Correlation ID: 8732b1bc-4247-4e7f-876d-9a1ee75e18d5
Timestamp: 2017-08-31 15:55:25Z: リモート サーバーがエラーを返しました: (400) 要求が不適切です
発生場所 行:4 文字:1
+ Login-AzureRmAccount -Credential $cred
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : CloseError: (:) [Add-AzureRmAccount]、AadAuthenticationFailedException
+ FullyQualifiedErrorId : Microsoft.Azure.Commands.Profile.AddAzureRMAccountCommand
Get-AzureRmVM : Run Login-AzureRmAccount to login.
発生場所 行:5 文字:1
+ Get-AzureRmVM
+ ~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [Get-AzureRmVM]、PSInvalidOperationException
+ FullyQualifiedErrorId : InvalidOperation,Microsoft.Azure.Commands.Compute.GetAzureVMC
この課題を解決する方法はいくつかありますが、ここでは -AccessToken を使用する方法を紹介します。
-AccessToken パラメタには”アクセストークン”と呼ばれるチケット的なものを指定します。このチケットは誰が発行してくれるのかといえば Azure AD です。Azure AD にチケットを発行してもらうには、発行先(つまりチケットを使う人)を明確にしておかなければなりません。発行されたチケットを他人が使用することはできないのです。
発行先を明確にするためには、”サービスプリンシパル”を登録する必要があります。
サービスプリンシパルとはなんぞや
サービスプリンシパルとは”サービスの識別子”です。と書いてもちょっとわかりずらいんですよね。なので、ここはぶっちゃけましょう。”サービス(プログラム)”を”ユーザーの一人”だと考えましょう。今回は PowerShell がサービスに相当するので、PowerShell を一人のユーザーだと考えます。こんなこと書くとその筋の識者に怒られますがww
そう、”PowerShellちゃん” です。みんな大好き、擬人化です。せっかくなので萌え絵でも描いておきますか。

”PowerShellちゃん”は、Azureサブスクリプションにログインして、Azure のリソースに触りたいんです。VM を作ったりとか、WEBアプリを再起動したりとか。

でも、”PowerShellちゃん”自身にはそんな権限はありません。そもそも Azure にログインすることができません。
なので、普段は管理者の人にログインしてもらって、その権限を借りて操作していました。

管理者が不在のときは、パスワードを事前に教えてもらっておき、-Credential パラメタを使用して好き勝手出来ました。

そこに「ちょっとまった!」をかけたのがAzure AD の"多要素認証"です。
Azure AD は Azure の番人です。いままでは、管理者の認証によって発行された”権限”を、PowerShell が流用することを許可していました。が、管理者に多要素認証が必要になると、権限の流用が不可能になってしまったのです。

”PowerShellちゃん” は困りました。毎回毎回、真夜中のタスクを実行するために管理者のスマートフォンに電話をして PIN を入力してもらうなんてことは不可能だからです。
そこで、”PowerShell ちゃん”を Azure の利用者として登録し、きちんとAzureのリソースにアクセスできる権限を与えてあげることにしましょう。
PowerShell を Azure AD に登録する(サービスプリンシパルを作成する)
まずは、Azure AD に PowerShell を登録します。
といっても、Azure AD は登録されたサービスが WEBアプリなのか PowerShell なのかについては意識していません。Azure AD にサービスを登録して”サービスプリンシパル(ユーザー ID みたいなもの)”が作成されると、Application ID と Key が発行されます。これらが、UserID と Password に相当します。PowerShell スクリプトの中で Application ID と Key を使用することで Azure にログインできるようになるのです。
多要素認証はサービスプリンシパルには適用されないので、UserID と Password のようにエラーが返されることはありません。
サービスプリンシパルを作成するための手順を以下に示します。
- Azure ポータルを開く
- "Azure Active Directory"を開く




- "Create" をクリック
- 今登録したサービスプリンシパルが存在するか、念のために検索してみましょう。





※以下の手順を追加しました 20180519
11. 「Required Permission」をクリックし、このアプリケーションからアクセスするAPIとして「Windows Azure Management API」を追加します。
以上でサービスプリンシパルの作成が完了しました。そして、サービスプリンシパルを特定するための情報として、Application ID と Key が発行されました。
これで終わりかといえばそうではありません。サービスプリンシパルは、発行されただけでは Azure に対して何の権限も持っていません。これを有効活用するためには、PowerShell が使うであろう権限を割り当てなければならないのです。
サービスプリンシパルに権限を割り当てる
Azure AD にユーザーを作成する場合は、その過程で”ロール”を割り当てることができます。ロールとはその名の通り”役割”のことです。役割には事前に”権限”が割り当てられています。
ユーザーに直接権限を割り当てるのではなく、[ユーザー]-[ロール]-[権限] のように、ロールを介して権限を割り当てる手法を RBAC(Role-Based Access Control)と言います。
ロールを介することで、ユーザーと権限を直結させる必要がなくなります。この方式の何が素敵かと言えば、「ユーザーが認証されてロールが決定される」というプロセスと、「リソースにアクセスする際にロールを見て権限を判定する」というプロセスを完全に分離することができるのです。
つまり、認証基盤側でロールさえ変えてしまえば、リソース側の権限が自動的に変わる。。。ということです。これを「認証と認可の分離」などと言ったりしますが、これを説明しはじめると休憩込みで3日ほどかかるので、ここは割愛。
以下の手順で、サービスプリンシパルにロールを割り当てましょう。
- Azure Portal を開く
- "サブスクリプション(Subscription)" を開く






ここは考慮のしどころです。ロールの選択によっては必要以上の権限を与えてしまうことになるので、本当にPowerShellが欲している権限を与えるようにしましょう。
Azure ロールベースのアクセス制御の組み込みロール
https://docs.microsoft.com/ja-jp/azure/active-directory/role-based-access-built-in-roles
Azure のロールベースのアクセス制御のためのカスタム ロールを作成する
https://docs.microsoft.com/ja-jp/azure/active-directory/role-based-access-control-custom-roles
以上でAzure上のリソースに対してロールを割り当てることができました。
Tenant ID と Subscription ID を控える
サービスプリンシパルを作成したAzure ADと、割り当てたサブスクリプションを正確に識別するために、スクリプトの中では Tenant ID と Subscription ID を明示する必要があります。そのため、以下からそれぞれの情報を控えておきましょう。
- Tenant ID(Directory ID)

- Subscription ID

スクリプトを作成する
ここまでに取得した情報を使用して、以下のようなスクリプトを作成しましょう。
これで、パスワードを使用することなく、MFAに邪魔されることなく、スクリプトを実行することができます。
※現在は以下のスクリプトは動作しません 20180519
$ClientId = "09f50028-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
$ClientSecret = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx="
$SubscriptionId = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
$TenantId = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
$ClientCredential = new-object "Microsoft.IdentityModel.Clients.ActiveDirectory.ClientCredential" $ClientId,$ClientSecret
$authority = "https://login.windows.net/" + $TenantId
$Resource = "https://management.azure.com/"
$authContext = New-Object "Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext" $authority,$false
$authResult = $authContext.AcquireToken($Resource, $ClientCredential)
Login-AzureRmAccount -AccessToken $authResult.AccessToken -AccountId $AuthClientId -SubscriptionId $SubscriptionId
※以下のスクリプトに修正しました 20180519
$ClientId = "xxxxxxxx-xxxx-xxxx-xxxx-fe7037d73d68"
$redirectUri = "https://Login-AzureRMAccount"
$SubscriptionId = "xxxxxxxx-xxxx-xxxx-xxxx-bd98aef0acbe"
$TenantId = "xxxxxxxx-xxxx-xxxx-xxxx-c2df404a71ee"
$authority = "https://login.windows.net/" + $TenantId
$resourceId = "https://management.azure.com/"
$authContext = New-Object "Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext" $authority,$false
$authResult = $authContext.AcquireToken($resourceId, $ClientId, $redirectUri)
Login-AzureRmAccount -AccessToken $authResult.AccessToken -AccountId $ClientId -SubscriptionId $SubscriptionId
ここで疑問がでますよねこの方法って安全なの???
それについては、別の投稿で解説することにします。