AWS SAMを触ってみる

# はじめに
lambda関係を勉強中です。この記事は、やった作業内容の備忘録です

# AWS SAMとは
似た記事はたくさんあるので、詳細は割愛させていただきますが、公式ページでは以下ののように説明されています。

AWS Serverless Application Model (AWS SAM) は、AWS 上でサーバーレスアプリケーションを構築および実行するデベロッパーのエクスペリエンスを改善するツールキットです。AWS SAM は次の 2 つの主要な部分で構成されます。

1. AWS SAM テンプレート仕様
    - AWS でサーバーレスアプリケーションインフラストラクチャを定義するために使用できるオープンソースフレームワーク。
1. AWS SAM コマンドラインインターフェイス (AWS SAM CLI) 
    - AWS SAM テンプレートやサポートされているサードパーティーの統合と併用することで、サーバーレスアプリケーションを構築し、実行できるコマンドラインツール。


https://docs.aws.amazon.com/ja_jp/serverless-application-model/latest/developerguide/what-is-sam.html

# 利用するアカウントのポリシー
コチラの記事のポリシーを利用さえてもらいました(ありがとうございます)
https://nobelabo.hatenablog.com/entry/2022/07/31/142738
```
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "CloudFormationTemplate",
            "Effect": "Allow",
            "Action": [
                "cloudformation:CreateChangeSet"
            ],
            "Resource": [
                "arn:aws:cloudformation:*:aws:transform/Serverless-2016-10-31"
            ]
        },
        {
            "Sid": "CloudFormationStack",
            "Effect": "Allow",
            "Action": [
                "cloudformation:CreateChangeSet",
                "cloudformation:CreateStack",
                "cloudformation:DeleteStack",
                "cloudformation:DescribeChangeSet",
                "cloudformation:DescribeStackEvents",
                "cloudformation:DescribeStacks",
                "cloudformation:ExecuteChangeSet",
                "cloudformation:GetTemplateSummary",
                "cloudformation:GetTemplate",
                "cloudformation:ListStackResources",
                "cloudformation:UpdateStack"
            ],
            "Resource": [
                "arn:aws:cloudformation:*:111122223333:stack/*"
            ]
        },
        {
            "Sid": "S3",
            "Effect": "Allow",
            "Action": [
                "s3:*"
            ],
            "Resource": [
                "*"
            ]
        },
        {
            "Sid": "ECRRepository",
            "Effect": "Allow",
            "Action": [
                "ecr:BatchCheckLayerAvailability",
                "ecr:BatchGetImage",
                "ecr:CompleteLayerUpload",
                "ecr:CreateRepository",
                "ecr:DeleteRepository",
                "ecr:DescribeImages",
                "ecr:DescribeRepositories",
                "ecr:GetDownloadUrlForLayer",
                "ecr:GetRepositoryPolicy",
                "ecr:InitiateLayerUpload",
                "ecr:ListImages",
                "ecr:PutImage",
                "ecr:SetRepositoryPolicy",
                "ecr:UploadLayerPart"
            ],
            "Resource": [
                "arn:aws:ecr:*:111122223333:repository/*"
            ]
        },
        {
            "Sid": "ECRAuthToken",
            "Effect": "Allow",
            "Action": [
                "ecr:GetAuthorizationToken"
            ],
            "Resource": [
                "*"
            ]
        },
        {
            "Sid": "Lambda",
            "Effect": "Allow",
            "Action": [
                "lambda:AddPermission",
                "lambda:CreateFunction",
                "lambda:DeleteFunction",
                "lambda:GetFunction",
                "lambda:GetFunctionConfiguration",
                "lambda:ListTags",
                "lambda:RemovePermission",
                "lambda:TagResource",
                "lambda:UntagResource",
                "lambda:UpdateFunctionCode",
                "lambda:UpdateFunctionConfiguration"
            ],
            "Resource": [
                "arn:aws:lambda:*:111122223333:function:*"
            ]
        },
        {
            "Sid": "IAM",
            "Effect": "Allow",
            "Action": [
                "iam:CreateRole",
                "iam:AttachRolePolicy",
                "iam:DeleteRole",
                "iam:DetachRolePolicy",
                "iam:GetRole",
                "iam:TagRole"
            ],
            "Resource": [
                "arn:aws:iam::111122223333:role/*"
            ]
        },
        {
            "Sid": "IAMPassRole",
            "Effect": "Allow",
            "Action": "iam:PassRole",
            "Resource": "*",
            "Condition": {
                "StringEquals": {
                    "iam:PassedToService": "lambda.amazonaws.com"
                }
            }
        },
        {
            "Sid": "APIGateway",
            "Effect": "Allow",
            "Action": [
                "apigateway:DELETE",
                "apigateway:GET",
                "apigateway:PATCH",
                "apigateway:POST",
                "apigateway:PUT"
            ],
            "Resource": [
                "arn:aws:apigateway:*::*"
            ]
        }
    ]
}
```

