【Pulumi de Azure】 Pulumiのクイックスタートをやってみました


## 概要
[Pulumi](https://www.pulumi.com/docs/index.html)は Indrastracture as Code を実現するソフトウェア。 Azure Resource Manager(Template) や Terraform で出来ることと同じですが、一般的なプログラム言語(Node.js,JavaScript,TypeScript,Python,GO)で記述できるのが強みでしょうか、、、

今まで terraform を駆使してきましたが、Pulumi を見かけたのでクイックスタートをやってみました（初めてさわってみました）。

### 実行環境
macOS Sonoma 14.0
python 3.8.12
Azure CLI 2.53.0

---

## 事前準備

### Azure へのログイン

```
## Azure Login
$ az login

## 対象のサブスクリプションの定義
$ az account set --subscription <サブスクリプション名>
```

### Pulumi のインストール

homebrew から Pulumi をインストールします。詳しくは[ここ](https://www.pulumi.com/docs/install/#download-install-pulumi)を参照ください。

```
## インストール
$ brew install pulumi
==> Downloading https://ghcr.io/v2/homebrew/core/pulumi/manifests/3.89.0
################################################################################################################################### 100.0%
==> Fetching pulumi
==> Downloading https://ghcr.io/v2/homebrew/core/pulumi/blobs/sha256:d5dd04de88bddf5d350795218b2212ff29a4340632493498846bba9f7d96f94f
################################################################################################################################### 100.0%
==> Pouring pulumi--3.89.0.sonoma.bottle.tar.gz
==> Caveats
zsh completions have been installed to:
  /usr/local/share/zsh/site-functions
==> Summary
🍺  /usr/local/Cellar/pulumi/3.89.0: 21 files, 370.1MB
==> Running `brew cleanup pulumi`...
Disable this behaviour by setting HOMEBREW_NO_INSTALL_CLEANUP.
Hide these hints with HOMEBREW_NO_ENV_HINTS (see `man brew`).


## 確認
$ pulumi version
v3.89.0
```

### ログイン

基本的に、Pulumiは状態管理などをPulumiが提供しているCloud上（SaaS）で行う。 が、今回はこのSaaS使わない事にしてローカルで状態管理を行うことに。

```
## 作業場所の定義
$ mkdir ~/MyDevelops/Pulumi/quickstart
$ cd ~/MyDevelops/Pulumi/quickstart

## ログイン
$ pulumi login "file://~/MyDevelops/Pulumi/quickstart"
Logged in to IsseinoMacBook-Pro.local as ituru (file://~/MyDevelops/Pulumi/quickstart)
```

---

## Pulumi IaC

### プロジェクトの作成

[Azure の Get Start](https://www.pulumi.com/docs/clouds/azure/get-started/begin/)を読みながら進める

プロバイダはAzure、言語はPythonでプロジェクトを作成します。

```
## プロジェクト作成
$ pulumi new azure-python
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 Azure Native Python Pulumi program):  
Created project 'quickstart'

stack name (dev):  
Created stack 'dev'
Enter your passphrase to protect config/secrets:  **************
Re-enter your passphrase to confirm:  *************

azure-native:location: The Azure location to use (WestUS2): Japaneast 
Saved config

Installing dependencies...

Creating virtual environment...
Finished creating virtual environment
Updating pip, setuptools, and wheel in virtual environment...
Requirement already satisfied: pip in /Users/ituru/MyDevelops/Pulumi/quickstart/venv/lib/python3.8/site-packages (21.1.1)
Collecting pip
  Downloading pip-23.3.1-py3-none-any.whl (2.1 MB)
     |████████████████████████████████| 2.1 MB 7.1 MB/s 
Requirement already satisfied: setuptools in /Users/ituru/MyDevelops/Pulumi/quickstart/venv/lib/python3.8/site-packages (56.0.0)
Collecting setuptools
  Downloading setuptools-68.2.2-py3-none-any.whl (807 kB)
     |████████████████████████████████| 807 kB 7.7 MB/s 
Collecting wheel
  Downloading wheel-0.41.2-py3-none-any.whl (64 kB)
     |████████████████████████████████| 64 kB 8.4 MB/s 
Installing collected packages: wheel, setuptools, pip
  Attempting uninstall: setuptools
    Found existing installation: setuptools 56.0.0
    Uninstalling setuptools-56.0.0:
      Successfully uninstalled setuptools-56.0.0
  Attempting uninstall: pip
    Found existing installation: pip 21.1.1
    Uninstalling pip-21.1.1:
      Successfully uninstalled pip-21.1.1
Successfully installed pip-23.3.1 setuptools-68.2.2 wheel-0.41.2
Finished updating
Installing dependencies in virtual environment...
Collecting pulumi<4.0.0,>=3.0.0 (from -r requirements.txt (line 1))
  Downloading pulumi-3.89.0-py3-none-any.whl.metadata (11 kB)
Collecting pulumi-azure-native<3.0.0,>=2.0.0 (from -r requirements.txt (line 2))
  Downloading pulumi_azure_native-2.12.0-py3-none-any.whl.metadata (3.6 kB)
Collecting protobuf~=4.21 (from pulumi<4.0.0,>=3.0.0->-r requirements.txt (line 1))
  Downloading protobuf-4.24.4-cp37-abi3-macosx_10_9_universal2.whl.metadata (540 bytes)
Collecting grpcio==1.56.2 (from pulumi<4.0.0,>=3.0.0->-r requirements.txt (line 1))
  Downloading grpcio-1.56.2-cp38-cp38-macosx_10_10_universal2.whl.metadata (4.0 kB)
Collecting dill~=0.3 (from pulumi<4.0.0,>=3.0.0->-r requirements.txt (line 1))
  Downloading dill-0.3.7-py3-none-any.whl.metadata (9.9 kB)
Collecting six~=1.12 (from pulumi<4.0.0,>=3.0.0->-r requirements.txt (line 1))
  Downloading six-1.16.0-py2.py3-none-any.whl (11 kB)
Collecting semver~=2.13 (from pulumi<4.0.0,>=3.0.0->-r requirements.txt (line 1))
  Downloading semver-2.13.0-py2.py3-none-any.whl (12 kB)
Collecting pyyaml~=6.0 (from pulumi<4.0.0,>=3.0.0->-r requirements.txt (line 1))
  Downloading PyYAML-6.0.1-cp38-cp38-macosx_10_9_x86_64.whl.metadata (2.1 kB)
Collecting parver>=0.2.1 (from pulumi-azure-native<3.0.0,>=2.0.0->-r requirements.txt (line 2))
  Downloading parver-0.5-py3-none-any.whl.metadata (2.7 kB)
Collecting arpeggio>=1.7 (from parver>=0.2.1->pulumi-azure-native<3.0.0,>=2.0.0->-r requirements.txt (line 2))
  Downloading Arpeggio-2.0.2-py2.py3-none-any.whl.metadata (2.4 kB)
Collecting attrs>=19.2 (from parver>=0.2.1->pulumi-azure-native<3.0.0,>=2.0.0->-r requirements.txt (line 2))
  Downloading attrs-23.1.0-py3-none-any.whl (61 kB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 61.2/61.2 kB 1.5 MB/s eta 0:00:00
Collecting typing-extensions (from parver>=0.2.1->pulumi-azure-native<3.0.0,>=2.0.0->-r requirements.txt (line 2))
  Downloading typing_extensions-4.8.0-py3-none-any.whl.metadata (3.0 kB)
Downloading pulumi-3.89.0-py3-none-any.whl (190 kB)
   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 190.5/190.5 kB 3.9 MB/s eta 0:00:00
Downloading grpcio-1.56.2-cp38-cp38-macosx_10_10_universal2.whl (8.9 MB)
   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 8.9/8.9 MB 5.4 MB/s eta 0:00:00
Downloading pulumi_azure_native-2.12.0-py3-none-any.whl (47.9 MB)
   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 47.9/47.9 MB 5.2 MB/s eta 0:00:00
Downloading dill-0.3.7-py3-none-any.whl (115 kB)
   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 115.3/115.3 kB 3.2 MB/s eta 0:00:00
Downloading parver-0.5-py3-none-any.whl (15 kB)
Downloading protobuf-4.24.4-cp37-abi3-macosx_10_9_universal2.whl (409 kB)
   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 409.4/409.4 kB 5.4 MB/s eta 0:00:00
Downloading PyYAML-6.0.1-cp38-cp38-macosx_10_9_x86_64.whl (191 kB)
   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 191.7/191.7 kB 3.8 MB/s eta 0:00:00
Downloading Arpeggio-2.0.2-py2.py3-none-any.whl (55 kB)
   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 55.3/55.3 kB 1.8 MB/s eta 0:00:00
Downloading typing_extensions-4.8.0-py3-none-any.whl (31 kB)
Installing collected packages: arpeggio, typing-extensions, six, semver, pyyaml, protobuf, grpcio, dill, attrs, pulumi, parver, pulumi-azure-native
Successfully installed arpeggio-2.0.2 attrs-23.1.0 dill-0.3.7 grpcio-1.56.2 parver-0.5 protobuf-4.24.4 pulumi-3.89.0 pulumi-azure-native-2.12.0 pyyaml-6.0.1 semver-2.13.0 six-1.16.0 typing-extensions-4.8.0
Finished installing dependencies
Finished installing dependencies

Your new project is ready to go! ✨

To perform an initial deployment, run `pulumi up`
```

ローカルの場合はパスフレーズを使い暗号化を行う。これはプロジェクト毎に設定するようです。
 
 次に、プロジェクトを作成した後、devというstackが作られる。stackというのは環境を表すもので、開発者が使う「Development(開発環境)」や、お客様が検証時使う「staging(検証環境)」や、実際に利用する「production(本番環境)」と定義することが一般的だが、Pulumiではこれらを1つ1つをstackと呼んで分離している。 なので、プロジェクトの中には複数のstackがあり、stack毎に変更等を反映していくことになる。今回はデフォルトのstackとしてdev（開発環境）が作られたが、stackの操作はサブコマンドのpulumi stackで一通り操作できます。

### Pulumiのプログラムを書く

「\_\_main__.py」が実際のコードになる。デフォルトでリソースグループとストレージアカウントを作成するコードが用意される。その２つのリソース名の変更と作成されたリソース名の画面表示（Output）を追加した。

```python:__main__.py
"""An Azure RM Python Pulumi program"""

import pulumi
from pulumi_azure_native import storage
from pulumi_azure_native import resources

# Create an Azure Resource Group
resource_group = resources.ResourceGroup("rg_ituru_pulumi_")

# Create an Azure resource (Storage Account)
account = storage.StorageAccount(
    "saiturupulumi",
    resource_group_name=resource_group.name,
    sku=storage.SkuArgs(
        name=storage.SkuName.STANDARD_LRS,
    ),
    kind=storage.Kind.STORAGE_V2,
)

# Export the primary key of the Storage Account
primary_key = (
    pulumi.Output.all(resource_group.name, account.name)
    .apply(
        lambda args: storage.list_storage_account_keys(
            resource_group_name=args[0], account_name=args[1]
        )
    )
    .apply(lambda accountKeys: accountKeys.keys[0].value)
)

pulumi.export("ResourceGroup", resource_group.name)
pulumi.export("StorageAccount", account.name)
pulumi.export("primary_storage_key", primary_key)
```

### pulumi up で 設定を反映

```
## Deploy Stack
$ pulumi up                              
Enter your passphrase to unlock config/secrets
    (set PULUMI_CONFIG_PASSPHRASE or PULUMI_CONFIG_PASSPHRASE_FILE to remember):  
Enter your passphrase to unlock config/secrets
Previewing update (dev):
     Type                                     Name              Plan       
 +   pulumi:pulumi:Stack                      quickstart-dev    create     
 +   ├─ azure-native:resources:ResourceGroup  rg_ituru_pulumi_  create     
 +   └─ azure-native:storage:StorageAccount   saiturupulumi     create     

Outputs:
    ResourceGroup      : output<string>
    StorageAccount     : output<string>
    primary_storage_key: output<string>

Resources:
    + 3 to create

Do you want to perform this update? yes
Updating (dev):
     Type                                     Name              Status            
 +   pulumi:pulumi:Stack                      quickstart-dev    created (28s)     
 +   ├─ azure-native:resources:ResourceGroup  rg_ituru_pulumi_  created (1s)      
 +   └─ azure-native:storage:StorageAccount   saiturupulumi     created (26s)     

Outputs:
    ResourceGroup      : "rg_ituru_pulumi_0dd5a1af"
    StorageAccount     : "saiturupulumie1e8f28d"
    primary_storage_key: "jMP11aD0HUJ0zn69Us+I5x1DhAV3jMRoNjffBmx0IC07uyKtDNNzNTDe6mgJWjVFQq9FkPXPssvY+ASt62Jf1Q=="

Resources:
    + 3 created

Duration: 29s
```

実際に作られたリソース名は rg_ituru_pulumi_0dd5a1af や saiturupulumie1e8f28d という感じで後ろに8文字のサフィックスをPulumiが勝手につけているみたい。

### リソースグループ名を明示的に変更

ストレージアカウント はともかく、リソースグループはサフィックスを付けられたくないので、明示的に指定する。

```python:__main__.py（変更箇所のみ）
# Create an Azure Resource Group
# resource_group = resources.ResourceGroup("rg_ituru_pulumi_")
resource_group = resources.ResourceGroup("resourceGroup",
    resource_group_name="rg_ituru_pulumi",
)
```

### pulumi up で 設定を再反映

```
## Deploy Stack
$ pulumi up                              
Enter your passphrase to unlock config/secrets
    (set PULUMI_CONFIG_PASSPHRASE or PULUMI_CONFIG_PASSPHRASE_FILE to remember):  
Enter your passphrase to unlock config/secrets
Previewing update (dev):
     Type                                     Name              Plan        Info
     pulumi:pulumi:Stack                      quickstart-dev                
 +   ├─ azure-native:resources:ResourceGroup  resourceGroup     create      
 +-  ├─ azure-native:storage:StorageAccount   saiturupulumi     replace     [diff: ~resourceGroupName]
 -   └─ azure-native:resources:ResourceGroup  rg_ituru_pulumi_  delete      

Resources:
    + 1 to create
    - 1 to delete
    +-1 to replace
    3 changes. 1 unchanged

Do you want to perform this update? yes
Updating (dev):
     Type                                     Name              Status              Info
     pulumi:pulumi:Stack                      quickstart-dev                        
 +   ├─ azure-native:resources:ResourceGroup  resourceGroup     created (0.54s)     
 +-  ├─ azure-native:storage:StorageAccount   saiturupulumi     replaced (3s)       [diff: ~resourceGroupName]
 -   └─ azure-native:resources:ResourceGroup  rg_ituru_pulumi_  deleted (15s)       

Outputs:
  ~ ResourceGroup      : "rg_ituru_pulumi_0dd5a1af" => "rg_ituru_pulumi"
  ~ StorageAccount     : "saiturupulumie1e8f28d" => "saiturupulumi7bc3549e"
  ~ primary_storage_key: "jMP11aD0HUJ0zn69Us+I5x1DhAV3jMRoNjffBmx0IC07uyKtDNNzNTDe6mgJWjVFQq9FkPXPssvY+ASt62Jf1Q==" => "e8xELGxafP6ot7QxCtrw0vXFFmx+DHZ/jXzXppkijGx4hcAJ658bIZz5mscA3bwaDPj+AUzac14y+AStxRiamg=="

Resources:
    + 1 created
    - 1 deleted
    +-1 replaced
    3 changes. 1 unchanged

Duration: 46s
```

Azureのリソースグループは通常名前変更ができない。しかしpulumiは前回実行後の状態を(今回は)ローカルで管理しているので、使われなくなったリソースを削除し、再度新規作成する形で置き換わる。リソースグループに紐づくストレージアカウントも作り直しになっている。つまり、既存のリソース（データ含む）が全て吹っ飛ぶので注意が必要です。

### Output情報の取得方法

```
## まとめて取得
$ pulumi stack output              
Enter your passphrase to unlock config/secrets
    (set PULUMI_CONFIG_PASSPHRASE or PULUMI_CONFIG_PASSPHRASE_FILE to remember):  *************
Enter your passphrase to unlock config/secrets
Current stack outputs (4):
    OUTPUT               VALUE
    ResourceGroup        rg_ituru_pulumi
    StorageAccount       saiturupulumi7bc3549e
    primary_storage_key  e8xELGxafP6ot7QxCtrw0vXFFmx+DHZ/jXzXppkijGx4hcAJ658bIZz5mscA3bwaDPj+AUzac14y+AStxRiamg==

## 個別に取得（例）
$ pulumi stack output ResourceGroup                  
Enter your passphrase to unlock config/secrets 
    (set PULUMI_CONFIG_PASSPHRASE or PULUMI_CONFIG_PASSPHRASE_FILE to remember):  *************
Enter your passphrase to unlock config/secrets
rg_ituru_pulumi
```

---

## 静的Websiteの立ち上げ

[次はここ](https://www.pulumi.com/docs/clouds/azure/get-started/modify-program/)を参照して、静的Webサイトを立ち上げます。

### コンテンツの作成

ストレージアカウントがプロビジョニングされたので、それにオブジェクトを追加していきます。 コンテンツを含む新しいindex.html ファイルを作成します。

```
cat <<EOT > index.html
<html>
    <body>
        <h1>Hello, Pulumi!</h1>
    </body>
</html>
EOT
```

### プログラムの追加

静的Webサイトを有効にし、index.html をストレージコンテナーにアップロードし、リソースプロパティを使用してパブリックURLを取得します。

静的Webサイトのリソースは、プログラムで以前に定義されたストレージアカウントとリソースグループを利用します。

```python:__main__.py（追加）
# Enable static website support
static_website = storage.StorageAccountStaticWebsite(
    "staticWebsite",
    account_name=account.name,
    resource_group_name=resource_group.name,
    index_document="index.html",
)
```

次に、index.html をストレージコンテナーにアップロードできるようにします。

```python:__main__.py（追加）
# Upload the file
index_html = storage.Blob(
    "index.html",
    resource_group_name=resource_group.name,
    account_name=account.name,
    container_name=static_website.container_name,
    source=pulumi.FileAsset("index.html"),
    content_type="text/html",
)
```

最後に、結果のストレージ コンテナーのエンドポイントURL をOutput出来るようにします。

```python:__main__.py（新規追加）
# Web endpoint to the website
pulumi.export("staticEndpoint", account.primary_endpoints.web)
```

#### pulumi up で 設定を再々反映

```
## Deploy Stack
$ pulumi up                        
Enter your passphrase to unlock config/secrets
    (set PULUMI_CONFIG_PASSPHRASE or PULUMI_CONFIG_PASSPHRASE_FILE to remember):  
Enter your passphrase to unlock config/secrets
Previewing update (dev):
     Type                                                 Name            Plan       
     pulumi:pulumi:Stack                                  quickstart-dev             
 +   ├─ azure-native:storage:StorageAccountStaticWebsite  staticWebsite   create     
 +   └─ azure-native:storage:Blob                         index.html      create     

Outputs:
  + staticEndpoint     : "https://saiturupulumi7bc3549e.z11.web.core.windows.net/"

Resources:
    + 2 to create
    3 unchanged

Do you want to perform this update? yes
Updating (dev):
     Type                                                 Name            Status              
     pulumi:pulumi:Stack                                  quickstart-dev                      
 +   ├─ azure-native:storage:StorageAccountStaticWebsite  staticWebsite   created (1s)        
 +   └─ azure-native:storage:Blob                         index.html      created (0.33s)     

Outputs:
    ResourceGroup      : "rg_ituru_pulumi"
    StorageAccount     : "saiturupulumi7bc3549e"
    primary_storage_key: "e8xELGxafP6ot7QxCtrw0vXFFmx+DHZ/jXzXppkijGx4hcAJ658bIZz5mscA3bwaDPj+AUzac14y+AStxRiamg=="
  + staticEndpoint     : "https://saiturupulumi7bc3549e.z11.web.core.windows.net/"

Resources:
    + 2 created
    3 unchanged

Duration: 3s
```

結果を確認してみます。問題なくアクセスできました（もちろんブラウザでも確認とれました）。

```
## Webアクセス動作確認
$ curl https://saiturupulumi7bc3549e.z11.web.core.windows.net/
     <html>
          <body>
               <h1>Hello, Pulumi!</h1>
          </body>
     </html>
```

### Stack情報の確認

Pulumiで作成したリソースは、Pulumi上ではStack単位で管理されています（今回は "dev"）

```
## Stack情報の取得
$ pulumi stack                     
Enter your passphrase to unlock config/secrets
    (set PULUMI_CONFIG_PASSPHRASE or PULUMI_CONFIG_PASSPHRASE_FILE to remember):  **************
Enter your passphrase to unlock config/secrets
Current stack is dev:
    Managed by IsseinoMacBook-Pro.local
    Last updated: 16 minutes ago (2023-10-25 21:07:51.212534 +0900 JST)
    Pulumi version used: v3.89.0
Current stack resources (7):
    TYPE                                                 NAME
    pulumi:pulumi:Stack                                  quickstart-dev
    ├─ azure-native:resources:ResourceGroup              resourceGroup
    ├─ azure-native:storage:StorageAccount               saiturupulumi
    ├─ azure-native:storage:StorageAccountStaticWebsite  staticWebsite
    ├─ azure-native:storage:Blob                         index.html
    ├─ pulumi:providers:azure-native                     default_2_12_0
    └─ pulumi:providers:azure-native                     default

Current stack outputs (4):
    OUTPUT               VALUE
    ResourceGroup        rg_ituru_pulumi
    StorageAccount       saiturupulumi7bc3549e
    primary_storage_key  e8xELGxafP6ot7QxCtrw0vXFFmx+DHZ/jXzXppkijGx4hcAJ658bIZz5mscA3bwaDPj+AUzac14y+AStxRiamg==
    staticEndpoint       https://saiturupulumi7bc3549e.z11.web.core.windows.net/

Use `pulumi stack select` to change stack; `pulumi stack ls` lists known ones
```

Stack一覧の取得

```
$ pulumi stack ls
NAME  LAST UPDATE     RESOURCE COUNT
dev*  19 minutes ago  7
```

---

## 後片付け

### リソースの全削除
 
```
$ pulumi destroy
Enter your passphrase to unlock config/secrets
    (set PULUMI_CONFIG_PASSPHRASE or PULUMI_CONFIG_PASSPHRASE_FILE to remember):  **************
Enter your passphrase to unlock config/secrets
Previewing destroy (dev):
     Type                                                 Name            Plan       
 -   pulumi:pulumi:Stack                                  quickstart-dev  delete     
 -   ├─ azure-native:storage:Blob                         index.html      delete     
 -   ├─ azure-native:storage:StorageAccountStaticWebsite  staticWebsite   delete     
 -   ├─ azure-native:storage:StorageAccount               saiturupulumi   delete     
 -   └─ azure-native:resources:ResourceGroup              resourceGroup   delete     

Outputs:
  - ResourceGroup      : "rg_ituru_pulumi"
  - StorageAccount     : "saiturupulumi7bc3549e"
  - primary_storage_key: "e8xELGxafP6ot7QxCtrw0vXFFmx+DHZ/jXzXppkijGx4hcAJ658bIZz5mscA3bwaDPj+AUzac14y+AStxRiamg=="
  - staticEndpoint     : "https://saiturupulumi7bc3549e.z11.web.core.windows.net/"

Resources:
    - 5 to delete

Do you want to perform this destroy? yes
Destroying (dev):
     Type                                                 Name            Status              
 -   pulumi:pulumi:Stack                                  quickstart-dev  deleted (0.00s)     
 -   ├─ azure-native:storage:Blob                         index.html      deleted (0.51s)     
 -   ├─ azure-native:storage:StorageAccountStaticWebsite  staticWebsite   deleted (0.15s)     
 -   ├─ azure-native:storage:StorageAccount               saiturupulumi   deleted (3s)        
 -   └─ azure-native:resources:ResourceGroup              resourceGroup   deleted (15s)       

Outputs:
  - ResourceGroup      : "rg_ituru_pulumi"
  - StorageAccount     : "saiturupulumi7bc3549e"
  - primary_storage_key: "e8xELGxafP6ot7QxCtrw0vXFFmx+DHZ/jXzXppkijGx4hcAJ658bIZz5mscA3bwaDPj+AUzac14y+AStxRiamg=="
  - staticEndpoint     : "https://saiturupulumi7bc3549e.z11.web.core.windows.net/"

Resources:
    - 5 deleted

Duration: 20s

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

---

### まとめ

Terraformも良いですが、後発のPulumiも良いです。

### 参考記事

以下の記事を参考にさせていただきました。感謝申し上げます。
[PulumiはIaCの革命児になれるか](https://zenn.dev/yuta28/articles/pulumi-ai-revolutionary)