tech.guitarrapc.cóm

Technical updates

Azure DevOps Pipeline をYAML で定義しよう

何度か挙げている Azure DevOps Pipeline ですが、ずっとYAML で紹介してきたかと思います。

実際に私はAzure DevOps にYAML がPreview で来てからずっとYAML にしています。

これはほかのCIサービスも複数触っていたことからもYAML でかけることに大きなメリットを見出していたからですが、改めてAzure DevOps でPipeline をYAML で定義するのをなぜススメるのか一部を書いておきます。

目次

TL;DR

ビルドは、そのプロジェクトを透明に維持するための大きなファクタの1つです。 コードとの密な連携を求められるCI/CD において、ビルドの定義はコードと近いほうがイテレーションを短くできます。 YAML はその手段の一つであり、現時点において大勢をなす方法の一つです。

Pipeline first な開発をするためにも、開発者自身が主体的にビルドを組めるYAML はいい方法になるでしょう。

個人的には、YAML で定義できないビルドサービスはなるべく避けたほうがいいと判断します。

YAML 定義のメリット

YAMLで定義できるということは、そのプロジェクトのビルド設定を、そのプロジェクトのリポジトリに置けます。結果として、アプリケーションの変更で生じたビルドの変更も適用しやすく素早くビルドを開発に合わせられるメリットがあります。(Config as Code の1つと言われたりするようですね)

Azure Pipeline でもYAML で定義することで、どのようなビルドをしているかが定義でわかります。

また、Multi Stage Pipeline でワークフローっぽく複数のジョブの連携を1つのYAMLで定義できるようになっています。*1 GUI では難しかった同じような内容のビルドでパラメータが違うだけ、というのも、YAMLならJob/Step Template を使うことで、なんども同じ定義をせず再利用が可能です。*2

YAMLでパイプラインを組みたいときの流れ

YAML Pieplineを選択してビルドパイプラインを設定しましょう。

Azure DevOps Pipeline は、後付けでYAML定義がきました。 そのため、ほかのYAML定義ができるCI サービス に比べると処理をタスクという単位で設定することが多い傾向にあります。 このタスクの中身は、Pipeline で実行したい特定の操作を TypeScript で定義してMarket Store で公開、利用するものです。 CircleCI だとOrb が近いでしょう。

Orbがそうであるように、タスクを利用するときはそのタスクに渡すパラメーターを知る必要があります。 この時、Orbなら Explore Orbsで定義が探すとOrbの利用方法が一緒に公開されているので困ることがほぼありません。

circleci.com

しかし タスクを配布している Market Place にこの仕組みがないため、どのように利用するかのリファレンスはタスクの作者がGitHub で紹介しているか頼みです。で、GUI でタスクを設定するときの画面を見つつやることが多いという悪循環があります。

Azure DevOps でUnity Tools for Azure DevOpsタスクをLegacyパイプラインでUIから設定する

YAML ではこういう入力は見れないでしょうか? そうでもありません。 Azure DevOps Pipeline のサービス上で Pipeline の Edit を行うとUIからのUIからの入力がされます。*3 GUI -> YAML がまとめて提供されているCIサービスって珍しいのでなかなか不思議な感覚です。

Azure DevOpsのPipeline 画面から Unity Tools for Azure DevOps を設定する

あるいは 以前のUIからのパイプラインを組んでおいてYAML にExport という手もあります。 以前はカスタムタスクで必ずと言っていいほど使っていましたが、UI からのYAML入力ができてからは使わなくなりました。

Legacy Pipeline でUI上で組んだ設定をYAML でPreviewする

YAML で困ること

Azure DevOps Pipeline のYAML は、Multi Stage Pipeline がきて、一般的なSaaS 型CI のYAML 定義に比べてもかなり近づきました。 とはいえ、YAML だけ未サポートだったり、アンドキュメントな挙動はあります。

そのあたりを見てみましょう。

Multi Stage Pipeline と失敗ジョブの再開

ありません。つらい。

CircleCI でいうところの、Workflow 中の失敗Job だけの再実行 (Rerun from failed)はないので、ビルドをやり直すしかありません。

CircleCI のWorkflow 失敗時の選択

Failed Job の再開くればいいんですけど。

Reference variable などの未サポート機能

Reference Variable はYAMLでは定義できません。 しかし、タスクが出力変数を設定しているなら、タスクの name: を設定することで後続で利用できます。

こういった未サポート機能でも、なんかやる方法があったりします。アンドキュメントですが。

適当にフィードバックの各種や GitHub Issue をみるといいでしょう。

docs.microsoft.com

github.com

リリースとYAML定義