:::note alert
arnは、利用するアカウントのものに書き換えてご利用ください
:::

# SAMの実験

## 初期生成
```
$ sam init

You can preselect a particular runtime or package type when using the `sam init` experience.
Call `sam init --help` to learn more.

Which template source would you like to use?
        1 - AWS Quick Start Templates
        2 - Custom Template Location
Choice: 1

Choose an AWS Quick Start application template
        1 - Hello World Example
        2 - Data processing
        3 - Hello World Example with Powertools for AWS Lambda
        4 - Multi-step workflow
        5 - Scheduled task
        6 - Standalone function
        7 - Serverless API
        8 - Infrastructure event management
        9 - Lambda Response Streaming
        10 - Serverless Connector Hello World Example
        11 - Multi-step workflow with Connectors
        12 - GraphQLApi Hello World Example
        13 - Full Stack
        14 - Lambda EFS example
        15 - Hello World Example With Powertools for AWS Lambda
        16 - DynamoDB Example
        17 - Machine Learning
Template: 1

Use the most popular runtime and package type? (Python and zip) [y/N]: n

Which runtime would you like to use?
        1 - aot.dotnet7 (provided.al2)
        2 - dotnet6
        3 - go1.x
        4 - go (provided.al2)
        5 - go (provided.al2023)
        6 - graalvm.java11 (provided.al2)
        7 - graalvm.java17 (provided.al2)
        8 - java21
        9 - java17
        10 - java11
        11 - java8.al2
        12 - java8
        13 - nodejs20.x
        14 - nodejs18.x
        15 - nodejs16.x
        16 - nodejs14.x
        17 - python3.9
        18 - python3.8
        19 - python3.7
        20 - python3.12
        21 - python3.11
        22 - python3.10
        23 - ruby3.2
        24 - ruby2.7
        25 - rust (provided.al2)
        26 - rust (provided.al2023)
Runtime: 23

What package type would you like to use?
        1 - Zip
        2 - Image
Package type: 1

Based on your selections, the only dependency manager available is bundler.
We will proceed copying the template using bundler.

Would you like to enable X-Ray tracing on the function(s) in your application?  [y/N]: n

Would you like to enable monitoring using CloudWatch Application Insights?
For more info, please view https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/cloudwatch-application-insights.html [y/N]: n

Would you like to set Structured Logging in JSON format on your Lambda functions?  [y/N]: y
Structured Logging in JSON format might incur an additional cost. View https://docs.aws.amazon.com/lambda/latest/dg/monitoring-cloudwatchlogs.html#monitoring-cloudwatchlogs-pricing for more details

Project name [sam-app]: hello-world

    -----------------------
    Generating application:
    -----------------------
    Name: hello-world
    Runtime: ruby3.2
    Architectures: x86_64
    Dependency Manager: bundler
    Application Template: hello-world
    Output Directory: .
    Configuration file: hello-world/samconfig.toml
    
    Next steps can be found in the README file at hello-world/README.md
        

Commands you can use next
=========================
[*] Create pipeline: cd hello-world && sam pipeline init --bootstrap
[*] Validate SAM template: cd hello-world && sam validate
[*] Test Function in the Cloud: cd hello-world && sam sync --stack-name {stack-name} --watch
```

