mito’s blog

IT技術メインの雑記。思い立ったが吉日。

PulumiのIaCコードをPythonで出力した

Pulumiとは
使い慣れたプログラミング言語を使用してインフラストラクチャコードを作成します。
オートコンプリート、型チェック、ドキュメントを備えた IDE を使用して、インフラストラクチャを定義するステートメントを作成します。


はじめに

PulumiのIaCコードは、様々な言語で出力することができます。
本記事では、Pythonで出力してみました。

参考 mitomito.hatenablog.jp

所感

今のところ、使い勝手はTerraformと変わりはなさそうという感触。
SaaSのPulumi Cloudの利用が必須で、そこでterraformでいうtfstateを管理している。
Pythonなどの言語でIaCコードを書けるので、開発者には向いているかもしれない。


単一のWebサーバを構築するコード

以下のリソースを作成するコードです。
IaC触ったことがある人は、Python知らなくても何となく読める気がします。



$ cat __main__.py 
import pulumi
import pulumi_aws as aws

# Get some configuration values or set default values.
config = pulumi.Config()
instance_type = config.get("instanceType")
if instance_type is None:
    instance_type = "t3.micro"
vpc_network_cidr = config.get("vpcNetworkCidr")
if vpc_network_cidr is None:
    vpc_network_cidr = "10.0.0.0/16"

# Look up the latest Amazon Linux 2 AMI.
ami = aws.ec2.get_ami(filters=[aws.ec2.GetAmiFilterArgs(
        name="name",
        values=["amzn2-ami-hvm-*"],
    )],
    owners=["amazon"],
    most_recent=True).id

# User data to start a HTTP server in the EC2 instance
user_data = """#!/bin/bash
echo "Hello, World from Pulumi!" > index.html
nohup python -m SimpleHTTPServer 80 &
"""

# Create VPC.
vpc = aws.ec2.Vpc("vpc",
    cidr_block=vpc_network_cidr,
    enable_dns_hostnames=True,
    enable_dns_support=True)

# Create an internet gateway.
gateway = aws.ec2.InternetGateway("gateway", vpc_id=vpc.id)

# Create a subnet that automatically assigns new instances a public IP address.
subnet = aws.ec2.Subnet("subnet",
    vpc_id=vpc.id,
    cidr_block="10.0.1.0/24",
    map_public_ip_on_launch=True)

# Create a route table.
route_table = aws.ec2.RouteTable("routeTable",
    vpc_id=vpc.id,
    routes=[aws.ec2.RouteTableRouteArgs(
        cidr_block="0.0.0.0/0",
        gateway_id=gateway.id,
    )])

# Associate the route table with the public subnet.
route_table_association = aws.ec2.RouteTableAssociation("routeTableAssociation",
    subnet_id=subnet.id,
    route_table_id=route_table.id)

# Create a security group allowing inbound access over port 80 and outbound
# access to anywhere.
sec_group = aws.ec2.SecurityGroup("secGroup",
    description="Enable HTTP access",
    vpc_id=vpc.id,
    ingress=[aws.ec2.SecurityGroupIngressArgs(
        from_port=80,
        to_port=80,
        protocol="tcp",
        cidr_blocks=["0.0.0.0/0"],
    )],
    egress=[aws.ec2.SecurityGroupEgressArgs(
        from_port=0,
        to_port=0,
        protocol="-1",
        cidr_blocks=["0.0.0.0/0"],
    )])

# Create and launch an EC2 instance into the public subnet.
server = aws.ec2.Instance("server",
    instance_type=instance_type,
    subnet_id=subnet.id,
    vpc_security_group_ids=[sec_group.id],
    user_data=user_data,
    ami=ami,
    tags={
        "Name": "webserver",
    })

# Export the instance's publicly accessible IP address and hostname.
pulumi.export("ip", server.public_ip)
pulumi.export("hostname", server.public_dns)
pulumi.export("url", server.public_dns.apply(lambda public_dns: f"http://{public_dns}"))
$ 





環境構築&プロジェクト作成

前回同様、VScodeのDevcontainerでPulumiのコンテナイメージを使用しています。 環境はGitHubに上げています。

まず、適当なディレクトリを作成し、移動します。
次に、pulumi new aws-pythonを実行します。これが便利で、プロジェクトの作成はもちろん、必要なpkgのインストールも実行してくれました。
なお、Purumiコマンドの初回実行時の場合は、アクセストークンの入力が必要です。

$ mkdir get_started/python
$ cd get_started/python/
$ 
$ pulumi new aws-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 (python):    // ブランクのままエンターを推すと、(デフォルト値)が入る
project description (A minimal AWS Python Pulumi program):  
Created project 'python'

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

Creating virtual environment...
Finished creating virtual environment
Updating pip, setuptools, and wheel in virtual environment...
(略)
Downloading attrs-23.2.0-py3-none-any.whl (60 kB)
   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 60.8/60.8 kB 59.0 MB/s eta 0:00:00