ReleaseにはYAMLがありません。そのためReleaseはどうしようかと思ってしばらくしましたが、2019/6にMulti Stage Pipeline がきました。

これでRelease はオワコンっぽい雰囲気しつつあります。

ただしDeployment Group が YAML では扱えないのでそこは置いておきましょう。Gate 機能も使えないので困ったものです。

一方でビルドトリガーに関しては、Multi Stage Pipeline + Pipeline Artifact (Build Artifact はLegacyです) で checkout: none にして、Condition をかければいいでしょう。

approve などのGate と Deployment Group だけ困りますが、ほかまぁ何とかなります。

Display Name のうざさ

Azure DevOps では ビルドタスクで DisplayName: をつけなかった場合、ステップの名称はタスクの名称 になります。 Comand Line タスクなら、CmdLine となってしまい、実行しているコマンドが自動的に付けられません。

DisplayName: を省いたときのタスク実行時の表示

結果、DisplayName: を設定することになり、ビルド定義が冗長になっていくことが多いでしょう。

一方のCircleCI では、ビルドタスクに名前をつけず、YAMLをなるべく最小限にしても困りません。 ビルドステップに名前を使えなかった時に、実行ログのstep 名にはコマンドがそのまま名称になるからです。

CircleCIでステップの名称を付けないと、ステップの実行内容がそのまま表示される

こういう細かいところが、まだまだ改善の余地を感じます。

キャッシュ機能

AzureDevOps には現状ビルドstep のキャッシュ機能はありません。 これがこないと毎ビルドでパッケージを復元したり無駄極まりありません。

Peeview でくるらしいので待ちましょう。 キャッシュが来る場合、CircleCI のキャッシュのようにキーによる世代ハンドリングをしたいところです。 これが来ないとかなりアレなので。

おわりに

YAML 定義は、Multi Stage Pipeline が来るまではリリース含めてどうするか、厳しいと言わざるを得ませんでした。 しかし、Multi Stage Pipeline が来て、ジョブの定義をより強力に記述できるようになったことでだいぶんよくなりました。

もし Azure DevOps Pipeline を使うなら、YAML 一択です。

*1:CircleCI の Workflow に相当

*2:CirclecI でいう Command やJobに相当

*3:あくまで入力値だけで一度YAMLにいれたものの編集では開きません

Azure DevOpsの Microsoft-hosted Agent で HoloLens ビルドを行う(MRTK/MRTKv2)

前回、Azure DevOps で HoloLens のビルド環境を行うこと、Microsoft-hosted Agent が今ならいいということを書きました。

tech.guitarrapc.com

今回は、MRTKとMRTKv2 を Microsoft-hosted Agent で現実的にビルドすることを考えてみましょう。 内容としては、次の記事が近いです。

withmr.nextscape.net

この記事では、MRTKv1とMRTKv2 それぞれについてリポジトリとAzure Devops Pipeline の YAMLを示しつつ、現実にビルドしていくことに必要なことをカバーします。

目次

Unity のビルドエラーがわからない

Windows上でUnityのバッチビルドをしたときに困るのが、Unity ビルドの経過、ログが標準出力に出ないことです。*1

結果Unity 内部でどのようなエラーが起こってもエラーの詳細がわからないので、記事にあるUnityBuildTask@2 を使う方法は現実的ではありません。*2

コンパイルエラーでどこでコンパイルエラー出たかわからないとか絶望です。

そのため、Unity Tools for Azure DevOps でUnity ビルドを行うのは厳しいものがあります。

- task: UnityBuildTask@2
  displayName: 'Unity Build WindowsStoreApps'
  name: 'unityBuild'
  inputs:
    buildTarget: 'WindowsStoreApps'
    unityEditorsPathMode: 'unityHub'
    unityProjectPath: '$(unityProjectPath)'
    commandLineArgumentsMode: 'default'

marketplace.visualstudio.com

代わりに「Unityビルド呼び出しつつlogファイルに吐いたログを読み込んで標準出力に吐く」ラッパーアプリケーションを用意する方法をよく採用されています。

chiepomme.gitbook.io

お手伝い先で必要になり一年前に作り使っていただいているのが UnityBuildRunner です。*3

github.com

dotnet global tool として公開してあるので、dotnet tool install -g UnityBuildRunner でWindows/Mac問わずサクッとインストールできます。

使い方は単純です。Unity のバッチビルドは次のフォーマットです。

Unity.exe [args]

この引数をUnityBuildRunnerにそのまま渡せばokです。 加えてタイムアウトにも対応してあります。

UnityBuildRunner [-UnityPath|-unityPath|-u] [-timeout|-t 00:60:00] [-version] [-help] [args]

たとえば、MRTKv2 でビルドするならこのように書けます。

