PulumiのGet Started with AWSを試してみた

2022.03.31

こんにちは!DA(データアナリティクス)事業本部 サービスソリューション部の大高です。

マルチクラウドに対応した Infrastructure as Code のツールとしては、弊社内ではTerraformのユーザが多いと思われます。一方で、最近同様のツールとしてPulumiというツールを知ったので、今回はこちらを試してみたいと思います。

なお、既に以下のエントリで同様の「やってみた」が公開されています。

やりたいこと

以下の公式ドキュメントに記載されている「Get Started with Pulumi」の「Get Started with AWS」に沿って進めていきたいと思います。

こちらでは簡単なリソースの作成から、変更、削除までの流れを試すことができます。

前提条件

実施する環境として、OSはMacOSを利用し、Nodeも事前に導入済みです。

% node -v
v16.14.2

また、AWSアカウントで s3:CreateBucket ポリシーが付与されている「Pulumi用のIAMユーザ」を作成しており、アクセスキーを発行済みの状態です。

AWS CLIも導入しており、上記のIAMユーザのアクセスキー、シークレットキーを設定したprofileとしてpulumiを作成済みです。

Get Started with Pulumi

では、実際にドキュメントに従って始めていきます。

Before You Begin

Install Pulumi

まずはPulumiのインストールです。Homwbrew経由でのインストールなので特に問題ありません。

% brew install pulumi
% pulumi version
v3.27.0

Install Language Runtime

今回はTypeScriptで試したいと思いますので、Node.jsのインストールが必要となります。

こちらは「前提条件」に記載のとおり事前に導入済みなので詳細はスキップしますが、私はanyenvを利用して導入しています。

% node -v
v16.14.2

Configure Pulumi to access your AWS account

このGet Startedでは s3:CreateBucket のポリシーが付与されたIAM Userが必要となります。こちらは「前提条件」に記載のとおり事前に作成済みです。

設定についてはprofileを利用したいので、ここでは設定をスキップして後述のプロジェクト作成が済んでから実施します。

Pulumiのアカウント作成とトークンの発行

ドキュメントには記載がありませんが、後で利用するので「Pulumiのアカウント作成」と「トークンの発行」を実施しておきます。

下記のページの「Create an account」からアカウントを作成します。

2022/03/30現在では、GitHub、GitLab、Atlassian、Emailのいずれかからアカウント作成が可能でした。

ユーザーを作成したら、下記のアクセストークン発行ページのURLにアクセスし「Create token」からトークンを発行します。

Create a New Project

では、事前準備ができたのでプロジェクトを作成していきます。

以下のとおりディレクトリを作成して、プロジェクトの初期化を行います。初期化時には先程作成したアクセストークンを聞かれるので、入力をして進めます。

% mkdir quickstart && cd quickstart
% pulumi new aws-typescript
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 <ENTER> to log in using your browser                   : #←ここでトークンを入れる


  Welcome to Pulumi!

  Pulumi helps you create, deploy, and manage infrastructure on any cloud using
  your favorite language. You can get started today with Pulumi at:

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

  Tip of the day: Resources you create with Pulumi are given unique names (a randomly
  generated suffix) by default. To learn more about auto-naming or customizing resource
  names see https://www.pulumi.com/docs/intro/concepts/resources/#autonaming.

続けて、プロジェクトの設定を入力していきます。基本的にはそのままENTERで進めますが、AWS Regionの設定だけはap-northeast-1を指定しました。

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: (quickstart) 
project description: (A minimal AWS TypeScript Pulumi program) 
Created project 'quickstart'

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

あとはインストールが進むのを待ちます。

Installing dependencies...

added 125 packages, and audited 126 packages in 30s

32 packages are looking for funding
  run `npm fund` for details

found 0 vulnerabilities
Finished installing dependencies

Your new project is ready to go! ✨

To perform an initial deployment, run 'pulumi up'

完了しました。

AWS CLIのProfileをPulumiに設定する

追加作業として、以下のコマンドを実行してprofileを設定しておきます。

