Pythonで書いたシンプルなスクリプトをTerraformを用いてAWS Lambdaにデプ ロイする方法を示す。

Terraformの設定

AWS Lambdaで用いるPythonスクリプトのアーカイブ

ここが一番気になるところだろう。シンプルなPythonスクリプトの場合、わざ わざリポジトリを分割したくないしパッケージングもしたくない。できれば Terraformの構成と同様にPythonスクリプトも管理したい。Terraformはこの要 求にももちろん答えることができる。Terraformの archive_file pluginを 用いてTerraformの適応と同時にZIPファイルとしてアーカイブを作成し、それ を用いてAWS Lambdaへの展開を行う。

以下では example_lambda_functions 配下に "example.py" というファイル を予め配置している。このファイルがAWS Lambdaに展開するPythonスクリプト になる。 source_file でアーカイブに含めるファイルを指定している。 output_path で出力するアーカイブのパスを指定している。ここには Terraformの適応毎にアーカイブが出力されるため、Gitに登録しないように .gitignore に指定する。

data "archive_file" "example_lambda_functions_zip" {
  type        = "zip"
  source_file = "${path.module}/example_lambda_functions/example.py"
  output_path = "${path.module}/files/example_lambda_functions.zip"
}

AWS Lambdaで用いるIAM Roleの定義

AWS Lambdaを定義する際にIAM Roleが必要になるので作成しておく。

resource "aws_iam_role" "iam_for_example_function" {
  name = "iam_for_example_function"

  assume_role_policy = <<EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Action": "sts:AssumeRole",
      "Principal": {
        "Service": "lambda.amazonaws.com"
      },
      "Effect": "Allow",
      "Sid": ""
    }
  ]
}
EOF
}

AWS Lambdaの定義

上記のリソースを用いてAWS Lambdaを定義する。

resource "aws_lambda_function" "example_function" {
  filename         = data.archive_file.example_lambda_functions_zip.output_path
  function_name    = "example"
  role             = aws_iam_role.iam_for_example_function.arn
  handler          = "example.lambda_handler"
  source_code_hash = data.archive_file.example_lambda_functions_zip.output_base64sha256
  runtime          = "python3.8"
  memory_size      = 128
  timeout          = 30
}

ローカルでのデプロイ

Terraformが呼び出すAWSのAPIは設定により変更できるため、AWSのAPIをエミュ レートするダミーサーバーに対して動作確認を行える。以下ではDockerを用い てlocalstackを起動し、それをダミーサーバーとして動作を確認する方法を示 す。

localstackを起動する

localstackとはAWSのAPIをエミュレートするAPIサーバーである。ローカル環 境での検証のために用いる。localstackの起動のためにDockerや docker-composeを用いることが必須というわけではないが、ここではそれを採 用する。

docker-compose.ymlを作成し、以下を記述する。

version: "3.8"

services:
  localstack:
    image: localstack/localstack:0.12.2
    ports:
      - "4566:4566"
      - "4569:4569"
    environment:
      - "DEFAULT_REGION=ap-northeast-1"
docker-compose.yml

ファイルを保存したらコンテナを起動する。

$ docker-compose up

ローカルで起動しているlocalstackに対してTerraformを行う際にはこのコン テナが起動していることが必要となる。起動したままの状態でコマンドを復帰 させたければ docker-compose up -d とするのが良いだろう。このコンテナ は4566番のTCPポートでリクエストを待ち受けているため、通常 http://localhost:4566 に対してリクエストを送信できる。

Terraformが呼び出すAWSのAPIエンドポイントをローカルに変更する