UnityBuildRunner -UnityPath "C:\Program Files\Unity\Hub\Editor\2018.3.13f1\Editor\Unity.exe" -quit -batchmode -projectPath パス -buildTarget WSAPlayer -logfile Editor.log -executeMethod Microsoft.MixedReality.Toolkit.Build.Editor.UnityPlayerBuildTools.StartCommandLineBuild

Unityパスは環境変数から読むこともできるので次のようにも書けます。

set unityPath=C:\Program Files\Unity\Hub\Editor\2018.3.13f1\Editor\Unity.exe
UnityBuildRunner -quit -batchmode -projectPath パス -buildTarget WSAPlayer -logfile Editor.log -executeMethod Microsoft.MixedReality.Toolkit.Build.Editor.UnityPlayerBuildTools.StartCommandLineBuild

https://github.com/guitarrapc/UnityBuildRunner を使ったWindowsでのUnityビルド

MRTKv1 のビルドを行う

MRTK のビルドは、HoloToolkit.Unity.BuildDeployTools.BuildSLN メソッドを使います。*4

MRTKv2を使ったビルドとYAML定義を含んだリポジトリです。

github.com

Azure DevOps でのビルド結果も示します。

dev.azure.com

ビルドのYAML定義を見てみましょう。

gist.github.com

途中のコメントを外すとsln一式がartifacts から取得できます。

# if you want to build locally
# - task: PublishPipelineArtifact@0
#   displayName: 'Publish Pipeline Artifact'
#   inputs:
#     artifactName: 'UWP_Sln'
#     targetPath: 'UWP'

ちなみにMRTKv1 のシンプルなシーンをビルドしてみると、ビルドに30min、長い。

MRTKv1 の Azure DevOps Microsoft-host Agent でのビルド

プロジェクト固有の情報を知らずビルドするコツとして、VSビルド時にslnファイルを指定するのではなくslnのあるフォルダを指定 + / と書くことでslnビルドできます。/ がないとsln が見つからないので注意です。

MRTKv2 のビルドを行う

MRTKv2 は、Windows SDK 18362 が必要ですが、これがHosted Agent に来てビルドできるようになったのは6/25です。 Sprint 153 で追加対応が来てから、自分たちのAgent にくるまで約2週間かかりました。

MRTKv2のビルドは、Microsoft.MixedReality.Toolkit.Build.Editor.UnityPlayerBuildTools.StartCommandLineBuild メソッドを使います。 相変わらずDevelopment ビルドの指定ができないのがアレなのでラッパーを書くことになるでしょう。

MRTKv1を使ったビルドとYAML定義を含んだリポジトリです。

github.com

Azure DevOps でのビルド結果も示します。

dev.azure.com

ビルド定義のYAMLは次のようになります。

gist.github.com

途中のコメントを外すとsln一式がartifacts から取得できます。

# if you want to build locally
#- task: PublishPipelineArtifact@0
#  displayName: 'Publish Pipeline Artifact'
#  inputs:
#    artifactName: 'sln'
#    targetPath: 'Builds/WSAPlayer'

MRTKv2 のサンプルシーンをビルドしてみると、ビルドに30min、長い。

MRTKv2 の Azure DevOps Microsoft-host Agent でのビルド

プロジェクト固有の情報を知らずビルドするコツとして、VSビルド時にslnファイルを指定するのではなくslnのあるフォルダを指定 と書くことでslnビルドできます。先ほどと違い、/ で終わるとsln が見つからないので注意です。

学び

Unityのインストールにかかる時間

Microsft-hosted Agent にはUnity がインストールされていません。 そのためこのYAML ではUnityを都度インストールしています。

このUnityの準備にかかる時間をリードタイムとしてみると、10min 程度かかります。 毎ビルドで10min かかるのはアレでもあり、その程度で済むならいいかもしれません。((よくない)))

Output変数が定義されたタスクの変数はnameを指定することでとれるようになる

UnityGetProjectVersionTask@1 で取得したUnityバージョンを変数に出力します。 これを使うことでUnityプロジェクトのバージョンを知ることなく、同じYAMLをどのUnityバージョンでも変わらず使えます。

さて、UI と違って YAML では reference variable の設定はサポートされていません。 しかしTASKで出力変数が設定されている場合は、name をつけることで取得できます。

- task: DinomiteStudios.64e90d50-a9c0-11e8-a356-d3eab7857116.custom-unity-get-project-version-task.UnityGetProjectVersionTask@1
  displayName: 'Unity Get Project Version'
  name: unitygetprojectversion

これで、後段のstep において $(unitygetprojectversion.projectVersion) で参照できます。 教えてもらって気づきました、これはむりげー。

