NRIネットコム Blog

NRIネットコム社員が様々な視点で、日々の気づきやナレッジを発信するメディアです

JenkinsおじさんにSAMの使い方を教えてみた

はじめまして、西です。

入社 2 年目で、配属から現在まで、AWS 上に構築されたシステムの開発/運用を行なっています。 業務で触れる機会が多かったので、今回は AWS SAM を既存システムの CI/CD 環境へ組み込むまでの内容をご紹介します。 皆様の中に既存システムのサーバレス化などで AWS SAM の導入を検討されている方がおられましたら、参考になればと思います。

AWS SAM について

AWS SAM (以下 SAM) とは AWS Serverless Application Model のことで、AWS Lambda をベースとしたサーバレスアプリケーションを開発するためのフレームワークです。

docs.aws.amazon.com

Lambda だけでなく、API Gateway や Dynamo DB のようなサーバレスアプリケーションでよく利用されるリソース定義をシンプルに記述できること等が嬉しい点です。

SAM によって管理するサーバレスアプリケーションは、AWS SAM CLI (以下 SAM CLI) というツールでビルドからデプロイ、テスト用のローカル実行等が行えます。 Lambda が利用するアプリケーションファイルの保存場所には S3 と ECR が選択できますが、今回は template.yaml と併せて 1 箇所で管理できる S3 を採用した形を前提に話を進めます。

SAM 導入のユースケースと、実現すること

既存システムの一部機能などをサーバレス化する場合は、既に CI/CD 環境が整備されている所へ SAM 用のビルド/デプロイジョブを追加する形になるかと思います。 今回の記事ではそういったケースにフォーカスします。 CI/CD ツールには様々なものがありますが、今回は代表的なものの 1 つである Jenkins を想定した内容をご紹介できればと思います。

前提として、Jenkins にクレデンシャルを持たせてリソース作成/変更をデプロイする形になっていることを想定します。 今回実現する点として、Jenkins で SAM テンプレートをデプロイする場合は SAM 用の IAM ロールを利用させることを考えます。 最終的に目指す形は以下のようになります。図中の赤枠で囲っている範囲が今回追加する部分です。

f:id:nnc-y-nishi:20220302102430p:plain

では、必要な作業を順に考えていきます。

SAM 用の IAM Role を準備

Jenkins が利用しているクレデンシャルに SAM で作成/編集するリソースの操作権限を付与すればデプロイ自体はできます。

しかし、AWS には実行 Role という概念がありますので、適切に IAM 権限を管理するためにその仕組みを利用します。 SAM CLI でデプロイする際には実行 Role を指定するオプションが存在し、SAM 用に用意した IAM Role を指定することができます。 これにより、Jenkins に付与する権限を肥大化させることなくSAM に必要な権限だけを管理でき、SAM に関係しないリソースへの操作は制限することができます。

SAM は CloudFormation の拡張機能に相当するため、SAM についての操作を実行する主体は CloudFormation となります。 そのため、IAM Role は CloudFormation に渡す形になるのでポリシーの設定は以下のようになります。

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

作成した Role に、SAM で作成/変更するリソースについての権限を付与

SAM 用 Role には、SAM のテンプレートで作成/変更したいリソースについての権限を付与する必要があります。 図中の赤枠の部分が該当する箇所です。

f:id:nnc-y-nishi:20220301193428p:plain

サーバレスアプリケーションとなるため、Lambda の他、API Gateway や Dynamo DB などの権限が主になり、以下がその例となります。 IAM ロールやポリシーを定義する場合はiamの権限も別途必要になりますので、必要なリソースに応じて適宜権限の追加が必要です。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "AllowSamDeploy",
            "Effect": "Allow",
            "Action": [
                "apigateway:*",
                "cloudformation:*",
                "dynamodb:*",
                "iam:*",
                "lambda:*",
                "s3:*",
            ],
            "Resource": "*"
        }
    ]
}

Jenkins のクレデンシャルに PassRole 権限と S3/CloudFormation 権限を付与

Jenkins で利用する IAM ユーザー (クレデンシャル) に、SAM テンプレートのデプロイに必要な権限を付与する必要があります。 図中の赤枠の部分が該当する箇所です。

f:id:nnc-y-nishi:20220301193443p:plain

はじめに、CloudFormation に対して SAM 用 IAM Role の権限を渡す (PassRole) ための権限を付与する必要があります。 全体図では省略しましたが、デプロイ処理の流れは以下のようになります。

f:id:nnc-y-nishi:20220301172721p:plain

そのため、template.yaml やアプリケーションファイルを S3 にアップロードする権限や、CloudFormation スタックを操作する権限は IAM ユーザー側にも必要となります。 こちらも併せて権限を付与します。

デプロイ処理で必要な権限を付与するために、Jenkinsの IAM ユーザーに以下のようなポリシーを追記します。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "AllowPutStackTemplate",
            "Action": [
                "s3:CreateBucket",
                "s3:PutObject"
            ],
            "Effect": "Allow",
            "Resource": "*"
        },
        {
            "Sid": "AllowStackOperation",
            "Action": [
                "cloudformation:CreateChangeSet",
                "cloudformation:CreateStack",
                "cloudformation:DeleteChangeSet",
                "cloudformation:DeleteStack",
                "cloudformation:DescribeChangeSet",
                "cloudformation:DescribeStacks",
                "cloudformation:ExecuteChangeSet",
                "cloudformation:ValidateTemplate"
            ],
            "Effect": "Allow",
            "Resource": "*"
        },
        {
            "Sid": "AllowPassRole",
            "Action": [
                "iam:PassRole"
            ],
            "Effect": "Allow",
            "Resource": "arn:aws:iam::{AWSアカウントID}:role/{SAM用Role名}"
        }
    ]
}