Downloading typing_extensions-4.10.0-py3-none-any.whl (33 kB)
Installing collected packages: arpeggio, typing-extensions, six, semver, pyyaml, protobuf, grpcio, dill, attrs, pulumi, parver, pulumi-aws

Successfully installed arpeggio-2.0.2 attrs-23.2.0 dill-0.3.8 grpcio-1.60.1 parver-0.5 protobuf-4.25.3 pulumi-3.111.1 pulumi-aws-6.27.0 pyyaml-6.0.1 semver-2.13.0 six-1.16.0 typing-extensions-4.10.0
Finished installing dependencies
Finished installing dependencies

Your new project is ready to go! 

To perform an initial deployment, run `pulumi up`

$ 


Pythonで出力したIaCコード

作成された各ファイルを確認していきます。

$ ls -la
total 0
drwxr-xr-x 1 vscode vscode 512 Apr 30 12:45 .
drwxr-xr-x 1 vscode vscode 512 Apr 30 12:42 ..
-rw-r--r-- 1 vscode vscode  12 Apr 30 12:45 .gitignore
-rw-r--r-- 1 vscode vscode  37 Apr 30 12:45 Pulumi.dev.yaml
-rw-r--r-- 1 vscode vscode 186 Apr 30 12:45 Pulumi.yaml
-rw-r--r-- 1 vscode vscode 219 Apr 30 12:45 __main__.py
-rw-r--r-- 1 vscode vscode  46 Apr 30 12:45 requirements.txt
drwxr-xr-x 1 vscode vscode 512 Apr 30 12:45 venv
$ ls -l ./venv/
total 0
drwxr-xr-x 1 vscode vscode 512 Apr 30 12:48 bin
drwxr-xr-x 1 vscode vscode 512 Apr 30 12:45 include
drwxr-xr-x 1 vscode vscode 512 Apr 30 12:45 lib
lrwxrwxrwx 1 vscode vscode   3 Apr 30 12:45 lib64 -> lib
-rw-r--r-- 1 vscode vscode  76 Apr 30 12:45 pyvenv.cfg
$ 


  • Pulumi.yaml
    • Yamlファイルではあるのですが、Yamlで出力した内容とは異なり、環境と言語が指定されているのみです
    • Yamlで出力した場合は、例えばS3バケットなどAWSリソース情報が記載されます。
$ cat Pulumi.yaml 
name: python
runtime:
  name: python
  options:
    virtualenv: venv
description: A minimal AWS Python Pulumi program
config:
  pulumi:tags:
    value:
      pulumi:template: aws-python
$


  • Pulumi.dev.yaml
    • Yamlで出力した内容と同じで、リージョン情報のみが記載されています。
$ cat Pulumi.dev.yaml 
config:
  aws:region: ap-northeast-1


  • requirements.txt
$ cat requirements.txt 
pulumi>=3.0.0,<4.0.0
pulumi-aws>=6.0.2,<7.0.0


$ cat __main__.py 
"""An AWS Python Pulumi program"""

import pulumi
from pulumi_aws import s3

# Create an AWS resource (S3 Bucket)
bucket = s3.Bucket('my-bucket')

# Export the name of the bucket
pulumi.export('bucket_name', bucket.id)


Pulumi CloudでプロジェクトをYaml出力と比較したところ、言語の違いだけでこれといった差異はありませんでした。




スタックのデプロイ

pulumiコマンドで、リソースを作成します。

$ pulumi up
Previewing update (dev)

View in Browser (Ctrl+O): https://app.pulumi.com/mito-201/python/dev/previews/***

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

Outputs:
    bucket_name: output<string>

Resources:
    + 2 to create

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

View in Browser (Ctrl+O): https://app.pulumi.com/mito-201/python/dev/updates/1

     Type                 Name        Status           
 +   pulumi:pulumi:Stack  python-dev  created (5s)     
 +   └─ aws:s3:Bucket     my-bucket   created (2s)     

Outputs:
    bucket_name: "my-bucket-e5e00c1"

Resources:
    + 2 created

Duration: 9s

$ 



スタックのデストロイ

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

$ pulumi destroy
Previewing destroy (dev)

View in Browser (Ctrl+O): https://app.pulumi.com/mito-201/python/dev/previews/e75bfeab-cb2b-4086-b33b-22a0d77bad9e

     Type                 Name        Plan       
 -   pulumi:pulumi:Stack  python-dev  delete     
 -   └─ aws:s3:Bucket     my-bucket   delete     

Outputs:
  - bucket_name: "my-bucket-e5e00c1"

Resources:
    - 2 to delete

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

View in Browser (Ctrl+O): https://app.pulumi.com/mito-201/python/dev/updates/2

     Type                 Name        Status              
 -   pulumi:pulumi:Stack  python-dev  deleted (0.49s)     
 -   └─ aws:s3:Bucket     my-bucket   deleted (0.81s)     

Outputs:
  - bucket_name: "my-bucket-e5e00c1"

