TerraformでAWS Lambda + API GatewayのサーバーレスAPIを構築する方法

## はじめに

この記事では、Terraformを使用してAWS LambdaとAmazon API Gatewayを組み合わせたシンプルなサーバーレスAPIをデプロイする方法を解説します。aws-terraform-sandboxプロジェクトのs05_api_lambda環境を例に、ステップバイステップで実装方法を紹介します。

<blockquote class="twitter-tweet" data-media-max-width="800"><p lang="ja" dir="ltr">terraformベースでラムダ＋APIGatewayでシンプルなAPI作ってみた！！！結構苦戦した。。。。<br><br>これがラムダに作る自作APIか！！！ <a href="https://t.co/flEqzNtzxS">https://t.co/flEqzNtzxS</a> <a href="https://t.co/j3lAvaplxM">pic.twitter.com/j3lAvaplxM</a></p>&mdash; Maki@Sunwood AI Labs. (@hAru_mAki_ch) <a href="https://twitter.com/hAru_mAki_ch/status/1822653024386601342?ref_src=twsrc%5Etfw">August 11, 2024</a></blockquote> 

## プロジェクト構成

s05_api_lambda環境の主要なファイル構成は以下の通りです：

```bash
s05_api_lambda/
├── main.tf
├── variables.tf
├── terraform.tfvars
├── lambda/
│   └── test/
│       └── src/
│           └── test_terraform.py
└── client.py
```

- `main.tf`: Terraformの主要な設定ファイル
- `variables.tf`: 変数定義ファイル
- `terraform.tfvars`: 変数の値を設定するファイル
- `lambda/test/src/test_terraform.py`: Lambda関数のソースコード
- `client.py`: APIをテストするためのクライアントスクリプト

## Terraformの設定

### プロバイダーの設定

`main.tf`ファイルの冒頭で、AWSプロバイダーを設定します：

```hcl
provider "aws" {
  region = var.aws_region
}
```

### Lambda関数の設定

Lambda関数のリソースを定義します：

```hcl
resource "aws_lambda_function" "api_lambda" {
  filename         = data.archive_file.test_terraform.output_path
  function_name    = var.lambda_function_name
  role             = aws_iam_role.lambda_role.arn
  handler          = "test_terraform.handler"
  runtime          = "python3.9"
  source_code_hash = data.archive_file.test_terraform.output_base64sha256

  environment {
    variables = {
      STAGE = var.stage
    }
  }
}
```

### API Gatewayの設定

API Gatewayのリソースを定義します：

```hcl
resource "aws_apigatewayv2_api" "lambda_api" {
  name          = "lambda-api"
  protocol_type = "HTTP"
  cors_configuration {
    allow_origins = ["*"]
    allow_methods = ["GET", "POST", "PUT", "DELETE"]
    allow_headers = ["Content-Type", "Authorization"]
    max_age       = 300
  }
}
```

### Lambda関数とAPI Gatewayの統合

Lambda関数とAPI Gatewayを統合します：

```hcl
resource "aws_apigatewayv2_integration" "lambda_integration" {
  api_id           = aws_apigatewayv2_api.lambda_api.id
  integration_type = "AWS_PROXY"
  integration_uri  = aws_lambda_function.api_lambda.invoke_arn
  integration_method = "POST"
  payload_format_version = "2.0"
}
```

## Lambda関数の実装

`lambda/test/src/test_terraform.py`ファイルにLambda関数を実装します：

```python
import json

def handler(event, context):
    return {
        'statusCode': 200,
        'body': json.dumps({
            'message': 'Hello, World!'
        })
    }
```

この関数は、単純に「Hello, World!」メッセージを返します。

## デプロイ手順

1. Terraformの初期化：
```bash
terraform init
```

1. 実行プランの確認：
```bash
terraform plan
```

1. リソースのデプロイ：
```bash
terraform apply
```

## APIのテスト

`client.py`スクリプトを使用してAPIをテストできます：

```python
import requests
import json
from rich.console import Console
from rich.panel import Panel

console = Console()

def call_lambda_api():
    api_url = "https://your-api-gateway-url.com/dev/test"
    
    try:
        with console.status("[bold green]Calling Lambda API...", spinner="dots"):
            response = requests.get(api_url)
        response.raise_for_status()
        return response.json()
    except requests.exceptions.RequestException as e:
        console.print(f"[bold red]API request error:[/bold red] {e}", style="red")
        return None

def main():
    result = call_lambda_api()
    if result:
        console.print(Panel(json.dumps(result, indent=2), title="API Response", border_style="green"))
    else:
        console.print(Panel("API call failed", title="Error", border_style="red"))

if __name__ == "__main__":
    main()
```

このスクリプトを実行すると、API呼び出しの結果が表示されます。

```bash
(aws-terraform-sandbox-py3.12) C:\Prj\aws-terraform-sandbox\sandbox\s05_api_lambda>python client.py
╭────────────────────────────────────────────────────────────────────────────╮
│ API call successful                                                        │
╰────────────────────────────────────────────────────────────────────────────╯
       API Response        
┏━━━━━━━━━┳━━━━━━━━━━━━━━━┓
┃ Key     ┃ Value         ┃
┡━━━━━━━━━╇━━━━━━━━━━━━━━━┩
│ message │ Hello, World! │
└─────────┴───────────────┘
╭──────────────────────────── Full JSON Response ────────────────────────────╮
│   1 {                                                                      │
│   2   "message": "Hello, World!"                                           │
│   3 }                                                                      │
╰────────────────────────────────────────────────────────────────────────────╯
```

## まとめ

この記事では、Terraformを使用してAWS LambdaとAmazon API Gatewayを組み合わせたシンプルなサーバーレスAPIをデプロイする方法を解説しました。この方法を応用することで、より複雑なサーバーレスアプリケーションも効率的に構築できます。

Terraformを使用することで、インフラストラクチャをコードとして管理し、再現性の高いデプロイメントを実現できます。今回のような小規模なプロジェクトから始めて、徐々に大規模なインフラストラクチャ管理にも挑戦してみてください。


## リポジトリ

https://github.com/Sunwood-ai-labs/aws-terraform-sandbox/tree/main/sandbox/s05_api_lambda

<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>