kkamegawa's weblog

Visual Studio,TFS,ALM,VSTS,DevOps関係のことについていろいろと書いていきます。Google Analyticsで解析を行っています

Azure PipelinesのYAMLでPATを参照する

何回も書いているにもかかわらず、そのたびに過去のパイプラインを見直すので自分用まとめ。

Azure PipelinesではLibraryに登録した値をパイプライン中で参照することができます。

learn.microsoft.com

デプロイ先とかリージョン名とかであれば平文でもいいですが、パスワードやトークンであれば🔒を設定しておくことで初回登録以降表示されなくなります。Azure Pipelinesの中でも暗号化されているので、二度と平文の表示はできません(そのはずです)。より厳密に保護したい場合、Azure KeyVaultに登録してください。シームレスに連携しているので、参照している先がどちらかというだけです。

learn.microsoft.com

KeyVaultに入れておくとLog Analyticsでアクセスの検索もできますし、そもそもKeyVaultのシークレットを管理する人とパイプラインを管理する人の分離ができます。そういうセキュリティ上の要件がある時は使ってください。一人で使うだけなら別に要らないとは思います。

パイプラインからLibraryを使う場合、Permissionから使用できるパイプラインの指定を忘れないようにしてください。ほっとくと数日間動いてないってことがあります(警告出るのですぐわかります)。

で、本題のこのPAT(個人用アクセストークン)をYAML内で参照する方法です。

YAML内で使用を宣言

variables:
- group: ReleasenoteGen

- task: PowerShell@2
  displayName: 'Check Release Notes for Azure DevOps'
  inputs:
    filePath: '$(Build.SourcesDirectory)/vsts-translate/create-task-issue.ps1'
    pwsh: true
  env:
    PAT: $(PAT)

variablesセクションのgroupにLibraryの値を指定します。これでLibraryに登録しているPATという変数の値が参照できます。これをPowerShell / Bashなどで使う場合、envに環境変数値のペアで登録しておくとスクリプト内で参照できます。例えばPowerShellの例。

$token = [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes(":$ENV:PAT"))
$header = @{authorization = "Basic $token"}
$uri = 'https://dev.azure.com/{organization}/{Project}/{Team}/_apis/wit/wiql/{QueryID}?api-version=7.1-preview.2'
$json = Invoke-RestMethod -Uri $Uri -Method Get -ContentType "application/json" -Headers $header
if($json.workItems.Count -gt 0) {
  write-host "work in progress"
  exit 
}

PowerShellでは$ENV:{環境変数名}でOSの環境変数の参照ができます。$ENV:PATでYAMLで設定した環境変数をスクリプト内で使うことが可能になります。余談ですがこのREST APIは事前に登録したQuery(WIQL)を呼び出すものです。クエリで条件に引っかかるものがあればスクリプトを終了しています。

Azure DevOps CLIから参照する

learn.microsoft.com

Azure DevOpsを制御するためのCLIがAzure CLIの拡張機能として提供されています。REST API使うよりも便利なケースがありますので、こちらを使うことがあります。最近ではREST APIでどうしてもタスク作成時に日本語渡せなかったので、挫折してaz boards work-item create使いました。このCLIも中身はPythonで同じREST API呼び出しているはずなのでソース見ればいいのですが…。

Azure DevOps CLIは基本的にインタラクティブな処理を前提にしていますが、自動化用にいくつかの方法があります。

  1. 標準入力のリダイレクト echo $PAT | az devops login --org {organization URL}
  2. AZURE_DEVOPS_EXT_PAT環境変数を使う

1に関してはストレージに格納されちゃうからちゃんとaz devops logoutしろよ、という警告が出ます。スクリプトで使うならちゃんとログアウトしましょう。

WARNING: Failed to store PAT using keyring; falling back to file storage.
WARNING: You can clear the stored credential by running az devops logout.
WARNING: Refer https://aka.ms/azure-devops-cli-auth to know more on sign in with PAT.

learn.microsoft.com

ファイルからリダイレクトする方法もありますが、PATをファイル(レポジトリ)に置いておくのはちょっと…ということで、2番目の方法を使います。これはAzure DevOps CLIのログイン処理なしでaz boardsコマンド実行するときに自動的に参照してくれるので便利です。

- task: Bash@3
    displayName: 'Create work item'
    inputs:
      targetType: 'inline'
      script: 'az boards work-item create --title "リリースノートの翻訳を行う" --type "User Story" --area "エリア名" --org https://dev.azure.com/{org名}/ -p {project} --assigned-to ID'
    env:
      AZURE_DEVOPS_EXT_PAT: $(PAT)

envに指定しておくと、上のvariablesで定義されているPATAZURE_DEVOPS_EXT_PATにマップしてくれるので、az devops loginしなくても自動的にログイン(というかREST APIのトークンとして参照)してくれます。こっちのほうがいいんじゃないかな。

YAML内での参照方法の注意点

Azure Pipelinesには複数の変数があります。

マクロ(タスク実行前)、テンプレート(YAMLをパースするとき)、ランタイム(実行時)と置換されるタイミングが違うので、気をつけてください。最初に使うのはマクロでしょう。⁠.NETであれば-c $(BuildConfiguration)とかそういった類ですね。