SAM のデプロイについて

ここまでで、Jenkins で SAM テンプレートをデプロイする上で必要な IAM 関連の準備を行いました。 ここからはデプロイ処理に関する点について考えていきます。

S3 バケットを作成し、samconfig.toml を設定

この状態でSAM テンプレートをデプロイすることも可能なのですが、その場合はアプリケーションや template.yaml をアップロードする先の S3 バケット が自動で作成されます。 利用に支障はありませんが、バケット名にデフォルト名用の文字列とランダム文字列が入ってしまいます。

私は名称を決めて予めバケットを用意しておきたい党なので、事前にバケットを作成する前提で進めます。

S3 バケット を事前に作成する場合は、作成後に SAM テンプレートのディレクトリに samconfig.toml を用意して S3 バケットについての情報を設定しましょう。 以下のファイルは S3 バケット nnc-sam-backend-devとnnc-sam-backend-prod を用意した際の例となります。

version = 0.1
[dev.deploy.parameters]
stack_name = "nnc-y-nishi"
s3_bucket = "nnc-sam-backend-dev"
s3_prefix = "nnc-y-nishi-sam"
region = "ap-northeast-1"
confirm_changeset = false
capabilities = "CAPABILITY_NAMED_IAM"

[prod.deploy.parameters]
stack_name = "nnc-y-nishi"
s3_bucket = "nnc-sam-backend-prod"
s3_prefix = "nnc-y-nishi-sam"
region = "ap-northeast-1"
confirm_changeset = true
capabilities = "CAPABILITY_NAMED_IAM"
parameter_overrides = "PARAM=\"HOGE\""

設定を dev/prod のように区別させることで、例えば以下のような環境毎の設定を 1 ファイル内で定義できます。

f:id:nnc-y-nishi:20220301181344p:plain

図はアカウント毎に設定を分ける例でしたが、同アカウント内で環境を分ける場合はstack_names3_prefixを別に設定することで実現できます。 また、以下のように template.yaml でParametersを利用している場合は、必要に応じてparameter_overridesで template.yaml に渡す値を上書きすることもできます。

AWSTemplateFormatVersion: "2010-09-09"
Transform: AWS::Serverless-2016-10-31
Description: >
  SAM tamplate for nnc-y-nishi.

Globals:
  Function:
    Timeout: 3

Parameters:
  PARAM:
    Type: String
    Default: FUGA

Resources:
 :
 :

regionは必ずしも samconfig.toml 内で設定しなくても問題ありませんが、Jenkins 上の環境変数等で設定をしていない場合は AWS のエンドポイントに接続できなくなるため、念のため設定しておくのが個人的にはお勧めです。

Jenkinsfile で定義すること

Jenkinsfile 上では、SAM CLI でデプロイするための一連の処理を定義します。 最終的にsam deployコマンドでのデプロイとなりますが、その際の実行 Role や、samconfig.toml で定義した設定を指定するには以下のオプションを利用します。

sam deploy --role-arn ${SAM用RoleのARN} --config-env dev

SAM CLI では sam pipeline コマンドで Jenkinsfile を自動生成させることもできるので、そちらを利用しても良いと思います。 また、SAMテンプレート をデプロイするにあたり、Terraform のように変更差分だけを確認したい場面もあるかと思います。 SAM の場合は以下のコマンドで、デプロイさせずに差分確認だけを行うことができます。

sam deploy --role-arn ${SAM用RoleのARN} --config-env dev --no-execute-changeset

差分確認をしてからデプロイさせるような運用をしたい場合には、こちらのオプションが活用できます。 (Jenkins ジョブ実行中に入力受付を可能にしている場合は、通常のオプションなしのsam deployコマンドをそのまま利用できるかと思います)

例えば以下のような Stage を構成することで、template.yaml の検証/ビルド/差分確認/デプロイ、という流れのジョブが実現できます。

steps {
    script {
        templates.each { template ->

            dir(template) {
                stage(template) {
                    stage(‘Validate’) {
                        sh ‘sam validate --template ./template.yaml’
                    }
                    stage('Build') {
                        sh “sam build --config-env ${ENV}”
                    }
                    stage('Confirm Changeset') {
                        sh “sam deploy --config-env ${ENV} --role-arn ${SAM_ROLE} --no-execute-changeset”
                    }
                    stage('Deploy') {
                        if (!autoApprove) {
                            timeout(time:10, unit:'MINUTES') {
                                input message: “Approve?”
                            }
                        }
                        sh “sam deploy --config-env ${ENV} --role-arn ${SAM_ROLE} --no-confirm-changeset”
                    }
                }
            }
        }
    }
}

実行すると、Changeset が作成された後にそのままデプロイを進めるかどうかが選択できます。

f:id:nnc-y-nishi:20220301185932p:plain

おわりに

Jenkins で SAM をデプロイさせるまでの準備についてご紹介しました。 Jenkinsへの過剰な権限付与を避けるためにも、必要な権限を IAM Role に分割することが重要な点となります。 samconfig.toml の設定項目や Jenkins での処理で、それぞれの環境に合った CI/CD 環境が実現できるのではないかと思います。

また、以上の内容で挙げた他にも SAM CLI には様々な機能がありますので、それらも活用していければと思います。

執筆者西 洋平

AWS を利用したシステムの開発/運用を行なっています。