AWS SAMでSlack bot作ってみた【Golang】

# TL; DR
AWS SAMを利用してLambdaをこねこねしたことがなかったので、テストなども交えつつやってみようと思います。
今回は簡単なbotをGoを利用して作ってみたいと思います。

# Tutorial
公式ドキュメントのチュートリアルをサクッとやっていきます。

```bash
# インストール
$ pip install aws-sam-cli
$ sam --version
SAM CLI, version 0.37.0

# 初期化
$ sam init --runtime go1.x --name go-api
$ tree go-api
go-api
├── Makefile
├── README.md
├── hello-world
│   ├── main.go
│   └── main_test.go
└── template.yaml
```

```go:go-api/hello-world/main.go
// 初期ファイル
package main

import (
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"

	"github.com/aws/aws-lambda-go/events"   // パッケージが必要
	"github.com/aws/aws-lambda-go/lambda"   // パッケージが必要
)

var (
	// DefaultHTTPGetAddress Default Address
	DefaultHTTPGetAddress = "https://checkip.amazonaws.com"

	// ErrNoIP No IP found in response
	ErrNoIP = errors.New("No IP in HTTP response")

	// ErrNon200Response non 200 status code in response
	ErrNon200Response = errors.New("Non 200 Response found")
)

func handler(request events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) {
	resp, err := http.Get(DefaultHTTPGetAddress)
	if err != nil {
		return events.APIGatewayProxyResponse{}, err
	}

	if resp.StatusCode != 200 {
		return events.APIGatewayProxyResponse{}, ErrNon200Response
	}

	ip, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return events.APIGatewayProxyResponse{}, err
	}

	if len(ip) == 0 {
		return events.APIGatewayProxyResponse{}, ErrNoIP
	}

	return events.APIGatewayProxyResponse{
		Body:       fmt.Sprintf("Hello, %v", string(ip)),
		StatusCode: 200,
	}, nil
}

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

```bash
# パッケージインストール
$ go get "github.com/aws/aws-lambda-go/events"
$ go get "github.com/aws/aws-lambda-go/lambda"
```

これでビルドが実施できるようになりました。
せっかくテンプレートにテストファイルが付いているのでテストしてみましょう。

```go:go-api/hello-world/main_test.go
// テストファイル
package main

import (
	"fmt"
	"net/http"
	"net/http/httptest"
	"testing"

	"github.com/aws/aws-lambda-go/events"
)

func TestHandler(t *testing.T) {
	t.Run("Unable to get IP", func(t *testing.T) {
		DefaultHTTPGetAddress = "http://127.0.0.1:12345"

		_, err := handler(events.APIGatewayProxyRequest{})
		if err == nil {
			t.Fatal("Error failed to trigger with an invalid request")
		}
	})

	t.Run("Non 200 Response", func(t *testing.T) {
		ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			w.WriteHeader(500)
		}))
		defer ts.Close()

		DefaultHTTPGetAddress = ts.URL

		_, err := handler(events.APIGatewayProxyRequest{})
		if err != nil && err.Error() != ErrNon200Response.Error() {
			t.Fatalf("Error failed to trigger with an invalid HTTP response: %v", err)
		}
	})

	t.Run("Unable decode IP", func(t *testing.T) {
		ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			w.WriteHeader(500)
		}))
		defer ts.Close()

		DefaultHTTPGetAddress = ts.URL

		_, err := handler(events.APIGatewayProxyRequest{})
		if err == nil {
			t.Fatal("Error failed to trigger with an invalid HTTP response")
		}
	})

	t.Run("Successful Request", func(t *testing.T) {
		ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			w.WriteHeader(200)
			fmt.Fprintf(w, "127.0.0.1")
		}))
		defer ts.Close()

		DefaultHTTPGetAddress = ts.URL

		_, err := handler(events.APIGatewayProxyRequest{})
		if err != nil {
			t.Fatal("Everything should be ok")
		}
	})
}
```

```bash
# テスト実施
$ cd go-api
go-api $ go test -v ./hello-world/
=== RUN   TestHandler
=== RUN   TestHandler/Unable_to_get_IP
=== RUN   TestHandler/Non_200_Response
=== RUN   TestHandler/Unable_decode_IP
=== RUN   TestHandler/Successful_Request
--- PASS: TestHandler (0.00s)
    --- PASS: TestHandler/Unable_to_get_IP (0.00s)
    --- PASS: TestHandler/Non_200_Response (0.00s)
    --- PASS: TestHandler/Unable_decode_IP (0.00s)
    --- PASS: TestHandler/Successful_Request (0.00s)
