tech.guitarrapc.cóm

Technical updates

Pulumi でよく使うコマンド

この記事は、Pulumi dotnet Advent Calendar 2019 の24日目です。

qiita.com

そろそろ終わりです。pulumi でよく使うコマンドを見てみましょう。

目次

TL;DR

普段の実行はCI/CD でかけるはずなので、state を操作したりといった特殊操作で使うことになるでしょう。

ドキュメント

コマンドリファレンスは結構丁寧なのでドキュメントにあたるのがよいです。

Pulumi CLI | Pulumi

操作

普段の操作系です。 だいたいCIで実行するので案外使わないやつ。

コンパイルついでに preview までが手元でやるパターン。

pulumi up

圧倒的再頻出。リソース作成のドライラン、実行を行う。

pulumi up --refresh にしないと、state が実リソースとずれていることに気づけない罠。

主にCIでしか適用しないが、手元でIDEからどうしてもpreview 見たいときに pulumi up --refresh をIDEの Debug Runに仕込む。

pulumi config

REF: Pulumi のコンセプト - プログラミングモデル - Config 参照 | kinoco Kibela

pulumi config set Key Value で値をセット pulumi config get Key で値を取得

pulumi destroy

リソースを全部ころす。 あるいは特定のだけ --target Array でころす。

pulumi preview

リソースの dry run。なんで君には、--refresh がないんだい?

と思っていたら、1.8.0 (2019/12/19 リリース) で、pulumi preview --refresh がサポートされました。 desuyone。

https://github.com/pulumi/pulumi/blob/master/CHANGELOG.md#180-2019-12-19

pulumi refresh

主にCI でしか実行しない。

pulumi refreshpulumi preview の代わりに使うのは違うんだなぁ.... この辺り、terraform的な利用とpulumi の想定に違いを感じる。

State

state 直接いじるのはしたくないものの、terraform 同様あるはある。

pulumi state delete URN

ステートから 実リソースのURN参照を削除する。 つまり、実リソースを Pulumi の管理外にする。

stack

stack は最初とかしか触らないので案の定頻繁にはやらない。

pulumi stack --show-urns

スタックのリソースを URN 付きで表示します。 URN把握するのに便利。

pulumi stack init STACK

スタック を作成、あるいは紐づけします。

pulumi stack list

スタック一覧の出力

pulumi stack output

スタックのOutput の出力

pulumi stack select STACK

スタックの切り替え

バージョン確認

Issue 報告のおともに

pulumi version

バージョンの確認

pulumi plugin ls

プラグイン一覧の出力

Pulumi のState と実リソースの差分を同期する

この記事は、Pulumi dotnet Advent Calendar 2019 の23日目です。

qiita.com

Terraform を使っていると、HCLで定義したリソースがHCLの定義とずれると差分として検出されました。 しかし、Pulumi では pulumi up をしただけでは検出されないことに気づきます。

こういった IaC に期待するのは Configuration Drift の検出であり、Desired State への収束です。 そのDesired State は Code でしょうか? それとも?

Pulumi と Terraform の考え方の違いが垣間見えます。

目次

TL;DR

特に terraform で慣れていて Pulumi を使う場合は、pulumi refreshpulumi up --refresh を使うと違和感が少ないでしょう。

  • Terraform は実行時にState と実リソースの差を同期する (デフォルトの動作)
  • Pulumi は実行時に State と 実リソースの差を同期しない (デフォルトの動作)
  • Pulumi は意図的に同期しないことをデフォルトにしている
  • Pulumi でTerraform 同様にState と実リソースの差分を同期するには、 pulumi refreshpulumi up --refresh を使う

期待する挙動とは

pulumi でも terraform でも期待する挙動は同じだと思います。

私が期待する挙動は、コードと実リソースの合致がされているか(Desired State にあるか) の保証です。

端的にいうと、コードの表現 = 実リソース を期待します。

terraform はこれがデフォルト挙動で提供されており、Pulumiでは明示的に指定する必要があります。

Pulumi のデフォルトの挙動

再現 Issue を立ててあります。これに沿ってみてみます。

