複数Subscriptionへのデプロイ

この記事はMicrosoft Azure Advent Calendar 2017の2日目の記事です。

ちょっと便利な、ARM template の小技を紹介します。

以前から1つのテンプレートを使って、複数のリソースグループへデプロイすることができましたが[1]。さらに、Microsoft.Resources/deploymentsAPI Version: 2017-05-10から、別のサブスクリプションへもデプロイできるようになりました。[2]

リソースを異なるサブスクリプションにデプロイするには、別のリソースグループへデプロイする時と同じように、nested template(Microsoft.Resources/deployments) を使います。 deployments リソースにsubscriptionId プロパティが追加され、そこに指定したサブスクリプションにnested templateで定義したリソースが展開されるってわけです、簡単ですね。

IMG_3105

ちょっとやってみましょう、下にテンプレートの抜粋を見て下さい。[3]テンプレートは、別のリソースグループへデプロイする時とほぼ同じで、8行目のsubscriptionId のところが違うぐらいなのがわかります。

これで上手くデプロイできるか確認しましょう。 local subscription (083c462a-7c24-4a16-8bfe-876cb0ab434b) を選択した状態でデプロイします。そうするとストレージアカウント(東日本)が作成され、同時に remote subscription (ff05d8ad-12ee-4c68-97bb-78baefa1c01a) にもストレージアカウント(西日本)も作成されます。

実行には事前に、リソースグループが必要です。サブスクリプションを切替ながら、リソースグループを両側に用意します。

$ Select-AzureRmSubscription -SubscriptionId 083c462a-7c24-4a16-8bfe-876cb0ab434b
$ New-AzureRmResourceGroup -Name LocalRG -Location japaneast

$ Select-AzureRmSubscription -SubscriptionId ff05d8ad-12ee-4c68-97bb-78baefa1c01a
$ New-AzureRmResourceGroup -Name RemoteRG -Location japanwest

これでリソースグループが出来たので、local subscription を選択し直して、前記のテンプレートをデプロイします。

