tech.guitarrapc.cóm

Technical updates

GitHub Actions でモノレポ上の特定パスをチェックアウトスキップしたい

モノレポでリポジトリサイズが大きくチェックアウトが長くて困ることがあります。

今回はこういったときにどうできるのかを考えてみましょう。

tl;dr;

CIでは特定のパスしかいらないのに、ほかのパスもチェックアウトされるのを制御したい場合、スパースチェックアウトが使えます。

  • スパースチェックアウトを使えば、モノレポ上で、特定のパスをチェックアウトスキップしたり、特定のパスだけチェックアウトすることができる
  • actions/checkout は スパースチェックアウトに対応していないので、自前でチェックアウトが必要。(自前できるactionsチェックアウトはしたくないがやむなし。)
  • actions/checkout の スパースチェックアウト 対応PR はあるので、upvote してマージされてほしい声が多くなるといいかもしれない

github.com

GitHub Actions でスパースチェックアウトをする

GitHub Actions でスパースチェックアウトをする例を2つ紹介します。(両方ともシャロークローンも行っています)

もともとのルート直下は次のディレクトリです。このうち、src/ を対象にスパースチェックアウトしてみましょう。

$ ls -la
.editorconfig
.git
.gitatributes
.github
.gitignore
LICENSE.md
README.md
mermaid
samples
src
  • 1つ目の例 git-sparse-checkout-exclude.yaml は、特定のパスを除外してチェックアウトする例です
    • チェックアウト後、src/ がチェックアウトから除外されていることがわかります
  • 2つ目の例 git-sparse-checkout-only.yaml は、特定のパスだけチェックアウトする例です
    • チェックアウト後、src/ だけチェックアウトされていることがわかります

gist.github.com

過去の経験では actions/checkout で 1m30s 程度かかるリポジトリで35s 程度まで高速化されています。 すでにしゃろークローンしているが、それでもチェックアウトに時間がかかっており、もっと高速化が強く望まれるときはスパースチェックアウトを検討してもいいかもしれません。

ただ、現在の GitHub Actions の actions/checkout はスパースチェックアウトに対応しておらず、自前チェックアウトが必要です。 チェックアウトのコードはメンテしにくくくなりやすいため、自前チェックアウトは避けたい気持ちもあります。

actions/checkout にスパースチェックアウト対応が入ったら、気兼ねなく使っていけると思うので期待したいです。

CI におけるチェックアウトの基本

さて、CI においてチェックアウトをどのように行うといいのかを自分のために文章にします。 よく知られていることだとは思うので、知ってる方はスルーでどうぞ。

CI でのチェックアウトの基本は、「高速であればあるほど良い」であり、これは多くの方が同意するところだと思います。

高速なチェックアウトをしようとして課題になりやすいのが、マネージドCIサービスでのビルドです。(GitHub Actionsなどのホストランナー環境をイメージしてください) マネージドCIサービスは、環境を起動する度にクリーンな環境が用意されます。このため、前回のチェックアウトを再利用できず、毎回 git clone をする必要があります。リポジトリサイズが大きい場合、何も考えず git clone をするとチェックアウト完了まで数分かかり、ビルド全体が遅くなるなるのです。

一方で、Jenkins のように前回のジョブ結果 = チェックアウト結果を再利用できるセルフホストランナー環境では、2回目のチェックアウトはgit差分で済むため高速に終わります。このため、オンプレや自社サーバー、EC2などでビルドしている場合はチェックアウト速度に悩む機会が少ないでしょう。

では、どのようにしたらマネージドCIサービスでチェックアウトを高速化できるでしょうか? よくある失敗が、前回のキャッシュを取って次の起動でリストアして git 差分で済ませようという案です。残念ながら、キャッシュはただのダウンロード/アップロードであり、リポジトリをキャッシュに突っ込んでも高速化しないことがほとんどです。

git clone をいかに高速に行うかが課題となります。

git clone を高速化する定番の方法

よく使われる方法が2つあります。

  • シャロークローン (shallow clone)
  • スパースチェックアウト (sparse checkout)

それぞれを見てみましょう。

シャロークローン

「git clone が重いので履歴をすべて持ってくるのではなく、直近履歴だけ取得する」というのがシャロークローンです。

