1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

SkillnoteAdvent Calendar 2024

Day 18

元CloudFormationユーザーがTerraform、Pulumiどっち使ってみたいか

Posted at

この記事はSkillnote Advent Calendar 2024の13日目の記事です。

はじめに

こんにちは!2024年11月より株式会社Skillnoteで主にSRE領域を担当している平山です。
前職では客先常駐の形態で金融系の基盤をAWSで構築しておりました。

そこではAWSのみだったため、CloudFormationを約1年半ほど使用しておりましたが、現職ではAWSだけでなくDatadogやSnowflakeなども使用しているため、
IaCツールとしてマルチベンダーに対応しているTerraformとPulumiの使用感を確認したいと思います。

この記事でわかること

  • Terraform、Pulumiそれぞれの特徴
  • 両IaCツールのチュートリアル的なコード
  • CFnユーザーからの両IaCツールの使用感想

Terraform、Pulumiの特徴

複数ありますが、大きな特徴の違いとしては書ける言語の違いになります。

言語
Teraform HCL(独自の言語)
Pulumi 好きなプログラミング言語

その他の特徴は以下のブログやPulumi公式の比較が参考になります。

検証内容

構成図

100万回見たね、これって感じのサーバーレスな構成図で試してみます。
serverless.drawio.png

実行環境

セキュリティの考慮をなくすため、Cloud9で実施していきます。
※デフォルトの権限では実行できないため、以下のようにCloud9のAMTCをOFFにして適切なIAMロールをアタッチする必要があります。
image.png

Terraformでの実装

適当な作業用フォルダを作成&移動して以下を実施していく

# インストール
# https://developer.hashicorp.com/terraform/install#linux
sudo yum install -y yum-utils shadow-utils
sudo yum-config-manager --add-repo https://rpm.releases.hashicorp.com/AmazonLinux/hashicorp.repo
sudo yum -y install terraform
# インストールできたか確認
terraform --version
# ワークスペースを初期化
terraform init

Terraform実行のための資材を以下の通り配置

ll | awk '{print $9}'
provider.tf
apigw.tf
lambda.tf
lambda_function.zip

各tfファイルは以下の通り作成
※lambda用のpythonコードは本筋と関係ないため割愛。単にメッセージをリターンするだけです。

tfファイル
provider.tf
provider "aws" {
  region = "ap-northeast-1"
}
lambda.tf
## Lambda関数
resource "aws_lambda_function" "lambda" {
  function_name = "tf_lambda_function"
  runtime       = "python3.13"
  handler       = "lambda_function.lambda_handler"

  filename = "lambda_function.zip"

  role = aws_iam_role.lambda_role.arn
}

## Lambdaロール
resource "aws_iam_role" "lambda_role" {
  name = "lambda_tf_role"

  assume_role_policy = jsonencode({
    Version = "2012-10-17",
    Statement = [
      {
        Action = "sts:AssumeRole",
        Effect = "Allow",
        Principal = {
          Service = "lambda.amazonaws.com"
        },
      },
    ],
  })
}
apigw.tf
## APIGateway
resource "aws_api_gateway_rest_api" "api" {
  name        = "terrafor_API"
  description = "terraform API"
}

resource "aws_api_gateway_resource" "api_resource" {
  rest_api_id = aws_api_gateway_rest_api.api.id
  parent_id   = aws_api_gateway_rest_api.api.root_resource_id
  path_part   = "terraform"
}

resource "aws_api_gateway_method" "api_method" {
  rest_api_id   = aws_api_gateway_rest_api.api.id
  resource_id   = aws_api_gateway_resource.api.id
  http_method   = "GET"
  authorization = "NONE"
}

resource "aws_api_gateway_integration" "backend" {
  rest_api_id = aws_api_gateway_rest_api.api.id
  resource_id = aws_api_gateway_resource.api_resource.id
  http_method = aws_api_gateway_method.api_method.http_method

  integration_http_method = "POST"
  type                    = "AWS_PROXY"
  uri                     = aws_lambda_function.lambda.invoke_arn
}

resource "aws_api_gateway_deployment" "api_stage" {
  depends_on = [aws_api_gateway_integration.backend]

  rest_api_id = aws_api_gateway_rest_api.api.id
  stage_name  = "test"
}

resource "aws_lambda_permission" "api_gateway" {
  statement_id  = "AllowAPIGatewayInvoke"
  action        = "lambda:InvokeFunction"
  function_name = aws_lambda_function.lambda.function_name
  principal     = "apigateway.amazonaws.com"

  source_arn = "${aws_api_gateway_rest_api.api.execution_arn}/*/*/*"
}
準備ができたので以下のterraformコマンドで実行
# テンプレートの整形
terraform fmt
# 変更内容の確認だけのコマンド
terraform plan
# 変更内容の確認と適用
terraform apply

作ったAPIへリクエストを投げてみたらレスポンス帰ってきましたね!
image.png

Pulumiでの実装

こちらでも適当な作業用フォルダを作成&移動して以下を実施していく

# インストール
curl -fsSL https://get.pulumi.com | sh
# インストールできたか確認
pulumi version
# pythonでawsリソースを定義できるように環境を設定
# puumiの使用にはアカウント所持が必須の為、初回実行時はここでPulumiのurlが表示され、アカウントの登録が必要になります。
pulumi new aws-python
# 以下の設定ファイルとpythonコードが作成される
ll | awk '{print $9}'
Pulumi.test.yaml
Pulumi.yaml
__main__.py
__pycache__
requirements.txt
venv