## ビルド
```
$ cd hello-world
$ sam build
Starting Build use cache                                                                                                                                                                                       
Manifest file is changed (new hash: 47a3f7f04a4143b5f5e90f367f1dacf9) or dependency folder (.aws-sam/deps/bfad8fa5-654a-4591-8f54-67296bdf8628) is missing for (HelloWorldFunction), downloading dependencies  
and copying/building source                                                                                                                                                                                    
Building codeuri: /home/hogehoge/lamda_study/hello-world/hello_world runtime: ruby3.2 metadata: {} architecture: x86_64 functions: HelloWorldFunction                                                            
 Running RubyBundlerBuilder:CopySource                                                                                                                                                                         
 Running RubyBundlerBuilder:RubyBundle                                                                                                                                                                         
 Running RubyBundlerBuilder:RubyBundleDeployment                                                                                                                                                               
 Running RubyBundlerBuilder:CleanUp                                                                                                                                                                            
 Running RubyBundlerBuilder:CopyDependencies                                                                                                                                                                   

Build Succeeded

Built Artifacts  : .aws-sam/build
Built Template   : .aws-sam/build/template.yaml

Commands you can use next
=========================
[*] Validate SAM template: sam validate
[*] Invoke Function: sam local invoke
[*] Test Function in the Cloud: sam sync --stack-name {{stack-name}} --watch
[*] Deploy: sam deploy --guided
```

