

# Amazon RDS で AWS Lambda 使用する
<a name="services-rds"></a>

Lambda 関数は、Amazon Relational Database Service (Amazon RDS) データベースに直接接続することも、Amazon RDS Proxy 経由で接続することもできます。単純なシナリオでは直接接続が有効で、本番環境ではプロキシの使用が推奨されます。データベースプロキシは、共有されたデータベース接続のプールを管理します。これを使用することで、データベース接続を消耗することなく、関数の同時実行レベルを上げることができます。

データベースへの短時間の接続を頻繁に実行したり、多数のデータベースへの接続を開閉したりする Lambda 関数には、Amazon RDS Proxy を使用することをお勧めします。詳細については、「Amazon Relational Database Service デベロッパーガイド」の「[Lambda 関数と DB インスタンスを自動的に接続する](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/lambda-rds-connect.html)」を参照してください。

**ヒント**  
Lambda 関数を Amazon RDS データベースにすばやく接続するには、コンソール内ガイドウィザードを使用できます。ウィザードを開くには、次の操作を行います。  
Lambda コンソールの [[関数]](https://console.aws.amazon.com/lambda/home#/functions) ページを開きます。
データベースを接続する関数を選択します。
**[設定]** タブで **[RDS データベース]** を選択します。
**[RDS データベースに接続]** を選択します。
関数をデータベースに接続したら、**[プロキシの追加]** を選択してプロキシを作成できます。

## RDS リソースを操作するように関数を設定する
<a name="rds-configuration"></a>

Lambda コンソールでは、Amazon RDS データベースのインスタンスおよびプロキシリソースのプロビジョニングおよび設定することができます。これを行うには、**[設定]** タブの **[RDS データベース]** に移動します。または、Amazon RDS コンソールで Lambda 関数への接続を作成して設定することもできます。Lambda で使用するように RDS データベースインスタンスを設定するときは、次の条件に注意してください。
+ データベースに接続するには、関数をデータベースが実行されているものと同じ Amazon VPC に置く必要があります。
+ Amazon RDS データベースは、MySQL、MariaDB、PostgreSQL、または Microsoft SQL Server エンジンで使用できます。
+ また、MySQL または PostgreSQL エンジンでは、Aurora DB クラスターも使用できます。
+ データベース認証用の Secrets Manager シークレットを用意する必要があります。
+ IAM ロールはシークレットを使用するためのアクセス許可を付与する必要があり、信頼ポリシーは Amazon RDS にロールの引き受けを許可する必要があります。
+  コンソールを使用して Amazon RDS リソースを設定し、それを関数に接続する IAM プリンシパルには、次のアクセス許可が必要です。

### アクセス許可ポリシーの例。
<a name="rds-lambda-permissions"></a>

**注記**  
 Amazon RDS Proxy のアクセス許可は、データベース接続のプールを管理するように Amazon RDS Proxy を設定する場合にのみ必要です。

------
#### [ JSON ]

****  

```
{
  "Version":"2012-10-17",		 	 	 
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "ec2:CreateSecurityGroup",
        "ec2:DescribeSecurityGroups",
        "ec2:DescribeSubnets",
        "ec2:DescribeVpcs",
        "ec2:AuthorizeSecurityGroupIngress",
        "ec2:AuthorizeSecurityGroupEgress",
        "ec2:RevokeSecurityGroupEgress",
        "ec2:CreateNetworkInterface",
        "ec2:DeleteNetworkInterface",
        "ec2:DescribeNetworkInterfaces"
      ],
      "Resource": "*"
    },
    {
      "Effect": "Allow",
      "Action": [
        "rds-db:connect",
        "rds:CreateDBProxy",
        "rds:CreateDBInstance",
        "rds:CreateDBSubnetGroup",
        "rds:DescribeDBClusters",
        "rds:DescribeDBInstances",
        "rds:DescribeDBSubnetGroups",
        "rds:DescribeDBProxies",
        "rds:DescribeDBProxyTargets",
        "rds:DescribeDBProxyTargetGroups",
        "rds:RegisterDBProxyTargets",
        "rds:ModifyDBInstance",
        "rds:ModifyDBProxy"
      ],
      "Resource": "*"
    },
    {
      "Effect": "Allow",
      "Action": [
        "lambda:CreateFunction",
        "lambda:ListFunctions",
        "lambda:UpdateFunctionConfiguration"
      ],
      "Resource": "*"
    },
    {
      "Effect": "Allow",
      "Action": [
        "iam:AttachRolePolicy",
        "iam:CreateRole",
        "iam:CreatePolicy"
      ],
      "Resource": "*"
    },
    {
      "Effect": "Allow",
      "Action": [
        "secretsmanager:GetResourcePolicy",
        "secretsmanager:GetSecretValue",
        "secretsmanager:DescribeSecret",
        "secretsmanager:ListSecretVersionIds",
        "secretsmanager:CreateSecret"
      ],
      "Resource": "*"
    }
  ]
}
```

------

Amazon RDS は、データベースインスタンスのサイズに基づいたプロキシの料金が時間単位で請求されます。詳細については、「[RDS プロキシの料金](https://aws.amazon.com/rds/proxy/pricing/)」をご覧ください。プロキシ接続についての全般的な詳細は、Amazon RDS User Guide の「[Using Amazon RDS Proxy](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/rds-proxy.html)」を参照してください。

### Amazon RDS 接続の SSL/TLS 要件
<a name="rds-lambda-certificates"></a>

Amazon RDS データベースインスタンスへの安全な SSL/TLS 接続を行うには、Lambda 関数が信頼できる証明書を使用してデータベースサーバーの ID を検証する必要があります。Lambda は、デプロイパッケージタイプに応じてこれらの証明書を異なる方法で処理します。
+ [.zip ファイルアーカイブ](configuration-function-zip.md): 証明書の処理はランタイムによって異なります。
  + **Node.js 18 以前**: Lambda には CA 証明書と RDS 証明書が自動的に含まれます。
  + **Node.js 20 以降**: Lambda でデフォルトで追加の CA 証明書がロードされなくなりました。`NODE_EXTRA_CA_CERTS` 環境変数を `/var/runtime/ca-cert.pem` に設定します。

  新しい AWS リージョン の Amazon RDS 証明書が Lambda マネージドランタイムに追加されるまでに、最大 4 週間かかる場合があります。
+ [コンテナイメージ](images-create.md): AWS ベースイメージには CA 証明書のみが含まれます。関数が Amazon RDS データベースインスタンスに接続する場合は、コンテナイメージに適切な証明書を含める必要があります。Dockerfile で、[データベースをホストする AWS リージョンに対応する証明書バンドルをダウンロードします。](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/UsingWithRDS.SSL.html#UsingWithRDS.SSL.CertificatesDownload)例:

  ```
  RUN curl https://truststore.pki.rds.amazonaws.com/us-east-1/us-east-1-bundle.pem -o /us-east-1-bundle.pem
  ```

このコマンドは、Amazon RDS 証明書バンドルをダウンロードし、コンテナのルートディレクトリの絶対パス `/us-east-1-bundle.pem` に保存します。関数コードでデータベース接続を設定するときは、このパスを正確に参照する必要があります。例:

------
#### [ Node.js ]

Node.js データベースクライアントは、証明書ファイルへのパスだけでなく、メモリ内の実際の証明書コンテンツを必要とするため、`readFileSync` 関数が必要です。`readFileSync` がない場合、クライアントはパス文字列を証明書コンテンツとして解釈し、「証明書チェーン内の自己署名証明書」エラーが発生します。

**Example OCI 関数の Node.js 接続設定**  

```
import { readFileSync } from 'fs';

// ...

let connectionConfig = {
    host: process.env.ProxyHostName,
    user: process.env.DBUserName,
    password: token,
    database: process.env.DBName,
    ssl: {
        ca: readFileSync('/us-east-1-bundle.pem') // Load RDS certificate content from file into memory
    }
};
```

------
#### [ Python ]

**Example OCI 関数の Python 接続設定**  

```
connection = pymysql.connect(
    host=proxy_host_name,
    user=db_username,
    password=token,
    db=db_name,
    port=port,
    ssl={'ca': '/us-east-1-bundle.pem'}  #Path to the certificate in container
)
```

------
#### [ Java ]

JDBC 接続を使用する Java 関数の場合、接続文字列には以下を含める必要があります。
+ `useSSL=true`
+ `requireSSL=true`
+ コンテナイメージ内の Amazon RDS 証明書の場所を指す `sslCA` パラメータ

**Example OCI 関数の Java 接続文字列**  

```
// Define connection string
String connectionString = String.format("jdbc:mysql://%s:%s/%s?useSSL=true&requireSSL=true&sslCA=/us-east-1-bundle.pem", // Path to the certificate in container
        System.getenv("ProxyHostName"),
        System.getenv("Port"),
        System.getenv("DBName"));
```

------
#### [ .NET ]

**Example OCI 関数での MySQL 接続の .NET 接続文字列**  

```
/// Build the Connection String with the Token 
string connectionString = $"Server={Environment.GetEnvironmentVariable("RDS_ENDPOINT")};" +
                         $"Port={Environment.GetEnvironmentVariable("RDS_PORT")};" +
                         $"Uid={Environment.GetEnvironmentVariable("RDS_USERNAME")};" +
                         $"Pwd={authToken};" +
                         "SslMode=Required;" +
                         "SslCa=/us-east-1-bundle.pem";  // Path to the certificate in container
```

------
#### [ Go ]

MySQL 接続を使用する Go 関数の場合、Amazon RDS 証明書を証明書プールにロードし、MySQL ドライバーに登録します。次に、接続文字列は `tls` パラメータを使用してこの設定を参照する必要があります。

**Example OCI 関数での MySQL 接続の Go コード**  

```
import (
    "crypto/tls"
    "crypto/x509"
    "os"
    "github.com/go-sql-driver/mysql"
)

...

// Create certificate pool and register TLS config
rootCertPool := x509.NewCertPool()
pem, err := os.ReadFile("/us-east-1-bundle.pem")  // Path to the certificate in container
if err != nil {
    panic("failed to read certificate file: " + err.Error())
}
if ok := rootCertPool.AppendCertsFromPEM(pem); !ok {
    panic("failed to append PEM")
}

mysql.RegisterTLSConfig("custom", &tls.Config{
    RootCAs: rootCertPool,
})

dsn := fmt.Sprintf("%s:%s@tcp(%s)/%s?allowCleartextPasswords=true&tls=custom",
    dbUser, authenticationToken, dbEndpoint, dbName,
)
```

------
#### [ Ruby ]

**Example OCI 関数の Ruby 接続設定**  

```
conn = Mysql2::Client.new(
    host: endpoint,
    username: user,
    password: token,
    port: port,
    database: db_name,
    sslca: '/us-east-1-bundle.pem',  # Path to the certificate in container
    sslverify: true
)
```

------

## Lambda 関数での Amazon RDS データベースへの接続
<a name="rds-connection"></a>

次のコード例は、Amazon RDS データベースに接続する Lambda 関数を実装する方法を示しています。この関数は、シンプルなデータベースリクエストを実行し、結果を返します。

**注記**  
これらのコード例は、[.zip デプロイパッケージ](configuration-function-zip.md)でのみ有効です。[コンテナイメージ](images-create.md)を使用して関数をデプロイする場合は、[前のセクション](#oci-certificate)で説明したように、関数コードで Amazon RDS 証明書ファイルを指定する必要があります。

------
#### [ .NET ]

**SDK for .NET**  
 GitHub には、その他のリソースもあります。[サーバーレスサンプル](https://github.com/aws-samples/serverless-snippets/tree/main/lambda-function-connect-rds-iam)リポジトリで完全な例を見つけて、設定と実行の方法を確認してください。
.NET を使用した Lambda 関数での Amazon RDS データベースへの接続。  

```
using System.Data;
using System.Text.Json;
using Amazon.Lambda.APIGatewayEvents;
using Amazon.Lambda.Core;
using MySql.Data.MySqlClient;

// Assembly attribute to enable the Lambda function's JSON input to be converted into a .NET class.
[assembly: LambdaSerializer(typeof(Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer))]

namespace aws_rds;

public class InputModel
{
    public string key1 { get; set; }
    public string key2 { get; set; }
}

public class Function
{
    /// <summary>
    // Handles the Lambda function execution for connecting to RDS using IAM authentication.
    /// </summary>
    /// <param name="input">The input event data passed to the Lambda function</param>
    /// <param name="context">The Lambda execution context that provides runtime information</param>
    /// <returns>A response object containing the execution result</returns>

    public async Task<APIGatewayProxyResponse> FunctionHandler(APIGatewayProxyRequest request, ILambdaContext context)
    {
        // Sample Input: {"body": "{\"key1\":\"20\", \"key2\":\"25\"}"}
        var input = JsonSerializer.Deserialize<InputModel>(request.Body);

        /// Obtain authentication token
        var authToken = RDSAuthTokenGenerator.GenerateAuthToken(
            Environment.GetEnvironmentVariable("RDS_ENDPOINT"),
            Convert.ToInt32(Environment.GetEnvironmentVariable("RDS_PORT")),
            Environment.GetEnvironmentVariable("RDS_USERNAME")
        );

        /// Build the Connection String with the Token 
        string connectionString = $"Server={Environment.GetEnvironmentVariable("RDS_ENDPOINT")};" +
                                  $"Port={Environment.GetEnvironmentVariable("RDS_PORT")};" +
                                  $"Uid={Environment.GetEnvironmentVariable("RDS_USERNAME")};" +
                                  $"Pwd={authToken};";


        try
        {
            await using var connection = new MySqlConnection(connectionString);
            await connection.OpenAsync();

            const string sql = "SELECT @param1 + @param2 AS Sum";

            await using var command = new MySqlCommand(sql, connection);
            command.Parameters.AddWithValue("@param1", int.Parse(input.key1 ?? "0"));
            command.Parameters.AddWithValue("@param2", int.Parse(input.key2 ?? "0"));

            await using var reader = await command.ExecuteReaderAsync();
            if (await reader.ReadAsync())
            {
                int result = reader.GetInt32("Sum");

                //Sample Response: {"statusCode":200,"body":"{\"message\":\"The sum is: 45\"}","isBase64Encoded":false}
                return new APIGatewayProxyResponse
                {
                    StatusCode = 200,
                    Body = JsonSerializer.Serialize(new { message = $"The sum is: {result}" })
                };
            }

        }
        catch (Exception ex)
        {
            Console.WriteLine($"Error: {ex.Message}");
        }

        return new APIGatewayProxyResponse
        {
            StatusCode = 500,
            Body = JsonSerializer.Serialize(new { error = "Internal server error" })
        };
    }
}
```

------
#### [ Go ]

**SDK for Go V2**  
 GitHub には、その他のリソースもあります。[サーバーレスサンプル](https://github.com/aws-samples/serverless-snippets/tree/main/lambda-function-connect-rds-iam)リポジトリで完全な例を見つけて、設定と実行の方法を確認してください。
Go を使用した Lambda 関数での Amazon RDS データベースへの接続  

```
/*
Golang v2 code here.
*/

package main

import (
	"context"
	"database/sql"
	"encoding/json"
	"fmt"
	"os"

	"github.com/aws/aws-lambda-go/lambda"
	"github.com/aws/aws-sdk-go-v2/config"
	"github.com/aws/aws-sdk-go-v2/feature/rds/auth"
	_ "github.com/go-sql-driver/mysql"
)

type MyEvent struct {
	Name string `json:"name"`
}

func HandleRequest(event *MyEvent) (map[string]interface{}, error) {

	var dbName string = os.Getenv("DatabaseName")
	var dbUser string = os.Getenv("DatabaseUser")
	var dbHost string = os.Getenv("DBHost") // Add hostname without https
	var dbPort int = os.Getenv("Port")      // Add port number
	var dbEndpoint string = fmt.Sprintf("%s:%d", dbHost, dbPort)
	var region string = os.Getenv("AWS_REGION")

	cfg, err := config.LoadDefaultConfig(context.TODO())
	if err != nil {
		panic("configuration error: " + err.Error())
	}

	authenticationToken, err := auth.BuildAuthToken(
		context.TODO(), dbEndpoint, region, dbUser, cfg.Credentials)
	if err != nil {
		panic("failed to create authentication token: " + err.Error())
	}

	dsn := fmt.Sprintf("%s:%s@tcp(%s)/%s?tls=true&allowCleartextPasswords=true",
		dbUser, authenticationToken, dbEndpoint, dbName,
	)

	db, err := sql.Open("mysql", dsn)
	if err != nil {
		panic(err)
	}

	defer db.Close()

	var sum int
	err = db.QueryRow("SELECT ?+? AS sum", 3, 2).Scan(&sum)
	if err != nil {
		panic(err)
	}
	s := fmt.Sprint(sum)
	message := fmt.Sprintf("The selected sum is: %s", s)

	messageBytes, err := json.Marshal(message)
	if err != nil {
		return nil, err
	}

	messageString := string(messageBytes)
	return map[string]interface{}{
		"statusCode": 200,
		"headers":    map[string]string{"Content-Type": "application/json"},
		"body":       messageString,
	}, nil
}

func main() {
	lambda.Start(HandleRequest)
}
```

------
#### [ Java ]

**SDK for Java 2.x**  
 GitHub には、その他のリソースもあります。[サーバーレスサンプル](https://github.com/aws-samples/serverless-snippets/tree/main/lambda-function-connect-rds-iam)リポジトリで完全な例を見つけて、設定と実行の方法を確認してください。
Java を使用した Lambda 関数での Amazon RDS データベースへの接続  

```
import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.RequestHandler;
import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent;
import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent;
import software.amazon.awssdk.auth.credentials.DefaultCredentialsProvider;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.rdsdata.RdsDataClient;
import software.amazon.awssdk.services.rdsdata.model.ExecuteStatementRequest;
import software.amazon.awssdk.services.rdsdata.model.ExecuteStatementResponse;
import software.amazon.awssdk.services.rdsdata.model.Field;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;

public class RdsLambdaHandler implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> {

    @Override
    public APIGatewayProxyResponseEvent handleRequest(APIGatewayProxyRequestEvent event, Context context) {
        APIGatewayProxyResponseEvent response = new APIGatewayProxyResponseEvent();

        try {
            // Obtain auth token
            String token = createAuthToken();

            // Define connection configuration
            String connectionString = String.format("jdbc:mysql://%s:%s/%s?useSSL=true&requireSSL=true",
                    System.getenv("ProxyHostName"),
                    System.getenv("Port"),
                    System.getenv("DBName"));

            // Establish a connection to the database
            try (Connection connection = DriverManager.getConnection(connectionString, System.getenv("DBUserName"), token);
                 PreparedStatement statement = connection.prepareStatement("SELECT ? + ? AS sum")) {

                statement.setInt(1, 3);
                statement.setInt(2, 2);

                try (ResultSet resultSet = statement.executeQuery()) {
                    if (resultSet.next()) {
                        int sum = resultSet.getInt("sum");
                        response.setStatusCode(200);
                        response.setBody("The selected sum is: " + sum);
                    }
                }
            }

        } catch (Exception e) {
            response.setStatusCode(500);
            response.setBody("Error: " + e.getMessage());
        }

        return response;
    }

    private String createAuthToken() {
        // Create RDS Data Service client
        RdsDataClient rdsDataClient = RdsDataClient.builder()
                .region(Region.of(System.getenv("AWS_REGION")))
                .credentialsProvider(DefaultCredentialsProvider.create())
                .build();

        // Define authentication request
        ExecuteStatementRequest request = ExecuteStatementRequest.builder()
                .resourceArn(System.getenv("ProxyHostName"))
                .secretArn(System.getenv("DBUserName"))
                .database(System.getenv("DBName"))
                .sql("SELECT 'RDS IAM Authentication'")
                .build();

        // Execute request and obtain authentication token
        ExecuteStatementResponse response = rdsDataClient.executeStatement(request);
        Field tokenField = response.records().get(0).get(0);

        return tokenField.stringValue();
    }
}
```

------
#### [ JavaScript ]

**SDK for JavaScript (v3)**  
 GitHub には、その他のリソースもあります。[サーバーレスサンプル](https://github.com/aws-samples/serverless-snippets/tree/main/lambda-function-connect-rds-iam)リポジトリで完全な例を見つけて、設定と実行の方法を確認してください。
JavaScript を使用した Lambda 関数での Amazon RDS データベースへの接続  

```
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
/* 
Node.js code here.
*/
// ES6+ example
import { Signer } from "@aws-sdk/rds-signer";
import mysql from 'mysql2/promise';

async function createAuthToken() {
  // Define connection authentication parameters
  const dbinfo = {

    hostname: process.env.ProxyHostName,
    port: process.env.Port,
    username: process.env.DBUserName,
    region: process.env.AWS_REGION,

  }

  // Create RDS Signer object
  const signer = new Signer(dbinfo);

  // Request authorization token from RDS, specifying the username
  const token = await signer.getAuthToken();
  return token;
}

async function dbOps() {

  // Obtain auth token
  const token = await createAuthToken();
  // Define connection configuration
  let connectionConfig = {
    host: process.env.ProxyHostName,
    user: process.env.DBUserName,
    password: token,
    database: process.env.DBName,
    ssl: 'Amazon RDS'
  }
  // Create the connection to the DB
  const conn = await mysql.createConnection(connectionConfig);
  // Obtain the result of the query
  const [res,] = await conn.execute('select ?+? as sum', [3, 2]);
  return res;

}

export const handler = async (event) => {
  // Execute database flow
  const result = await dbOps();
  // Return result
  return {
    statusCode: 200,
    body: JSON.stringify("The selected sum is: " + result[0].sum)
  }
};
```
TypeScript を使用した Lambda 関数での Amazon RDS データベースへの接続  

```
import { Signer } from "@aws-sdk/rds-signer";
import mysql from 'mysql2/promise';

// RDS settings
// Using '!' (non-null assertion operator) to tell the TypeScript compiler that the DB settings are not null or undefined,
const proxy_host_name = process.env.PROXY_HOST_NAME!
const port = parseInt(process.env.PORT!)
const db_name = process.env.DB_NAME!
const db_user_name = process.env.DB_USER_NAME!
const aws_region = process.env.AWS_REGION!


async function createAuthToken(): Promise<string> {

    // Create RDS Signer object
    const signer = new Signer({
        hostname: proxy_host_name,
        port: port,
        region: aws_region,
        username: db_user_name
    });

    // Request authorization token from RDS, specifying the username
    const token = await signer.getAuthToken();
    return token;
}

async function dbOps(): Promise<mysql.QueryResult | undefined> {
    try {
        // Obtain auth token
        const token = await createAuthToken();
        const conn = await mysql.createConnection({
            host: proxy_host_name,
            user: db_user_name,
            password: token,
            database: db_name,
            ssl: 'Amazon RDS' // Ensure you have the CA bundle for SSL connection
        });
        const [rows, fields] = await conn.execute('SELECT ? + ? AS sum', [3, 2]);
        console.log('result:', rows);
        return rows;
    }
    catch (err) {
        console.log(err);
    }
}

export const lambdaHandler = async (event: any): Promise<{ statusCode: number; body: string }> => {
    // Execute database flow
    const result = await dbOps();

    // Return error is result is undefined
    if (result == undefined)
        return {
            statusCode: 500,
            body: JSON.stringify(`Error with connection to DB host`)
        }

    // Return result
    return {
        statusCode: 200,
        body: JSON.stringify(`The selected sum is: ${result[0].sum}`)
    };
};
```

------
#### [ PHP ]

**SDK for PHP**  
 GitHub には、その他のリソースもあります。[サーバーレスサンプル](https://github.com/aws-samples/serverless-snippets/tree/main/lambda-function-connect-rds-iam)リポジトリで完全な例を見つけて、設定と実行の方法を確認してください。
PHP を使用した Lambda 関数での Amazon RDS データベースへの接続  

```
<?php
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: Apache-2.0

# using bref/bref and bref/logger for simplicity

use Bref\Context\Context;
use Bref\Event\Handler as StdHandler;
use Bref\Logger\StderrLogger;
use Aws\Rds\AuthTokenGenerator;
use Aws\Credentials\CredentialProvider;

require __DIR__ . '/vendor/autoload.php';

class Handler implements StdHandler
{
    private StderrLogger $logger;
    public function __construct(StderrLogger $logger)
    {
        $this->logger = $logger;
    }


    private function getAuthToken(): string {
        // Define connection authentication parameters
        $dbConnection = [
            'hostname' => getenv('DB_HOSTNAME'),
            'port' => getenv('DB_PORT'),
            'username' => getenv('DB_USERNAME'),
            'region' => getenv('AWS_REGION'),
        ];

        // Create RDS AuthTokenGenerator object
        $generator = new AuthTokenGenerator(CredentialProvider::defaultProvider());

        // Request authorization token from RDS, specifying the username
        return $generator->createToken(
            $dbConnection['hostname'] . ':' . $dbConnection['port'],
            $dbConnection['region'],
            $dbConnection['username']
        );
    }

    private function getQueryResults() {
        // Obtain auth token
        $token = $this->getAuthToken();

        // Define connection configuration
        $connectionConfig = [
            'host' => getenv('DB_HOSTNAME'),
            'user' => getenv('DB_USERNAME'),
            'password' => $token,
            'database' => getenv('DB_NAME'),
        ];

        // Create the connection to the DB
        $conn = new PDO(
            "mysql:host={$connectionConfig['host']};dbname={$connectionConfig['database']}",
            $connectionConfig['user'],
            $connectionConfig['password'],
            [
                PDO::MYSQL_ATTR_SSL_CA => '/path/to/rds-ca-2019-root.pem',
                PDO::MYSQL_ATTR_SSL_VERIFY_SERVER_CERT => true,
            ]
        );

        // Obtain the result of the query
        $stmt = $conn->prepare('SELECT ?+? AS sum');
        $stmt->execute([3, 2]);

        return $stmt->fetch(PDO::FETCH_ASSOC);
    }

    /**
     * @param mixed $event
     * @param Context $context
     * @return array
     */
    public function handle(mixed $event, Context $context): array
    {
        $this->logger->info("Processing query");

        // Execute database flow
        $result = $this->getQueryResults();

        return [
            'sum' => $result['sum']
        ];
    }
}

$logger = new StderrLogger();
return new Handler($logger);
```

------
#### [ Python ]

**SDK for Python (Boto3)**  
 GitHub には、その他のリソースもあります。[サーバーレスサンプル](https://github.com/aws-samples/serverless-snippets/tree/main/lambda-function-connect-rds-iam)リポジトリで完全な例を見つけて、設定と実行の方法を確認してください。
Python を使用した Lambda 関数での Amazon RDS データベースへの接続  

```
import json
import os
import boto3
import pymysql

# RDS settings
proxy_host_name = os.environ['PROXY_HOST_NAME']
port = int(os.environ['PORT'])
db_name = os.environ['DB_NAME']
db_user_name = os.environ['DB_USER_NAME']
aws_region = os.environ['AWS_REGION']


# Fetch RDS Auth Token
def get_auth_token():
    client = boto3.client('rds')
    token = client.generate_db_auth_token(
        DBHostname=proxy_host_name,
        Port=port
        DBUsername=db_user_name
        Region=aws_region
    )
    return token

def lambda_handler(event, context):
    token = get_auth_token()
    try:
        connection = pymysql.connect(
            host=proxy_host_name,
            user=db_user_name,
            password=token,
            db=db_name,
            port=port,
            ssl={'ca': 'Amazon RDS'}  # Ensure you have the CA bundle for SSL connection
        )
        
        with connection.cursor() as cursor:
            cursor.execute('SELECT %s + %s AS sum', (3, 2))
            result = cursor.fetchone()

        return result
        
    except Exception as e:
        return (f"Error: {str(e)}")  # Return an error message if an exception occurs
```

------
#### [ Ruby ]

**SDK for Ruby**  
 GitHub には、その他のリソースもあります。[サーバーレスサンプル](https://github.com/aws-samples/serverless-snippets/tree/main/lambda-function-connect-rds-iam)リポジトリで完全な例を見つけて、設定と実行の方法を確認してください。
Ruby を使用した Lambda 関数での Amazon RDS データベースへの接続  

```
# Ruby code here.

require 'aws-sdk-rds'
require 'json'
require 'mysql2'

def lambda_handler(event:, context:)
  endpoint = ENV['DBEndpoint'] # Add the endpoint without https"
  port = ENV['Port']           # 3306
  user = ENV['DBUser']
  region = ENV['DBRegion']     # 'us-east-1'
  db_name = ENV['DBName']

  credentials = Aws::Credentials.new(
    ENV['AWS_ACCESS_KEY_ID'],
    ENV['AWS_SECRET_ACCESS_KEY'],
    ENV['AWS_SESSION_TOKEN']
  )
  rds_client = Aws::RDS::AuthTokenGenerator.new(
    region: region, 
    credentials: credentials
  )

  token = rds_client.auth_token(
    endpoint: endpoint+ ':' + port,
    user_name: user,
    region: region
  )

  begin
    conn = Mysql2::Client.new(
      host: endpoint,
      username: user,
      password: token,
      port: port,
      database: db_name,
      sslca: '/var/task/global-bundle.pem', 
      sslverify: true,
      enable_cleartext_plugin: true
    )
    a = 3
    b = 2
    result = conn.query("SELECT #{a} + #{b} AS sum").first['sum']
    puts result
    conn.close
    {
      statusCode: 200,
      body: result.to_json
    }
  rescue => e
    puts "Database connection failed due to #{e}"
  end
end
```

------
#### [ Rust ]

**SDK for Rust**  
 GitHub には、その他のリソースもあります。[サーバーレスサンプル](https://github.com/aws-samples/serverless-snippets/tree/main/lambda-function-connect-rds-iam)リポジトリで完全な例を見つけて、設定と実行の方法を確認してください。
Rust を使用した Lambda 関数での Amazon RDS データベースへの接続  

```
use aws_config::BehaviorVersion;
use aws_credential_types::provider::ProvideCredentials;
use aws_sigv4::{
    http_request::{sign, SignableBody, SignableRequest, SigningSettings},
    sign::v4,
};
use lambda_runtime::{run, service_fn, Error, LambdaEvent};
use serde_json::{json, Value};
use sqlx::postgres::PgConnectOptions;
use std::env;
use std::time::{Duration, SystemTime};

const RDS_CERTS: &[u8] = include_bytes!("global-bundle.pem");

async fn generate_rds_iam_token(
    db_hostname: &str,
    port: u16,
    db_username: &str,
) -> Result<String, Error> {
    let config = aws_config::load_defaults(BehaviorVersion::v2024_03_28()).await;

    let credentials = config
        .credentials_provider()
        .expect("no credentials provider found")
        .provide_credentials()
        .await
        .expect("unable to load credentials");
    let identity = credentials.into();
    let region = config.region().unwrap().to_string();

    let mut signing_settings = SigningSettings::default();
    signing_settings.expires_in = Some(Duration::from_secs(900));
    signing_settings.signature_location = aws_sigv4::http_request::SignatureLocation::QueryParams;

    let signing_params = v4::SigningParams::builder()
        .identity(&identity)
        .region(&region)
        .name("rds-db")
        .time(SystemTime::now())
        .settings(signing_settings)
        .build()?;

    let url = format!(
        "https://{db_hostname}:{port}/?Action=connect&DBUser={db_user}",
        db_hostname = db_hostname,
        port = port,
        db_user = db_username
    );

    let signable_request =
        SignableRequest::new("GET", &url, std::iter::empty(), SignableBody::Bytes(&[]))
            .expect("signable request");

    let (signing_instructions, _signature) =
        sign(signable_request, &signing_params.into())?.into_parts();

    let mut url = url::Url::parse(&url).unwrap();
    for (name, value) in signing_instructions.params() {
        url.query_pairs_mut().append_pair(name, &value);
    }

    let response = url.to_string().split_off("https://".len());

    Ok(response)
}

#[tokio::main]
async fn main() -> Result<(), Error> {
    run(service_fn(handler)).await
}

async fn handler(_event: LambdaEvent<Value>) -> Result<Value, Error> {
    let db_host = env::var("DB_HOSTNAME").expect("DB_HOSTNAME must be set");
    let db_port = env::var("DB_PORT")
        .expect("DB_PORT must be set")
        .parse::<u16>()
        .expect("PORT must be a valid number");
    let db_name = env::var("DB_NAME").expect("DB_NAME must be set");
    let db_user_name = env::var("DB_USERNAME").expect("DB_USERNAME must be set");

    let token = generate_rds_iam_token(&db_host, db_port, &db_user_name).await?;

    let opts = PgConnectOptions::new()
        .host(&db_host)
        .port(db_port)
        .username(&db_user_name)
        .password(&token)
        .database(&db_name)
        .ssl_root_cert_from_pem(RDS_CERTS.to_vec())
        .ssl_mode(sqlx::postgres::PgSslMode::Require);

    let pool = sqlx::postgres::PgPoolOptions::new()
        .connect_with(opts)
        .await?;

    let result: i32 = sqlx::query_scalar("SELECT $1 + $2")
        .bind(3)
        .bind(2)
        .fetch_one(&pool)
        .await?;

    println!("Result: {:?}", result);

    Ok(json!({
        "statusCode": 200,
        "content-type": "text/plain",
        "body": format!("The selected sum is: {result}")
    }))
}
```

------

## Amazon RDS からのイベント通知を処理する
<a name="rds-events"></a>

Lambda を使用して、Amazon RDS データベースからのイベント通知を処理できます。Amazon RDS は、Amazon Simple Notification Service (Amazon SNS) トピックに通知を送信します。このトピックは Lambda 関数を呼び出すように設定することができます。Amazon SNS は、Amazon RDS からのメッセージを独自のイベントドキュメントにラップし、これを関数に送信します。

通知を送信するように Amazon RDS データベースを設定する方法の詳細については、「[Using Amazon RDS event notifications](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/USER_Events.html)」を参照してください。

**Example Amazon SNS イベントでの Amazon RDS メッセージ**  

```
{
        "Records": [
          {
            "EventVersion": "1.0",
            "EventSubscriptionArn": "arn:aws:sns:us-east-2:123456789012:rds-lambda:21be56ed-a058-49f5-8c98-aedd2564c486",
            "EventSource": "aws:sns",
            "Sns": {
              "SignatureVersion": "1",
              "Timestamp": "2023-01-02T12:45:07.000Z",
              "Signature": "tcc6faL2yUC6dgZdmrwh1Y4cGa/ebXEkAi6RibDsvpi+tE/1+82j...65r==",
              "SigningCertUrl": "https://sns.us-east-2.amazonaws.com/SimpleNotificationService-ac565b8b1a6c5d002d285f9598aa1d9b.pem",
              "MessageId": "95df01b4-ee98-5cb9-9903-4c221d41eb5e",
              "Message": "{\"Event Source\":\"db-instance\",\"Event Time\":\"2023-01-02 12:45:06.000\",\"Identifier Link\":\"https://console.aws.amazon.com/rds/home?region=eu-west-1#dbinstance:id=dbinstanceid\",\"Source ID\":\"dbinstanceid\",\"Event ID\":\"http://docs.amazonwebservices.com/AmazonRDS/latest/UserGuide/USER_Events.html#RDS-EVENT-0002\",\"Event Message\":\"Finished DB Instance backup\"}",
              "MessageAttributes": {},
              "Type": "Notification",
              "UnsubscribeUrl": "https://sns.us-east-2.amazonaws.com/?Action=Unsubscribe&amp;SubscriptionArn=arn:aws:sns:us-east-2:123456789012:test-lambda:21be56ed-a058-49f5-8c98-aedd2564c486",
              "TopicArn":"arn:aws:sns:us-east-2:123456789012:sns-lambda",
              "Subject": "RDS Notification Message"
            }
          }
        ]
      }
```

## Lambda と Amazon RDS の完全なチュートリアル
<a name="rds-database-samples"></a>
+ [Lambda 関数を使用して Amazon RDS データベースにアクセスする](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/rds-lambda-tutorial.html) – Amazon RDS ユーザーガイドで、Lambda 関数を使用して、Amazon RDS Proxy 経由で Amazon RDS データベースにデータを書き込む方法を学びます。Lambda 関数は、メッセージが追加されるたびに Amazon SQS キューからレコードを読み取り、データベース内のテーブルに新しい項目を書き込みます。

# Lambda ベースのアプリケーションのデータベースサービスを選択する
<a name="ddb-rds-database-decision"></a>

多くのサーバーレスアプリケーションは、データを保存および取得する必要があります。AWS には、Lambda 関数で動作する複数のデータベースオプションが用意されています。最も一般的な選択肢は、NoSQL データベースサービスである Amazon DynamoDB と、従来のリレーショナルデータベースソリューションである Amazon RDS の 2 つです。以下のセクションでは、Lambda で使用する際のこれらのサービスの主な違いについて説明します。これは、サーバーレスアプリケーションに適したデータベースサービスを選択するのに役立ちます。

AWS が提供する他のデータベースサービスの詳細を確認し、そのユースケースとトレードオフをより一般的に理解するには、「[Choosing an AWS database service](https://docs.aws.amazon.com/decision-guides/latest/databases-on-aws-how-to-choose/databases-on-aws-how-to-choose.html)」を参照してください。すべての AWS データベースサービスは Lambda と互換性がありますが、すべてのサービスが特定のユースケースに適しているとは限りません。

## Lambda を使用するデータベースサービスを選択する場合、どのような選択肢がありますか。
<a name="w2aad101d101c19b9"></a>

AWS は、複数のデータベースサービスを提供しています。サーバーレスアプリケーションの場合、最も一般的な選択肢は DynamoDB と Amazon RDS の 2 つです。
+ **DynamoDB** は、サーバーレスアプリケーション用に最適化されたフルマネージド NoSQL データベースサービスです。シームレスなスケーリングと 1 桁ミリ秒の安定したパフォーマンスをあらゆる規模で提供します。
+ **Amazon RDS** は、MySQL や PostgreSQL などの複数のデータベースエンジンをサポートするマネージドリレーショナルデータベースサービスです。マネージドインフラストラクチャで使い慣れた SQL 機能を提供します。

## 要件が既にわかっている場合の推奨事項
<a name="w2aad101d101c19c11"></a>

要件が既に明確な場合は、基本的な推奨事項を以下に示します。

一貫した低レイテンシーのパフォーマンスや自動スケーリングが必要で、複雑な結合やトランザクションが不要なサーバーレスアプリケーションには、[DynamoDB](with-ddb.md) をお勧めします。サーバーレスであるため、特に Lambda ベースのアプリケーションに適しています。

[Amazon RDS](services-rds.md) は、複雑な SQL クエリや結合が必要な場合、またはリレーショナルデータベースを使用する既存のアプリケーションがある場合に適しています。ただし、Lambda 関数を Amazon RDS に接続するには追加の設定が必要であり、コールドスタート時間に影響する可能性があることに注意してください。

## データベースサービスを選択する際の考慮事項
<a name="w2aad101d101c19c13"></a>

Lambda アプリケーション用に DynamoDB または Amazon RDS を選択するときは、次の要素を考慮してください。
+ 接続管理とコールドスタート
+ データアクセスパターン
+ クエリの複雑さ
+ データ整合性の要件
+ スケーリング特性
+ コストモデル

これらの要因を理解することで、特定のユースケースのニーズに最適なオプションを選択できます。

### 接続管理とコールドスタート
<a name="w2aad101d101c19c13b9b1"></a>
+ DynamoDB は、すべてのオペレーションに HTTP API を使用します。Lambda 関数は、接続を維持せずに即時リクエストを行うことができ、コールドスタートのパフォーマンスが向上します。各リクエストは、接続オーバーヘッドなしで AWS 認証情報を使用して認証されます。
+ Amazon RDS では、従来のデータベース接続を使用するため、接続プールの管理が必要です。新しい Lambda インスタンスが接続を確立する必要があるため、コールドスタートに影響する可能性があります。接続プーリング戦略を実装し、場合によっては [Amazon RDS Proxy](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/rds-proxy.html) を使用して接続を効果的に管理する必要があります。Amazon RDS Proxy を使用すると追加料金が発生することに注意してください。

### データアクセスパターン
<a name="w2aad101d101c19c13b9b3"></a>
+ DynamoDB は、既知のアクセスパターンと単一テーブルの設計に最適です。これは、プライマリキーまたはセカンダリインデックスに基づいたデータへの一貫した低レイテンシーアクセスを必要とする Lambda アプリケーションに最適です。
+ Amazon RDS は、複雑なクエリやアクセスパターンの変更に柔軟に対応します。Lambda 関数が複数のテーブルに対して一意のカスタマイズされたクエリまたは複雑な結合を実行する必要がある場合に適しています。

### クエリの複雑さ
<a name="w2aad101d101c19c13b9b5"></a>
+ DynamoDB は、シンプルなキーベースのオペレーションや事前定義されたアクセスパターンにおいて優れた力を発揮します。複雑なクエリはインデックス構造を中心に設計する必要があり、結合はアプリケーションコードで処理する必要があります。
+ Amazon RDS は、結合、サブクエリ、集計を含む複雑な SQL クエリをサポートします。これにより、複雑なデータオペレーションが必要な場合に Lambda 関数コードを簡素化できます。

### データ整合性の要件
<a name="w2aad101d101c19c13b9b7"></a>
+ DynamoDB は、結果整合性オプションと強力な整合性オプションの両方を提供し、1 つの項目の読み取りで強力な整合性を利用できます。トランザクションはサポートされていますが、いくつかの制限があります。
+ Amazon RDS は、完全なアトミック性、一貫性、分離性、耐久性 (ACID) コンプライアンスと複雑なトランザクションのサポートを提供します。Lambda 関数で複雑なトランザクションが必要な場合や、複数のレコード間で強力な整合性が必要な場合は、Amazon RDS の方が適していることがあります。

### スケーリング特性
<a name="w2aad101d101c19c13b9b9"></a>
+ DynamoDB はワークロードに合わせて自動的にスケーリングされます。事前プロビジョニングなしで Lambda 関数からのトラフィックの急増に対処できます。オンデマンドキャパシティモードを使用すると、Lambda のスケーリングモデルと完全に一致させて、使用した分のみの支払いにできます。
+ Amazon RDS には、選択したインスタンスサイズに基づいた固定容量があります。複数の Lambda 関数が同時に接続しようとすると、接続クォータを超える可能性があります。接続プールを慎重に管理し、場合によっては再試行ロジックを実装する必要があります。

### コストモデル
<a name="w2aad101d101c19c13b9c11"></a>
+ DynamoDB の料金体系は、サーバーレスアプリケーションに適しています。オンデマンド容量では、Lambda 関数によって実行された実際の読み取りと書き込みに対してのみ料金が発生します。アイドル時間には料金はかかりません。
+ Amazon RDS では、使用状況に関係なく、実行中のインスタンスに対して課金されます。これは、サーバーレスアプリケーションで一般的な散発的なワークロードではコスト効率が低下する可能性があります。ただし、使用量が一貫している高スループットのワークロードでは、より経済的になる可能性があります。

## 選択したデータベースサービスの開始方法
<a name="w2aad101d101c19c15"></a>

DynamoDB と Amazon RDS の選択基準とその主な違いについて確認した後は、ニーズに最適なオプションを選択し、以下のリソースを使用して利用を開始できます。

------
#### [ DynamoDB ]

**以下のリソースを使用して DynamoDB の使用を開始する**
+ DynamoDB サービスの概要については、「*Amazon DynamoDB デベロッパーガイド*」の「[DynamoDB とは](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Introduction.html)」を参照してください。
+ 「[チュートリアル: API Gateway で Lambda を使用する](services-apigateway-tutorial.md)」に従って、Lambda 関数を使用して、API リクエストに応答して DynamoDB テーブルで CRUD オペレーションを実行する例を確認してください。
+ AWS SDK のいずれかを使用して Lambda 関数内から DynamoDB にアクセスする方法の詳細については、「*Amazon DynamoDB デベロッパーガイド*」の「[DynamoDB と AWS SDK を使用したプログラミング](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Programming.html)」を参照してください。

------
#### [ Amazon RDS ]

**以下のリソースを使用して Amazon RDS の使用を開始する**
+ Amazon RDS サービスの概要については、「*Amazon Relational Database Service ユーザーガイド*」の「[Amazon Relational Database Service (Amazon RDS) とは](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/Welcome.html)」を参照してください。
+ 「*Amazon Relational Database Service ユーザーガイド*」の「[チュートリアル: Lambda 関数を使用して Amazon RDS にアクセスする](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/rds-lambda-tutorial.html)」に従ってください。
+ Amazon RDS での Lambda の使用の詳細については、「[Amazon RDS で AWS Lambda 使用する](services-rds.md)」を参照してください。

------