Resources:
    - 2 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`.
$ 

Pulimi Cloudのスタックの削除

Pulimi Cloudにはスタックが残っているので、pulumi stack rm devで完全に削除します。
なお、IaCコードファイルは残ります。

$ 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!
$  
$ ls
Pulumi.yaml  __main__.py  __pycache__  requirements.txt  venv
$ 


その他

AWSクレデンシャルを登録しなかった場合のエラー

この程度のエラーであれば分かりやすいです。対応方法も提示されています。

error: pulumi:providers:aws resource 'default_6_32_0' has a problem: No valid credential sources found.

$ pulumi up
Previewing update (dev)

View in Browser (Ctrl+O): https://app.pulumi.com/mito-201/python/dev/previews/b74b3442-46fc-4bf7-955f-5aed8248d705

Downloading plugin: 229.75 MiB / 229.75 MiB [======================] 100.00% 23s

[resource plugin aws-6.32.0] installing
     Type                     Name            Plan       Info
 +   pulumi:pulumi:Stack      python-dev      create     
     └─ pulumi:providers:aws  default_6_32_0             1 error

Diagnostics:
  pulumi:providers:aws (default_6_32_0):
    error: pulumi:providers:aws resource 'default_6_32_0' has a problem: No valid credential sources found.
    Please see https://www.pulumi.com/registry/packages/aws/installation-configuration/ for more information about providing credentials.
    NEW: You can use Pulumi ESC to set up dynamic credentials with AWS OIDC to ensure the correct and valid credentials are used.
    Learn more: https://www.pulumi.com/registry/packages/aws/installation-configuration/#dynamically-generate-credentials

IaCツールのPulumiに入門、IaCコードをYamlで出力した


はじめに

PulumiはオープンソースのIaCツールで、クラウドリソースのプロビジョニングとデプロイを行います。
TerraformやOpenTofu相当の役割を担えます。
それらとの大きな違いの一つは、定義ファイルが複数のプログラミング言語で書けるという点です。そこが気になって触ってみました。

Pulumi - Infrastructure as Code in Any Programming Language


主な特徴は以下の通りです。


Pulumi Cloudのダッシュボードです。Pulumiの利用に登録必須で、個人利用は無料です。


基本的なコマンドの流れです。

Pulumi.yamlの有無 Pulumi Cloud登録の有無 AWSリソースの有無 実行コマンド 結果
なし なし なし pulumi new aws-yaml Pulumi.yamlを作成し、Pulumi Cloudにも登録する
あり あり なし pulumi up Yesでリソースを作成する
あり あり あり pulumi destroy Yesでリソースを削除する
あり あり なし pulumi stack rm dev スタック名の入力で、Pulumi Cloudの登録を削除する
あり なし なし rm Pulumi.yaml Pulumi.yamlを削除する



Get Started(S3バケットの構築)

PulumiでAWSのS3バケットを構築し、静的Webサイトをホスティングする手順です。
本記事では、s3バケットの構築までを行います。

Get started with Pulumi & AWS | Pulumi Docs


環境の構築

まず、公式ページでPulumi Cloudのアカウントを作成します。

Pulumi - Infrastructure as Code in Any Programming Language

次にPulumiをインストールしますが、Pulumi公式のコンテナイメージがDockerhubで公開されていますので、それをVscodeのDevcontaierで利用します。

今回Pulumiの定義ファイルはYamlで書くので、言語ランタイムのインストールは不要です。

環境変数に、AWSのアクセスキーとシークレットアクセスキーを設定します。
なお、aws cliはコンテナイメージにインストール済みです。

$ aws --version
aws-cli/2.15.30 Python/3.11.8 Linux/5.15.146.1-microsoft-standard-WSL2 exe/x86_64.debian.11 prompt/off


$ export AWS_ACCESS_KEY_ID="<YOUR_ACCESS_KEY_ID>"
$ export AWS_SECRET_ACCESS_KEY="<YOUR_SECRET_ACCESS_KEY_ID>"


適当な作業ディレクトリを作成します。

$ mkdir -p get_started/yaml   // 適当なディレクトリを作成
$ cd get_started/yaml         // yamlディレクトリは他の言語と環境が混ざらないように


プロジェクトの作成

Pulumi CLIコマンドを実行し、プロジェクトを作成します。

$ pulumi new aws-yaml
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  // URLを開いてトークンを作成する
    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: 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.


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 (yaml):      // ブランクでEnterを押すと、デフォルト名のyamlが入力される。以降、デフォルトで進める
project description (A minimal AWS Pulumi YAML program):  
Created project 'yaml'

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`

$ 


Pulumi.yamlPulumi.dev.yamlが作成されます。

  • Pulumi.yaml
    • プロジェクトとスタック リソースを管理するプログラムの両方を定義します。
  • Pulumi.dev.yaml
    • 初期化したスタックの構成値が含まれています。
$ ls -la
total 0
drwxr-xr-x 1 vscode vscode 512 Mar 20 09:11 .
drwxr-xr-x 1 vscode vscode 512 Mar 20 07:42 ..
-rw-r--r-- 1 vscode vscode  37 Mar 20 09:11 Pulumi.dev.yaml
-rw-r--r-- 1 vscode vscode 298 Mar 20 09:08 Pulumi.yaml