シャロークローンは git clone --depth 1 のように --depth n を指定することで直近n件の履歴だけ持ってくることで、大量のコミット履歴があるリポジトリでもクローンが軽くなります。1

シャロークローンは、チェックアウトする履歴を直近から指定できると捉えることができます。 詳しい内容は GitHub ブログがいい記事を出しているのでここを見るといいでしょう。2

github.blog

git履歴は運用や開発が進むごとに積み重なる割に、CIではそのコミットしかビルドにいらないことが多く、フルクローンはCIにおいては無駄になりやすいです。そのため、CIにおけるチェックアウトはしゃろークローンにするのが定番になっていると認識しています。

履歴指定は必ずしも --depth 1 にする必要はありません。1件前のコミットログが欲しいから--depth 2 にしたり、直近100件ぐらいまではほぼ速度が変わらないことが多いので、--depth 100 にすることもあります。

スパースチェックアウト

「git clone が重いので全部のパスをチェックアウトするのではなく、チェックアウトアウトするパスを特定のものに絞り込む」というのがスパースチェックアウトです。

スパースチェックアウトは git clone --no-checkout --sparse のようにクローン時点ではチェックアウトしません。チェックアウト前にgit sparse-checkout set "パス" でチェックアウト対象パスを指定してから、チェックアウトをします。特定のパスだけチェックアウトができるので、大量にファイルがあるリポジトリでも、クローンやチェックアウトが軽くなります。

スパースチェックアウトは、チェックアウト対象のパスを管理できると言い換えてもいいでしょう。 詳しい内容は GitHub ブログがいい記事を出しているのでここを見るといいでしょう。3

github.blog

スパースチェックアウトを使って効果が出やすい例としては、モノレポでそれぞれの言語やサーバー/クライアントごとにパスを切ってビルドしている場合や、テストには特定のパスだけチェックアウトが必要というケースが考えられます。(参考にある Zenn はそれ)

私はゲーム開発が多いので、サーバーとクライアント(Unity)がモノレポで管理されているケースでほしくなる時があります。Unityは管理しているファイルごとに .meta が必ず必要になるためクライアントコードのファイル数が膨れやすい性質があります。このため、クライアントコードのファイル数が枷となって、サーバーサイドビルドのチェックアウトに影響がでることがあるのです。

GitHub Actions でスパースチェックアウトをしたい

スパースチェックアウトが便利なのはわかりましたが、GitHub Actions ではどうやればスパースチェックアウトができるのでしょうか?

Gactions/checkout はスパースチェックアウトに対応していない

GitHub Actions でリポジトリを チェックアウト = git clone するときは、actions/checkout を使うのが定番です。4

しかし、actions/checkout はスパースチェックアウトには対応していません。スパースチェックアウトをしたい場合は、自前チェックアウトが必要があります。

スパースチェックアウトを書いてみよう

自前でスパースチェックアウトするときは、いくつか参考を見るといいでしょう。(記事の末尾にリンクをおいておきます。) 今回は、冒頭にあげたコードを使って流れを説明します。

git clone --filter=blob:none --no-checkout --depth 1 --sparse "https://github.com/なんとか.git" .

スパースチェックアウトを行う場合、git clone 時点では checkout をしません。このため、--no-checkout --sparse がセットで登場します。ちなみに --filter=blob:none はパーシャルクローン(の1つであるブロブレスクローン)で、--depth 1 はシャロークローンで、一緒に使うことが多いです。

git sparse-checkout set --no-cone "${{ env.SPARSECHECKOUT_DIR }}" "/*"