こちらでは事前に作成したpulumiというAWS CLI用のprofileを利用して設定をすすめます。以下に記載のとおりコマンドを利用することで設定が可能です。

% pulumi config set aws:profile pulumi
Please choose a stack, or create a new one: dev

Review the New Project

以下の3つのファイルが作成され、yamlファイルは想定どおりの設定になっていることを確認します。

  • Pulumi.yaml
  • Pulumi.dev.yaml
  • index.ts

設定ファイルは以下のようになっていました。

Pulumi.yaml

name: quickstart
runtime: nodejs
description: A minimal AWS TypeScript Pulumi program

Pulumi.dev.yaml

config:
  aws:profile: pulumi
  aws:region: ap-northeast-1

また、リソース構築用のスクリプトは以下のようになっています。

index.ts

import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
import * as awsx from "@pulumi/awsx";

// Create an AWS resource (S3 Bucket)
const bucket = new aws.s3.Bucket("my-bucket");

// Export the name of the bucket
export const bucketName = bucket.id;

今回、バケット名だけ変更してみます。

index.ts

import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
import * as awsx from "@pulumi/awsx";

// Create an AWS resource (S3 Bucket)
const bucket = new aws.s3.Bucket("cm-ootaka-get-started-with-pulumi");

// Export the name of the bucket
export const bucketName = bucket.id;

Deploy the Stack

準備ができたのでデプロイに進みます。

以下のpulumi upコマンドを実行することで、何が作成されるかを表示されます。

% pulumi up
Previewing update (dev)

View Live: https://app.pulumi.com/ootaka-daisuke/quickstart/dev/previews/e70c40f7-3855-481e-a366-f767c28807af

     Type                 Name                               Plan       
 +   pulumi:pulumi:Stack  quickstart-dev                     create     
 +   └─ aws:s3:Bucket     cm-ootaka-get-started-with-pulumi  create     
 
Resources:
    + 2 to create

Do you want to perform this update?  [Use arrows to move, enter to select, type to filter]
  yes
> no
  details

問題がなければカーソルをyesに合わせて続けます。

Do you want to perform this update? yes
Updating (dev)

View Live: https://app.pulumi.com/ootaka-daisuke/quickstart/dev/updates/1

     Type                 Name                               Status      
 +   pulumi:pulumi:Stack  quickstart-dev                     created     
 +   └─ aws:s3:Bucket     cm-ootaka-get-started-with-pulumi  created     
 
Outputs:
    bucketName: "cm-ootaka-get-started-with-pulumi-484d188"

Resources:
    + 2 created

Duration: 6s

S3バケットが作成されました!なお、作成したリソースはOutputsに表示されており、以下のコマンドでも確認できます。

% pulumi stack output bucketName
cm-ootaka-get-started-with-pulumi-484d188

Modify the Program

次に、プログラムを修正して静的サイトホスティングができるようにしていきます。

まずは以下のようなindex.htmlファイルを追加します。

index.html

<html>
    <body>
        <h1>Hello, Pulumi!</h1>
    </body>
</html>

ファイルを追加したので、プログラムもこのファイルをS3バケットに配置するように修正します。

index.ts

import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
import * as awsx from "@pulumi/awsx";

// Create an AWS resource (S3 Bucket)
const bucket = new aws.s3.Bucket("cm-ootaka-get-started-with-pulumi");

const bucketObject = new aws.s3.BucketObject("index.html", {
  bucket: bucket,
  source: new pulumi.asset.FileAsset("index.html"),
});

// Export the name of the bucket
export const bucketName = bucket.id;

Deploy the Changes

では、もう一度デプロイします。

index.htmlをデプロイする

% pulumi up
Previewing update (dev)

View Live: https://app.pulumi.com/ootaka-daisuke/quickstart/dev/previews/ac4bd4ff-8618-4192-a8a0-6d9d6b54ab67

     Type                    Name            Plan       Info
     pulumi:pulumi:Stack     quickstart-dev             
 +   └─ aws:s3:BucketObject  index.html      create     2 warnings
 