Terraformが呼び出したAWSのAPIがローカルに起動したlocalstackに送信されるように設定を変更する。

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

  access_key = "mock_access_key"
  secret_key = "mock_secret_key"

  s3_force_path_style         = true
  skip_credentials_validation = true
  skip_metadata_api_check     = true
  skip_requesting_account_id  = true

  endpoints {
    accessanalyzer         = "http://localhost:4566"
    acm                    = "http://localhost:4566"
    acmpca                 = "http://localhost:4566"
    amplify                = "http://localhost:4566"
    apigateway             = "http://localhost:4566"
    applicationautoscaling = "http://localhost:4566"
    applicationinsights    = "http://localhost:4566"
    appmesh                = "http://localhost:4566"
    appstream              = "http://localhost:4566"
    appsync                = "http://localhost:4566"
    athena                 = "http://localhost:4566"
    autoscaling            = "http://localhost:4566"
    autoscalingplans       = "http://localhost:4566"
    backup                 = "http://localhost:4566"
    batch                  = "http://localhost:4566"
    budgets                = "http://localhost:4566"
    cloud9                 = "http://localhost:4566"
    cloudformation         = "http://localhost:4566"
    cloudfront             = "http://localhost:4566"
    cloudhsm               = "http://localhost:4566"
    cloudsearch            = "http://localhost:4566"
    cloudtrail             = "http://localhost:4566"
    cloudwatch             = "http://localhost:4566"
    cloudwatchevents       = "http://localhost:4566"
    cloudwatchlogs         = "http://localhost:4566"
    codeartifact           = "http://localhost:4566"
    codebuild              = "http://localhost:4566"
    codecommit             = "http://localhost:4566"
    codedeploy             = "http://localhost:4566"
    codepipeline           = "http://localhost:4566"
    codestarconnections    = "http://localhost:4566"
    # codestarnotifications    = "http://localhost:4566"
    cognitoidentity    = "http://localhost:4566"
    cognitoidp         = "http://localhost:4566"
    configservice      = "http://localhost:4566"
    connect            = "http://localhost:4566"
    cur                = "http://localhost:4566"
    dataexchange       = "http://localhost:4566"
    datapipeline       = "http://localhost:4566"
    datasync           = "http://localhost:4566"
    dax                = "http://localhost:4566"
    devicefarm         = "http://localhost:4566"
    directconnect      = "http://localhost:4566"
    dlm                = "http://localhost:4566"
    dms                = "http://localhost:4566"
    docdb              = "http://localhost:4566"
    ds                 = "http://localhost:4566"
    dynamodb           = "http://localhost:4566"
    ec2                = "http://localhost:4566"
    ecr                = "http://localhost:4566"
    ecrpublic          = "http://localhost:4566"
    ecs                = "http://localhost:4566"
    efs                = "http://localhost:4566"
    eks                = "http://localhost:4566"
    elasticache        = "http://localhost:4566"
    elasticbeanstalk   = "http://localhost:4566"
    elastictranscoder  = "http://localhost:4566"
    elb                = "http://localhost:4566"
    emr                = "http://localhost:4566"
    emrcontainers      = "http://localhost:4566"
    es                 = "http://localhost:4566"
    firehose           = "http://localhost:4566"
    fms                = "http://localhost:4566"
    forecast           = "http://localhost:4566"
    fsx                = "http://localhost:4566"
    gamelift           = "http://localhost:4566"
    glacier            = "http://localhost:4566"
    globalaccelerator  = "http://localhost:4566"
    glue               = "http://localhost:4566"
    guardduty          = "http://localhost:4566"
    greengrass         = "http://localhost:4566"
    iam                = "http://localhost:4566"
    identitystore      = "http://localhost:4566"
    imagebuilder       = "http://localhost:4566"
    inspector          = "http://localhost:4566"
    iot                = "http://localhost:4566"
    iotanalytics       = "http://localhost:4566"
    iotevents          = "http://localhost:4566"
    kafka              = "http://localhost:4566"
    kinesis            = "http://localhost:4566"
    kinesisanalytics   = "http://localhost:4566"
    kinesisanalyticsv2 = "http://localhost:4566"
    kinesisvideo       = "http://localhost:4566"
    kms                = "http://localhost:4566"
    lakeformation      = "http://localhost:4566"
    lambda             = "http://localhost:4566"
    lexmodels          = "http://localhost:4566"
    licensemanager     = "http://localhost:4566"
    lightsail          = "http://localhost:4566"
    macie              = "http://localhost:4566"
    macie2             = "http://localhost:4566"
    managedblockchain  = "http://localhost:4566"
    marketplacecatalog = "http://localhost:4566"
    mediaconnect       = "http://localhost:4566"
    mediaconvert       = "http://localhost:4566"
    medialive          = "http://localhost:4566"
    mediapackage       = "http://localhost:4566"
    mediastore         = "http://localhost:4566"
    mediastoredata     = "http://localhost:4566"
    mq                 = "http://localhost:4566"
    mwaa               = "http://localhost:4566"
    neptune            = "http://localhost:4566"
    networkfirewall    = "http://localhost:4566"
    networkmanager     = "http://localhost:4566"
    opsworks           = "http://localhost:4566"
    organizations      = "http://localhost:4566"
    outposts           = "http://localhost:4566"
    personalize        = "http://localhost:4566"
    pinpoint           = "http://localhost:4566"
    pricing            = "http://localhost:4566"
    # prometheusservice        = "http://localhost:4566"
    qldb                     = "http://localhost:4566"
    quicksight               = "http://localhost:4566"
    ram                      = "http://localhost:4566"
    rds                      = "http://localhost:4566"
    redshift                 = "http://localhost:4566"
    resourcegroups           = "http://localhost:4566"
    resourcegroupstaggingapi = "http://localhost:4566"
    route53                  = "http://localhost:4566"
    route53domains           = "http://localhost:4566"
    route53resolver          = "http://localhost:4566"
    s3                       = "http://localhost:4566"
    s3control                = "http://localhost:4566"
    s3outposts               = "http://localhost:4566"
    sagemaker                = "http://localhost:4566"
    sdb                      = "http://localhost:4566"
    secretsmanager           = "http://localhost:4566"
    securityhub              = "http://localhost:4566"
    serverlessrepo           = "http://localhost:4566"
    servicecatalog           = "http://localhost:4566"
    servicediscovery         = "http://localhost:4566"
    servicequotas            = "http://localhost:4566"
    ses                      = "http://localhost:4566"
    shield                   = "http://localhost:4566"
    signer                   = "http://localhost:4566"
    sns                      = "http://localhost:4566"
    sqs                      = "http://localhost:4566"
    ssm                      = "http://localhost:4566"
    ssoadmin                 = "http://localhost:4566"
    stepfunctions            = "http://localhost:4566"
    storagegateway           = "http://localhost:4566"
    sts                      = "http://localhost:4566"
    swf                      = "http://localhost:4566"
    synthetics               = "http://localhost:4566"
    timestreamwrite          = "http://localhost:4566"
    transfer                 = "http://localhost:4566"
    waf                      = "http://localhost:4566"
    wafregional              = "http://localhost:4566"
    wafv2                    = "http://localhost:4566"
    worklink                 = "http://localhost:4566"
    workmail                 = "http://localhost:4566"
    workspaces               = "http://localhost:4566"
    xray                     = "http://localhost:4566"
  }
}

長くなったが必要な部分だけを設定すればよい。

Terraformの実行

実行方法は通常の方法と変わらない。 適応した際の変更内容を確認する場合には terraform plan を実行する。

$ terraform plan

実際に適応する場合には terraform apply を実行する。

$ terraform apply