Pulumi could not detect Resource change without code change. · Issue #3664 · pulumi/pulumi

Pulumi で S3 Bucketを作成します。

new Pulumi.Aws.S3.Bucket("hogemoge", new Pulumi.Aws.S3.BucketArgs
{
    Tags = new Dictionary<string, object>
    {
        { "foo", "bar" },
    },
});

適用すると、Bucket が作成されます。

$ pulumi up

     Type                 Name                Plan     Info                                                                 pulumi:pulumi:Stack  aws-sandbox-master           'dotnet build -nologo .' completed successfully
     pulumi:pulumi:Stack  aws-sandbox-master             2 messages                                                         Type                 Name                Plan       Info                                                           +   └─ aws:s3:Bucket     hogemoge            create

Resources:
    + 1 to create

Do you want to perform this update?
> yes
  no
  details

Resources:
    + 1 created

意図通り、Bucket が生成されています。

Bucket のタグを変更してみましょう。仮にキーを foo から foo-1 にしますが、Valueの変更でも同じ挙動です。

Pulumi のコードには変更を加えず、pulumi up をするとどうなるでしょうか? 結果は、「実リソースで生じた変更を検知しない」です。

$ pulumi up

Previewing update (master):
     Type                 Name                Plan     Info                                                                 pulumi:pulumi:Stack  aws-sandbox-master           'dotnet build -nologo .' completed successfully                 
     pulumi:pulumi:Stack  aws-sandbox-master           2 messages                                                           

Resources:
    1 unchanged

Terraform の挙動

terraform ではデフォルトでコードと実リソースの差分が検出されていると書きましたが、どういう挙動をするかおさらいしておきます。

terraform で S3 Bucket を作成します。

resource "aws_s3_bucket" "foo" {
  bucket = "foo-1234sdfgb"
  tags = {
    foo = "bar"
  }
}

applyします。

$ terraform apply

�Terraform v0.12.18

------------------------------------------------------------------------

An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # aws_s3_bucket.foo will be created
  + resource "aws_s3_bucket" "foo" {
      + acceleration_status         = (known after apply)
      + acl                         = "private"
      + arn                         = (known after apply)
      + bucket                      = "foo-1234sdfgb"
      + bucket_domain_name          = (known after apply)
      + bucket_regional_domain_name = (known after apply)
      + force_destroy               = false
      + hosted_zone_id              = (known after apply)
      + id                          = (known after apply)
      + region                      = (known after apply)
      + request_payer               = (known after apply)
      + tags                        = {
          + "foo" = "bar"
        }
      + website_domain              = (known after apply)
      + website_endpoint            = (known after apply)

      + versioning {
          + enabled    = (known after apply)
          + mfa_delete = (known after apply)
        }
    }

Apply complete! Resources: 1 added, 0 changed, 0 destroyed.

期待通りリソースが作られます。Pulumi と一緒です。

Bucket のタグを変更してみましょう。仮にキーを foo から foo-1 にしますが、Valueの変更でも同じ挙動です。

コードに変更を加えずに、terraform planterraform apply をすると、コードと実リソースの変更が検出されます。

$ terraform apply

�Terraform v0.12.18
Configuring remote state backend...
Initializing Terraform configuration...
-----------------------------------------------------

An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
  ~ update in-place

Terraform will perform the following actions:

  # aws_s3_bucket.foo will be updated in-place
  ~ resource "aws_s3_bucket" "foo" {
        acl                         = "private"
        arn                         = "arn:aws:s3:::foo-1234sdfgb"
        bucket                      = "foo-1234sdfgb"
        bucket_domain_name          = "foo-1234sdfgb.s3.amazonaws.com"
        bucket_regional_domain_name = "foo-1234sdfgb.s3.ap-northeast-1.amazonaws.com"
        force_destroy               = false
        hosted_zone_id              = "Z2M4EHUR26P7ZW"
        id                          = "foo-1234sdfgb"
        region                      = "ap-northeast-1"
        request_payer               = "BucketOwner"
      ~ tags                        = {
          + "foo"   = "bar"
          - "foo-1" = "bar" -> null
        }

        versioning {
            enabled    = false
            mfa_delete = false
        }
    }