## デプロイ
```
$ sam deploy --guided

Configuring SAM deploy
======================

        Looking for config file [samconfig.toml] :  Found
        Reading default arguments  :  Success

        Setting default arguments for 'sam deploy'
        =========================================
        Stack Name [hello-world]: hello-world
        AWS Region [us-west-2]: 
        #Shows you resources changes to be deployed and require a 'Y' to initiate deploy
        Confirm changes before deploy [Y/n]: y
        #SAM needs permission to be able to create roles to connect to the resources in your template
        Allow SAM CLI IAM role creation [Y/n]: y
        #Preserves the state of previously provisioned resources when an operation fails
        Disable rollback [y/N]: n
        HelloWorldFunction has no authentication. Is this okay? [y/N]: y
        Save arguments to configuration file [Y/n]: y
        SAM configuration file [samconfig.toml]: 
        SAM configuration environment [default]: 

        Looking for resources needed for deployment:
        Creating the required resources...
        Successfully created!

        Managed S3 bucket: aws-sam-cli-managed-default-samclisourcebucket-zjkn4fga0nks
        A different default S3 bucket can be set in samconfig.toml and auto resolution of buckets turned off by setting resolve_s3=False
                                                                                                                                                                                                               
        Parameter "stack_name=hello-world" in [default.deploy.parameters] is defined as a global parameter [default.global.parameters].                                                                        
        This parameter will be only saved under [default.global.parameters] in /home/hogehoge/lamda_study/hello-world/samconfig.toml.                                                                            

        Saved arguments to config file
        Running 'sam deploy' for future deployments will use the parameters saved above.
        The above parameters can be changed by modifying samconfig.toml
        Learn more about samconfig.toml syntax at 
        https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-config.html

        Uploading to hello-world/36baa31f777cb5cfd824301c6abe7e12  209751 / 209751  (100.00%)

        Deploying with following values
        ===============================
        Stack name                   : hello-world
        Region                       : us-west-2
        Confirm changeset            : True
        Disable rollback             : False
        Deployment s3 bucket         : aws-sam-cli-managed-default-samclisourcebucket-zjkn4fga0nks
        Capabilities                 : ["CAPABILITY_IAM"]
        Parameter overrides          : {}
        Signing Profiles             : {}

Initiating deployment
=====================

        Uploading to hello-world/38c8345147375fb6b529d15039dd2f1a.template  1250 / 1250  (100.00%)


Waiting for changeset to be created..

CloudFormation stack changeset
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Operation                                          LogicalResourceId                                  ResourceType                                       Replacement                                      
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ Add                                              HelloWorldFunctionHelloWorldPermissionProd         AWS::Lambda::Permission                            N/A                                              
+ Add                                              HelloWorldFunctionRole                             AWS::IAM::Role                                     N/A                                              
+ Add                                              HelloWorldFunction                                 AWS::Lambda::Function                              N/A                                              
+ Add                                              ServerlessRestApiDeployment47fc2d5f9d              AWS::ApiGateway::Deployment                        N/A                                              
+ Add                                              ServerlessRestApiProdStage                         AWS::ApiGateway::Stage                             N/A                                              
+ Add                                              ServerlessRestApi                                  AWS::ApiGateway::RestApi                           N/A                                              
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------


Changeset created successfully. arn:aws:cloudformation:us-west-2:104949618184:changeSet/samcli-deploy1706594531/3adc93ed-9611-4ae3-ad3b-54d0024d0245


Previewing CloudFormation changeset before deployment
======================================================
Deploy this changeset? [y/N]: y

2024-01-30 15:02:27 - Waiting for stack create/update to complete

CloudFormation events from stack operations (refresh every 5.0 seconds)
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
ResourceStatus                                     ResourceType                                       LogicalResourceId                                  ResourceStatusReason                             
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
CREATE_IN_PROGRESS                                 AWS::CloudFormation::Stack                         hello-world                                        User Initiated                                   
CREATE_IN_PROGRESS                                 AWS::IAM::Role                                     HelloWorldFunctionRole                             -                                                
CREATE_IN_PROGRESS                                 AWS::IAM::Role                                     HelloWorldFunctionRole                             Resource creation Initiated                      
CREATE_COMPLETE                                    AWS::IAM::Role                                     HelloWorldFunctionRole                             -                                                
CREATE_IN_PROGRESS                                 AWS::Lambda::Function                              HelloWorldFunction                                 -                                                
CREATE_IN_PROGRESS                                 AWS::Lambda::Function                              HelloWorldFunction                                 Resource creation Initiated                      
CREATE_COMPLETE                                    AWS::Lambda::Function                              HelloWorldFunction                                 -                                                
CREATE_IN_PROGRESS                                 AWS::ApiGateway::RestApi                           ServerlessRestApi                                  -                                                
CREATE_IN_PROGRESS                                 AWS::ApiGateway::RestApi                           ServerlessRestApi                                  Resource creation Initiated                      
CREATE_COMPLETE                                    AWS::ApiGateway::RestApi                           ServerlessRestApi                                  -                                                
CREATE_IN_PROGRESS                                 AWS::ApiGateway::Deployment                        ServerlessRestApiDeployment47fc2d5f9d              -                                                
CREATE_IN_PROGRESS                                 AWS::Lambda::Permission                            HelloWorldFunctionHelloWorldPermissionProd         -                                                
CREATE_IN_PROGRESS                                 AWS::Lambda::Permission                            HelloWorldFunctionHelloWorldPermissionProd         Resource creation Initiated                      
CREATE_COMPLETE                                    AWS::Lambda::Permission                            HelloWorldFunctionHelloWorldPermissionProd         -                                                
CREATE_IN_PROGRESS                                 AWS::ApiGateway::Deployment                        ServerlessRestApiDeployment47fc2d5f9d              Resource creation Initiated                      
CREATE_COMPLETE                                    AWS::ApiGateway::Deployment                        ServerlessRestApiDeployment47fc2d5f9d              -                                                
CREATE_IN_PROGRESS                                 AWS::ApiGateway::Stage                             ServerlessRestApiProdStage                         -                                                
CREATE_IN_PROGRESS                                 AWS::ApiGateway::Stage                             ServerlessRestApiProdStage                         Resource creation Initiated                      
CREATE_COMPLETE                                    AWS::ApiGateway::Stage                             ServerlessRestApiProdStage                         -                                                
CREATE_COMPLETE                                    AWS::CloudFormation::Stack                         hello-world                                        -                                                
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

CloudFormation outputs from deployed stack
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Outputs                                                                                                                                                                                                    
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Key                 HelloWorldFunctionIamRole                                                                                                                                                              
Description         Implicit IAM Role created for Hello World function                                                                                                                                     
Value               arn:aws:iam::104949618184:role/hello-world-HelloWorldFunctionRole-DF7T0YaKpKgB                                                                                                         

Key                 HelloWorldApi                                                                                                                                                                          
Description         API Gateway endpoint URL for Prod stage for Hello World function                                                                                                                       
Value               https://t8zpifhjo8.execute-api.us-west-2.amazonaws.com/Prod/hello/                                                                                                                     

Key                 HelloWorldFunction                                                                                                                                                                     
Description         Hello World Lambda Function ARN                                                                                                                                                        
Value               arn:aws:lambda:us-west-2:104949618184:function:hello-world-HelloWorldFunction-OvWx2I6ys8Ui                                                                                             
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------


Successfully created/updated stack - hello-world in us-west-2
```