*1:macOS/Linux では、-logFile を無引数で与えることでログが標準出力に出ます

*2:MRTKv1 で私の環境ではビルドが60min 立っても終わらない状況も発生しました

*3:↑の記事より前に作ったのですが、結果似たことしててあぁって感じです。悲しい

*4:Unity BatchビルドはDevelopment 指定が引数でできないのがアレなので、たいがいラッパーを書いている気がします。

Azure DevOpsでのHoloLens ビルドを行うためのCI/CD選択

お手伝い先でHoloLens でのCI/CD を構築、運用して1年を超えましたが、日々安定してビルドができています。

先日素敵な記事も出てて良い感じです。*1

Azure DevOpsでHoloLensアプリをビルド(MS-hosted編) | NEXTSCAPE with MR

HoloLens のCI環境について、必要な要件、CI環境の選択、Azure DevOps のMicrosoft-hosted と Self-hosted の選択を見てみます。

目次

ビルド環境の選択(OS)

HoloLens に限定してビルドについて検討すると、UWP ビルド = Windows SDK が鍵になります。 現状のWindows SDKは、Windows でのみインストールが可能であり、結果としてビルド環境もWindows に限定されます。

Unity は Unity 2019 においても動作環境として、Windows Server を想定していません。(以前サポートにも問い合わせた)

OS:Windows 7 SP1 以降、8、10(64 ビットバージョンのみ)、macOS 10.12 以降

サーバー版の Windows、OS X についてはテストされていません。

https://unity3d.com/jp/unity/system-requirements

Windows Server 2012 R2 でUnity 起動すると即クラッシュします、対策にはDesktop Heap を広げる必要がありました。 幸いにもWindows Server 2016/Windows Server 2019 では問題なく起動し、ビルドも可能です。*2

今HoloLens ビルドを行うなら、Windows Server 2019 でビルドが可能です。(やってる)

ビルドの選択(コンテナ)

現状の Windows SDK + Unity をコンテナでビルドするのは難しい感触です。

Windows Container でコンテナイメージが公開されているようにも見えますがuwp という名はトラップです。中身は、Portal Library (Target UWP 10.0) であり UWPビルドはできません。

coderobin/windows-sdk-10.1 - Docker Hub

UWP のセルフビルドでわんちゃんいけそうですが、ここにUnity が入ると更にサイズが.... 。

windows-container/windowsservercore/windows-sdk-10.1 at master · coderobin/windows-container · GitHub

コンテナビルドができても、Windows Container だと旨味が少ないので悩ましいですが..... コンテナだと楽なのも事実なのでなんともです。

合わせて必要に応じてこのあたりも対応ですね。

Visual Studio Build Tools をコンテナーにインストールする | Microsoft Docs

なお、HoloLens はすでにIL2CPP ビルドがデファクトまったなしなので、C# と VC++ のビルドが必要です。また、vcxproj/csproj は SDK 形式ではないため dotnet ではなく msbuildが必要です。*3

Windows のビルドサービス

Windows でのビルドを行うとき選択肢はいくつか考えられます。

  • Azure DevOps
  • AppVeyor
  • Jenkins v2
  • Drone
  • Team City

Managed な環境でWindowsビルド を提供しているのは Azure DevOps と AppVeyor です。 Self-hosted Agent (自分でWindowsホストにエージェントをインストールして利用する)を提供しているのは、Azure DevOps、AppVeyor(Enterprise)、Jenkins v2、Drone、Team City です。

Team Cityを除きどれもYAMLでのビルドを定義できるのでそこは別にいいでしょう。

Open Source ではビルドをコスト、並列数、時間制約で考えると、Managed な環境であるAzure DevOps が最も強い選択肢になるのではないでしょうか?*4 AppVeyorは癖があることと、Spin upの遅さから選択しにくいものがあります。

個人的にはCircleCI が好きなので見てみると、Windowビルド対応予定があるように書いてあります。

https://circleci.jp/pricing/usage/

リポジトリもあり、内容を見てみるとPerformance plan でトライアルできるようです。

GitHub - CircleCI-Public/windows-preview-docs: Temporary docs on how to use the pre-release preview of Windows builds on CircleCI

Windows 向けOrb もあり、使えそうな感触があります。

orbs:
  win: sandbox/windows-tools@dev:preview

基本的なビルド機能である、caching, workspaces, SSH into the build がサポートされている一方で、Previewの現在、デフォルトでインストールされているのはごく一部のソフトウェアなので注意です。

We install Git, Chocolatey, and 7zip. We don’t install any other dependencies at the moment.

ビルド環境としての AzureDevOps Pipeline