Diagnostics:
  aws:s3:BucketObject (index.html):
    warning: urn:pulumi:dev::quickstart::aws:s3/bucketObject:BucketObject::index.html verification warning: Argument is deprecated
    warning: urn:pulumi:dev::quickstart::aws:s3/bucketObject:BucketObject::index.html verification warning: Argument is deprecated
 

Do you want to perform this update?  [Use arrows to move, enter to select, type to filter]
  yes
> no
  details

yesを選択して適用します。(引数のdeprecatedについて警告が出ていますが、今回は無視します)

Do you want to perform this update? yes
Updating (dev)

View Live: https://app.pulumi.com/ootaka-daisuke/quickstart/dev/updates/2

     Type                    Name            Status      Info
     pulumi:pulumi:Stack     quickstart-dev              
 +   └─ aws:s3:BucketObject  index.html      created     2 warnings
 
Diagnostics:
  aws:s3:BucketObject (index.html):
    warning: urn:pulumi:dev::quickstart::aws:s3/bucketObject:BucketObject::index.html verification warning: Argument is deprecated
    warning: urn:pulumi:dev::quickstart::aws:s3/bucketObject:BucketObject::index.html verification warning: Argument is deprecated
 
Outputs:
    bucketName: "cm-ootaka-get-started-with-pulumi-484d188"

Resources:
    + 1 created
    2 unchanged

Duration: 3s

実際にAWS CLIで確認もできます。

% aws s3 ls $(pulumi stack output bucketName) --profile pulumi
2022-03-31 11:29:28         69 index.html

ちゃんとファイルが置かれていますね。

静的サイトホスティングをする

続けて、改めて静的サイトホスティングがされるようにコードを修正していきます。

index.ts

import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
import * as awsx from "@pulumi/awsx";

// Create an AWS resource (S3 Bucket)
const bucket = new aws.s3.Bucket("cm-ootaka-get-started-with-pulumi", {
  website: {
    indexDocument: "index.html",
  },
});

const bucketObject = new aws.s3.BucketObject("index.html", {
  acl: "public-read",
  contentType: "text/html",
  bucket: bucket,
  source: new pulumi.asset.FileAsset("index.html"),
});

// Export the name of the bucket
export const bucketName = bucket.id;
export const bucketEndpoint = pulumi.interpolate`http://${bucket.websiteEndpoint}`;

バケットのインデックスファイルとしてindex.htmlを指定し、パブリックアクセスを許可しました。

また、bucketEndpointとしてウェブサイトにアクセスするためのエンドポイントURLを設定しています。

この状態で改めてデプロイします。

% pulumi up
Previewing update (dev)

View Live: https://app.pulumi.com/ootaka-daisuke/quickstart/dev/previews/156247be-b8c9-47e1-a3bf-91824cf27bf2

     Type                    Name                               Plan       Info
     pulumi:pulumi:Stack     quickstart-dev                                
 ~   ├─ aws:s3:Bucket        cm-ootaka-get-started-with-pulumi  update     [diff: +website]
 ~   └─ aws:s3:BucketObject  index.html                         update     [diff: ~acl,contentType]; 2 warnings
 
Diagnostics:
  aws:s3:BucketObject (index.html):
    warning: urn:pulumi:dev::quickstart::aws:s3/bucketObject:BucketObject::index.html verification warning: Argument is deprecated
    warning: urn:pulumi:dev::quickstart::aws:s3/bucketObject:BucketObject::index.html verification warning: Argument is deprecated
 
Outputs:
  + bucketEndpoint: output<string>


Do you want to perform this update?  [Use arrows to move, enter to select, type to filter]
  yes
> no
  details

yesを選択します。

Do you want to perform this update? yes
Updating (dev)

View Live: https://app.pulumi.com/ootaka-daisuke/quickstart/dev/updates/3

     Type                    Name                               Status      Info
     pulumi:pulumi:Stack     quickstart-dev                                 
 ~   ├─ aws:s3:Bucket        cm-ootaka-get-started-with-pulumi  updated     [diff: +website]
 ~   └─ aws:s3:BucketObject  index.html                         updated     [diff: ~acl,contentType]; 2 warnings
 
