tech.guitarrapc.cóm

Technical updates

Pulumiの.NET CoreのPreviewリリースとGetting Started

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

https://qiita.com/advent-calendar/2019/pulumi-dotnet

Pulumiの .NET Core対応版がPreviewリリースされました。

Getting Startedから、実際に環境を構築したり疑問点の解決を回を重ねながらみていきましょう。

概要

実際にPulumi x .NET Core 3.0 (C#) で使ってみていますがそのまとめをざっくり。

  • まだPreviewなので、TypeScriptやPythonと比べてできない機能も数多い
  • 動作もいくつかバグが残っていたりして、NuGetのPreviewパッケージが週一でどんどん改善されている
  • 現状はVisual Studioで実行まで完結するわけではないので注意
  • 型は偉大、terraformのようにドキュメントを見る手間が格段に減った
  • 型でわからないときでもterraformのドキュメントをみればok
  • ドキュメントのメソッドやクラスは結構古かったりする

Getting Startedはいい出来にはなっているので見てみるといいでしょう。

Pulumi Getting Started

これに基づいて進めていく。

https://www.pulumi.com/docs/get-started/

Login

まずは何はともあれアカウントを作る。

https://app.pulumi.com/signin

アクセストークンを作ることで、pulumi cliとアカウントが紐づいて操作できるようになる。

https://app.pulumi.com/guitarrapc/settings/tokens

事前準備

install .NET Core 3.0 SDK

Pulumiの実行は、コンパイルしてから実行されます。 このコンパイル、実行のために .NET Core 3.0 SDKが必要です。

https://dotnet.microsoft.com/download

Install Pulumi cli

Windows

scoopに対応したので、これでpulumi cliを入れましょう。main bucketで利用できます。(scoopインストールしたらすぐにつかえるということです)

scoop install pulumi

cliでログインします。これでCLIがWebと紐づいて、ログが記録されたりリソースが可視化されます。

pulumi login

macOS

Homebrewでどうぞ。

brew install pulumi

更新はupgradeで。

brew upgrade pulumi

linux

curlで拾ってきて、実行するだけです。

curl -fsSL https://get.pulumi.com | sh

アクセストークン の生成方法

3つあります。アクセストークンを管理したくないので、自動生成がお勧めです。

  • (recommended) pulumi loginでログインしたら、何も入力せずにEnter。ブラウザが立ち上がるので、ログインすると自動的にアクセストークンが生成

  • アクセストークンをpulumi loginで入れる
  • アクセストークンをPULUMI_ACCESS_TOKEN環境変数に入れる

Language Support

  • GA: TYPESCRIPT, JAVASCRIPT, PYTHON
  • PREVIEW: C# (.NET Core 3.0 SDK or later.), Golang

Configure AWS

AWSを操作する前に、認証だけ解決しておくこと。 defaultプロファイルを使わない場合、権限のあるUserを以下の環境変数に入れておくのが手っ取り早い。

  • AWS_PROFILE

Visual StudioのLaunch Profileで環境変数に入れてもokです。 pulumi upでdotnet buildされるので、それが使われます。

Create a New Project

AWS C# projectを作成します。

mkdir pulumi
cd pulumi
pulumi new aws-csharp

デフォルトだと次のようになる。

> pulumi new aws-csharp

This command will walk you through creating a new Pulumi project.

Enter a value or leave blank to accept the (default), and press <ENTER>.
Press ^C at any time to quit.

project name: (pulumi)
project description: (A minimal AWS C# Pulumi program)
Created project 'pulumi'

Please enter your desired stack name.
To create a stack in an organization, use the format <org-name>/<stack-name> (e.g. `acmecorp/dev`).
stack name: (dev)
Created stack 'dev'

aws:region: The AWS region to deploy into: (us-east-1) ap-northeast-1
Saved config

Your new project is ready to go!

To perform an initial deployment, run 'pulumi up'

これで自動的にcsprojやProgram.cs、 yamlが生成される。

$ ls -la
drw-rw-rw-  6 S07671 0 4096 2019-11-13 10:21 .
drw-rw-rw-  6 S07671 0 4096 2019-11-12 18:33 ..
-rw-rw-rw-  1 S07671 0 6401 2019-11-12 18:41 .gitignore
-rw-rw-rw-  1 S07671 0  318 2019-11-12 18:41 Infra.csproj
-rw-rw-rw-  1 S07671 0 1115 2019-11-12 19:00 Infra.sln
-rw-rw-rw-  1 S07671 0 1357 2019-11-13 10:21 Program.cs
-rw-rw-rw-  1 S07671 0   37 2019-11-12 18:41 Pulumi.dev.yaml
-rw-rw-rw-  1 S07671 0   74 2019-11-12 18:41 Pulumi.yaml

ファイルは次の通り。

  • Pulumi.yaml defines the project
  • Pulumi.dev.yaml contains configuration values for the stack we initialized
  • Program.cs is the Pulumi program that defines our stack resources. Let’s examine it
name: pulumi
runtime: dotnet
description: A minimal AWS C# Pulumi program
config:
  aws:region: ap-northeast-1
using System.Collections.Generic;
using System.Threading.Tasks;

using Pulumi;
using Pulumi.Aws.S3;

class Program
{
    static Task<int> Main()
    {
        return Deployment.RunAsync(() => {

            // Create an AWS resource (S3 Bucket)
            var bucket = new Bucket("my-bucket");

            // Export the name of the bucket
            return new Dictionary<string, object>
            {
                { "bucketName", bucket.Id },
            };
        });
    }
}
1.8.0

仮実行とエラー解消

この状態でpulumi upとするとスタックがどう生成されるか表示される。

Config に aws:region の設定が抜けているとエラーが発生する

と思いきや、エラー。

$ pulumi up
Previewing update (sandbox):
     Type                 Name            Plan     Info
     pulumi:pulumi:Stack  pulumi-sandbox           'dotnet build -nologo .' completed successfully
 ms
 +   pulumi:pulumi:Stack          pulumi-sandbox  create     1 error; 2 messages
����ł����B                Name            Plan       Info
 +   └─ pkg:component:ekscluster  sandbox         create

Diagnostics:
  pulumi:pulumi:Stack (pulumi-sandbox):
    �N���v���t�@�C�� "(����)" ���K�p�ł��܂����ł����B
    �g�p�\�ȋN���v���t�@�C�����������܂����ł����B

    error: Running program 'C:\git\infra\pulumi\sandbox\bin\Debug\netcoreapp3.0\Infra.dll' failed with an unhandled exception:
    System.NullReferenceException: Object reference not set to an instance of an object.
       at Pulumi.Deployment.InvokeAsync[T](String token, ResourceArgs args, InvokeOptions options, Boolean convertResult)
       at Infra.EksClusterComponent.CreateAsync() in C:\git\infra\pulumi\sandbox\EksClusterComponent.cs:line 25
       at Program.<>c.<<Main>b__0_0>d.MoveNext() in C:\git\infra\pulumi\sandbox\Program.cs:line 39
    --- End of stack trace from previous location where exception was thrown ---
       at Pulumi.Stack.RunInitAsync(Func`1 init)
       at Pulumi.Output`1.GetValueAsync()
       at Pulumi.Deployment.RegisterResourceOutputsAsync(Resource resource, Output`1 outputs)
       at Pulumi.Deployment.Runner.WhileRunningAsync()

エラーは、System.NullReferenceException: Object reference not set to an instance of an object.。リソースの読み取りを使用とする時点で怒られる。

原因は、pulumiでAWSのRegion情報aws:regionをconfigに設定していないため。 なので、Config設定する。

$ pulumi config set aws:region ap-northeast-1

AWS Profile の指定が抜けているとエラーが発生する

configにaws:regionの設定後、再実行するとまた失敗する。

$ pulumi up
Previewing update (dev):
     Type                 Name        Plan     Info
     pulumi:pulumi:Stack  pulumi-dev           .NET Core ���� Microsoft (R) Build Engine �o�[�W���� 16.3.0+0f     pulumi:pulumi:Stack  pulumi-dev             C:\git\xxx\infra\pulumi\Infra.csproj �̕����� 45.39 ms �Ŋ��     pulumi:pulumi:Stack  pulumi-dev           'dotnet build .' completed successfully
[resource plugin aws-1.8.0] installing
Downloading plugin: 61.24 MiB / 61.24 MiB [=========================] 100.00% 5s
Moving plugin... done.

     Type                 Name        Plan       Info
 +   pulumi:pulumi:Stack  pulumi-dev  create
     └─ aws:s3:Bucket     my-bucket              1 error

Diagnostics:
  aws:s3:Bucket (my-bucket):
    error: unable to discover AWS AccessKeyID and/or SecretAccessKey - see https://pulumi.io/install/aws.html for details on configuration

エラーはerror: unable to discover AWS AccessKeyID and/or SecretAccessKey - see https://pulumi.io/install/aws.html for details on configurationなのでAWSプロファイルがないことを示す。

ということで、aws configureでdefault profileの認証を設定する。

https://www.pulumi.com/docs/intro/cloud-providers/aws/setup/

もし複数プロファイルがあり、任意のプロファイルを参照したい場合は、AWS_PROFILE環境変数にプロファイルを指定する。

リソースを作成してみる

これでpulumi upすると、yes / noが選択できる。 yesでリソースが作成、削除される。

$ pulumi up

Previewing update (dev):
     Type                 Name        Plan     Info
     pulumi:pulumi:Stack  pulumi-dev           .NET Core ���� Microsoft (R) Build Engine �o�[�W���� 16.3.0+0f     pulumi:pulumi:Stack  pulumi-dev             C:\git\infra\pulumi\Infra.csproj �̕����� 34.9 ms �Ŋ��      pulumi:pulumi:Stack  pulumi-dev           'dotnet build .' completed successfully

     Type                 Name        Plan
 +   pulumi:pulumi:Stack  pulumi-dev  create
 +   └─ aws:s3:Bucket     my-bucket   create

Resources:
    + 2 to create

Do you want to perform this update? yes
Updating (dev):
     Type                 Name        Status     Info
     pulumi:pulumi:Stack  pulumi-dev             .NET Core ���� Microsoft (R) Build Engine �o�[�W���� 16.3.0+     pulumi:pulumi:Stack  pulumi-dev               C:\git\infra\pulumi\Infra.csproj �̕����� 32.92 ms �Ŋ�     pulumi:pulumi:Stack  pulumi-dev             'dotnet build .' completed successfully

     Type                 Name        Status
 +   pulumi:pulumi:Stack  pulumi-dev  created
 +   └─ aws:s3:Bucket     my-bucket   created

Outputs:
    bucketName: "my-bucket-626a03e"

Resources:
    + 2 created

Duration: 9s

Permalink: https://app.pulumi.com/guitarrapc/pulumi/dev/updates/1

リソースを見てみると、後ろにハッシュがつくことで名前の重複が防がれていることがわかる。

リソースを削除してみる

消すと、pulumi upで実行したときに消える

            // Create an AWS resource (S3 Bucket)
            //var bucket = new Bucket("my-bucket");
> pulumi up
Previewing update (dev):
     Type                 Name        Plan     Info
     pulumi:pulumi:Stack  pulumi-dev           'dotnet build .' completed successfully
����܂
     Type                 Name        Plan
     pulumi:pulumi:Stack  pulumi-dev
 -   └─ aws:s3:Bucket     my-bucket   delete

Outputs:
  - bucketName: "my-bucket-626a03e"

Resources:
    - 1 to delete
    1 unchanged

Do you want to perform this update? yes
Updating (dev):
     Type                 Name        Status     Info
     pulumi:pulumi:Stack  pulumi-dev             'dotnet build .' completed successfully
�����
     Type                 Name        Status
     pulumi:pulumi:Stack  pulumi-dev
 -   └─ aws:s3:Bucket     my-bucket   deleted

Outputs:
  - bucketName: "my-bucket-626a03e"
    hogemoge  : "piyopiyo"

Resources:
    - 1 deleted
    1 unchanged

Duration: 5s

Permalink: https://app.pulumi.com/guitarrapc/pulumi/dev/updates/3

余談: VS での F5 実行

F5で実行はまだ無理げ。

Complete Support for .NET · Issue #3470 · pulumi/pulumi https://github.com/pulumi/pulumi/issues/3470

VS上のPulumi_Monitorとかも環境変数仕込むとgRPCのchannelがフックされるのでいったんなしがよさそう。

new Pulumi.Aws.Provider()で指定できる? 例えば、pulumi-sandbox profileのap-northeast-1 regionならこう。-> profile参照されてないときと同じエラーでダメだった。

        new Pulumi.Aws.Provider("sandbox", new Pulumi.Aws.ProviderArgs()
        {
            Profile = "pulumi-sandbox",
            Region = "ap-northeast-1",
        });

さらにリソースを追加

KMSを追加してみる。

    static Task<int> Main()
    {
        return Deployment.RunAsync(() => {

            // Create a KMS Key for S3 server-side encryption
            var key = new Pulumi.Aws.Kms.Key("my-key");

            // Create an AWS resource (S3 Bucket)
            var bucket = new Pulumi.Aws.S3.Bucket("my-bucket", new Pulumi.Aws.S3.BucketArgs
            {
                ServerSideEncryptionConfiguration = new Pulumi.Aws.S3.Inputs.BucketServerSideEncryptionConfigurationArgs
                {
                    Rule = new Pulumi.Aws.S3.Inputs.BucketServerSideEncryptionConfigurationRuleArgs
                    {
                        ApplyServerSideEncryptionByDefault = new Pulumi.Aws.S3.Inputs.BucketServerSideEncryptionConfigurationRuleApplyServerSideEncryptionByDefaultArgs
                        {
                            SseAlgorithm = "aws:kms",
                            KmsMasterKeyId = key.Id,
                        },
                    },
                },
            });

            // Export the name of the bucket
            return new Dictionary<string, object> {
                { "bucket_name", bucket.Id },
            };
        });
    }

追加される。

pulumi up
Previewing update (dev):
     Type                 Name        Plan     Info
     pulumi:pulumi:Stack  pulumi-dev           'dotnet build .' completed successfully
����܂
     Type                 Name        Plan
     pulumi:pulumi:Stack  pulumi-dev
 +   ├─ aws:kms:Key       my-key      create
 +   └─ aws:s3:Bucket     my-bucket   create

Outputs:
  + bucket_name: output<string>
  - hogemoge   : "piyopiyo"

Resources:
    + 2 to create
    1 unchanged

Do you want to perform this update?

kmsを見ると、nameに指定した文字列がどこにもないのが気になる。

stack destroy

$ pulumi destroy

Previewing destroy (dev):

     Type                 Name        Plan
 -   pulumi:pulumi:Stack  pulumi-dev  delete
 -   ├─ aws:s3:Bucket     my-bucket   delete
 -   └─ aws:kms:Key       my-key      delete

Outputs:
  - bucket_name: "my-bucket-c3f5c37"

Resources:
    - 3 to delete

Do you want to perform this destroy?
  yes
> no
  details
Duration: 24s

Permalink: https://app.pulumi.com/guitarrapc/pulumi/dev/updates/5
The resources in the stack have been deleted, but the history and configuration associated with the stack are still maintained.
If you want to remove the stack completely, run 'pulumi stack rm dev'.

TODO

Getting Startedの中で幾つもわからないことが出てきました。 次回から順にみていきましょう。

  • pulumi up時の文字化け対策
  • Pulumi Web UIでできること
  • 後ろのハッシュはずすのどうするの?
  • IAM Document PolicyのようなJSONをいい感じで型から生成できるの?
  • リソース作成後の型がOutput<string>だけどいい感じに変換できるの?
  • Terraform Module的な仕組みどうするの?
  • 既存リソースのimportどうするの?
  • よく使うコマンドは?
  • stateってどうなってるの?
  • 他のstate参照どうするの?
  • VS上でF5実行のステータスどうなってる?