PASS
ok  	_/Users/user/Documents/GitHub/api-demo/go-api/hello-world	(cached)
```

問題ないですね。
ではビルドしてローカルでも実施しましょう。

```bash
# ビルド
$ make build
GOOS=linux GOARCH=amd64 go build -o hello-world/hello-world ./hello-world

# ローカル起動
$ sam local start-api
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. You only need to restart SAM CLI if you update your AWS SAM template
2019-12-08 18:18:14  * Running on http://127.0.0.1:3000/ (Press CTRL+C to quit)
Invoking hello-world (go1.x)
```

これで準備できたのでリクエストを投げます。

```bash
# リクエスト
$ curl http://127.0.0.1:3000/hello
Hello, 10.100.110.111

# start-apiの画面
Fetching lambci/lambda:go1.x Docker container image......
Mounting /Users/user/Documents/GitHub/api-demo/go-api/hello-world as /var/task:ro,delegated inside runtime container
START RequestId: 0d0e4ea4-9c7c-1c63-b642-54048a3faadc Version: $LATEST
END RequestId: 0d0e4ea4-9c7c-1c63-b642-54048a3faadc
REPORT RequestId: 0d0e4ea4-9c7c-1c63-b642-54048a3faadc	Init Duration: 159.66 ms	Duration: 999.77 ms	Billed Duration: 1000 ms	Memory Size: 128 MB	Max Memory Used: 26 MB	
No Content-Type given. Defaulting to 'application/json'.
2019-12-08 18:20:31 127.0.0.1 - - [08/Dec/2019 18:20:31] "GET /hello HTTP/1.1" 200 -
```

リクエストが来たタイミングでDockerイメージがフェッチされるためレスポンスは少し遅いです。
ではデプロイしてみます。

```bash
$ sam deploy --guided

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

<details><summary>Configure</summary><div>

```bash
	Looking for samconfig.toml :  Not found

	Setting default arguments for 'sam deploy'
	=========================================
	Stack Name [sam-app]: go-api
	AWS Region [us-east-1]: 
	#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
	Save arguments to samconfig.toml [Y/n]: Y

	Looking for resources needed for deployment: Found!

		Managed S3 bucket: aws-sam-cli-managed-default-samclisourcebucket-1gh4mza9gyjtv
		A different default S3 bucket can be set in 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

	Deploying with following values
	===============================
	Stack name                 : go-api
	Region                     : us-east-1
	Confirm changeset          : True
	Deployment s3 bucket       : aws-sam-cli-managed-default-samclisourcebucket-1gh4mza9gyjtv
	Capabilities               : ["CAPABILITY_IAM"]
	Parameter overrides        : {}

Initiating deployment
=====================
Uploading to go-api/fff25b2d08e7c662a508f2b2277701bf  4342086 / 4342086.0  (100.00%)
Uploading to go-api/b3420e24fd8fe453f7bb26b67f59c286.template  1152 / 1152.0  (100.00%)

Waiting for changeset to be created..

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

Changeset created successfully. arn:aws:cloudformation:us-east-1:123456789012:changeSet/samcli-deploy1575797125/4f1a8198-1d0a-4c1e-86d3-1a77fc7e587b


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

</div></details>

```
2019-12-08 18:25:44 - Waiting for stack create/update to complete

CloudFormation events from changeset
---------------------------------------------------------------------------------------------------------------------------------------------------------
ResourceStatus                         ResourceType                           LogicalResourceId                      ResourceStatusReason                 
---------------------------------------------------------------------------------------------------------------------------------------------------------
CREATE_IN_PROGRESS                     AWS::IAM::Role                         HelloWorldFunctionRole                 Resource creation Initiated          
CREATE_IN_PROGRESS                     AWS::IAM::Role                         HelloWorldFunctionRole                 -                                    
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            ServerlessRestApiDeployment47fc2d5f9   -                                    
                                                                              d                                                                           
