Pulumi リソース管理の仕組み / architecture ついて調査してみました #Pulumi #GCP #Go
この記事は1年以上前に投稿されました。情報が古い可能性がありますので、ご注意ください。
terraformやAnsible、AWS CloudFormationなど、クラウドリソースオーケストレーションツール(または構成管理ツール)の類似ツールとなるPulumiのarchitecture概要や、どのようにしてインフラストラクチャを管理しているか、少し深掘りしていきます。本記事の後半では既にクラウド環境にデプロイされているリソースのPulumi管理方法について検証していきます。初めてPulumiを触る方は、Pulumiのチュートリアル実施blogを前回の記事でまとめていますので、ご参考ください。
目次
Pulumi architectureとインフラストラクチャ管理について
Pulumiの主なコンポーネント/StackとState (参考)
前回blogで触れたPulumiの主なコンポーネントについて以下に再掲します
・ Resource → インフラストラクチャを構成するオブジェクト(実際のインフラリソース)。オブジェクトのプロパティ(設定値)は他のオブジェクトに共有することが可能(図のInputs/Outputsの部分)
・ Project → Programのソースコードとメタデータ(どのようにProgramを動かすかの情報)を格納するディレクトリ
・ Stack → Projectディレクトリ内でProgramを「pulumi up」(Pulumi CLI) した後のインスタンス。同一のProgramから本番環境/ステージング環境など、用途に応じて複数の環境用にインスタンスを作成可能
Stackは本番環境用やテスト環境用など、様々な用途でStackを作成することができます。このStackごとに、現在のインフラストラクチャに関する情報がjsonで記録されていて、これを「State」と呼びます。
[stateの例]
{ "version": 3, "deployment": { "manifest": { "time": "2022-09-13T17:36:36.814054+09:00", "magic": "c57d2cc6e4ddc1ad908bcf42d81d39304d5e92a1de5a6e1d5a5c485a70ff807b", "version": "v3.39.1" }, "secrets_providers": { "type": "service", "state": { "url": "https://api.pulumi.com", "owner": "CL_Kenneth", "project": "quickstart2", "stack": "dev" } }, "resources": [ { "urn": "urn:pulumi:dev::quickstart2::pulumi:pulumi:Stack::quickstart2-dev", "custom": false, "type": "pulumi:pulumi:Stack", "outputs": { "bucketEndpoint": "http://storage.googleapis.com/my-bucket-b5f5f89/index.html-*****", "bucketName": "gs://my-bucket-b5f5f89" } },
PulumiはStateから、そのインフラストラクチャがいつどのように作成/削除/読み取り/更新されたかなどの情報を読み取り、「pulumi up」コマンド実行時のアクション(次はリソースを作成/更新または削除すべきかどうか)を決定します
ちなみに、現在のStateは以下の手順で実際に確認することが可能です
- 現在アクティブなStackを確認します
$ pulumi stack ls NAME LAST UPDATE RESOURCE COUNT URL dev 1 hour ago 5 https://app.pulumi.com/****/quickstart2/dev stg* n/a n/a https://app.pulumi.com/****/quickstart2/stg
$ pulumi stack select dev $ pulumi stack ls NAME LAST UPDATE RESOURCE COUNT URL dev* 1 hour ago 5 https://app.pulumi.com/****/quickstart2/dev stg n/a n/a https://app.pulumi.com/****/quickstart2/stg
$ pulumi stack export { "version": 3, "deployment": { "manifest": { "time": "2022-09-13T17:36:36.814054+09:00", "magic": "c57d2cc6e4ddc1ad908bcf42d81d39304d5e92a1de5a6e1d5a5c485a70ff807b", "version": "v3.39.1" }, "secrets_providers": { "type": "service", "state": { "url": "https://api.pulumi.com", "owner": "CL_Kenneth", "project": "quickstart2", "stack": "dev" } }, "resources": [ { "urn": "urn:pulumi:dev::quickstart2::pulumi:pulumi:Stack::quickstart2-dev", "custom": false, "type": "pulumi:pulumi:Stack", "outputs": { "bucketEndpoint": "http://storage.googleapis.com/my-bucket-b5f5f89/index.html-12e5385", "bucketName": "gs://my-bucket-b5f5f89" } }, { "urn": "urn:pulumi:dev::quickstart2::pulumi:providers:gcp::default", "custom": true, "id": "80738ccf-25df-4478-8b0a-f6c4c3a15dba", "type": "pulumi:providers:gcp", "inputs": { "project": "pulumi-test", "version": "6.29.0" }, "outputs": { "project": "pulumi-test", "version": "6.29.0" }, "parent": "urn:pulumi:dev::quickstart2::pulumi:pulumi:Stack::quickstart2-dev" }, ~~~~~~(略)~~~~~~
また、Stateの内容についてPulumi service (GUI) でも「いつどのように作成/削除/読み取り/更新されたか」が視覚的に分かりやすい形で確認することができます。
$ pulumi console Manage your Pulumi stacks by logging in. Run `pulumi login --help` for alternative login options. Enter your access token from https://app.pulumi.com/account/tokens or hit to log in using your browser :
Pulumiインフラストラクチャ更新の流れ (参考)
PulumiにおけるStateを用いたインフラストラクチャ管理の具体的なフローについて、公式サイトを参考に説明用の図を作成しましたので以下掲載します。
大まかな流れとしては、「Language Host」で「Program」を実行し、「Deployment Engine」が「Program(あるべき状態)」と「State(現在の状態)」を比較したのち、必要なアクション(リソースの作成/更新/削除)を決定します。実際のリソース作成/更新/削除は「Deployment Engine」からの情報を元に、AWSやGCPなどの各サービスに対応した「Provider」で行われます。
・ Language Host → 「Language executor」と「Language runtime」の2つのバイナリで構成されます。「Language executor」はPulumi Programの実行を担い、Pulumi CLIインストール時にplugin(binary name: pulumi-language-<language-name>)として同時にインストールされます。「Language runtime」は「pulumi preview」および「pulumi up」実行時に、Program内でリソースが更新されたかどうか確認し、リソースの更新を検知したら、後述の「Deployment Engine」にリソース更新のリクエストを投げます。「Language runtime」は開発言語(python,go,node.js等)毎にパッケージとして配布されます(例:Node runtimeはnpm、Python runtimeはPyPI、Go runtimeはgo moduleなど)
・ Deployment Engine → Language Hostからのリソース更新リクエストを元に、現在の「State」から次のアクション(リソースの作成/更新/削除)を決定します。決定後は後述の「Resource Provider」に実際にリソースを作成/更新/削除を依頼します。ちなみに、Deployment Engine自体は「Pulumi CLI」に組み込まれています
・ Resource Providers → AWSやAzureなどの実際のインフラ環境上のリソース管理をDeployment Engine経由で実施するためのプラグインと、リソースのタイプ毎に用意されるSDKで構成されます。
Pulumi architecture(参考)
PulumiはこのStateを「Backend」と呼ばれるストレージに格納します。Pulumiのデフォルト設定ではオンライン上の「Pulumi Service」のbackendが利用されます。
「Pulumi Service」のarchitectureは以下になります(公式サイトより引用)
Pulumi Serviceはインターネット上で2つのエンドポイント(web application用/REST API用、どちらも「app.pulumi.com」)が提供されます。Pulumi Serviceは管理対象のインフラストラクチャと直接通信することは無いため、Pulumi Service側でインフラストラクチャのクレデンシャル情報を要求することはありません。代わりに、クライアントCLI側でPulumi Serviceとインフラストラクチャの双方と通信する形となります。Pulumi Service自体はSOC2やpen-testingなど複数の監査を経てセキュリティが担保され、安全に管理されています。
ちなみに、以下のようにPulumi Service自体をお好みのプライベートクラウド環境(AWS/Azure/GCP/Kubernetesなど)に自前でホストすることも可能で、インターネットアクセスを発生させないようなPulumi利用環境の構築が出来ます。
既存リソースをPulumiで管理する
state importについて (参考)
既に存在しているクラウド環境のインフラストラクチャ(リソース)をPulumiで管理したい場合は、以下のようなimport処理を実施して、PulumiのStateに対して「既に存在しているリソースの情報」を記録することが出来ます。Stateに「既に存在しているリソースの情報」が記録されていない状態で、かつ、Programにはリソースの情報が記載されている状態で「Pulumi up」を実施してしまうと、同じリソースが重複して作成されてしまいますので(詳細は後述)、既存リソースのimport処理は必ず実施しておく必要があります。
・ Pulumiで管理したいリソースのリソースIDを調べる
・ import option(リソースID)をProgramに記載する
・ 管理したいリソースと同様の設定値をProgramに記載する
実際にやってみる (参考)
今回はGCP環境に既に作成されているGCS Bucketを、Pulumiで管理させるケースを想定して検証実施します。事前準備として、新しくPulumi ProjectとStackを作成します。
・ GCS bucket
・ 名前:my-bucket-0927
・ ロケーションタイプ:Region
・ 場所:asia-northeast1
・ GCPプロジェクト:pulumi-test
・ macOS v11.6.4
・ go1.18.3 darwin/amd64
・ Pulumi v3.39.1
Pulumi Projectを新規で作成します。新規ディレクトリを作成して、pulumi newコマンドを実行します。project nameは「quickstart2」、stack nameはデフォルト「dev」に設定します。その他の設定もデフォルトにします。「gcp:project:」には、今回の対象リソースがデプロイされているGCPプロジェクト「pulumi-test」を設定します。
$ mkdir quickstart2 $ cd quickstart2 $ pulumi new gcp-go This command will walk you through creating a new Pulumi project. Enter a value or leave blank to accept the (default), and press . Press ^C at any time to quit. project name: (quickstart2) project description: (A minimal Google Cloud Go Pulumi program) Created project 'quickstart2' Please enter your desired stack name. To create a stack in an organization, use the format / (e.g. 'acmecorp/dev'). stack name: (dev) Created stack 'dev' gcp:project: The Google Cloud project to deploy into: pulumi-test Saved config Installing dependencies... Finished installing dependencies Your new project is ready to go! To perform an initial deployment, run 'pulumi up'
Stack作成直後はPulumiでリソースのデプロイは実施していないので、リソースに関する情報は記載されていません。
$ pulumi stack export { "version": 3, "deployment": { "manifest": { "time": "2022-09-27T14:26:05.35656+09:00", "magic": "c57d2cc6e4ddc1ad908bcf42d81d39304d5e92a1de5a6e1d5a5c485a70f*****", "version": "v3.39.1" }, "secrets_providers": { "type": "service", "state": { "url": "https://api.pulumi.com", "owner": "CL_Kenneth", "project": "quickstart2", "stack": "dev" } } } }
import option(リソースID)を付与しない場合のPulumi up
既にGCP環境に作成されているリソースについて、stateに情報が記載されていない状態でpulumi upを実施するとどうなるか確認します。
- 既存リソースと設定値(名前、ロケーションなど)を合わせて、以下Program(main.go)を作成します
package main import ( "github.com/pulumi/pulumi-gcp/sdk/v6/go/gcp/storage" "github.com/pulumi/pulumi/sdk/v3/go/pulumi" ) func main() { pulumi.Run(func(ctx *pulumi.Context) error { // Create a GCP resource (Storage Bucket) _, err := storage.NewBucket(ctx, "my-bucket-0927", &storage.BucketArgs{ Location: pulumi.String("ASIA-NORTHEAST1"), StorageClass: pulumi.String("STANDARD"), UniformBucketLevelAccess: pulumi.Bool(true), ForceDestroy: pulumi.Bool(true), }) if err != nil { return err } return nil }) }
$ pulumi up Previewing update (dev) View Live: https://app.pulumi.com/CL_Kenneth/quickstart2/dev/previews/3db8d890-d3f4-4e5c-ba7f-***** Type Name Plan + pulumi:pulumi:Stack quickstart2-dev create + └─ gcp:storage:Bucket my-bucket-0927 create Resources: + 2 to create Do you want to perform this update? yes Updating (dev) View Live: https://app.pulumi.com/CL_Kenneth/quickstart2/dev/updates/* Type Name Status + pulumi:pulumi:Stack quickstart2-dev created + └─ gcp:storage:Bucket my-bucket-0927 created Resources: + 2 created Duration: 6s
$ pulumi stack export { "version": 3, "deployment": { "manifest": { "time": "2022-09-27T22:28:10.351319+09:00", "magic": "c57d2cc6e4ddc1ad908bcf42d81d39304d5e92a1de5a6e1d5a5c485a70ff807b", "version": "v3.39.1" }, "secrets_providers": { "type": "service", "state": { "url": "https://api.pulumi.com", "owner": "CL_Kenneth", "project": "quickstart2", "stack": "dev" } }, "resources": [ { "urn": "urn:pulumi:dev::quickstart2::pulumi:pulumi:Stack::quickstart2-dev", "custom": false, "type": "pulumi:pulumi:Stack" }, { "urn": "urn:pulumi:dev::quickstart2::pulumi:providers:gcp::default", "custom": true, "id": "3f0d2c97-9218-4ff9-bb2f-73ff82141b67", "type": "pulumi:providers:gcp", "inputs": { "project": "pulumi-test", "version": "6.29.0" }, "outputs": { "project": "pulumi-test", "version": "6.29.0" }, "parent": "urn:pulumi:dev::quickstart2::pulumi:pulumi:Stack::quickstart2-dev" }, { "urn": "urn:pulumi:dev::quickstart2::gcp:storage/bucket:Bucket::my-bucket-0927", "custom": true, "id": "my-bucket-0927-dd72df8", "type": "gcp:storage/bucket:Bucket", "inputs": { "__defaults": [ "name" ], "forceDestroy": true, "location": "ASIA-NORTHEAST1", "name": "my-bucket-0927-dd72df8", "storageClass": "STANDARD", "uniformBucketLevelAccess": true }, "outputs": { "__meta": "{\"e2bfb730-ecaa-11e6-8f88-34363bc7c4c0\":{\"create\":240000000000,\"read\":240000000000,\"update\":240000000000}}", "cors": [], "defaultEventBasedHold": false, "encryption": null, "forceDestroy": true, "id": "my-bucket-0927-dd72df8", "labels": {}, "lifecycleRules": [], "location": "ASIA-NORTHEAST1", "logging": null, "name": "my-bucket-0927-dd72df8", "project": "pulumi-test", "publicAccessPrevention": "inherited", "requesterPays": false, "retentionPolicy": null, "selfLink": "https://www.googleapis.com/storage/v1/b/my-bucket-0927-******", "storageClass": "STANDARD", "uniformBucketLevelAccess": true, "url": "gs://my-bucket-0927-dd72df8", "versioning": null, "website": null }, "parent": "urn:pulumi:dev::quickstart2::pulumi:pulumi:Stack::quickstart2-dev", "provider": "urn:pulumi:dev::quickstart2::pulumi:providers:gcp::default::3f0d2c97-9218-4ff9-bb2f-73ff82141b67", "propertyDependencies": { "forceDestroy": [], "location": [], "storageClass": [], "uniformBucketLevelAccess": [] } } ] } }
$ pulumi destroy Previewing destroy (dev) View Live: https://app.pulumi.com/CL_Kenneth/quickstart2/dev/previews/d5fcd57a-7498-494d-aee3-******** Type Name Plan - pulumi:pulumi:Stack quickstart2-dev delete - └─ gcp:storage:Bucket my-bucket-0927 delete Resources: - 2 to delete Do you want to perform this destroy? yes Destroying (dev) View Live: https://app.pulumi.com/CL_Kenneth/quickstart2/dev/updates/** Type Name Status - pulumi:pulumi:Stack quickstart2-dev deleted - └─ gcp:storage:Bucket my-bucket-0927 deleted Resources: - 2 deleted Duration: 3s
$ pulumi stack export { "version": 3, "deployment": { "manifest": { "time": "2022-09-27T22:37:04.390227+09:00", "magic": "c57d2cc6e4ddc1ad908bcf42d81d39304d5e92a1de5a6e1d5a5c485a70ff807b", "version": "v3.39.1" }, "secrets_providers": { "type": "service", "state": { "url": "https://api.pulumi.com", "owner": "CL_Kenneth", "project": "quickstart2", "stack": "dev" } } } }
import option(リソースID)を付与した場合のPulumi up (参考)
次に、import option(リソースID)を付与した場合のPulumi upの挙動を確認していきます。
- APIリファレンスによると、bucketの場合「name」がリソースIDとなるため、以下のように「NewBucket functionの引数」にimport option(リソースID)を追記します
pulumi.Run(func(ctx *pulumi.Context) error { // Create a GCP resource (Storage Bucket) _, err := storage.NewBucket(ctx, "my-bucket-0927", &storage.BucketArgs{ Location: pulumi.String("ASIA-NORTHEAST1"), StorageClass: pulumi.String("STANDARD"), UniformBucketLevelAccess: pulumi.Bool(true), }, pulumi.Import(pulumi.ID("my-bucket-0927")))
$ pulumi up Previewing update (dev) View Live: https://app.pulumi.com/CL_Kenneth/quickstart2/dev/previews/774dfb50-a363-451d-845b-******** Type Name Plan Info + pulumi:pulumi:Stack quickstart2-dev create = └─ gcp:storage:Bucket my-bucket-0927 import [diff: ~name]; 1 warning Diagnostics: gcp:storage:Bucket (my-bucket-0927): warning: inputs to import do not match the existing resource; importing this resource will fail Do you want to perform this update? [Use arrows to move, enter to select, type to filter] yes > no details
Do you want to perform this update? details + pulumi:pulumi:Stack: (create) [urn=urn:pulumi:dev::quickstart2::pulumi:pulumi:Stack::quickstart2-dev] = gcp:storage/bucket:Bucket: (import) [id=my-bucket-0927] [urn=urn:pulumi:dev::quickstart2::gcp:storage/bucket:Bucket::my-bucket-0927] ~ name: "my-bucket-0927" => "my-bucket-0927-ad559e3"
「name」が既存のリソース「my-bucket-0927」から、「my-bucket-0927-ad559e3」に変更されようとしています。importを実行する際は、nameなどの設定値がProgramとGCP環境の既存リソースとで一致させる必要がありますが、このままだと「name」が異なっているので、plan内容では「importが失敗します」との警告が表示されていることになります。
ここで変更後のbucket name「my-bucket-0927-ad559e3」のsuffixに付与されている文字列ですが、これはpulumiの「Auto-naming」処理により自動的に付与されているランダムの16進数になります。「Auto-naming」の目的は主に以下2つです
上記の通り、複数Stackを作成する際のリソース名重複によるエラーがたびたび発生し得るので、公式ドキュメントではProject名とStack名をリソースの名前に含めるか(例:test-bucet-<project_name>-<stack_name>)、またはリソースのオプションに「deleteBeforeReplace: true」を追加して、リソース更新時にリソースが新規作成される際に古いリソースが削除されるようにするよう、推奨されています
_, err := storage.NewBucket(ctx, "my-bucket-0927", &storage.BucketArgs{ Name: pulumi.String("my-bucket-0927"), Location: pulumi.String("ASIA-NORTHEAST1"), StorageClass: pulumi.String("STANDARD"), UniformBucketLevelAccess: pulumi.Bool(true), }, pulumi.Import(pulumi.ID("my-bucket-0927")))
$ pulumi up Previewing update (dev) View Live: https://app.pulumi.com/CL_Kenneth/quickstart2/dev/previews/ef046ad6-7345-4ce7-b423-********* Type Name Plan + pulumi:pulumi:Stack quickstart2-dev create = └─ gcp:storage:Bucket my-bucket-0927 import Resources: + 1 to create = 1 to import 2 changes Do you want to perform this update? details + pulumi:pulumi:Stack: (create) [urn=urn:pulumi:dev::quickstart2::pulumi:pulumi:Stack::quickstart2-dev] = gcp:storage/bucket:Bucket: (import) [id=my-bucket-0927] [urn=urn:pulumi:dev::quickstart2::gcp:storage/bucket:Bucket::my-bucket-0927] location : "ASIA-NORTHEAST1" name : "my-bucket-0927" project : "pulumi-test" publicAccessPrevention : "enforced" uniformBucketLevelAccess: true Do you want to perform this update? yes Updating (dev) View Live: https://app.pulumi.com/CL_Kenneth/quickstart2/dev/updates/** Type Name Status + pulumi:pulumi:Stack quickstart2-dev created = └─ gcp:storage:Bucket my-bucket-0927 imported Resources: + 1 created = 1 imported 2 changes Duration: 5s
$ pulumi stack export { "version": 3, "deployment": { "manifest": { "time": "2022-09-28T00:30:29.247685+09:00", "magic": "c57d2cc6e4ddc1ad908bcf42d81d39304d5e92a1de5a6e1d5a5c485a70ff807b", "version": "v3.39.1" }, "secrets_providers": { "type": "service", "state": { "url": "https://api.pulumi.com", "owner": "CL_Kenneth", "project": "quickstart2", "stack": "dev" } }, "resources": [ { "urn": "urn:pulumi:dev::quickstart2::pulumi:pulumi:Stack::quickstart2-dev", "custom": false, "type": "pulumi:pulumi:Stack" }, { "urn": "urn:pulumi:dev::quickstart2::pulumi:providers:gcp::default", "custom": true, "id": "45fdfb64-6f42-4a8e-b6c5-29eceae70e6f", "type": "pulumi:providers:gcp", "inputs": { "project": "pulumi-test", "version": "6.29.0" }, "outputs": { "project": "pulumi-test", "version": "6.29.0" }, "parent": "urn:pulumi:dev::quickstart2::pulumi:pulumi:Stack::quickstart2-dev" }, { "urn": "urn:pulumi:dev::quickstart2::gcp:storage/bucket:Bucket::my-bucket-0927", "custom": true, "id": "my-bucket-0927", "type": "gcp:storage/bucket:Bucket", "inputs": { "__defaults": [ "forceDestroy" ], "forceDestroy": false, "location": "ASIA-NORTHEAST1", "name": "my-bucket-0927", "storageClass": "STANDARD", "uniformBucketLevelAccess": true }, "outputs": { "__meta": "{\"e2bfb730-ecaa-11e6-8f88-34363bc7c4c0\":{\"create\":240000000000,\"read\":240000000000,\"update\":240000000000}}", "cors": [], "defaultEventBasedHold": false, "encryption": null, "forceDestroy": false, "id": "my-bucket-0927", "labels": {}, "lifecycleRules": [], "location": "ASIA-NORTHEAST1", "logging": null, "name": "my-bucket-0927", "project": "pulumi-test", "publicAccessPrevention": "enforced", "requesterPays": false, "retentionPolicy": null, "selfLink": "https://www.googleapis.com/storage/v1/**/my-bucket-0927", "storageClass": "STANDARD", "uniformBucketLevelAccess": true, "url": "gs://my-bucket-0927", "versioning": null, "website": null }, "parent": "urn:pulumi:dev::quickstart2::pulumi:pulumi:Stack::quickstart2-dev", "provider": "urn:pulumi:dev::quickstart2::pulumi:providers:gcp::default::45fdfb64-6f42-4a8e-b6c5-29eceae70e6f", "propertyDependencies": { "location": [], "name": [], "storageClass": [], "uniformBucketLevelAccess": [] }, "importID": "my-bucket-0927" } ] } }
_, err := storage.NewBucket(ctx, "my-bucket-0927", &storage.BucketArgs{ Name: pulumi.String("my-bucket-0927"), Location: pulumi.String("ASIA-NORTHEAST1"), StorageClass: pulumi.String("STANDARD"), UniformBucketLevelAccess: pulumi.Bool(true), })
$ pulumi up Previewing update (dev) View Live: https://app.pulumi.com/CL_Kenneth/quickstart2/dev/previews/802ed26c-03e1-4fa3-91fe-******** Type Name Plan pulumi:pulumi:Stack quickstart2-dev Resources: 2 unchanged
Pulumiコマンドによるimport(参考)
これまではProgramにimport option(リソースID)を追記してimport実施しましたが、pulumiコマンドによるimportも実施出来ます。import方法については概ね各リソースのAPIリファレンスに記載されています。今回はGCS Bucketのimportを実施します。
- stateは空(リソースの情報が無い)の状態で、GCP Consoleには未だPulumiで管理されていないbucketが存在している状態です
$ pulumi stack export { "version": 3, "deployment": { "manifest": { "time": "2022-09-28T00:53:02.915989+09:00", "magic": "c57d2cc6e4ddc1ad908bcf42d81d39304d5e92a1de5a6e1d5a5c485a70ff807b", "version": "v3.39.1" }, "secrets_providers": { "type": "service", "state": { "url": "https://api.pulumi.com", "owner": "CL_Kenneth", "project": "quickstart2", "stack": "dev" } } } }
$ pulumi import gcp:storage/bucket:Bucket my-bucket-0927 my-bucket-0927 Previewing import (dev) View Live: https://app.pulumi.com/CL_Kenneth/quickstart2/dev/previews/f34c0f49-cb16-4bfb-889a-******* Type Name Plan + pulumi:pulumi:Stack quickstart2-dev create = └─ gcp:storage:Bucket my-bucket-0927 import Resources: + 1 to create = 1 to import 2 changes Do you want to perform this import? details + pulumi:pulumi:Stack: (create) [urn=urn:pulumi:dev::quickstart2::pulumi:pulumi:Stack::quickstart2-dev] = gcp:storage/bucket:Bucket: (import) 🔒 [id=my-bucket-0927] [urn=urn:pulumi:dev::quickstart2::gcp:storage/bucket:Bucket::my-bucket-0927] [provider=urn:pulumi:dev::quickstart2::pulumi:providers:gcp::default_6_29_0::04da6b54-80e4-46f7-96ec-b56ff0331ba9] location : "ASIA-NORTHEAST1" name : "my-bucket-0927" project : "pulumi-test" publicAccessPrevention : "enforced" uniformBucketLevelAccess: true Do you want to perform this import? yes Importing (dev) View Live: https://app.pulumi.com/CL_Kenneth/quickstart2/dev/updates/** Type Name Status + pulumi:pulumi:Stack quickstart2-dev created = └─ gcp:storage:Bucket my-bucket-0927 imported Resources: + 1 created = 1 imported 2 changes Duration: 2s
$ pulumi stack export { "version": 3, "deployment": { "manifest": { "time": "2022-09-28T01:03:49.870847+09:00", "magic": "c57d2cc6e4ddc1ad908bcf42d81d39304d5e92a1de5a6e1d5a5c485a70ff807b", "version": "v3.39.1" }, "secrets_providers": { "type": "service", "state": { "url": "https://api.pulumi.com", "owner": "CL_Kenneth", "project": "quickstart2", "stack": "dev" } }, "resources": [ { "urn": "urn:pulumi:dev::quickstart2::pulumi:pulumi:Stack::quickstart2-dev", "custom": false, "type": "pulumi:pulumi:Stack" }, { "urn": "urn:pulumi:dev::quickstart2::pulumi:providers:gcp::default_6_29_0", "custom": true, "id": "597778d7-cd2d-4070-96ff-7c244c0dc3ff", "type": "pulumi:providers:gcp", "inputs": { "project": "pulumi-test", "version": "6.29.0" }, "outputs": { "project": "pulumi-test", "version": "6.29.0" } }, { "urn": "urn:pulumi:dev::quickstart2::gcp:storage/bucket:Bucket::my-bucket-0927", "custom": true, "id": "my-bucket-0927", "type": "gcp:storage/bucket:Bucket", "inputs": { "__defaults": [], "location": "ASIA-NORTHEAST1", "name": "my-bucket-0927", "project": "pulumi-test", "publicAccessPrevention": "enforced", "uniformBucketLevelAccess": true }, "outputs": { "__meta": "{\"e2bfb730-ecaa-11e6-8f88-34363bc7c4c0\":{\"create\":240000000000,\"read\":240000000000,\"update\":240000000000}}", "cors": [], "defaultEventBasedHold": false, "encryption": null, "forceDestroy": false, "id": "my-bucket-0927", "labels": {}, "lifecycleRules": [], "location": "ASIA-NORTHEAST1", "logging": null, "name": "my-bucket-0927", "project": "pulumi-test", "publicAccessPrevention": "enforced", "requesterPays": false, "retentionPolicy": null, "selfLink": "https://www.googleapis.com/storage/v1/**/my-bucket-0927", "storageClass": "STANDARD", "uniformBucketLevelAccess": true, "url": "gs://my-bucket-0927", "versioning": null, "website": null }, "parent": "urn:pulumi:dev::quickstart2::pulumi:pulumi:Stack::quickstart2-dev", "protect": true, "provider": "urn:pulumi:dev::quickstart2::pulumi:providers:gcp::default_6_29_0::597778d7-cd2d-4070-96ff-7c244c0dc3ff" } ] } }
ちなみにimportコマンド実行直後に、importしたリソースの以下ProgramがCLIに表示されます。これをそのままProgramにコピーして利用できます。「実際にimportは実行したくないけど、リソースのAPIリファレンス参照前に、Programのコードだけ確認したい」時などは、pulumi importコマンドを一度実行して、plan内容確認時(Do you want to perform this import?)で「no」を選択すれば、importを実行せずにコードが出力されます
Please copy the following code into your Pulumi application. Not doing so will cause Pulumi to report that an update will happen on the next update command. Please note that the imported resources are marked as protected. To destroy them you will need to remove the `protect` option and run `pulumi update` *before* the destroy will take effect. package main import ( "github.com/pulumi/pulumi-gcp/sdk/v6/go/gcp/storage" "github.com/pulumi/pulumi/sdk/v3/go/pulumi" ) func main() { pulumi.Run(func(ctx *pulumi.Context) error { _, err := storage.NewBucket(ctx, "my-bucket-0927", &storage.BucketArgs{ Location: pulumi.String("ASIA-NORTHEAST1"), Name: pulumi.String("my-bucket-0927"), Project: pulumi.String("pulumi-test"), PublicAccessPrevention: pulumi.String("enforced"), UniformBucketLevelAccess: pulumi.Bool(true), }, pulumi.Protect(true)) if err != nil { return err } return nil }) }
その他state関連操作について
その他state関連のPulumiコマンドについて一部紹介します
pulumi state delete [URN] (参考)
特定リソースのURN(Uniform Resource Name)を用いて、stateからリソースの情報を削除することができます(実際にクラウド環境にデプロイされているリソース自体は削除されません)
「実際にリソース自体は削除したくないが、Pulumiの管理からは外したい」場合などに利用します
- 削除したいリソースのURNを調べます(今回はbucketのstate情報を削除します)。stateから該当リソースのURNを確認することができます
$ pulumi stack export { ~~(略)~~ "resources": [ ~~(略)~~ { "urn": "urn:pulumi:dev::quickstart2::gcp:storage/bucket:Bucket::my-bucket-0927", "custom": true, "id": "my-bucket-0927", "type": "gcp:storage/bucket:Bucket", "inputs": { "__defaults": [ "forceDestroy" ], "forceDestroy": false, "location": "ASIA-NORTHEAST1", "name": "my-bucket-0927", "storageClass": "STANDARD", "uniformBucketLevelAccess": true }, ~~(略)~~ }
$ pulumi stack --show-urns Current stack is dev: Owner: CL_Kenneth Last updated: 23 minutes ago (2022-09-28 01:03:49.870847 +0900 JST) Pulumi version: v3.39.1 Current stack resources (3): TYPE NAME pulumi:pulumi:Stack quickstart2-dev │ URN: urn:pulumi:dev::quickstart2::pulumi:pulumi:Stack::quickstart2-dev ├─ gcp:storage/bucket:Bucket my-bucket-0927 │ URN: urn:pulumi:dev::quickstart2::gcp:storage/bucket:Bucket::my-bucket-0927 └─ pulumi:providers:gcp default_6_29_0 URN: urn:pulumi:dev::quickstart2::pulumi:providers:gcp::default_6_29_0 Current stack outputs (0): No output values currently in this stack More information at: https://app.pulumi.com/CL_Kenneth/quickstart2/dev Use `pulumi stack select` to change stack; `pulumi stack ls` lists known ones
$ pulumi state delete urn:pulumi:dev::quickstart2::gcp:storage/bucket:Bucket::my-bucket-0927 warning: This command will edit your stack's state directly. Confirm? Yes Resource deleted
$ pulumi stack export { "version": 3, "deployment": { "manifest": { "time": "2022-09-28T01:40:56.95378+09:00", "magic": "c57d2cc6e4ddc1ad908bcf42d81d39304d5e92a1de5a6e1d5a5c485a70ff807b", "version": "v3.39.1" }, "secrets_providers": { "type": "service", "state": { "url": "https://api.pulumi.com", "owner": "CL_Kenneth", "project": "quickstart2", "stack": "dev" } }, "resources": [ { "urn": "urn:pulumi:dev::quickstart2::pulumi:pulumi:Stack::quickstart2-dev", "custom": false, "type": "pulumi:pulumi:Stack" }, { "urn": "urn:pulumi:dev::quickstart2::pulumi:providers:gcp::default", "custom": true, "id": "f8f41b6d-87d4-4cd2-93b8-ebdbf766398c", "type": "pulumi:providers:gcp", "inputs": { "project": "pulumi-test", "version": "6.29.0" }, "outputs": { "project": "pulumi-test", "version": "6.29.0" }, "parent": "urn:pulumi:dev::quickstart2::pulumi:pulumi:Stack::quickstart2-dev" } ] } }
pulumi preview (参考)
stateの情報とProgramのコードの差分を確認することができます。pulumi upコマンドでも差分の確認は出来ますが、間違えて実際のデプロイを実行してしまう場合もありますので、pulumi previewで差分を確認する方が無難かと思います
- 現在のProgramのコード
$ cat main.go package main import ( "github.com/pulumi/pulumi-gcp/sdk/v6/go/gcp/storage" "github.com/pulumi/pulumi/sdk/v3/go/pulumi" ) func main() { pulumi.Run(func(ctx *pulumi.Context) error { // Create a GCP resource (Storage Bucket) _, err := storage.NewBucket(ctx, "my-bucket-0927", &storage.BucketArgs{ Name: pulumi.String("my-bucket-0927"), Location: pulumi.String("ASIA-NORTHEAST1"), StorageClass: pulumi.String("STANDARD"), UniformBucketLevelAccess: pulumi.Bool(true), }) if err != nil { return err } return nil }) }
$ pulumi stack export { "version": 3, "deployment": { "manifest": { "time": "2022-09-28T01:40:56.95378+09:00", "magic": "c57d2cc6e4ddc1ad908bcf42d81d39304d5e92a1de5a6e1d5a5c485a70ff807b", "version": "v3.39.1" }, "secrets_providers": { "type": "service", "state": { "url": "https://api.pulumi.com", "owner": "CL_Kenneth", "project": "quickstart2", "stack": "dev" } }, "resources": [ { "urn": "urn:pulumi:dev::quickstart2::pulumi:pulumi:Stack::quickstart2-dev", "custom": false, "type": "pulumi:pulumi:Stack" }, { "urn": "urn:pulumi:dev::quickstart2::pulumi:providers:gcp::default", "custom": true, "id": "f8f41b6d-87d4-4cd2-93b8-ebdbf766398c", "type": "pulumi:providers:gcp", "inputs": { "project": "pulumi-test", "version": "6.29.0" }, "outputs": { "project": "pulumi-test", "version": "6.29.0" }, "parent": "urn:pulumi:dev::quickstart2::pulumi:pulumi:Stack::quickstart2-dev" } ] } }
$ pulumi preview Previewing update (dev) View Live: https://app.pulumi.com/CL_Kenneth/quickstart2/dev/previews/f8807e3a-5045-4e63-ae14-****** Type Name Plan pulumi:pulumi:Stack quickstart2-dev + └─ gcp:storage:Bucket my-bucket-0927 create Resources: + 1 to create 1 unchanged
まとめ
Pulumi architectureとstateの挙動について少し理解を深めることができました。Pulumiを運用していく上で便利だと感じたState関連の操作やPulumi Serviceに関する情報について、今後も機会があれば紹介していきたいと思います。