スパースチェックアウトのパス指定を行います。 パス指定は .gitignore と同様のスタイルで記載できるのですが、固定パス指定ではなく パスパターンで指定する場合、--no-cone が必要です。 cone にするとパスパターン (/* みたいなやつ) が使えないの注意です。

.gitignore で否定パスを書いたことがある人は、順番に違和感を覚えるかもしれません。.gitignore では、否定を後に書いて打消しますが、スパースチェックアウトでは先に書きます、書くまで知らなかったです。

NOTE: ちなみに .git/info/sparse-checkout に直接書き込もうと、echo -e "/*\n!node_modules" >> .git/info/sparse-checkout のようにするのは私は避けています。git sparse-checkout set だとパス指定に問題があるときにエラーなどで教えてくれるけど、直接書き込むと git sparse-checkout list するまで問題があるか気づけないためです。

git sparse-checkout init

スパースチェックアウトを有効にします。先に git sparse-checkout set しておいてから有効にするほうが、警告とか見やすいのでおすすめです。 パスパターンで指定するなら、git sparse-checkout init --cone しないこと。

git sparse-checkout list

デバッグ用です。スパースチェックアウトのパス指定があってるか表示させているだけです。.git/info/sparse-checkout にパスパターンで直書きしているのに、git sparse-checkout init --cone にしてると、「問題があるよ」と警告が出てくれます、便利。

git checkout "${GITHUB_SHA}"

チェックアウトします。ここで初めてファイルが取得されます。スパースチェックアウトがちゃんと指定されていれば、指定したパスだけチェックアウトしたり、指定したパスを除外してチェックアウトできているのが確認できるでしょう。

サブモジュールは割愛します。プライベートリポジトリなサブモジュールをチェックアウトする時はPATが必要なので気を付けて。

自前チェックアウトの最後は git reset --hard SHA します。これは定番ですね。後続のステップで git diff が出ないように気を払っておきましょう。

まとめ

GitHub Actions の actions/checkout は シャロークローンされており十分高速なため、99%のケースではこの記事は不要のはずです。 さらなる高速化としてスパースチェックアウトを使えば、特定のパスだけチェックアウトしたりできます。

特定のフォルダの大きさがチェックアウトが重い原因と判明している場合は、スパースチェックアウトが効果を発揮することが期待できます。

ただ、自前チェックアウトは書いた瞬間から重めの負債だと思っているので、慎重になりますね。

参考

これは特定のパスをチェックアウトする例ですが、やってることは大体一緒です。(この記事は パスパターン指定でのexclude なので cone はダメなのが違う) GitHub Actions の事情というより、git sparse-checkout の基本がいくつか触れられています。

zenn.dev

特定のフォルダだけ除外するときのコツが書かれています。cone には特に注意です。

stackoverflow.com


  1. 併せて --single-branch --branch=<branch> を指定することも多いでしょう。
  2. CIにおいては、そのコミットでのビルドができることが重要なので、ほとんどのケースではパーシャルクローンよりもシャロークローンが適しています。
  3. スパースチェックアウトが効果を発揮するのは、対象のパスのファイル数が数万、数十万オーダーなどチェックアウトに著しく影響ある場合です。
  4. シャロークローン、サブモジュール、PAT認証、特定のブランチチェックアウトと幅広く対応しています。特別な理由がないのに自前で git clone コマンドを書いているなら actions/checkout を使うほうがいいでしょう。

Terraform に startswith と endswith が来る

Terraform はその言語である HCLがシンプルで分かりやすいのでとっつきやすい、と言われることがあります。 一方で、組み込み関数であれこれある割に、これがないの!? というのもあります。

そのなくてたびたび困るのが、startswithendswith 関数です。

tl;dr;

  • startswith と endswith をビルトイン関数に入れるPR が先ほどマージされたので、Terraform のリリースにいつ入るか楽しみ (ETAは不明)
  • これで startswith("hello world", "hello")endswith("hello world", "world") が書けるようになる

Terraform の組み込み関数

Terraform には組み込み関数 がありますが、バージョンが積み重なっても関数が増えることが少ない印象があります。

どんな関数があるかというのは、 Build-in Functions を見るといいでしょう。

www.terraform.io

startswith や endswith は組み込み関数に存在しない

この中で String Functions = 文字列操作の関数を見ると、StartsWithEndsWith に相当する関数がないことに気づきます。 ないのでどうするかというと、regexsubstrtrimprefix/trimsuffix あたりでやられることが多いと思います。1

# regex での startswith 代わり
can(regex("^vpc-", var.vpc-id))

# substr での startswith 代わり
substr(var.domain2, 0, length(var.domain1) + 1) == "${var.domain1}."

# trimprefix での startswith 代わり
trimprefix(var.domain2, var.domain1) != var.domain2

これでもいいという意見もありそうですが、さすがに startswith で済むものを流し読みしにくい感じで書かれてもうれしくないものです。

# startswith があればこれでいい
startswith(var.vpc-id, "vpc-")
startswith(var.domain2, var.domain1)

ということで Issue が 2021年には立っていたのですが、そこそこスルーされていました。

github.com

別の Issueですが、開発チームとしては従来のやり方でできるなら足したくないという意向があるようです。 startswith でもそれが適用されるのは、あんまりな気もしますがそういう方針なのでしょう。この方針ならとあきらめてました。

We're being pretty cautious about adding new functions that overlap with existing use-cases because Terraform has grown quite an unwieldy collection of builtins over the years, and so we've been considering various ways to allow for externally-defined functions (e.g. #2771) to avoid continuing to grow that set. https://github.com/hashicorp/terraform/issues/28855#issuecomment-856345294

組み込み関数として入れる意向とPRマージ

突然、startswithendswith は組み込み関数に足すのはありという方向に変わります。

I took your comments back to the team, and we revisited this particular decision. We do not need to wait for a function provider framework for these functions. In this case, startswith and endswith meet a sufficiently wide use case need to be considered for built-in functions. https://github.com/hashicorp/terraform/issues/28209#issuecomment-1123022185 より引用

ということでPRが提案され、2022/July/15 にマージされています。

github.com

これにより、次か近いバージョンで startswithendswith が使えるようになりそうです。 ヤッター。

おまけ: カスタム関数という道

Issue で知ったんですが、プラグインでカスタム関数を入れるという提案が 2015年から放置されていたりするんですね。 あったとしても、HCL ではあんまり使いたくない気もしますがどうなんでしょう。

github.com


  1. 例を書いておいてなんですが、やっぱりひどい。

Visual Studio 2022 でインラインで型名やパラメーター名を表示する設定

Visual Studio は便利ですが、Rider や Resharper を考えるとまだまだよくなる余地を感じます。 そんな中で Visual Studio 2022 になって有効にしている便利な設定をメモしておきます。

tl;dr;

コード上で分析情報や型、パラメーターをインライン表示できるようになり、目線をコードから移動せずに済むので便利。

  • Text Editor > C# > Advanced > Display diagnostics inline (experimental)
  • Text Editor > C# > Advanced > Display inline parameter name hints
  • Text Editor > C# > Advanced > Display inline type hints

きっかけ

Rider の Inlay hints がうらやましい。VS2019 のころに Rider でいいなぁと思っていたのが Inlay hints でした。1

www.jetbrains.com

Type name hints や Code analysis hints は、個人的に欲しいと思ってたものの Visual Studio ではどうやるのかなぁと思いつつ放置していたのでした。(やれると思ってなかった)

Visual Studio でインラインでヒントを表示する

Visual Studio 2022 では、Rider を見ててよさそうだった次の3つの設定を有効にしています。実際とてもいいので、しばらくは有効なままでやっています。

  • Display diagnostics inline (experimental)
  • Display inline parameter name hints
  • Display inline type hints

設定前後のコード表示を見ると、得られる情報量が違っているのがわかります。

設定前のコード表示

設定後のコード表示

Display diagnostics inline (experimental)

この設定を有効にすると、Error List で表示されていた分析情報 が コード上にインラインで表示されます。 Error List は常に見たいですが、IDEの下は一等地なのでそればかり見るわけにもいかないのでインラインでお知らせしてくれるほうが好ましく感じます。

Display diagnostics inline (experimental) 有効化前

Display diagnostics inline (experimental) 有効化後

設定は Text Editor > C# > Advanced > Display diagnostics inline (experimental) から行えます。

Display diagnostics inline (experimental)の設定箇所

ただし欠点もあり、書いている最中でも警告を出してきます。例えば、書いている最中の、文末のセミコロンがないというのさえ警告が出てきます。この時 at the end of the line of code にしていると、書いている最中のコードに被って何も見えなくなるので、on the right edge of the editor in window が妥協ラインになります。ただ、画面ギリギリの時に結局かぶるのですが。

書いている最中は警告出ないか一行上とかにずれてほしいものですね。

Display inline parameter name hints

この設定を有効にすると、メソッドのパラメーター名がコード上にインラインで表示されます。 時々、メソッド引数を enableNantoka: true のようにパラメーター名を明示して指定することがありましたが、それはこの設定で見たい情報に他なりません。設定しても今後もやるんでしょうが、コードリード上も便利で好ましく感じます。

Display inline parameter name hints 有効化前

Display inline parameter name hints 有効化後

設定は Text Editor > C# > Advanced > Display inline parameter name hints から行えます。

Display inline parameter name hints の設定箇所

気づいていなかったのですが、Visual Studio 2019 v16.8 で Display inline parameter name hints (experimental) として提供されていたんですね。

devblogs.microsoft.com

Display inline type hints

この設定を有効にすると、メソッドのパラメーターごとの型がコード上にインラインで表示されます。 var の型表示や メソッドのパラメーターの型もうれしいのですが、特にラムダ式でパラメーターの型がインラインでわかるのは思ったより便利で好ましいです。

Display inline type hints 有効化前

Display inline type hints 有効化後

設定は Text Editor > C# > Advanced > Display inline type hints から行えます。

Display inline type hints 設定箇所

まとめ

Visual Studio も粛々と進化しているのでいいですね。 そういえば、VS2019 で .NET 6 がサポートされていないのは悲しいと思うのでした。 .NET 6 使いたかったら VS2022 というのは、.NET にとってはネガティブインパクトが強そうですね。


  1. inline じゃなくて inlay なんですね

Azure SDK for .NET の認証を DefaultAzureCredential にそろえる

Azure SDK for .NET はなぜか AzureCredentialsDefaultAzureCredential という2つの認証方法があります。

今回は AzureCredentials しか受け付けない Azure SDK のクライアントに、DefaultAzureCredential で得た認証を使いたいというメモです。

tl;dr;

  • DefaultAzureCredential でとった認証はTokenを経由してAzureCredentials で利用できる
  • Token は 10分で切れるので長時間実行に使いまわすには 再度認証をとるなり工夫が必要
  • 多少の手間があってもDefaultAzureCredential のほうが認証の取り方が楽なので使っていきたい

AzureCredentials をなぜ使いたくないのか

AzureCredentials は古い Azure SDK (あるいは移行されていないSDK) でのみ利用されている (?) 気配です。AzureCredentials を使うには、個別サービスパッケージ Microsoft.Azure.Management.Xxxxx 以外にMicrosoft.Azure.Management.ResourceManager.Fluent を参照します。

例えばこんな感じで使えます。

gist.github.com

AzureCredentials は触ってみると使いにくい点がいくつかあります。

  • ローカルでの認証 (例: ServicePrincipal) や Managed Service Identity (System Assigned Identity / UserAssigned Identity) ごとにインスタンスの生成方法が異なる
  • NewtonSoft.Json 10.0.3 に依存していて脆弱性が解消されていない。1
  • 生成方法が SdkContext.AzureCredentialsFactory.FromXxxnew AzureCredentials(new XxxInformation の2つがありAPIから推測する情報が多い

Newtonsoft.Json の脆弱性 https://github.com/advisories/GHSA-5crp-9r3c-p9vr
medium.com

次に示す DefaultAzureCredential や他クラウドでの認証を考えると触りたくないと感じます。

DefaultAzureCredential でどう変わるのか

DefaultAzureCredential は新しい Azure SDK for .NET で利用が進んでいる気配で、新しいSDK やインターフェースでは DefaultAzureCredential が利用されています。(全部じゃない) DefaultAzureCredential を使うには、Azure Identity SDK を参照します。

例えばこんな感じで使えます。

gist.github.com

DefaultAzureCredential は、new DefaultAzureCredential() あるいは new DefaultAzureCredential(new DefaultAzureCredentialOptions())とインスタンスを生成するだけで動作環境から自動的に認証を取得します。例えば、環境変数、Azure環境上で Managed Service Identity (System Assigned Identity / UserAssigned Identity) 、Visual Studio、Visual Studio Code、az login など一通りカバーされており便利です。

docs.microsoft.com

AzureCredentials での開発体験を振り返ると、DefaultAzureCredential の「同じインスタンス生成で動作環境に応じた認証をいい感じに取れる」というのは、環境ごとの書き分けがいらないため書き心地が安定しており好ましく感じます。2

特に UserAssignedIdentity と System AssignedIdentity が 環境変数 AZURE_CLIENT_ID の有無で切り替わるのは、利用側で自由に調整できるので非常に都合がいいです。3

docs.microsoft.com

DefaultAzureCredential で得た認証を AzureCredentials で利用する

DefaultAzureCredentialAzureCredentials と型レベルでは互換性がないため代わりに利用できません。 しかし、DefaultAzureCredential でとった認証のトークンは、Microsoft.Rest.TokenCredentials を経由することでAzureCredentials に差すことができます。

こうすることで、認証の取り方はDefaultAzureCredential に任せつつ、AzureCredentials を必要とするクライアントを利用できます。 例えば次のように書くことができます。

gist.github.com

TokenRequestContext は 2022年現在は、https://management.azure.com/.default 固定で問題ないはずです。(Azureはちょいちょい変わるので何も信用できない)

まとめ

ちょっと Azure Database for なんとかのSDK を使って操作しようとしたら AzureCredentials で面食らったのでした。 DefaultAzureCredential は普通な使い心地でいいのですが、AzureCredentials は厳しいものがあるので今後も避けていきたいところです。


  1. Newtonsoft.Json を明示的にパッケージ参照して解消しましょう
  2. 認証の取得の書き心地という意味では、AWS SDK for .NET は AWS IAM との紐づけを含めてもっと洗練されていると感じます。なお AWS SSO の認証は全然ダメなのでよくなってほしい。
  3. 環境変数 AZURE_CLIENT_ID があれば User Assigned Identity が参照される。

Pulumi リソースのプロパティが変わってしまい create-replacement が発生する場合の対処方法

Pulumi のパッケージ更新をしたら、プロパティ名が変わっており craete-replacement が生じてしまう。

Pulumi SDK 側でプロパティ名が変更されると発生するのですが、普段出会うことはまずありません。 しかし稀に、プレビューパッケージを使ったり、プレビューから GA した直後のサービスで起こることがあります。

Azure の Container Apps で最近起こったので備忘録に挙げておきます。

Warning: この対処は、リソースの再作成がかかる 必須プロパティでのみ必要になります。オプショナルなプロパティでは、単純にプロパティ名変更に追随するだけでokです。

tl;dr;

リソースのプロパティ名変更に対応するには、2つの方法が考えられます。

  1. 該当 URN をステートから削除してリソースをインポートしなおす
  2. スタックをエキスポートしてステートJSON を直接編集してスタックにインポートしなおす

残念ながら、リソースの必須プロパティ名の変更で生じる reate-replacement に対処できる Custom Resource Options は存在しません。 類似ケースのIssue があるのですが、プロパティに Alias がつけられるようになれば Terraform の moved ブロックのように何とかなりそうですね。

github.com

問題の概要

Pulumi はリソース URN 自体の移動に対しては CustomResourceoption で Alias を指定することで移動が可能です。 リソースプロパティの無視は Ignore で可能です。

しかし、リソースのプロパティ名の変更が起こった時に、旧プロパティ名から新プロパティ名に変更を追随するためのマーカーを渡す方法がありません。 オプショナルなプロパティであればプロパティ名を変えればいいのですが、必須プロパティの場合 create-replacement が生じてしまいます。

対処方法

要はステートのプロパティ名が違っているので、一度 URN を削除してから同じ URN にインポートしなおすことでプロパティ名の変更に追随できます。 ステートを直接JSONで触るか/触らないかで、2つのワークアラウンドが考えられます。

  1. 該当 URN をステートから削除してリソースをインポートしなおす
  2. スタックをエキスポートしてステートJSON を直接編集してスタックにインポートしなおす

JSONを直接いじりたくないので、1について説明します。

該当 URN をステートから削除してリソースをインポートしなおす。

この方法は JSON を直接触るより手間がかかります。 代わりに、JSON で万が一にも変なところを触ってインポート時におかしくなるという状況は避けられます。

該当ステートの状態をブラウザで確認しておく (オプショナル)

このステップは必須ではありませんが、やることをお勧めします。

この後、Pulumi から URN を削除すると今の Pulumi での状態が見れなくなります。

できれば ブラウザで Pulumi Console の街頭 URN を開いて、プロパティの状態をタブで開いておきましょう。 タブで開いていおくことで、「構築時のコード」と「インポート時に必要なプロパティ」の暗黙の指定が明確に把握できます。 インポート時にコードとリソースの差分が出ても、このタブを見ればどんな違いがあるか推測するのに役立ちます。

該当コードの URN を見つける

Pulumi Console のリソース一覧 (グラフからでも一覧でもよし) を開き、該当の URN を見つけてください。 あるいは pulumi cli であれば pulumi stack --show-urns から探してもいいでしょう。

pulumi stack --show-urns | grep "リソース名"

URN: urn:pulumi:xxx:Stack名::Provider名:なんとかかんとか といったフォーマットの URN が見つかったらメモします。

ステートから URN を削除する

URN を ステートから削除することによって、クラウドプロバイダー上にリソースを残したまま Pulumi の対応を消すことができます。 pulumi cli をつかって、該当の URN 消します。

pulumi state delete <先ほど見つけたURN>

確認されるので y で削除を実行します。

クラウド環境からリソースをインポートする

Terraform同様に、Pulumi もクラウド環境の状態をコードに取り込むことができます。 Terraform は cli で terraform import クラウドリソースID で取り込みましたが、Pulumi はコード上の CustomResourceOptions.ImportId で指定します。

www.pulumi.com

例えば、Azure の Managed Environment であれば次のようになります。

Note: Pulumi.AzureNative を .NET で使っている場合、NuGet パッケージ 1.64.1 から 1.65.0 に更新すると発生します。

_ = new ManagedEnvironment("FooBar", new ManagedEnvironmentArgs
{
    EnvironmentName = "foo", // ここが AzureNative nuget パッケージの更新で Name から EnvironmentName に変わった
    AppLogsConfiguration = new AppLogsConfigurationArgs
    {
        Destination = "log-analytics",
        LogAnalyticsConfiguration = new LogAnalyticsConfigurationArgs
        {
            CustomerId = "ログアナリティクスID"
            // SharedKey = "シェアドキー" <- インポート時に SharedKey は入らないのでコメントアウト必須
        },
        ZoneRedundant = false, // 暗黙のプロパティ で false だが、インポート時は 明示的に指定しないと Diff が出てインポートが失敗する
        ResourceGroupName = "Foo-Group",
        Location = "japan-east",
        Tags = .... // あればちゃんと指定する
    }
}, new CustomResourceoptions
{
    Parent = this, // インポート時のリソースの Parent 指定になるので、コンポーネントと親子関係持たせるなら必須
    ImportId = "/subscriptions/xxxxxxx/resourceGorups/Foo-Group/providers/Microsoft.App/managedEnvironment/foo" // Azure Resource Id
});

インポート時の注意は、現在のリソースとの Diff が生じてはいけないということです。 このため、暗黙のプロパティで与えられる値も明示的に指定が必要です。(例では ZoneRedundant = false は暗黙で与えられるが明示的に指定が必要) インポート時のリソースは、 CustomResourceoptions.ImportId で指定しましょう。Azure の場合はリソースID です。

あとは差分がないか pulumi cli で確認します。

差分があるとインポートは 100% 失敗します、差分がないように気を付けてください。 ただし差分は Diff ではプロパティ名までしか確認できず値がわかりません。 「該当ステートの状態をブラウザで確認しておく」のステップで開いておいたタブでプロパティ値とコードを見比べるといいでしょう。

pulumi preview

差分がなければインポートを実行します。

pulumi up

インポートが成功すればokです。

コードから ImportId を削除する

コードから CustomResourceoptions.ImportId セクションを消して、 pulumi を実行してみて差分が出ず実行完了すれば終わりです。

まとめ

Pulumi の人からリプライがついていますが、Alias がリソースのプロパティに対しても使えれば解決するので近々できるようになったりするといいですね。

相談を受けて、そういえば記事にしていなかったので起こしました。