$ Select-AzureRmSubscription -SubscriptionId 083c462a-7c24-4a16-8bfe-876cb0ab434b
$ New-AzureRmResourceGroupDeployment -TemplateFile .\deploystorage.json `
       -StorageAccountName kyrtlocal01 `
       -ResourceGroupName LocalRG `
       -RemoteResourceGroup RemoteRG `
       -RemoteSubscriptionId ff05d8ad-12ee-4c68-97bb-78baefa1c01a `
       -RemoteStorageAccountName kyrtremote01

思った通りに出来ているか確認しましょう。

$ Select-AzureRmSubscription -SubscriptionId 083c462a-7c24-4a16-8bfe-876cb0ab434b
$ Get-AzureRmStorageAccount -ResourceGroupName RemoteRG | fl ResourceGroupName,Id, Location

ResourceGroupName : localrg
Id                : /subscriptions/08.../resourceGroups/localrg/providers/Microsoft.Storage/storageAccounts/kyrtlocal01
Location          : japaneast


$ Select-AzureRmSubscription -SubscriptionId ff05d8ad-12ee-4c68-97bb-78baefa1c01a
$ Get-AzureRmStorageAccount -ResourceGroupName RemoteRG | fl ResourceGroupName,Id, Location

ResourceGroupName : remoterg
Id                : /subscriptions/ff.../resourceGroups/remoterg/providers/Microsoft.Storage/storageAccounts/kyrtremote01
Location          : japanwest

ストレージアカウントが2つ作成され、それぞれ別のサブスクリプションに東日本と西日本に配置されているのが確認できました。複数サブスクリプションへの配置が必要なケースはあまり無い気もしますが、なかなか面白いですね。

注意

テンプレートの、38行目で、location を西日本を直接指定しています。 ここを、location": "[resourceGroup().location]としたら、locationが東日本になってしまいました。resourceGroup()は、親のdeploymentsリソースを指すようです。ちょっと混乱しますが、「まあ納得できる範囲かな」という気はします。

最後に

今回の元ネタは、@rjmaxの、Demos for Ignite US 2017 session BRK3167[4]です。このレポジトリだけで無く彼のレポジトリはARM template を使う人には必見の情報満載です、素晴らしい。特に、rjmax/ArmExamplesはお勧めですよ、ここのテンプレートは短くて見やすいのも良い感じです。

2017 Microsoft MVP Award を受賞しました

2017年7月期、Microsoft Azure のカテゴリで、Microsoft MVP Award を受賞しました。

今年は、Cosmos DBの年ですね、楽しみな年になりそうです。

これからも、よろしくお願いいたします。

Stone

Azure に OAuth 2.0 Device Flow でログインする

この前az loginでログイン出来なくなった時に、azure cli 2.0 のコードを見ていたら、Azureのドキュメントには出てこない(見慣れない)使い方をしてたので確認も兼ねて生REST、OAuth 2.0 Device Flow[1]でAzrueにloginして Beare Tokenを取得するコンソールプログラムazlogin[2]を書いてみた。

curry

使い方

azloginを実行すると、azure cli 2.0 と同じように、デバイスコードと入力用のURLが表示される。この画面のメッセージがazure cli 2.0と一文一句違わず同じなのは、AIPが返して来るメッセージをそのまま表示してるからだ。

ログインに成功すると、アカウントに紐づいたテナントを全部取得して、テナントのアクセストークンを取得し、それを使ってサブスクリプションの情報を取得。結果をJSONで出力する。

$ .\azlogin
To sign in, use a web browser to open the page https://aka.ms/devicelogin and enter the code G9HFZ4NCY to authenticate.
....
[
   {
     "tenantId": "xxxxxxxx-xxxx-xxxxxxxxx-xxxxxxxxxxxx",
     "subscriptionId": "xxxxxxxx-xxxx-xxxxxxxxx-xxxxxxxxxxxx",
     "subscriptionName": "Developer Program Benefit",
     "bearer": "********************************************a"
   },
   {
     "tenantId": "xxxxxxxx-xxxx-xxxxxxxxx-xxxxxxxxxxxx",
     "subscriptionId": "xxxxxxxx-xxxx-xxxxxxxxx-xxxxxxxxxxxx",
     "subscriptionName": "foo",
     "bearer": "********************************************a"
   },
   {
     "tenantId": "xxxxxxxx-xxxx-xxxxxxxxx-xxxxxxxxxxxx",
     "subscriptionId": "xxxxxxxx-xxxx-xxxxxxxxx-xxxxxxxxxxxx",
     "subscriptionName": "kinmugi",
     "bearer": "********************************************a"
   }
]

このJSONから、Bearer token を取得し変数に保存するして置き、APIの呼び出しをしてみよう。

まずは、jp (JMESPath) の式を用意する。短いのでコマンドラインにそのまま書きたいところだが、Windowsだとエスケープ関係が難しすぎる(cmd.exeや、powershellでは)ので、諦めてファイルに書く。

この例だと、subscriptionName が ‘kinmugi’のサブスクリプションのBearer tokenを取り出している。Windowsだと素直にPowerShellで書いたほうが楽な気がする。最近、JMESPath が気に入っているので、あえてjpを使う。

ここでは、本物のcurlを使って、リソースグループの一覧を取得している。PowerShellだとcurlでaliasが切ってあるが使えないので、本物を入れてalias を切った方が良い。

$ cat filter.jp
[?subscriptionName == 'kinmugi'].bearer|[0]

$ $bearer = (.\azlogin | jp -u -e filter.jp)

$ curl -H  "Authorization: Bearer $bearer" "https://management.azure.com/subscriptions/xxxxxxxx-.../resourcegroups?api-version=2017-05-10"

Bearer tokenさえ手に入れれば、こんな感じでサクッと管理APIが呼べる。

最後に

Azureのドキュメントだとごちゃごちゃしてて良くわからないが、認証は基本普通のOAuth 2.0 なのでそれほど難しくない。このあたりで困ったら、OAuth2.0 のドキュメント(Googleのとか 、翻訳もある[3])を読むと良い。Azure固有の問題は、松崎さんのブログ[4]がお勧めだ。

今回は、生RESTだけで構築したが、認証だけならそれほど難しいことはない。本格的にAzureを触ろうと思うとモデルが欲しくなり。触っていくと大量のモデルが出てくるので自前で作るのはとてもメンドクサイ。その場合はAzureSDKを使うと既にモデルが用意去れているので便利だ。

参考

[1]Using OAuth 2.0 to Access Google APIs,OAuth 2.0 for TV and Limited-Input Device Applications,
[2]Azure Login OAuth 2.0 Device Flow Console Program.
[3]OAuth 2.0 Flow: DevicesYouTube Data APIのドキュメントだが、翻訳されている。
[4]Login UI が出せない Client の OAuth フロー (Azure AD)この記事より松崎さんのブログを読んだほうが良いだろう。

下記のコードも参考になる

Azure PowerShellのインストール事情

最近のAzure PowerShellのインストール事情が少々メンドクサイことになっているのでメモ代わりに書き留める。

基本は、Azure テクニカル サポート チームがAzure PowerShell 最新版のインストール手順 (v3.8.0 現在)に書いている状況だが、いろいろあるので書いた。

更新: 2017/7/7、Azure PowerShell 4.1.0 時点の情報

この問題を、VSのフィードバックに上げた所返事が来ました。

https://developercommunity.visualstudio.com/solutions/67232/view.html

To work around the issue please install the latest Azure PowerShell MSI from GitHub.

「VS 2017を入れている場合は、とりあえず MSI経由のインストールをして欲しい、最新版は、GitHubのリリースページにあるよ」ということで、色々あるけど当面はMSIで入れましょう。

2017/5/19、Azure PowerShell 4.0.1 時点の情報です

sky

推薦されるインストール方法

以前は、WebPI経由でAzure PowerShellをインストールしていたが、現在では PowerShell Gallery 経由のインストールが推薦になっている。公式ドキュメント、Install and configure Azure PowerShellにも、下記のように書かれている。(ちょっと翻訳が追いついていないようで、サイトは英語のまま)

Installing Azure PowerShell from the PowerShell Gallery is the preferred method of installation.

Windows 10、Windows Server 2016より古いOSではデフォルトでPowerShellGetが使えないので、Windows Management Framework (WMF) 5.0 を入れる必要がある[1]

PowerShellGetでPowerShell Galleryから方法は簡単だ。(以下、Install-Moduleで入れると記載)

$ Install-Module AzureRm

MSIからのインストール

Install-Moduleで入れる方法の他に。Web Platform Installer (WebPI) やVisual Studio 2017のVisual Studio Installer での、Azure「Azure development」の選択、GitHubからMSIファイルをダウンロード[2]などの方法で、MSIファイルからインストールすることができる。MSIファイルからインストールは推薦されていないが、VS経由の時にように選択の余地なくMSI経路でインストールされてしまうこともある。

また、ググって出てくる日本語の公式ページがその他のインストール方法で、その他の方法としてMSIでのインストール方法を紹介している。ここには推薦は、Install-Moduleを使うことだと書いてあるが、よく読まないで書いてある通りに操作するとMSIで入れてしまうという落とし穴もある。今のところVisual Studio 2017 で入るのは 2.1.0と大分古いバージョンだというのがいろいろな問題を面倒にしている。

問題点:MSI -> Install-Module でエラー

PowerShell Gallery と、MSI 経由のインストールには互換性が無く、MSI経由でAzure PowerShellが入った環境に PowerShell Gallery で新しいものを入れたりInstall-Module AzureRM、アップデートUpdate-Module AzureRmすることはできない。

例えば、Visual Studio 2017のインストールで、Azure PowerShellが入っている状態で、Install-Module AzureRMを やると下記のようなエラーになる。

$ install-module AzureRm

PackageManagement\Install-Package : The following commands are already available on this system:'Get-AzureStorageContainerAcl,Start-CopyAzureStorageBlob,Stop-CopyAzureStorageBlob'. This module 'Azure.Storage' may override the existing commands. If you still want to install this module 'Azure.Storage', use -AllowClobber parameter.
At C:\Program Files\WindowsPowerShell\Modules\PowerShellGet\1.0.0.1\PSModule.psm1:1809 char:21
+ ...          $null = PackageManagement\Install-Package @PSBoundParameters
+                      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (Microsoft.Power....InstallPackage:InstallPackage) [Install-Package], Exception
    + FullyQualifiedErrorId : CommandAlreadyAvailable,Validate-ModuleCommandAlreadyAvailable,Microsoft.PowerShell.PackageManagement.Cmdlets.InstallPackage

つまり、MSIインストールとInstall-Moduleは互換性が無く、「混ぜるな危険」という状況だ。

解決策

もし混ざってしまったからどうするか。

MSIで入れたものを、設定画面の、「アプリと機能」、あるいはコントロールパネルの「プログラムのアンインストールまたは変更」からアンイストール後、再度 Install-Moduleで PowerShell Gallery からインストールする。

下記は、PowerShellからアンインストールする方法。実行には、Admin権限が必要。

$ $app = Get-WmiObject -Class Win32_Product -Filter "Name like '%Azure PowerShell%'"
$ $app

IdentifyingNumber : {CB3F8A12-1570-4964-8206-17274AB9EF4D}
Name              : Microsoft Azure PowerShell - September 2016
Vendor            : Microsoft Corporation
Version           : 2.1.0
Caption           : Microsoft Azure PowerShell - September 2016

$ $app.Uninstall()

これで下記のように入れ直すと入る。

$ Install-Module AzureRM

原因

MSIで入れた場合とInstall-Moduleで入れた場合、モジュールは別の場所に入る。

MSIの場合は、下記のPSModulePathに入り

C:Program Files (x86)Microsoft SDKsAzurePowerShellResourceManagerAzureResourceManager
C:Program Files (x86)Microsoft SDKsAzurePowerShellServiceManagement
C:Program Files (x86)Microsoft SDKsAzurePowerShellStorage

Install-Moduleの場合は下記に入る

C:Program FilesWindowsPowerShellModules

MSIで入れた後にInstall-Moduleで入れた場合、MSIで入れたものを認識せずにインストールが進み、途中で別のバージョンのモジュールをロードして前記のエラーになる。Install-Moduleで入れた後に、MSIで入れると次のような問題が起きる。

MSIインストールの問題点

現在のMSIインストールで把握している問題点は3点ある(増えるかも)、そのうち深刻なのは1つ目の問題だ。

  1. Install-Moduleのモジュールを無条件上書きする
  2. Import-Module AzureRMでエラー
  3. WebPIでのリリースが遅い

1. Install-Moduleのモジュールを無条件上書

現在のMSIインストールは、Install-Moduleで入れたModuleがあれば削除してから、モジュールのインストールを行う。そのとき既存のモジュールのバージョンは見ていないので、Install-Moduleで4.0.1などのあたらしいバージョンを入れていても、VS 2017 を入れると、2.1.0になってしまうなどの現象が起きる。削除処理は、setup/Setup/RemoveGalleryModules.ps1のあたり。 Scope CurrentUserで入れた場合は考慮されておらず、なかなか残念なコードだ。

MSIだけで、バージョンアップを重ねている場合は、Windowsのインストールの仕組みでバージョンチャックされて古いバージョンは入らないので問題がないが、Install-Module(psget)で入れたものとの共存は考えられておらず、いきなり古いバージョンで上書きされるというわけだ。[3]

これは非常にメンドクサイ

2. Import-Module AzureRMでエラー

MSIで入れると、Import-Module AzureRmがエラーになる。これは、AzureRMというマニフェストモジュールが無いのが原因。Azure Poshの説明のあちこちで、Import-Module AzureRMが出て来るが、実はあまり意味がなく、モジュールはインストール時にModule Path配下に配置され、自動的にロードされるので、マニフェストモジュールが無くても動作上の不都合は無い。ただ、ドキュメントに書いてあるものがエラーになるのはちょと混乱する。ちなみに、Install-Module経由で入れてた場合は、AzureRM マニフェストモジュールが入り、Import-Module AzureRMも成功する。

$ import-module azurerm
import-module : The specified module 'azurerm' was not loaded because no valid module file was found in any module directory.
At line:1 char:1
+ import-module azurerm
+ ~~~~~~~~~~~~~~~~~~~~~
   + CategoryInfo          : ResourceUnavailable: (azurerm:String) [Import-Module], FileNotFoundException
   + FullyQualifiedErrorId : Modules_ModuleNotFound,Microsoft.PowerShell.Commands.ImportModuleCommand

3. WebPIでのリリースが遅い

なかなか、WebPIにリリースされない。今回だと、Managed Diskの機能がサポートされたバージョンはしばらく放置され、5/10にやっとリリースされた。最新版を使いたい場合は、Install-Module経由で入れるしかない。WebPIに頼らずに、GitHubからMSIをダウンロードして入れるという方法もある。[2]

まとめ

Azure PowerShell を使う場合、Install-Module AzureRMで入れる、VS 2017や以前WebPIで入れている場合は、一度コンパネからアンインストールしてから入れる。Windows 10より古い環境では、WMF 5.0を入れよう。

今のVS 2017は、リリースのたびにAzure PowerShellの古いバージョンを入れてくるが、挫けずにアンインストールしてから入れ直す。

現状だとGitHubからダウンロードして、MSIインストールで運用という手もあるが、推薦インストール方法がInstall-Module経由なので、そちらを取った方が無難だろう。VS 2017が原因で面倒なことになっているのは残念だ。

参考

[1]How to get PowerShellGetWMF 5.0 を入れようという話が書いてある
[2](1, 2) azure-powershell.4.0.1.msiリリースのとこに、msiへのリンクがある。
[3]VS Installer overwrite with old version Azure PowerShell.すぐTriaged になったけど、その後の動きがない。