それぞれの内容は以下になります。

$ cat Pulumi.yaml 
name: yaml
runtime: yaml
description: A minimal AWS Pulumi YAML program
config:
  pulumi:tags:
    value:
      pulumi:template: aws-yaml
outputs:
  # Export the name of the bucket
  bucketName: ${my-bucket.id}
resources:
  # Create an AWS resource (S3 Bucket)
  my-bucket:
    type: aws:s3:Bucket


  • Pulumi.dev.yaml
    • 東京リージョンを指定します。
    • 環境変数にリージョンを設定していても、プロンプトで聞かれます。
$ cat Pulumi.dev.yaml 
config:
  aws:region: ap-northeast-1


作成したプロジェクトは、Pulumi Cloudでは以下のように表示されます。
GitHubアカウントでサインインしたからか、クローンしたGitHubリポジトリでプロジェクトを作成したからなのか、GitHubリポジトリも表示されています。




リソースの構築

pulumiコマンドで、リソースを作成します。

$ pulumi up
Previewing update (dev)

View in Browser (Ctrl+O): https://app.pulumi.com/mito-201/yaml/dev/previews/******-****-****-****-******

Downloading plugin: 215.98 MiB / 215.98 MiB [======================] 100.00% 44s

[resource plugin aws-6.27.0] installing
     Type                 Name       Plan       
 +   pulumi:pulumi:Stack  yaml-dev   create     
 +   └─ aws:s3:Bucket     my-bucket  create     

Outputs:
    bucketName: output<string>

Resources:
    + 2 to create

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


定義ファイルを実行した場合のプレビューを表示します。
Terraform plan相当の意味を持ち、作成/差分/削除のリソースが表示されます。
その後に実行するコマンドは、3つの選択があります。

  • yes
    • 表示したリソースを構築します。
  • no
    • 構築せず、プロンプトに戻ります。
  • details
    • 差分リソースの詳細を表示します。


矢印キーで yes を選択します。

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

View in Browser (Ctrl+O): https://app.pulumi.com/mito-201/yaml/dev/updates/1

     Type                 Name       Status           
 +   pulumi:pulumi:Stack  yaml-dev   created (4s)     
 +   └─ aws:s3:Bucket     my-bucket  created (2s)     

Outputs:
    bucketName: "my-bucket-******"

Resources:
    + 2 created

Duration: 6s

$
$ pulumi stack output bucketName
my-bucket-******
$ 




もう一度pulumi upを実行すると、プレビューの結果が2 unchangedに変わりました。

$ pulumi up
Previewing update (dev)

View in Browser (Ctrl+O): https://app.pulumi.com/mito-201/yaml/dev/previews/******-****-****-****-**********

     Type                 Name      Plan     
     pulumi:pulumi:Stack  yaml-dev           

Resources:
    2 unchanged

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


リソースの削除

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

$ pulumi destroy
Previewing destroy (dev)

View in Browser (Ctrl+O): https://app.pulumi.com/mito-201/yaml/dev/previews/******-****-****-****-**********

     Type                 Name       Plan       
 -   pulumi:pulumi:Stack  yaml-dev   delete     
 -   └─ aws:s3:Bucket     my-bucket  delete     

Outputs:
  - bucketName: "my-bucket-******"

Resources:
    - 2 to delete

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

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

View in Browser (Ctrl+O): https://app.pulumi.com/mito-201/yaml/dev/updates/2

     Type                 Name       Status              
 -   pulumi:pulumi:Stack  yaml-dev   deleted (0.51s)     
 -   └─ aws:s3:Bucket     my-bucket  deleted (0.88s)     

Outputs:
  - bucketName: "my-bucket-******"