Azure DevOps のビルド環境には、「おまかせできる Microsoft-hosted」 と 「自分で環境を自由に設定できるSelf-hosted」 の2種類あります。

  • Microsft-hosted Agent: Microsoft がhost して各種ソフトウェアもビルトインで入っている
  • Self-hosted agent: ユーザーがWindows/macOS/Linux のいずれかにインストールしてミドルウェアや環境を好きに構築したうえで動かす

docs.microsoft.com

両環境で一番大きく異なるのは、価格、パフォーマンス、実行環境のクリーンさ、ミドルウェアの管理です。

私自身パフォーマンスからSelf-hosted Agent を使う決断を下すことが多かったのですが、現在はHosted agent がいいように思います。(理由は後述)

この選択に関しては、他のAzure DevOps ユーザーも記事をあげているので参考にするといいでしょう。

Too bad! You still need a private build agent when using VSTS – Henry Been

では、Self-hosted Agent と Microsoft-hosted Agent ののどちらを、どのような観点で選択するといいのか考えてみます。

価格面の違い

価格表があるので見てみましょう。

  • Microsoft-hosted Agent : 追加の並列ジョブごとに ¥4,480 (分数制限なし)
  • Self-hosted Agent : 追加の並列ジョブごとに ¥1,680 (分数制限なし)

https://azure.microsoft.com/ja-jp/pricing/details/devops/azure-devops-services/

Hosted Agentが1並列ビルドに4480円というのはかなり高く感じるのではないでしょうか。*5

一方のSelf-hosted は1680円と安く見えるのに加え、MSDN の Visual Studio Enterprise サブスクライバーはアカウント1つあたり1つの課金済みSelf-hosted Agent ライセンスが付きます。*6 開発者一人ずつにMSDN サブスクリプションを会社で付与している場合、実質無料で開発者の数だけ Private Agent を割り当てて並列ビルドをかけられるので、一見すると大きなメリットに見えます。

The free tier is one parallel job. In addition, you get one free parallel job for each Visual Studio Enterprise subscriber that is a member of your organization. You can get more using paid self-hosted parallel jobs.

Buy continuous integration, continuous deployment, Azure Pipelines - Azure DevOps Services | Microsoft Docs

実際に Self-hosted Agent のコストを考えるときは動作するホストやストレージ料金を含めることになります。*7 よくあるケースとして、Self-hosted Agent を AzureVMを実行ホストとして実行することを基準に簡単に価格を考えましょう。*8

ビルドVMにCPUとメモリがほしいのでコスパがいいBシリーズの4 vCPU/16G RAMを採用します。

インスタンス VCPU メモリ 一時ストレージ 従量課金制
B4MS 4 16 GiB 32 GiB ~¥19,131.84/月

https://azure.microsoft.com/ja-jp/pricing/details/virtual-machines/windows/

git で大量のファイルを読み書きし、ビルドで大量のファイルリードがかかるのが主なストレージ負荷になるため、CIには高IOに耐えられる プレミアムストレージが欲しくなるでしょう。

https://azure.microsoft.com/ja-jp/pricing/details/managed-disks/

以上を踏まえて、MSDNでSelf-hosted Agent がついてくる環境で、VM とプレミアムストレージを256GB/512GB/1TB の組み合わせと、Hosted agent で (¥4480) で換算して考えてみます。

VM Storage Self-hosted 合計 Hostedの台数換算
* ¥19,131.84(VM) ¥4,896.72(Storage 256GB) ¥24028.56 5.3台
* ¥19,131.84(VM) ¥9,430.40(Storage 512GB) ¥28562.24 6.36台
* ¥19,131.84(VM) ¥17,409.28(Storage 1TB) ¥36541.12 8.1台

ビルドの数にもよりますが、1VMに載せられるエージェントの数は2-4台程度です。 このため社内マシンを使うなどの選択をしない限り 、Self-hosted は並列度との兼ね合いはかなり悪いものがあります。*9

ビルド環境の手間

ビルド環境はただビルドだけしたいので、手間がかかるのは嫌なものです。 手間をかけるメリットがあるときはかけますが、かける必要がないならかけないのがいいでしょう。

この観点では、Microsoft-hosted Agent は一般的なビルド環境程度には楽です。

Microsoft-hosted Agent

ビルドエージェントが動作するホスト環境の管理が不要なため、手間が大きく軽減されます。 都度新しい環境でビルドされるので、ビルド環境のボリューム容量も気にする必要がありません。

逆にホストの管理ができないことに割り切れないと手間がかかるでしょう。 都度新しいホストが起動するため前回のジョブ実行状態を再利用することはできません。 Hosted Agentに入っていない、インストールに手間や時間のかかるミドルウェアを事前にインストールしておいてビルドのための時間を省略することもできません。 エージェントを選ぶことはできないので、エージェントに高スペック、高IOを期待することはできません。 ジョブごとにクリーンであることが面倒なポイントです。