NAT Gatewayの削除をスクリプトで実施する

AzureのNAT GatewayをPowerShellスクリプトで削除しようとしたんですよ。

docs.microsoft.com

ご存知の通り、NAT Gatewayはこれで削除する前にPublic IPとサブネットからデタッチしないといけません。デタッチしていないと-forceをつけても失敗します。じゃあ デタッチしようとしてもスクリプトでやる方法がちょっとわからなかった。

docs.microsoft.com

最初サブネット追加する時のようにこれでいけるのかなと思ったけど、だめ。結局bicepでやらないとダメ?ということで書いてみました。

github.com

NAT GatewayのサブネットとPublic IPを消してやることでちゃんとそれぞれからデタッチされました。作る方はこっち

github.com

まぁ作る方は散々サンプルがあるからいいと思います。消す方はあまりなかったので備忘録的に。おそらく他にもLoad Balancerとかのサブネットにアタッチするものはこういう感じでやらないといけない気がします。逆にBastionやVPN gatewayなどの専用のサブネットを作るものは普通に消せますね。

あと、Azure Cloud Shellでやっていたからかどうかわかりませんが、remove-aznatgatewayremove-azpublicipaddressを連続で実行するとどちらか片方しか消せませんでした。仕方ないので、-azjobつけてバックグラウンドジョブにしてみたらどちらも消せました。

PowerShellのForeach-object parallel内でインデクサを使う

docs.microsoft.com

PowerShell 7で導入されたForeach-Object -parallelちょっと用事があってこんな感じで使おうとしました。

$outervalue = 1,2,3
$array = 10,20

$array | foreach-object -parallel {
  for($i = 0; $i -lt $using:outervalue.length;$i++){
     write-host $_ * $using:outervalue[$i]
  }
}

すると…

ParserError:
Line |
   3 |       write-host $_ * $using:outervalue[$i]
     |                                         ~~
     | Expression is not allowed in a Using expression.

こんな式は認められないと怒られました。

github.com

原因はここで書かれているのですが、parallel内の式ではオブジェクトが読み取り専用になるために、インデクサを指定するとだめなのだそうです。なので、GetValueを使って参照することになります。

$outervalue = 1,2,3
$array = 10,20

$array | foreach-object -parallel {
  for($i = 0; $i -lt $using:outervalue.length;$i++){
     write-host $_ * ($using:outervalue).GetValue($i)
  }
}

いわれてみればそうだなぁという仕様ですが、結構悩みました。

Windows TerminalにPowerShell Coreを追加する

issueも出ているので、賞味期限は短いブログになるはずです(笑)。

Windows Terminalの初期プレビューが公開されました。Windows 10 1903を入れておかないといけませんが、ビルド環境がなくてもストアからインストールできます。

www.microsoft.com

ANK以外の環境だと日本語周りとかうまく動かない点も多いようですが、ぼちぼち使っていきます。インストールすると、Ubuntu(WSL1), cmd.exe, Windows PowerShellの三つの環境がプリインストールされています。PowerShell Coreも入れたいなぁ、と思ったので、Settingsを起動、jsonファイルを編集します。

(追記)
どうもPowerShell Core 6.2.xを入れていれば最初から認識されるようです。私は更新さぼってて6.1.1のままだったのでこの技が必要になりました。Terminal入れるまえに更新するといいようです。 (/追記)

余談ですが、settingsで単なるjsonが開くとは知らなかった&jsonを関連付けしていなかったので、結構困りました。VS Codeでもなんでも関連付けておいてください。

profilesセクションの配下に以下の設定をコピペします。フォントサイズはちょっと大きめに変えました。

        {
            "acrylicOpacity" : 0.5,
            "background" : "#012456",
            "closeOnExit" : true,
            "colorScheme" : "Campbell",
            "commandline" : "pwsh.exe",
            "cursorColor" : "#FFFFFF",
            "cursorShape" : "bar",
            "fontFace" : "Consolas",
            "fontSize" : 16,
            "guid" : "{574e775e-4f2a-5b96-ac1e-a2962a402336}",
            "historySize" : 9001,
            "icon" : "ms-appx:///ProfileIcons/{574e775e-4f2a-5b96-ac1e-a2962a402336}.png",
            "name" : "PowerShell Core",
            "padding" : "0, 0, 0, 0",
            "snapOnInput" : true,
            "startingDirectory" : "%USERPROFILE%",
            "useAcrylic" : false
        },

迷ったのがguidとiconでこれどこからとってくるんだろうと思ったら、GitHubの別のissueに載ってました。Windows 10に登録されているiconのms-appxのURLそのままでいいようです。

f:id:kkamegawa:20190623133822p:plain

これでPowerShell Coreも起動します。Settingsに登録すると、アイコンは即時反映されますが、起動はしないようなので、Terminalsを再起動してから使ってください。

github.com

もうちょっといっぱいプロファイルにいれてよ、というissueは立っています。あと、PowerShell Coreをscoopで入れている人はこれやっても認識しないというissueもたっています(scoop使ってないので、確認していません)。

github.com

見てわかる通り、guidの値はこのissueからもらってきました。

Scott Hanselman氏のブログもご参考に

www.hanselman.com