CREATE_IN_PROGRESS                     AWS::ApiGateway::Deployment            ServerlessRestApiDeployment47fc2d5f9   Resource creation Initiated          
                                                                              d                                                                           
CREATE_IN_PROGRESS                     AWS::Lambda::Permission                HelloWorldFunctionCatchAllPermission   Resource creation Initiated          
                                                                              Prod                                                                        
CREATE_IN_PROGRESS                     AWS::Lambda::Permission                HelloWorldFunctionCatchAllPermission   -                                    
                                                                              Prod                                                                        
CREATE_COMPLETE                        AWS::ApiGateway::Deployment            ServerlessRestApiDeployment47fc2d5f9   -                                    
                                                                              d                                                                           
CREATE_IN_PROGRESS                     AWS::ApiGateway::Stage                 ServerlessRestApiProdStage             -                                    
CREATE_COMPLETE                        AWS::ApiGateway::Stage                 ServerlessRestApiProdStage             -                                    
CREATE_IN_PROGRESS                     AWS::ApiGateway::Stage                 ServerlessRestApiProdStage             Resource creation Initiated          
CREATE_COMPLETE                        AWS::Lambda::Permission                HelloWorldFunctionCatchAllPermission   -                                    
                                                                              Prod                                                                        
CREATE_COMPLETE                        AWS::CloudFormation::Stack             go-api                                 -                                    
---------------------------------------------------------------------------------------------------------------------------------------------------------

Stack go-api outputs:
---------------------------------------------------------------------------------------------------------------------------------------------------------
OutputKey-Description                                                        OutputValue                                                                
---------------------------------------------------------------------------------------------------------------------------------------------------------
HelloWorldFunctionIamRole - Implicit IAM Role created for Hello World        arn:aws:iam::123456789012:role/go-api-HelloWorldFunctionRole-19P29ZGX2R4XQ 
function                                                                                                                                                
HelloWorldAPI - API Gateway endpoint URL for Prod environment for First      https://2v0ft8rx5a.execute-api.us-east-1.amazonaws.com/Prod/hello/         
Function                                                                                                                                                
HelloWorldFunction - First Lambda Function ARN                               arn:aws:lambda:us-east-1:123456789012:function:go-api-                     
                                                                             HelloWorldFunction-1447XD3VZZWOP                                           
---------------------------------------------------------------------------------------------------------------------------------------------------------

Successfully created/updated stack - go-api in us-east-1

```

リクエスト確認してみます。

```bash
$ curl https://2v0ft8rx5a.execute-api.us-east-1.amazonaws.com/Prod/hello/
Hello, 10.100.110.111
```

問題ないですね。
ちなみに上記のターミナルで設定した項目はディレクトリ直下に`samconfig.toml`として保存されます。

```bash:samconfig.toml
version = 0.1
[default]
[default.deploy]
[default.deploy.parameters]
stack_name = "go-api"
s3_bucket = "aws-sam-cli-managed-default-samclisourcebucket-1gh4mza9gyjtv"
s3_prefix = "go-api"
region = "us-east-1"
confirm_changeset = true
capabilities = "CAPABILITY_IAM"
```

S3バケットもできていますね。

```bash:s3&nbsp;comfirm
$ aws s3 ls
2019-12-08 16:57:54 aws-sam-cli-managed-default-samclisourcebucket-1gh4mza9gyjtv

$ aws s3 ls aws-sam-cli-managed-default-samclisourcebucket-1gh4mza9gyjtv
                           PRE go-api/
```

チュートリアルはこれで完了です。

# botの作成
今回はOutgoing webhookを利用して特定のチャンネルから特定のキーワードに対してレスポンスを返すようにします。
コードはこんな感じに変更しました。

```go
package main

import (
	"encoding/json"
	"log"
	"strings"
	"net/url"
	"errors"
	"github.com/aws/aws-lambda-go/events"
	"github.com/aws/aws-lambda-go/lambda"
)

var (
	ErrNon200Response = errors.New("Non 200 Response found")
)