Self-hosted Agent

ビルドエージェントが動作するホスト環境が管理できることから、うまく使うとビルドを高速化できます。 前回のJon結果やCheckOut 結果を再利用して、gitやdockerのキャッシュをかけておくこともできます。 Unityのようなインストールに時間のかかるミドルウェアをインストールしておくことも可能です。 ミルドウェアの特定のバージョンで問題があったときに、バージョンを固定するのも難しいでしょう。

うまく使う、の度合いが半端なくホストの管理が必要で手間が大きくかかります。 まずSelf-hosted Agent のインストールが必要です。 ビルドするホストのボリューム容量の管理が必要です。1ホストで複数エージェントを動かす場合、ホストごとにワークスペースを持つためn倍(n=ホストの数)の容量を使います。 ホスト管理の手間があるのは手間の多くを占めます。

VMとストレージが更に増え、それぞれにインストールすることを考えるとなかなか面倒なことがわかります。

Microsoft-hosted Agent と Self-hosted Agent の選択

ビルドの頻度にもよりますが、HoloLens 開発でSelf-hosted Agent か Microsoft-hosted Agent を選ぶ場合、今ならMicrosoft-hosted Agent がいいでしょう。

一見Microsoft-hosted Agednt はスペックが低かったり、1並列ビルドが高かったりしますが、手間がかからない、純粋に並列度で考えられるのは大きなメリットといえます。

IL2CPPが当然になった今、HoloLens の1ビルドにかかる時間は早くても15min、普通に20min~30min 程度かかります。 Unity のCacheを持った状態での起動高速化(Library) も、スクリプトの変更に弱かったりするので厳しいものがあります。

総合的にみて、割り切ってMicrosoft-hosted Agent がいい選択肢になるでしょう。

Microsoft-hosted Agent の懸念

Microsoft-hosted Agent は、UWP ビルドができる稀な Managed CI 環境ですが、Windows SDK が怖い部分でもあります。

MRTKv2 は、Windows SDK 18362 が必要ですが、これがHosted Agent のビルドで利用できるようになったのは6/25です。

Sprint 153 で追加対応が来てから自分たちのAgent にくるまで約2週間かかっています。多くの場合この程度かかることから、Azure DevOpsにおいては2週間程度は新機能の利用まで見ておく必要があります。*10

View linked GitHub activity from the Kanban board - Sprint 153 Update | Microsoft Docs

そういった意味で、Visual Studio はともかくとして*11、MRTK が 新Windows SDK に依存した実装を入れたときにCI環境の Windows SDK がなくて悲しい思いをすることはありえます。

とりあえず 今のMRTKv2 で使ってる最新Windows SDK 来たし、まぁしばらくは大丈夫でしょう。

*1:手離れされているので安心して見ていられます。

*2:以前は Windows Serverが使えない縛りから Windows 10 をAzure のDev利用などで持って来る必要がありました

*3:dotnet SDK で完結する今どきの.NET 環境ではない

*4:無料、10並列、時間無制限

*5:そもそもパイプラインで課金という価値観が、CircleCI の新料金プランで崩れていてつらさがあります

*6:企業でMSDNサブスクリプションを配布しており、このサブスクリプションをビルドに利用したい場合、MSDN サブスクライブ画面で会社のAzureアカウントと紐づけてもらうことで割り当てられます。

*7:人的コストを除外します

*8:EC2に置き換えても結構です

*9:VMやストレージ環境

*10:UIの変更を除く

*11:これは比較的すぐにくるので

Terraform 0.11.14から0.12.0 にアップグレードを行う

Terraform 0.12.0 がリリースされ、すでに 0.12.1 がリリースされました。 いくつかのTerraform 環境で随時0.11.14 から 0.12.0 にアップグレードしているのですが、その中で AzureRM Provider に関して少し困ったのでメモしておきます。

目次

TL;DR

基本は、Upgrade Guide をみること。

Upgrading to Terraform 0.12 - Terraform by HashiCorp

~破壊的変更として、タグの一部を無視できなくなっているので、タグすべてを無視して直変更なりで一時しのぎが良さそう。~ 0.12.3 で対処が入ったので一部無視が可能になり互換性とれるようになりました。

tfenv を入れておくとめちゃめちゃ捗るので、これまで入れてなかった人はこれを気に入れておくとよいです。

操作環境

私は普段、Terraform を5つの環境で動作できるように組んでいます。*1 もっぱらGitHub PR でのCI連携をするので、Docker(Atlantis) なのですが環境によっては Azure Cloud Shell が最も利用しやすい場合はこちらを利用します。

  • Windows 10 Desktop
  • WSL Ubuntu 18.04
  • macOS 10.14 Mojave
  • Azure CloudShell
  • Docker Container (Atlantis)

