mito’s blog

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

IaCツールのPulumiに入門した

はじめに

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

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 = ""
$ 


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

[Ansible X Terraform] Terraform Provider for Ansible 「resource "ansible_host"」を触ってみた

この記事は、Ansible Advent Calendar 2023 の3日目のエントリです。

はじめに

気になっていた Terraform Provider for Ansible をこの機に触ってみました。


Terraform Provider for Ansible のまとめ

  • Terrafromの定義ファイルにresource "ansible_host"を追加する
    • 上記の結果がtfstateに記載される
  • Ansibleのインベントリファイルにcloud.terraformプラグインを指定する
    • インベントリファイルがtfstateの該当箇所を読み込む



環境

Ubuntu 22.04.3に構築しています。

  • Ansible環境
    • Ansible core : 2.15.6
    • Python : 3.10.12
    • cloud.terraform : 2.0.0
  • Terraform環境
    • Terraform : 1.6.5


環境の準備

Ansibleコレクションにて、cloud.terraform をインストールします。

$ ansible-galaxy collection install cloud.terraform
Starting galaxy collection install process
Process install dependency map
Starting collection install process
Downloading https://galaxy.ansible.com/api/v3/plugin/ansible/content/published/collections/artifacts/cloud-terraform-2.0.0.tar.gz to /home/ubuntu/.ansible/tmp/ansible-local-12661qsgjsrrt/tmpzhqh3nnq/cloud-terraform-2.0.0-a_5loo9d
Installing 'cloud.terraform:2.0.0' to '/home/ubuntu/.ansible/collections/ansible_collections/cloud/terraform'
cloud.terraform:2.0.0 was installed successfully
$ 
$ 
$ ansible-galaxy collection list

# /home/ubuntu/.ansible/collections/ansible_collections
Collection                    Version
----------------------------- -------
cloud.terraform               2.0.0  

()
$ 


次に、terraformのansibleプラグインをインストールします。 ただしterrafrom initでインストールされるため、準備では割愛します。


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

3台のEC2インスタンスを構築します。


定義ファイルの作成

鍵はあらかじめ作成してあるものを流用、セキュリティグループはSSHのみ開けています。

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

### ansible host details   今回のメイン。後述のtfstateに記載される。
resource "ansible_host" "inventory" {
    for_each                            = var.instance
    
    name                                = aws_instance.server[each.value.name].public_dns
    groups                              = ["test_group"]
    
    variables = {
        ansible_user                    = "ec2-user",
        ansible_ssh_private_key_file    = "~/.ssh/xxx.pem", # Terraformkey指定と違い、ファイル名なので拡張子も必要。
    }
}

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

variable instance {
    type = map
    default = {
        mito        = { name = "mito" },
        mito-ec2    = { name = "mito-ec2" },
        mito-ec22   = { name = "mito-ec22" },
    }
}

resource "aws_instance" "server" {
    for_each                    = var.instance

    ami                         = "ami-012261b9035f8f938"
    instance_type               = "t2.micro"
    key_name                    = "xxx"
    associate_public_ip_address = "true"
    
    vpc_security_group_ids      = [aws_security_group.blog_sg.id]
    tags = {
        Name                    = each.value.name
    }
}

resource "aws_security_group" "blog_sg" {
    name        = "blog_sg"
    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"]
    }
}


terraform applyの実行結果

EC2インスタンス作成のほか、tfstateには以下のように resource "ansible_host" の結果が記載されます。

  "resources": [
    {
      "mode": "managed",
      "type": "ansible_host",
      "name": "inventory",
      "provider": "provider[\"registry.terraform.io/ansible/ansible\"]",
      "instances": [
        {
          "index_key": "mito",
          "schema_version": 0,
          "attributes": {
            "groups": [
              "test_group"
            ],
            "id": "ec2-13-114-117-20.ap-northeast-1.compute.amazonaws.com",
            "name": "ec2-13-114-117-20.ap-northeast-1.compute.amazonaws.com",
            "variables": {
              "ansible_ssh_private_key_file": "~/.ssh/xxx.pem",
              "ansible_user": "ec2-user"
            }
          },


Ansibleのansible-inventoryコマンドを実行する

まずは、resource "ansible_host" の結果がどう反映されるか確認するため、ansible-inventoryコマンドを実行します。


インベントリファイルの作成

cloud.terraformプラグインを指定します。

---
plugin: cloud.terraform.terraform_provider


ansible-inventoryコマンドの実行結果

無事に、ホスト情報が出力されました。

$ ansible-inventory -i inventory.yml --graph --vars
@all:
  |--@ungrouped:
  |--@test_group:
  |  |--ec2-13-114-117-20.ap-northeast-1.compute.amazonaws.com
  |  |  |--{ansible_ssh_private_key_file = ~/.ssh/xxx.pem}
  |  |  |--{ansible_user = ec2-user}
  |  |--ec2-35-78-213-104.ap-northeast-1.compute.amazonaws.com
  |  |  |--{ansible_ssh_private_key_file = ~/.ssh/xxx.pem}
  |  |  |--{ansible_user = ec2-user}
  |  |--ec2-13-231-192-241.ap-northeast-1.compute.amazonaws.com
  |  |  |--{ansible_ssh_private_key_file = ~/.ssh/xxx.pem}
  |  |  |--{ansible_user = ec2-user}
$ 


Ansibleのplaybookを実行する

インベントリファイルを指定して、簡単なPlaybookを実行し、動作を確認してみます。


playbookの作成

gitのみインストールします。

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

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


ansible.cfgの作成

Fingerprintのチェックを無効にしておきます。

[defaults]
host_key_checking = False


playbookの実行結果

Terraform Provider for Ansibleを指定したインベントリファイルで、正常にPlaybookが実行できました。

$ ansible-playbook -i inventory.yml playbook.yml 

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

TASK [install git] *****************************************************************************************
changed: [ec2-13-114-117-20.ap-northeast-1.compute.amazonaws.com]
changed: [ec2-35-78-213-104.ap-northeast-1.compute.amazonaws.com]
changed: [ec2-13-231-192-241.ap-northeast-1.compute.amazonaws.com]

PLAY RECAP ************************************************************************************************
ec2-13-114-117-20.ap-northeast-1.compute.amazonaws.com : ok=1    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
ec2-13-231-192-241.ap-northeast-1.compute.amazonaws.com : ok=1    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
ec2-35-78-213-104.ap-northeast-1.compute.amazonaws.com : ok=1    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

$ 


終わりに

インベントリーファイルに、フィルターを組み合わせて使う感じで良さそう。