「__main__.py」にはお試し用のコードが入っており、「pulumi up」ですぐにs3バケットが作成できるような状態になっております。
今回の構築内容になるようにpythonファイルを以下のように上書きしていきます。

__main__.py
__main__.py
import pulumi
import pulumi_aws as aws

# Define AWS Lambda role
role = aws.iam.Role("lambdaRole",
    assume_role_policy="""
    {
        "Version": "2012-10-17",
        "Statement": [
            {
                "Action": "sts:AssumeRole",
                "Principal": {
                    "Service": "lambda.amazonaws.com"
                },
                "Effect": "Allow",
                "Sid": ""
            }
        ]
    }
    """
)

# Attach AWSLambdaBasicExecutionRole policy to the role
aws.iam.RolePolicyAttachment("lambdaRolePolicyAttachment",
    role=role.id,
    policy_arn="arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
)

# Create the Lambda function
lambda_function = aws.lambda_.Function("pulumi_lambda_function",
    runtime="python3.8",
    name="pulumi_lambda_function",
    role=role.arn,
    handler="lambda_function.lambda_handler",
    code=pulumi.FileArchive("./lambda_function.zip")
)

# Create an API Gateway
api = aws.apigateway.RestApi("api", name="pulumi_api")

# Create a resource under the API Gateway
resource = aws.apigateway.Resource("api-resource",
    rest_api=api.id,
    parent_id=api.root_resource_id,
    path_part="pulumi"
)

# Create a method for the resource
method = aws.apigateway.Method("api-method",
    rest_api=api.id,
    resource_id=resource.id,
    http_method="GET",
    authorization="NONE"
)

# Create an integration for the Lambda function
integration = aws.apigateway.Integration("api-integration",
    rest_api=api.id,
    resource_id=resource.id,
    http_method=method.http_method,
    integration_http_method="POST",
    type="AWS_PROXY",
    uri=lambda_function.invoke_arn
)

# Deploy the API Gateway
deployment = aws.apigateway.Deployment("api-deployment",
    rest_api=api.id,
    stage_name="test"
)

# Grant API Gateway permissions to invoke the Lambda function
permission = aws.lambda_.Permission("api-permission",
    action="lambda:InvokeFunction",
    function=lambda_function.name,
    principal="apigateway.amazonaws.com",
    source_arn=pulumi.Output.concat(api.execution_arn, "/*")
)

# Export the API endpoint
pulumi.export("url", pulumi.Output.concat(deployment.invoke_url, "/pulumi"))
pulumi.export("lambda_function_arn", lambda_function.arn)
最後に以下のpulumiコマンドを実行して、表示される構築内容に問題なければyesを選択して作成する。
pulumi up

こちらも作ったAPIへリクエスト投げてみると、レスポンスが帰ってきました🙌
image.png

使用感

  • Terraform
    • CloudFormationだとネストされたスタックなどを使用しないと分割したテンプレートを一気に流し込むことができませんでしたが、そのような作り込みが無くても作成できました。
    • HCL言語は今回のような内容を単純に書く分には学習の負担はほとんどありませんでした。
  • Pulumi
    • Pulumi AIというコード作成支援が無料で使えるが、精度はいまいちでChatGPTに聞いた方がましでした。
    • 自分の環境ではpythonは問題なく作成できましたが、TypeScriptで試そうとしたときに「pulumi up」でタイムアウトとなり作成できませんでした。
    • TerraformやCloudFormationのようにリソース毎の内部的な依存関係はあまり考慮せずにAPIをたたいているようで、初回の「pulumi up」では全てのリソースは作成できず、今回の構成だと3回「pulumi up」を繰り返す必要がありました。
    • ここはTerraformも同様の認識ですが、自動ロールバックがないので途中まで作成されたリソースは残ってしまうので、変更する範囲はなるべく細かくするのが良いと感じました
    • lambdaのような一部のリソースに関してはオプションで名前を指定してあげないとデフォルトではランダム文字列がリソース名のsuffixとして付けられるようでした。
      もちろん新たに「pulumi up」する毎に新規リソースが作成されるわけではないですが、冪等性の観点から、デフォルトの動作としては不適切かなと思いました。
    • 変更内容の表示については視覚的にかなり見やすかったなと思いました。
      image.png

結論

pulumiにも光るものはありましたが、今回の使用感と書籍やネットで参考にできる情報がある等総合的に鑑みてTerraformを使用していきたいなと思いました!
ただ、IaCは設定値の確認のためにドキュメントみながら書くため時間がかかるのが一つのデメリットと時運は感じています。
そのためplumi AIの精度が上がってその部分がほぼ不要になればとても有効なツールだと感じました!
標準でTerraformなどからPulumiへコードを変換する機能も用意されているので今後も注目していきたいです…!!

今後について

今回は単純な構成でしか検証していないので、今後は以下の検証記事を記載していこうかなと思います。

  • Terraformのベストプラクティスに沿った構築方法やCICDの構成
  • DatadogやSnowflakeのようなAWS以外でのTerraformの構築

まとめ

弊社ではsnowflakeやdatadog等AWS以外にも触れるだけでなく、
フロント、バックエンド領域の方とスクラムを組んでいるので様々な領域に携わっていくことができます。
興味のある方はぜひご検討ください!

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?