Apply complete! Resources: 0 added, 1 changed, 0 destroyed.

Pulumi のデフォルトの挙動で困る状況

Pulumi は自分の State と実リソースの差分を検知するのですが、pulumi upをしても自分のstate を実リソースのstate と同期しないため、コードと実リソースで差分が生じていることを検知できません。 Terraform を使っていると「コードと実リソースの差分を検知する」ことに慣れているため、Pulumi でデフォルトで検知しないのは違和感を強く感じます。

私はそうなのですが、Pulumi で作ったリソースの変更はPulumi が検知しない限り気づけません。自分の興味の範疇外なので、それは Pulumi に気づいて教えてほしいと思います。いちいちPulumiが作ったリソースに変更があったことを、Webコンソールやaws cli で確認することはないでしょう。

pulumi refresh: Pulumi の State と実リソースの同期を行う

Pulumi の Stateと実リソースの同期を行うには、明示的に pulumi refreshpulumi up --refresh (-r) を行う必要があります。

このコマンドで、Pulumi State が実リソースと同期されるので、次に pulumi previewpulumi up をすると、コードと実リソースの差分が検出されます。

これを考慮すると、Pulumi コマンドは次の順で実行することになるでしょう。

$ pulumi refresh
$ pulumi up

コマンド2つの実行は面倒です。pulumi up 時にリフレッシュさせるのが多いでしょう。

$ pulumi up --refresh

実際に、これに気づいて pulumi previewをかけたところ、32 changes が生じて涙目になりました。 が、実際のところ、state の差分は コードで表現していないプロパティ でも生じます。 そのため、pulumi refresh で state の同期時に変更が検出されても、空がデフォルトである場合、次に pulumi up したときは検出されないのがほとんどです。

例えば、Route53 のレコードは多くのプロパティがありますが、これらは pulumi refresh で差分として検出されても、pulumi up では差分にならないでしょう。

正直、コードと実リソースに Drift が生じるのあり得ないので、pulumi refresh もれなくかけていくのがいいでしょう。

デフォルト挙動は変更されないのか

Issue 立っているものの、コメントを見ていると中の人としてはダウンサイドが目につくのでやりたくないらしいです。

Consider automatically refreshing on preview/update/destroy · Issue #2247 · pulumi/pulumi

まじかよと思いつつも、How Pulumi Works を見ると、Provider から State への向きがないのでなるほどと思わなくもない。

How Pulumi Works | Pulumi

ローカルPulumi ProjectとアカウントPulumi Project の紐づけ

この記事は、Pulumi dotnet Advent Calendar 2019 の22日目です。

qiita.com

あまりないのですが、時にすでにある Project と ローカルのpulumi を紐づけたいときがあります。

そんなときにどうやればいいのか見ておきます。

目次

TL;DR

  • アカウントの Projectとの紐づけは、Pulumi.yaml で行っている
  • pulumi up したディレクトリと同一名のstackがあればそれをつかい、なければ聞かれるのでそこで紐づける

Summary

Pulumi は、 Project - Stack という構造で組まれています。 アカウントは複数の Project を持つことができ、Project は複数の Stack を持つことができます。

そのため、手元のプロジェクトを、他のプロジェクトやStack と紐づけたい場合には、そのプロジェクトで明示したり、 pulumi up 時にstackの選択/stackの作成をする必要があります。

Pulumi Project の紐づけ

ローカルのPulumi 定義を見てみると、Pulumi.yamlPulumi.STACKNAME.yaml があることがわかります。

このうち、Pulumi Project と現在のPulumi定義の紐づけを行っているのが、Pulumi.yaml です。

例えば、Project名にaws-sandbox、ランタイムに C#、概要を AWS サンドボックスのプロジェクトであることを明示するなら次のようになります。

name: aws-sandbox
runtime: dotnet
description: AWS Sandbox Project

このYAMLが定義の元であるため、Web上の表示は YAML の内容で表示されます。 表示の更新タイミングは、pulumi up 時です。