## 動作確認
上記のログを見るとGatewayのエンドポイントが表示されているので、そこに実施
```
$ curl 'https://t8zpifhjo8.execute-api.us-west-2.amazonaws.com/Prod/hello/'
{"message":"Hello World!"}
```

## 削除
```
$ sam delete
        Are you sure you want to delete the stack hello-world in the region us-west-2 ? [y/N]: y
        Are you sure you want to delete the folder hello-world in S3 which contains the artifacts? [y/N]: y
        - Deleting S3 object with key hello-world/36baa31f777cb5cfd824301c6abe7e12                                                                                                                             
        - Deleting S3 object with key hello-world/38c8345147375fb6b529d15039dd2f1a.template                                                                                                                    
        - Deleting Cloudformation stack hello-world

Deleted successfully
```
## （再度）動作確認
```
$ curl 'https://t8zpifhjo8.execute-api.us-west-2.amazonaws.com/Prod/hello/'
{"message": "Internal server error"}
```

## ローカルでの実行
```
$ sam local start-api
Initializing the lambda functions containers.                                                                                                                                                                  
Local image was not found.                                                                                                                                                                                     
Removing rapid images for repo public.ecr.aws/sam/emulation-ruby3.2                                                                                                                                            
Building image.......................................................................................................................................................................................
Using local image: public.ecr.aws/lambda/ruby:3.2-rapid-x86_64.                                                                                                                                                
                                                                                                                                                                                                               
Mounting /home/hogehoge/lamda_study/hello-world/.aws-sam/build/HelloWorldFunction as /var/task:ro,delegated, inside runtime container                                                                            
Containers Initialization is done.                                                                                                                                                                             
Mounting HelloWorldFunction at http://127.0.0.1:3000/hello [GET]                                                                                                                                               
You can now browse to the above endpoints to invoke your functions. You do not need to restart/reload SAM CLI while working on your functions, changes will be reflected instantly/automatically. If you used  
sam build before running local commands, you will need to re-run sam build for the changes to be picked up. You only need to restart SAM CLI if you update your AWS SAM template 
```

# 動作確認
```
$ curl http://127.0.0.1:3000/hello
{"message":"Hello World!"}

$ curl http://localhost:3000/hello
{"message":"Hello World!"}
```

# さいごに
今回は、本を元にsamの基本をさらっと実施しました。
触った感じ使いやすいように感じます。
次回は、利用した何かを作りたいと思います。