Diagnostics:
  aws:s3:BucketObject (index.html):
    warning: urn:pulumi:dev::quickstart::aws:s3/bucketObject:BucketObject::index.html verification warning: Argument is deprecated
    warning: urn:pulumi:dev::quickstart::aws:s3/bucketObject:BucketObject::index.html verification warning: Argument is deprecated
 
Outputs:
  + bucketEndpoint: "http://cm-ootaka-get-started-with-pulumi-484d188.s3-website-ap-northeast-1.amazonaws.com"
    bucketName    : "cm-ootaka-get-started-with-pulumi-484d188"

Resources:
    ~ 2 updated
    1 unchanged

Duration: 4s

curlコマンドで確認してみましょう。

% curl $(pulumi stack output bucketEndpoint)
<html>
    <body>
        <h1>Hello, Pulumi!</h1>
    </body>
</html>

想定どおり、ホスティングされました!

Destroy the Stack

最後に後片付けをします。

以下のpulumi destroyコマンドで作成したリソースを削除します。

% pulumi destroy
Previewing destroy (dev)

View Live: https://app.pulumi.com/ootaka-daisuke/quickstart/dev/previews/dbc054dc-fdf9-47f7-a4d7-762efdee664a

     Type                    Name                               Plan       
 -   pulumi:pulumi:Stack     quickstart-dev                     delete     
 -   ├─ aws:s3:BucketObject  index.html                         delete     
 -   └─ aws:s3:Bucket        cm-ootaka-get-started-with-pulumi  delete     
 
Outputs:
  - bucketEndpoint: "http://cm-ootaka-get-started-with-pulumi-484d188.s3-website-ap-northeast-1.amazonaws.com"
  - bucketName    : "cm-ootaka-get-started-with-pulumi-484d188"

Resources:
    - 3 to delete

Do you want to perform this destroy?  [Use arrows to move, enter to select, type to filter]
  yes
> no
  details

yesを選択します。

Do you want to perform this destroy? yes
Destroying (dev)

View Live: https://app.pulumi.com/ootaka-daisuke/quickstart/dev/updates/4

     Type                    Name                               Status      
 -   pulumi:pulumi:Stack     quickstart-dev                     deleted     
 -   ├─ aws:s3:BucketObject  index.html                         deleted     
 -   └─ aws:s3:Bucket        cm-ootaka-get-started-with-pulumi  deleted     
 
Outputs:
  - bucketEndpoint: "http://cm-ootaka-get-started-with-pulumi-484d188.s3-website-ap-northeast-1.amazonaws.com"
  - bucketName    : "cm-ootaka-get-started-with-pulumi-484d188"

Resources:
    - 3 deleted

Duration: 4s

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'.

削除されました!

念の為、以下のコマンドで削除されているか確かめます。

% aws s3 ls cm-ootaka-get-started-with-pulumi-484d188 --profile pulumi

An error occurred (NoSuchBucket) when calling the ListObjectsV2 operation: The specified bucket does not exist

ちゃんと削除されていますね。

おまけ

リソースの削除時に以下の表示がされていました。

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'.

実際に試してスタックを完全に削除してみます。

% pulumi stack rm dev
This will permanently remove the 'dev' stack!
Please confirm that this is what you'd like to do by typing ("dev"): dev
Stack 'dev' has been removed!

削除されました。

実際に何が起きたかですが、まずPulumi.dev.yamlファイルが削除されました。また、Pulumiサービス上のquickstartプロジェクトも削除されたことも確認できました。

Pulumi.dev.yamlファイルが削除されることには、ちょっと気をつけたいですね。

まとめ

以上、PulumiのGet Started with AWSを試してみました。

普段はCloudFormationを利用することが多いのですが、コードでインフラを構築できるのはとても快適ですね。また、今回は試していませんが複数のクラウド環境に同時にリソースをデプロイすることもできるので、今後機会があったら試してみたいと思います。

どなたかのお役に立てば幸いです。それでは!

参考