Pulumi Stack の紐づけ

Stack は、具体的な定義ファイルで紐づいていません。 Web上に State を持っているので、手元のプロジェクトを pulumi uppulumi stack select で紐づけるだけです。

ローカルのPulumi定義とWebを紐づけるのは、config ファイルで、Pulumi.STACKNAME.yaml というルールで存在します。

例えば、awsのregionがap-northeast-1 であるStack ekscluster があるなら次のようなYAMLファイルがあるでしょう。

config:
  aws:region: ap-northeast-1

Pulumi で特定のリソースのみUpdateやDestroyする

この記事は、Pulumi dotnet Advent Calendar 2019 の21日目です。

qiita.com

Pulumi で terraform target のような操作をどうやるか見てみます。

目次

TL;DR

Pulumi CLI 1.3.0 から --target によって可能になった。

合わせて replace により入れ替えや、明示的な依存指示もサポートされている

--replace stringArray          Specify resources to replace. Multiple resources can be specified using --replace run1 --replace urn2
-t, --target stringArray           Specify a single resource URN to update. Other resources will not be updated. Multiple resources can be specified using --target urn1 --target urn2
--target-dependents            Allows updating of dependent targets discovered but not specified in --target list
--target-replace stringArray   Specify a single resource URN to replace. Other resources will not be updated. Shorthand for --target urn --replace urn.

実際に操作する

URN を調べる。

pulumi stack --show-urns

あとはURN を指定する。

pulumi up --target URN

DotNetCliToolReference から .NET Core Local Tools へ移行する

前回、.NET Core Tools と DotNetCliToolReference についてみてみました。

tech.guitarrapc.com

DotNetCliToolReference がオワコンということで、.NET Core Local Tools に移行しないと、ということもあるわけでどのようにやったかを残しておきます。

目次

TL;DR

  • .NET Core 3.1 からはDotNetCliToolReference を .NET Core Local Tools へ移行が必要
  • .NET Global Tools に公開してあるパッケージなら支障なく移行できる
  • 実行タイミングなどのTarget は今まで通りでok

Step by Step

都合により、.NET Framework 4.6 で組んでいたプロジェクトを .NET Core 3.1 プロジェクトに変更する流れで見ていきます。

net46 参照でも、SDK Style な csproj であれば DotNetCliToolReference は利用できます。

Before & After

もともと DotNetCliToolReference を使っていたプロジェクトを、.NET Core Local Tools に移行してみます。

Before

DotCliToolReference で参照していた dotnet-kustomizationconfigmapgenerator-project-tool をやめます。

NuGet Gallery | dotnet-kustomizationconfigmapgenerator-project-tool 0.2.1

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <TargetFramework>net46</TargetFramework>
    <DisableFastUpToDateCheck>True</DisableFastUpToDateCheck>
  </PropertyGroup>

  <ItemGroup>
    <DotNetCliToolReference Include="dotnet-kustomizationconfigmapgenerator-project-tool" Version="0.2.1" />
  </ItemGroup>

  <Target Name="PreBuild" BeforeTargets="PreBuildEvent">
    <Exec Command="dotnet kustomizationconfigmapgenerator" />
  </Target>

</Project>

After

.NET Tool として公開している .NET Core Tools のKustomizeConfigMapGenerator に切り替えます。

NuGet Gallery | KustomizeConfigMapGenerator 0.2.1

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <TargetFramework>netcoreapp3.1</TargetFramework>
    <DisableFastUpToDateCheck>True</DisableFastUpToDateCheck>
  </PropertyGroup>

  <Target Name="PreBuild" BeforeTargets="PreBuildEvent">
    <Exec Command="dotnet tool restore" />
    <Exec Command="dotnet tool run dotnet-kustomizeconfigmapgenerator" />
  </Target>

</Project>

Step1. DotNetCliToolReference を外す