type SlackMessage struct {
	Text string `json:"text"`
}

func handler(request events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) {
	var keyword string
	var ignore string
	for _, value := range strings.Split(request.Body, "&") {
		param := strings.Split(value, "=")
		if param[0] == "trigger_word" {
			keyword, _ = url.QueryUnescape(param[1])
		}
		if param[0] == "user_name" {
			ignore, _ = url.QueryUnescape(param[1])
		}
	}

	if ignore == "slackbot" {
		return events.APIGatewayProxyResponse {}, nil
	}

	var text string
	if keyword == "damn it" {
		text = "What's happen bro!?"
	} else if keyword == "weather" {
		text = "The mood is always sunny!"
	} else if keyword == "がんばる" || keyword == "頑張る" {
		text = "おう！気張りやにーちゃん！"
	} else if keyword == "つかれた" || keyword == "疲れた" {
		text = "たまには休んでええんやで？"
	} else if keyword == "おはよう" || keyword == "おはよー" {
		text = "やかましいわ！もう少し寝させんかい！"
	}

	j, err := json.Marshal(SlackMessage{Text: text})
	if err != nil {
		log.Print(err)
		return events.APIGatewayProxyResponse{Body: "Error"}, err
	}

	return events.APIGatewayProxyResponse{
		Body:       string(j),
		StatusCode: 200,
	}, nil
}

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