さて、Azure Cloud Shell 環境はTerraform をはじめとする各種ツールが組み込みで入っており、ツールのアップグレードも勝手にされます。 つまり、おもむろにバージョンが上がります。

運用面を除外すると便利です。 一方で、0.12.0 のような破壊的変更が加わったタイミングでは、ツールのアップデート時に速やかに追随できるように整えておくのも重要になります。

今回は、0.11.14 なterraform 構成をCloudShell で当てようとしたタイミングで0.12.0にアップグレードされており*2 サクッと対応する必要があったのでした。

アップグレードの流れ

  • tfenv で0.11.14 にしておいて、terrafrom 0.12checklistで確認とエラーを潰していく
  • Provider が0.12に対応していない場合があるので、バージョンを上げて 0.12でinit できるかで確認する
  • tfenvで0.12.0 (あるいは0.12.1)にして、terraform 0.12upgrade をかけて微妙に拾えてなかったシンタックスの変更を潰す 0.12upgrade で変更されないModuleが中にはいるので、手でちまちま治す。
  • terraform plan かけて変更でなかればまず大丈夫
  • atlantis など適用環境で terraform apply かけて出力を確認して終わり

よく引っかかる変更点

先に、0.12 の変更点だけ見ておくといいです。

Terraform 0.11→0.12で追加された新機能 | DevelopersIO

さて、次のものは度々0.12.0にアップグレード対応するたびに、あぁまたこれかという感じで引っ掛かります。

Provider not support 0.12

単純にアップグレードすればok なことが多いですが、サポートされているかの確認はしましょう。

azurerm provider なら次のようにかなり細かく出ています。

Releases · terraform-providers/terraform-provider-azurerm

Argument names must not be quoted

Terraform 0.11.14までは、map や リソース内のblock・map のハンドルが微妙にゆるふわでした。 これが、0.12 から比較的しっかり見るようになっています。

結果、例えばこういったmap 宣言が0.11.14まではokでしたが、

locals {
  common_tags {
    environment = "${var.ENV}"
  }
}

0.12.0 からは、 = が必須になっています。

locals {
  common_tags = {
    environment = var.ENV
  }
}

あるいは、AzureRM Provider の azurerm_function_app などが提供する identity ブロックもそうです。

0.11.14まではこれがokでした。

  identity = {
    type = "SystemAssigned"
  }

0.12.0 からはこうなります。

  identity {
    type = "SystemAssigned"
  }

ref: Error: Invalid argument name (Argument names must not be quoted) · Issue #19575 · hashicorp/terraform

Resource の Deprecation

たとえば、azurerm provider の data resource である azurerm_builtin_role_definition は deprecate が予定されており、azurerm_role_definition を使うように促されます。

0.11.14 まで

data "azurerm_builtin_role_definition" "contributor" {
  name = "Contributor"
}
data "azurerm_role_definition" "contributor" {
  name = "Contributor"
}

Resource の返す型から値取り出し時の注意

list/tuple<T, TN>と型を明示的に示せるようになったことで少し変更を受けます。

たとえば、azurerm_fucntion_app リソースが返す identity から principal_id を取得することを考えてみましょう。

これは次で 0.11.14 / 0.12 ともにとれます。

output identity {
  value = "${lookup(azurerm_function_app.main.identity[0], "principal_id")}"
}

しかし、モジュールの外でこの形式で取りたいがために、次のようにリストにラップしていると 0.12.0 では破壊的変更となります。

// Output of Module
output identity {
  value = ["${azurerm_function_app.main.identity}"]
}

0.11.14 まではこのようにして、identity の list からmap で拾えば取れましたが、0.12.0 では取れません。

// Get Principal from module output
output "api_functionapp_msi_identity" {
  value = "${lookup(module.functionapp.identity[0], "principal_id")}"
}

この小細工は意味ないので当初示した例に直しましょう。

このエラーのときに原因がぱっとわからなかったのできびしい。

破壊的変更

0.12.0 で、解消できていない破壊的変更が一件ありますが、これは0.12.3 で解消しました。

lifecycle / ignore_changes blocking upgrade from 0.11 to 0.12 · Issue #21433 · hashicorp/terraform

これは、構文が terraform 0.12upgrade で変わっていない場合、Attribute name required: Dot must be followed by attribute name.というエラーがでます。

ref: terraform 0.12 remote state : Attribute name required · Issue #20835 · hashicorp/terraform