DotNetCliToolReference は Visual Studio などのUIからは操作できません。 .csproj を直接触ってDotNetCliToolReference の参照を消しましょう。

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <TargetFramework>net46</TargetFramework>
    <DisableFastUpToDateCheck>True</DisableFastUpToDateCheck>
  </PropertyGroup>

  <Target Name="PreBuild" BeforeTargets="PreBuildEvent">
    <Exec Command="dotnet kustomizationconfigmapgenerator" />
  </Target>

</Project>

Step2. .NET Core 3.1に変更する

TargetFramework を net46 から netcoreapp3.1 に変更します。

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <TargetFramework>netcoreapp3.1</TargetFramework>
    <DisableFastUpToDateCheck>True</DisableFastUpToDateCheck>
  </PropertyGroup>

  <Target Name="PreBuild" BeforeTargets="PreBuildEvent">
    <Exec Command="dotnet kustomizationconfigmapgenerator" />
  </Target>

</Project>

Step3. .NET Core Local Tool のマニフェストを生成する

.NET Core Local Tools は、.csproj ではなく自分のマニフェストで管理されます。

マニフェストを、.NET Core 3.1 SDK で実行できる dotnet new tool-manifest で生成します。

dotnet new tool-manifest

Step4. ツールの参照を追加

.NET Core Local Tools は、.NET Global Tools で配布しているパッケージを追加できます。

dotnet-kustomizationconfigmapgenerator-project-tool は DotNetCliToolReference 専用の nuget パッケージでしたが、.NET Tool として公開している KustomizeConfigMapGenerator に変更します。

dotnet tool install KustomizeConfigMapGenerator

これで .config/dotnet-tools.jsonKustomizeConfigMapGenerator が追記されました。

{
  "version": 1,
  "isRoot": true,
  "tools": {
    "kustomizeconfigmapgenerator": {
      "version": "0.2.1",
      "commands": [
        "dotnet-kustomizeconfigmapgenerator"
      ]
    }
  }
}

Step5. ビルド前処理に差し込みます

.NET Local Tools を任意のビルドターゲットで実行します。

TIPS: 実行前に必ず dotnet tool restore を行います。

例えばビルド前 BeforeBuild に行いたいならこうでしょう。(PreBuild ターゲットもありです)

<Target Name="Your_Task" BeforeTargets="BeforeBuild">
    <Exec Command="dotnet tool restore" />
    <Exec Command="dotnet tool run dotnet-kustomizeconfigmapgenerator" />
</Target>

うまく実行できればあとはいい感じで引数を渡せばいいでしょう。

TIPS

複数の実行処理をTaskに定義したい

.NET Core Local Tools のTIPS ではないのですが、ターゲットの中で複数行にわたる処理を書きたい場合は、複数 Exec にすると見やすいです。 上からシーケンシャルに実行されることが保証されているのでシンプルでいいでしょう。

↑の例はそうですね。

Exec 内で処理が完結するなら見やすくなります。

指定したバージョンのNuGetパッケージが見つからないケース

dotnet tool install 後に、バージョンを更新した、ということで手で dotnet-tools.json を書き換えた場合、そのバージョンのNuGet パッケージがあるか保証されません。

もし放流直後だと、まだ NuGet Feed にはListingされてないことがあります。

そうすると以下のエラーが出るので、出たらパッケージがあるか確認するといいでしょう。

もう push したと思っても、NuGet にはまだ index されていないと出ています。

This package has not been indexed yet. It will appear in search results and will be available for install/restore after indexing is complete.

DisableFastUpToDateCheck は何

Visual Studio は、ファイル更新がない場合ビルドをスキップします。 このプロパティを有効にすると、ファイル更新の有無にかかわらず ビルド処理まで進んで dotnet cli / msbuild に再ビルドするかを任せます。

つまり、.NET Core Tools などで常にビルド前に処理を挟みたいときはこのプロパティを使うといい感じです。

.NET Core の決定的ビルドと組み合わさることでこれがいい感じになるのですが、詳細はまた別の機会に。

REF

.NET Core ツールの使用に関する問題のトラブルシューティング - .NET Core CLI | Microsoft Docs

New in .NET Core 3.0: local tools: Exploring ASP.NET Core 3.0 - Part 7

.NET Core 3 Local Tools