Resources:
    - 2 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`.
$ 



Pulimi Cloudにはスタックが残っているので、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!
$



EC2の構築

テンプレートvm-aws-yamlを使って、EC2を構築します。

プロジェクトの作成

コマンドの流れはS3バケットの構築と変わりません。

$ mkdir -p my-virtual-machine/yaml && cd my-virtual-machine/yaml
$ 
$ pulumi new vm-aws-yaml
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 (yaml):  
project description (A Pulumi YAML program to deploy a virtual machine on Amazon EC2):  
Created project 'yaml'

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-west-2): ap-northeast-1 
instanceType: The Amazon EC2 instance type (t3.micro):  
vpcNetworkCidr: The network CIDR to use for the VPC (10.0.0.0/16):  
Saved config

Your new project is ready to go! 

To perform an initial deployment, run `pulumi up`

$ 


作成したPulumi.yamlです。
テンプレートでは、シンプルなWEBサーバを公開するために、VPC、Subnet、SG、InternetGW、EC2などを構築します。

$ cat Pulumi.yaml 
name: yaml
runtime: yaml
description: A Pulumi YAML program to deploy a virtual machine on Amazon EC2
config:
  instanceType:
    type: string
    default: t3.micro
  pulumi:tags:
    value:
      pulumi:template: vm-aws-yaml
  vpcNetworkCidr:
    type: string
    default: 10.0.0.0/16
# Export the instance's publicly accessible IP address and hostname.            
outputs:
  hostname: ${server.publicDns}
  ip: ${server.publicIp}
  url: http://${server.publicDns}
resources:
  # Create an internet gateway.
  gateway:
    properties:
      vpcId: ${vpc.id}
    type: aws:ec2:InternetGateway
  # Create a route table.
  routeTable:
    properties:
      routes:
        - cidrBlock: 0.0.0.0/0
          gatewayId: ${gateway.id}
      vpcId: ${vpc.id}
    type: aws:ec2:RouteTable
  # Associate the route table with the public subnet.
  routeTableAssociation:
    properties:
      routeTableId: ${routeTable.id}
      subnetId: ${subnet.id}
    type: aws:ec2:RouteTableAssociation
  # Create a security group allowing inbound access over port 80 and outbound
  # access to anywhere.
  secGroup:
    properties:
      description: Enable HTTP access
      egress:
        - cidrBlocks:
            - 0.0.0.0/0
          fromPort: 0
          protocol: -1
          toPort: 0
      ingress:
        - cidrBlocks:
            - 0.0.0.0/0
          fromPort: 80
          protocol: tcp
          toPort: 80
      vpcId: ${vpc.id}
    type: aws:ec2:SecurityGroup
  # Create and launch an EC2 instance into the public subnet.
  server:
    properties:
      ami: ${ami}
      instanceType: ${instanceType}
      subnetId: ${subnet.id}
      tags:
        Name: webserver
      userData: ${userData}
      vpcSecurityGroupIds:
        - ${secGroup}
    type: aws:ec2:Instance
  # Create a subnet that automatically assigns new instances a public IP address.
  subnet:
    properties:
      cidrBlock: 10.0.1.0/24
      mapPublicIpOnLaunch: true
      vpcId: ${vpc.id}
    type: aws:ec2:Subnet
  # Create VPC.
  vpc:
    properties:
      cidrBlock: ${vpcNetworkCidr}
      enableDnsHostnames: true
      enableDnsSupport: true
    type: aws:ec2:Vpc
variables:
  # Look up the latest Amazon Linux 2 AMI.
  ami:
    fn::invoke:
      arguments:
        filters:
          - name: name
            values:
              - "amzn2-ami-hvm-*"
        mostRecent: true
        owners:
          - "amazon"
      function: aws:ec2:getAmi
      return: id
  # User data to start a HTTP server in the EC2 instance
  userData: |
    #!/bin/bash
    echo "Hello, World from Pulumi!" > index.html
    nohup python -m SimpleHTTPServer 80 &
$ 


リソースの構築

pulumi upでデプロイします。

$ pulumi up
Previewing update (dev)

View in Browser (Ctrl+O): https://app.pulumi.com/mito-201/yaml/dev/previews/********-****-****-****-**********

     Type                              Name                   Plan       
 +   pulumi:pulumi:Stack               yaml-dev               create     
 +   ├─ aws:ec2:Vpc                    vpc                    create     
 +   ├─ aws:ec2:Subnet                 subnet                 create     
 +   ├─ aws:ec2:SecurityGroup          secGroup               create     
 +   ├─ aws:ec2:InternetGateway        gateway                create     
 +   ├─ aws:ec2:RouteTable             routeTable             create     
 +   ├─ aws:ec2:RouteTableAssociation  routeTableAssociation  create     
 +   └─ aws:ec2:Instance               server                 create     

Outputs:
    hostname: output<string>
    ip      : output<string>
    url     : output<string>

Resources:
    + 8 to create

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

View in Browser (Ctrl+O): https://app.pulumi.com/mito-201/yaml/dev/updates/1

     Type                              Name                   Status              
 +   pulumi:pulumi:Stack               yaml-dev               created (40s)       
 +   ├─ aws:ec2:Vpc                    vpc                    created (12s)       
 +   ├─ aws:ec2:InternetGateway        gateway                created (0.95s)     
 +   ├─ aws:ec2:Subnet                 subnet                 created (11s)       
 +   ├─ aws:ec2:SecurityGroup          secGroup               created (3s)        
 +   ├─ aws:ec2:RouteTable             routeTable             created (1s)        
 +   ├─ aws:ec2:RouteTableAssociation  routeTableAssociation  created (0.76s)     
 +   └─ aws:ec2:Instance               server                 created (13s)       

Outputs:
    hostname: "ec2-13-112-244-67.ap-northeast-1.compute.amazonaws.com"
    ip      : "13.112.244.67"
    url     : "http://ec2-13-112-244-67.ap-northeast-1.compute.amazonaws.com"

Resources:
    + 8 created

Duration: 41s

$ 


無事にアクセスできました。



リソースの削除

確認後は、リソースを削除します。

$ pulumi destroy
Previewing destroy (dev)

View in Browser (Ctrl+O): https://app.pulumi.com/mito-201/yaml/dev/previews/*********-****-****-****-*********

     Type                              Name                   Plan       
 -   pulumi:pulumi:Stack               yaml-dev               delete     
 -   ├─ aws:ec2:RouteTableAssociation  routeTableAssociation  delete     
 -   ├─ aws:ec2:RouteTable             routeTable             delete     
 -   ├─ aws:ec2:Instance               server                 delete     
 -   ├─ aws:ec2:SecurityGroup          secGroup               delete     
 -   ├─ aws:ec2:InternetGateway        gateway                delete     
 -   ├─ aws:ec2:Subnet                 subnet                 delete     
 -   └─ aws:ec2:Vpc                    vpc                    delete     

Outputs:
  - hostname: "ec2-13-112-244-67.ap-northeast-1.compute.amazonaws.com"
  - ip      : "13.112.244.67"
  - url     : "http://ec2-13-112-244-67.ap-northeast-1.compute.amazonaws.com"

Resources:
    - 8 to delete

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

View in Browser (Ctrl+O): https://app.pulumi.com/mito-201/yaml/dev/updates/2

     Type                              Name                   Status              
 -   pulumi:pulumi:Stack               yaml-dev               deleted (0.26s)     
 -   ├─ aws:ec2:RouteTableAssociation  routeTableAssociation  deleted (0.76s)     
 -   ├─ aws:ec2:RouteTable             routeTable             deleted (0.95s)     
 -   ├─ aws:ec2:Instance               server                 deleted (61s)       
 -   ├─ aws:ec2:Subnet                 subnet                 deleted (0.70s)     
 -   ├─ aws:ec2:SecurityGroup          secGroup               deleted (1s)        
 -   ├─ aws:ec2:InternetGateway        gateway                deleted (1s)        
 -   └─ aws:ec2:Vpc                    vpc                    deleted (1s)        

Outputs:
  - hostname: "ec2-13-112-244-67.ap-northeast-1.compute.amazonaws.com"
  - ip      : "13.112.244.67"
  - url     : "http://ec2-13-112-244-67.ap-northeast-1.compute.amazonaws.com"

Resources:
    - 8 deleted

Duration: 1m9s

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.yamlの有無 Pulumi Cloud登録の有無 AWSリソースの有無 実行コマンド 結果
なし なし なし pulumi new aws-yaml Pulumi.yamlを作成し、Pulumi Cloudにも登録する
あり あり なし pulumi up Yesでリソースを作成する
あり あり あり pulumi destroy Yesでリソースを削除する
あり あり なし pulumi stack rm dev スタック名の入力で、Pulumi Cloudの登録を削除する
あり なし なし pulumi stack init スタック名の入力で、Pulumi Cloudに登録する
あり なし なし pulumi up <org-name>/スタック名の入力でPulumi Cloudに登録し、Yesでリソースを作成する
あり あり あり pulumi down Yesでリソースを削除する。Pulumi Cloudは削除しない
あり あり あり pulumi stack rm dev スタック名を入力しても、エラーで失敗する
なし なし なし pulumi up Pulumi.yamlがないため、コマンドエラー


新規プロジェクトは、空のディレクトリで作成できる

ファイルがあるディレクトリで、新規プロジェクトを作成しようとしても失敗します。
Pulumi.yamlのファイル名を変更しても、作成に失敗しました。

$ pulumi new aws-yaml
error: /workspaces/pulumi_learning/get_started/yaml is not empty; rerun in an empty directory, pass the path to an empty directory to --dir, or use --force
$ 

VScodeのDevContainerでC言語に再入門した

はじめに

新卒時代にCで組み込み系に携わっていたけど、それ以降はたまにVBAやShellScriptを、業務ではAnsibleやTeraformで定義ファイルを書いていました。 最近、プログラミングを学びなおすのに良さそうな複数の理由ができたので、経験のあるCで100本ノックを始めてみました。



感想

普通に楽しい。草で可視化されていくのもいい、草原にしたい。
気分転換にもいいかもしれない。
もう少しやったらGoあたりに手を出してみようかなと。


環境

VScodeのDevcontainerを使い、Cの開発環境を用意しています。
Dockerコンテナイメージは、Microsoft公式のイメージを利用します。
c以外も用意されているので、他言語で100本ノックを実施するとき参考になります。

  1. Docker Desktopをインストールする
  2. VScode をインストールする
  3. VScode のエクステンション「Dev Containers」をインストールする
  4. リポジトリをクローンする
  5. DevContainerに接続する


GitHubの設定

特に公開する気がなければ不要です。
トークンやメールアドレスの非表示設定などをGitHubのReadmeに載せています。


ファイルを作成後、コンパイル、実行するまでの流れ

knock00のHello World表示例です。

$ ls
main.c
$
$ more main.c
#include <stdio.h>

int main(void)
{
    printf("Hello World\n");
}
$
$ gcc main.c
$
$ ls
a.out  main.c
$
$ ./a.out
Hello World

Cloud Native Days Tokyo 2023 参加レポート

技術カンファレンス Advent Calendar 2023 シリーズ2の14日目の記事です。

はじめに

Cloud Native Days Tokyo 2023(以下、CNDT2023)の初日に現地参加してきました。
以下に、いくつかのセッションと現地参加の感想を記載しています。

event.cloudnativedays.jp


現地参加の感想

現地参加して本当に良かったです。CODT2023に続き、久しぶりの方もオフラインでは初の方とも会えたので大満足です!
よるイベでは、初めに話した一人がつい先日弊社の研修を受けた方だったり、10数年前に同じ製品を取り扱った方、出張研修でお会いした方もいて、個人的に盛り上がりました。

スポンサーブースでは、もともと全ブースに話しかけようと思っていましたが、スタンプラリーがあるおかげで話しやすかったです。 「スタンプください、あと何を紹介してるのですか?」って。話しかけるの楽。
業務で関わり合いがあって顔見知りの方から、あまり興味のなかったジャンルの知見を得たり、これはど真ん中な競合製品だなぁって思いながら話を聞いたりなど楽しかったです。 また、企業ロゴのTシャツを集めている(よく着ている)ので、Tシャツ数枚のほかパーカー、靴下まで貰えていうことなし! 自社オフィスで赤い帽子のポロシャツを着て過ごすこともあるので!

次回もぜひ現地参加しようと思います。弊社スポンサーで出展はしなかったけど、こんなに盛況なら出展しても良さそう。


セッションの感想

現地の人と交流してる時間が長く、後追いで見る予定です。 徐々に更新していきます!


「noteのKubernetes移行、ゼンブ見せます」

event.cloudnativedays.jp

  • 登壇者
  • ソフトウェア式年遷宮という考え方が興味深かった
    • クラウド移行をする際に参考になりそう。特に、後々困りそうな技術の継承は深掘りしておきたい
  • Kubernetesになれてないエンジニアにも操作できるよう、運用系ツールを開発
  • 上記だけでなく、ゼンブ見せますという言葉通り、ゼンブ見れます!


「10年モノのサービスのインフラを漸進的に改善する、頑張りすぎないクラウドネイティブ」

event.cloudnativedays.jp

  • 登壇者
  • 課題に対する頑張りすぎない改善策(どれだけコストをかけるか?)
    • 課題
      • バッチ周辺システムが10年物で、Ubuntu12.04のEC2インスタンス10台
        • 対応への切っ掛けは、AWSAPIがTLS1.1以下の接続をサポート終了
    • 対応
      • TLS1.2以上の通信に対応するほか、運用上の課題を洗い出し、各課題への優先度をつけ対応策を検討
      • コストと期待する成果を観点に入れることで、対応方針を決めやすくなった
      • 課題解決の目的意識を持つことで、クラウドネイティブを頑張らなくていい判断ができた
        • コンテナ化やサーバレスのようなクラウドネイティブ技術を導入しなくても解決できる


人工衛星の運用を支える、クラウドネイティブ民主化への取り組み」

event.cloudnativedays.jp

  • 登壇者
    • Synspective Inc. Masaya Araiさん @msy78
  • 宇宙開発・人口衛星分野でクラウドネイティブ技術が大いに活用されていることを知ってほしい
    • 地上システムやソリューション/データプラットフォームはGoogleCloud(GKE/OSS/BigQueryなど)で動いている
    • 観測衛星事業に関連する法律やコンプライアンス規制に基づくビジネス制約を加味しながら、クラウドネイティブ技術を活用している
  • クラウドネイティブ技術で技術の民主化とオーナーシップが高められることを知ってほしい
    • 民主化を進めるうえで大切なこと「スモールスタート」「既存の開発体験を極力壊さない」「利用を強制しない」「フィードバックを大切にする」

Terraform Provider for Ansible「resource "ansible_playbook"」を試してみた

terraform Advent Calendar 2023 の11日目の記事です。

はじめに

気になっていた Terraform Provider for Ansible をこの機に試してみました。
今回の対象は resource "ansible_playbook" です。


試した感触

TerraformとAnsibleを組み合わせるなら、今のところリソースansible_hostを使って、別途Playbookを実行したほうが良さそうです。

本投稿に成功したログを残していますが、投稿の検証のためにapply/destoryを繰り返していたところ、 削除したはずのホストがインベントリに表示していたり、ansibleプラグインが読み込めなかったこともありました。
時間を見つけて調査してみようかと。

mitomito.hatenablog.jp



環境

Ubuntu 22.04.3に構築しています。

  • Terraform : 1.6.5
  • Ansible core : 2.15.6
  • Python : 3.10.12


TerraformでEC2インスタンスを構築する

tfファイル、ansible.cfg、Playbookを作成し、実行します。


Terraformの定義ファイルの作成

resource "ansible_playbook" がPlaybook実行のリソースです。

terraform {
  required_providers {
    ansible = {
      version = "~> 1.1.0"
      source  = "ansible/ansible"
    }
  }
}

provider "aws" {
    region = "ap-northeast-1"
}

### playbookのメインとなるリソース
resource "ansible_playbook" "playbook" {
    playbook                            = "playbook.yml"
    name                                = aws_instance.server.public_dns
    replayable                          = true   ### terraform applyの度に、Playbookを実行

    extra_vars = {
        ansible_user                    = "ec2-user"
        ansible_ssh_private_key_file    = "~/.ssh/xxxx.pem"
    }
}

## 確認できる4つのoutputを表示
output "args" {
  value = ansible_playbook.playbook.args
}

output "temp_inventory_file" {
  value = ansible_playbook.playbook.temp_inventory_file
}

output "playbook_stderr" {
  value = ansible_playbook.playbook.ansible_playbook_stderr
}

output "playbook_stdout" {
  value = ansible_playbook.playbook.ansible_playbook_stdout
}

resource "aws_instance" "server" {
    ami                         = "ami-012261b9035f8f938"
    instance_type               = "t2.micro"
    key_name                    = "xxxx"
    associate_public_ip_address = "true"
    vpc_security_group_ids      = [aws_security_group.blog_sg.id]

    tags = {
        Name                    = "mito-ec2"
    }
}

resource "aws_security_group" "blog_sg" {
    name        = "blog_sg2"
    description = "blog_sg"

    dynamic "ingress" {
        for_each = [22]    # 

        content {
            from_port   = ingress.value
            to_port     = ingress.value
            protocol    = "tcp"
            cidr_blocks = ["0.0.0.0/0"]
        }
    }

    egress {
        from_port   = 0
        to_port     = 0
        protocol    = "-1"
        cidr_blocks = ["0.0.0.0/0"]
    }
}


ansible.cfgの作成

ホストキーのチェックを無効にします。 ドキュメントに記載はなかったのですが、今回コンフィグファイルは読み込めたようです。

[defaults]
host_key_checking = false


AnsibleのPlaybookの作成

gitをインストールするのみのPlaybookです。

---
- hosts: all
  gather_facts: no
  become: yes

  tasks:
    - name: install git
      ansible.builtin.yum:
        name: git
        state: present


terraform applyの実行結果

terraform applyを実行します。

$ terraform apply

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # ansible_playbook.playbook will be created
  + resource "ansible_playbook" "playbook" {
      + ansible_playbook_binary = "ansible-playbook"
      + ansible_playbook_stderr = (known after apply)
      + ansible_playbook_stdout = (known after apply)
      + args                    = (known after apply)
      + check_mode              = false
      + diff_mode               = false
      + extra_vars              = {
          + "ansible_ssh_private_key_file" = "~/.ssh/xxxx.pem"
          + "ansible_user"                 = "ec2-user"
        }
      + force_handlers          = false
      + id                      = (known after apply)
      + ignore_playbook_failure = false
      + name                    = (known after apply)
      + playbook                = "playbook.yml"
      + replayable              = true
      + temp_inventory_file     = (known after apply)
      + verbosity               = 0
    }

  # aws_instance.server will be created
()

Plan: 3 to add, 0 to change, 0 to destroy.

Changes to Outputs:
  + args                = (known after apply)
  + playbook_stderr     = (known after apply)
  + playbook_stdout     = (known after apply)
  + temp_inventory_file = (known after apply)

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes

aws_security_group.blog_sg: Creating...
aws_security_group.blog_sg: Creation complete after 3s [id=sg-07168004190e490bb]
aws_instance.server: Creating...
aws_instance.server: Still creating... [10s elapsed]
aws_instance.server: Still creating... [20s elapsed]
aws_instance.server: Still creating... [30s elapsed]
aws_instance.server: Still creating... [40s elapsed]
aws_instance.server: Creation complete after 41s [id=i-0cfa9440f6cb49b9c]
ansible_playbook.playbook: Creating...
ansible_playbook.playbook: Still creating... [10s elapsed]
ansible_playbook.playbook: Still creating... [20s elapsed]
ansible_playbook.playbook: Creation complete after 23s [id=2023-12-10 18:21:34.340075309 +0000 UTC m=+44.513961043]

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

Outputs:

args = tolist([
  "-e",
  "hostname=ec2-13-115-221-150.ap-northeast-1.compute.amazonaws.com",
  "-e",
  "ansible_ssh_private_key_file=~/.ssh/xxxx.pem",
  "-e",
  "ansible_user=ec2-user",
  "playbook.yml",
])
playbook_stderr = ""
playbook_stdout = <<EOT

PLAY [all] *********************************************************************

TASK [install git] *************************************************************
changed: [ec2-13-115-221-150.ap-northeast-1.compute.amazonaws.com]

PLAY RECAP *********************************************************************
ec2-13-115-221-150.ap-northeast-1.compute.amazonaws.com : ok=1    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   


EOT
temp_inventory_file = ""
$ 


インスタンスのステータスチェックをしているのか、たまたま成功したのか。。。