リクエストに対するレスポンスは`func handler(request events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) {}`内でラップしてあげる必要があるので注意です。
[README_ApiGatewayEvent](https://github.com/aws/aws-lambda-go/blob/master/events/README_ApiGatewayEvent.md)

### デプロイ前準備
Makefile内でビルドした際のバイナリファイル名をしているものが、`hello-world`のままだと気持ち悪いので変更しました。

```bash
build:
	GOOS=linux GOARCH=amd64 go build -o hello-world/slack-bot ./hello-world
```

それに伴い`template.yaml`も関数名など変更しました。

```yaml:template.yaml
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: >
  go-api
  
  Slack Bot for test

Globals:
  Function:
    Timeout: 5

Resources:
  slackBot:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: hello-world/
      Handler: slack-bot
      Runtime: go1.x
      Tracing: Active
      Events:
        CatchAll:
          Type: Api
          Properties:
            Path: /reply
            Method: POST

Outputs:
  slackBotAPI:
    Description: "API Gateway endpoint URL for Prod environment for Slack Bot Function"
    Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/reply/"
  slackBot:
    Description: "Slack Bot Function"
    Value: !GetAtt slackBot.Arn
  slackBotIamRole:
    Description: "Implicit IAM Role created for Slack Bot function"
    Value: !GetAtt slackBotRole.Arn
```

### sam packege
S3にファイルをアップロードする必要があるので実施します。
まずはS3バケットを作ります。

```bash
$ aws s3 mb s3://slack-bot-go --region us-east-1
make_bucket: slack-bot-go
```

下記コマンドを実施してLambdaのソースコードをパッケージングしてあげます。

```bash
$ sam package \
  --output-template-file packaged.yaml \
  --s3-bucket [BUCKET_NAME]
```

実行結果

```bash
$ sam package \
>     --output-template-file packaged.yaml \
>     --s3-bucket slack-bot-go
Uploading to 5626ff5aa050e5296410bbde19d2bd56  8654225 / 8654225.0  (100.00%)

Successfully packaged artifacts and wrote output template to file packaged.yaml.
Execute the following command to deploy the packaged template
sam deploy --template-file /Users/user/Documents/GitHub/api-demo/go-api/packaged.yaml --stack-name <YOUR STACK NAME>

$ aws s3 ls s3://slack-bot-go
2019-12-08 21:43:03    8654225 5626ff5aa050e5296410bbde19d2bd56
```

無事にアップロードできています。

### sam deploy
ここで別のStackとして管理したい場合は再度`--guided`オプションをつけてあげればいいのですが、今回は`hello-world`周りのリソースが不要なのでそのまま`samconfig.toml`を利用してついでにいらないものも消してしまいます。
※ちなみに初回は`sam deploy`だけだと怒られます。

```bash:Error
$ sam deploy
Usage: sam deploy [OPTIONS]
Try "sam deploy --help" for help.

Error: Missing option '--stack-name', 'sam deploy --guided' can be used to provide and save needed parameters for future deploys.
```

ではデプロイしてみます。

```bash
$ sam deploy
        # samconfig.tomlが読み込まれているのがわかる
	Deploying with following values
	===============================
	Stack name                 : go-api
	Region                     : us-east-1
	Confirm changeset          : True
	Deployment s3 bucket       : aws-sam-cli-managed-default-samclisourcebucket-1gh4mza9gyjtv
	Capabilities               : ["CAPABILITY_IAM"]
	Parameter overrides        : {}

Initiating deployment
=====================
Uploading to go-api/5626ff5aa050e5296410bbde19d2bd56  8654225 / 8654225.0  (100.00%)
Uploading to go-api/a4559abc92e44da84a60149a1a9f09a5.template  1020 / 1020.0  (100.00%)

Waiting for changeset to be created..

CloudFormation stack changeset
------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Operation                                                LogicalResourceId                                        ResourceType                                           
------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ Add                                                    ServerlessRestApiDeploymentcdebb0ffc6                    AWS::ApiGateway::Deployment                            
+ Add                                                    slackBotCatchAllPermissionProd                           AWS::Lambda::Permission                                
+ Add                                                    slackBotRole                                             AWS::IAM::Role                                         
+ Add                                                    slackBot                                                 AWS::Lambda::Function                                  
* Modify                                                 ServerlessRestApiProdStage                               AWS::ApiGateway::Stage                                 
* Modify                                                 ServerlessRestApi                                        AWS::ApiGateway::RestApi                               
# 不要なリソースがチェンジセットから削除されることを確認できる
- Delete                                                 HelloWorldFunctionCatchAllPermissionProd                 AWS::Lambda::Permission                                
- Delete                                                 HelloWorldFunctionRole                                   AWS::IAM::Role                                         
- Delete                                                 HelloWorldFunction                                       AWS::Lambda::Function                                  
- Delete                                                 ServerlessRestApiDeployment47fc2d5f9d                    AWS::ApiGateway::Deployment                            
------------------------------------------------------------------------------------------------------------------------------------------------------------------------

Changeset created successfully. arn:aws:cloudformation:us-east-1:123456789012:changeSet/samcli-deploy1575809093/6a54bb0c-1a8f-4659-b32d-198226d652cb


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

2019-12-08 21:45:17 - Waiting for stack create/update to complete

CloudFormation events from changeset
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------
ResourceStatus                             ResourceType                               LogicalResourceId                          ResourceStatusReason                     
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------
CREATE_IN_PROGRESS                         AWS::IAM::Role                             slackBotRole                               Resource creation Initiated              
CREATE_IN_PROGRESS                         AWS::IAM::Role                             slackBotRole                               -                                        
CREATE_COMPLETE                            AWS::IAM::Role                             slackBotRole                               -                                        
CREATE_IN_PROGRESS                         AWS::Lambda::Function                      slackBot                                   -                                        
CREATE_IN_PROGRESS                         AWS::Lambda::Function                      slackBot                                   Resource creation Initiated              
CREATE_COMPLETE                            AWS::Lambda::Function                      slackBot                                   -                                        
UPDATE_COMPLETE                            AWS::ApiGateway::RestApi                   ServerlessRestApi                          -                                        
UPDATE_IN_PROGRESS                         AWS::ApiGateway::RestApi                   ServerlessRestApi                          -                                        
CREATE_IN_PROGRESS                         AWS::ApiGateway::Deployment                ServerlessRestApiDeploymentcdebb0ffc6      -                                        
CREATE_COMPLETE                            AWS::ApiGateway::Deployment                ServerlessRestApiDeploymentcdebb0ffc6      -                                        
CREATE_IN_PROGRESS                         AWS::ApiGateway::Deployment                ServerlessRestApiDeploymentcdebb0ffc6      Resource creation Initiated              
CREATE_IN_PROGRESS                         AWS::Lambda::Permission                    slackBotCatchAllPermissionProd             Resource creation Initiated              
CREATE_IN_PROGRESS                         AWS::Lambda::Permission                    slackBotCatchAllPermissionProd             -                                        
UPDATE_COMPLETE                            AWS::ApiGateway::Stage                     ServerlessRestApiProdStage                 -                                        
UPDATE_IN_PROGRESS                         AWS::ApiGateway::Stage                     ServerlessRestApiProdStage                 -                                        
CREATE_COMPLETE                            AWS::Lambda::Permission                    slackBotCatchAllPermissionProd             -                                        
UPDATE_COMPLETE_CLEANUP_IN_PROGRESS        AWS::CloudFormation::Stack                 go-api                                     -                                        
DELETE_COMPLETE                            AWS::ApiGateway::Deployment                ServerlessRestApiDeployment47fc2d5f9d      -                                        
DELETE_IN_PROGRESS                         AWS::Lambda::Permission                    HelloWorldFunctionCatchAllPermissionProd   -                                        
DELETE_IN_PROGRESS                         AWS::ApiGateway::Deployment                ServerlessRestApiDeployment47fc2d5f9d      -                                        
DELETE_IN_PROGRESS                         AWS::Lambda::Function                      HelloWorldFunction                         -                                        
DELETE_COMPLETE                            AWS::Lambda::Permission                    HelloWorldFunctionCatchAllPermissionProd   -                                        
DELETE_IN_PROGRESS                         AWS::IAM::Role                             HelloWorldFunctionRole                     -                                        
DELETE_COMPLETE                            AWS::Lambda::Function                      HelloWorldFunction                         -                                        
UPDATE_COMPLETE                            AWS::CloudFormation::Stack                 go-api                                     -                                        
DELETE_COMPLETE                            AWS::IAM::Role                             HelloWorldFunctionRole                     -                                        
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------

Stack go-api outputs:
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------
OutputKey-Description                                                                OutputValue                                                                        
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------
slackBotAPI - API Gateway endpoint URL for Prod environment for Slack Bot Function   https://2v0ft8rx5a.execute-api.us-east-1.amazonaws.com/Prod/reply/                 
slackBot - Slack Bot Function                                                        arn:aws:lambda:us-east-1:123456789012:function:go-api-slackBot-8OP317NRCY48        
slackBotIamRole - Implicit IAM Role created for Slack Bot function                   arn:aws:iam::123456789012:role/go-api-slackBotRole-T7GY3L3LQGK5                    
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------

Successfully created/updated stack - go-api in us-east-1
```

デプロイ完了しました。
[リポジトリ](https://github.com/uluru-kinoshitat/api-demo)

### Outgoing Webhookの設定
Outgoing Webhook側で作成したリソースに対してアクションするように設定してあげます。
今回は`引き金となる言葉` / `URL` / `チャンネル` / `名前` / `アイコン` を設定しました。
![sc.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/423088/6c4b5f36-16b2-ae2c-facb-33eb66ea710f.png)
![sc 1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/423088/7edcb7ae-7455-ca31-f310-7933864cee2f.png)

これでOKです。

# テスト
では早速設定したキーワードを投げてみます。
<img width="860" alt="sc 2.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/423088/66ba661a-d065-3138-6014-960350a5212b.png">
ちゃんと返ってきましたー。

# 削除
こんなbotは使い所なくて邪魔なだけなので消しましょうw
[公式ドキュメント](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-command-reference.html)に`destroy`とか`terminate`みたいなコマンドはなさそうですね。
CloudFormationから消してあげるしかなさそうです。
S3空にしないといけないので、コマンド一発で消せないのはちょっと辛いですね。。

```bash
$ aws cloudformation delete-stack --stack-name go-api
```

![sc 3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/423088/375bf4e3-e2e8-0541-9525-dedc902694ba.png)

# まとめ
`AWS SAM`自体は正直すこし冗長だなあ…というのが個人的な感想でした。
その前に`AWS CDK`を触っていた反動もあるとは思いますが、、
うまく組み合わせて`AWS SAM`にはLambda関数の部分におけるテストやローカル実行、`AWS CDK`には周辺リソースなどを作ってもらう形がいいのではないかと思いました。
両方とも活用することでより便利に、安全なサーバーレス開発が捗ると思います。