どんな問題かざっくりいうと、「ignore_changes でattirbuteベースで指定するようになったが、これまで無視していたものが0.12.0 でignoreされなくなった」というものです。

私が直面したのは、Azure Web Apps の appsettings のignore ができないことでした。kubernetes のタグも同じ問題があります。

0.12.0 Error: Attribute name required · Issue #21444 · hashicorp/terraform

Issue の中にある通り、全体の無視はできます。

    ignore_changes = [
      tags
    ]
    ignore_changes = [
      appsettings
    ]

しかし、その一部だけ無視できません。これはIssueに提示されている tags["kubernetes.io"]A static variable reference is required. でできないことでもわかります。

というか、ignore_changes は静的な指定しかできないのは0.11でもその前からもそうなのに、この回答者はトンチンカンなこと言っててもんにょりなので、もう少し真面目にどうなるのか知りたいです。

なお、TBD 0.12.1 となっていますが 0.12.1 はすでに出ているため、いったんは運用回避しかなさそうです。terraform で値変更せず、コンソールで直変更..... 事故臭しかない。

うれしい構文変化

変数への型の明示

変数の型が増えました。 特に、list や map が変わったのはうれしいでしょう。

文字列だけしかないmap でも、これまで次のような宣言でした。

type = "map"

これが次のように宣言可能になりました。

type = map(string)

うれしいですね。map(list(string)) とかも行けますね。 map(list) が0.11は言語制約上かなり厳しいものがあったので、こういう型制約は言語の表現力、拡張性に寄与するのでうれしいです。

lookup から要素アクセスへ

map の value に対して key でアクセスするときに lookup をしてきましたが、0.12 から[key]でアクセスが可能になりました。

つまり、0.11まで書いていた次のようなコードが

${lookup(map, "key")}

次のように変わります。

map["key"]

プログラムと違って、ない場合を考慮する必要がなくなければそこで落ちるという割り切りがインフラにはあるので、こういうキーアクセスはちょうどよいです。 lookup 撲滅しました。

VSCode の terraform plugin 対応

hcl2 に追随していないのでIssueが上がっていましたが、すでにPRがマージされたので、もうじきでるんじゃないでしょうか。

Update syntax highlighting to better support terraform 0.12 by MattFenner · Pull Request #181 · mauve/vscode-terraform

*1:といっても大きな違いはなく、別に大したことはしていません。WindowsのCRLFと環境変数を使う場合が一番めんどくさいポイントになりやすいでしょう。

*2:なぜ 0.12.1 じゃないのかというと時間差、Azureせんせーさすが

Windows10 のPowerShellでF7を押しても履歴が画面に表示されない

以前書いたPowerShell の履歴に関する操作ですが、Windows 10 のWindows PowerShell ではF7による画面表示がされません。

tech.guitarrapc.com

これに関して少し見てみましょう。

目次

TL;DR

PSReadLine を外すか、代替コマンドを使いましょう。 PowerShell Core では代替コマンド使えないのであきらめで。

Windows 10 と Windows PowerShell と PSReadline

Windows 10 でもコマンドプロンプトではF7 で実行履歴が見えます。

コマンドプロンプトでF7を押して実行履歴を表示する

一方でWindows 10 の Windows PowerShell でF7で実行履歴が見えないのは、 PSReadline がデフォルトで有効になったためです。 なお、もちろん PowerShell Core (6.1) でもF7で実行履歴は表示されません。

従来のようにF7で実行履歴画面を出したい

PSReadLineモジュールを外すことで表示されます。 が、今のPowerShell でPSReadlineを外すのは副作用が大きいためちょっと悩みどころです。

Remove-Module -Name PSReadLine

PSReadline モジュールを外すとF7が利用可能になる

これは Windows PowerShell / PowerShell Core 両方で有効です。(そりゃぁそうですね)

PSReadline で F7を使ってGridViewによる代替表示を行う

PSReadline は、特定のキー入力に対してハンドラーを登録できます。

例えば、F7 を押すと Windows PowerShell で使える Out-GridView 表示してみましょう。

gist.github.com

この関数を Windows PowerShell で実行してから F7を押すと、実行ヒストリが最新の入力から降順にすべて表示されます。*1

Out-GridView を使った代替表示

なお、関数の$history | Sort-Object -Descending | Out-GridView -Title History -PassThru$history | Out-GridView -Title History -PassThru にすると古い履歴から昇順になります。 好きなほうでどうぞ。

PowerShell Core は、マルチプラットフォームで表示する View に相当するコマンドレットがないのでいったんなしで。

Ref

基本的にここの通りです。

Making the Command-History Pop-Up work via F7 in Windows 10 Powershell - Stack Overflow

*1:ここ最近 Vue.js ばかりやっているのがばれる....