slack+slash command+API Gateway를 이용한 SSM 명령실행
- 공유 링크 만들기
- 이메일
- 기타 앱
목표
slack에서 slash command 를 이용하여 특정 명령어를 입력하면 SSM을 이용하여 지정한 ec2에서 지정된 명령을 실행하여 그 결과를 slack 에 출력한다. 여기에서는 각 ec2에서 free -m 명령을 실행 한 후 그 결과를 출력하도록 한다.
전제조건 : 대상 EC2는 ssm을 이용한 명령실행이 가능한 상황이어야 한다.
AWS설정
IAM 설정
slash command를 api gateway에서 받은 후, lambda에서 ssm을 호출한다. 그러기 위해서 lambda 함수에는 ssm과 로그 출력을 위한 cloudwatch logs 에 대한 권한이 필요하다.
- IAM 에서 role을 선택하여 role을 작성
- lambda를 선택해주고 일단 다음, 다음을 클릭하여 적당한 이름을 주고 role을 생성하였다.
- 작성한 role을 선택후 「AmazonSSMFullAccess」AWS관리 Policy를 추가
- inline policy를 선택하여 아래 json내용으로 적당한 이름을 지정하여 추가(이외에도 쓸일이 있다면 policy를 따로 생성한후 지정하는 것도 좋다. 이 policy가 없으면 cloudwatch logs에 로그 출력이 불가능.
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "VisualEditor0",
"Effect": "Allow",
"Action": [
"logs:CreateLogStream",
"logs:DescribeLogStreams",
"logs:PutLogEvents"
],
"Resource": "*"
},
{
"Sid": "VisualEditor1",
"Effect": "Allow",
"Action": "logs:CreateLogGroup",
"Resource": "*"
}
]
}
Lambda 함수 작성
lambda를 먼저 만드느냐, api gateway를 먼저 만드느냐 정해진 방법은 없다. 각 장단점이 있고 익숙한 방식이 있을터이니 편한 방법으로 작성해도 문제 없다. 최종적으로 api gateay를 trigger로 지정한 lambda function이 실행되면 OK.
- lamda에서 함수 작성을 선택하여 Author from scratch를 선택
- Function name을 적당히 넣어주고, Runtime은 Python3.8을 지정
- Execution role에서 Use an existing role을 선택후 위에서 작성한 role을 지정하여 create function.
여기서 add trigger를 클릭하여 api gateway를 지정할 수도 있지만, 난 따로 만들겠다.
API Gateway 설정
- api gateway에서 Create API를 선택후, REST API를 작성
- API name을 적당히 입력해 주고 API를 생성
- Resource를 선택후 Action에서 Create Resource를 선택 후 적당한 이름을 주고(여기에선 free를 지정) resource를 생성
- /free 를 선택후 Action에서 Create Method를 선택후 Any를 지정
- Intergration type에서 Lambda Function지정후 Region확인 하고 Use Lambda Proxy Intergration을 선택
- 위에서 작성한 Lamda Function 이름을 지정 하여 Save하고 Permisson 추가에서 OK
- Method Execution이 표시되면 Method Execution에서 Response Body for 200의 application/json 을 삭제
- 완성된 any를 선택 후, actions에서 deply api를 선택하여 deply한다.
- 왼쪽에서 stages를 선택하면 지정한 stage가 생성되어 있다. /free 를 확인하면 Invoke URL가 생성되어 있으니 메모
- lambda function을 확인하면 trigger에 지정한 api gateway가 설정되어 있는 것을 확인
Lamda 코드
잘 알지도 못하는 python으로 겨우겨우 만들어 냈다. python다운 코드가 아니지만 잘 모르니 어쩔 수 없다.
import boto3
import json
import time
import requests
from urllib.parse import parse_qs
SLACKTOKEN = '<slack token>'
target_instances = ['<대상 ec2 ID>','대상 ec2 ID','대상 ec2 ID']
def command(ssm, instance, commands):
r = ssm.send_command(
InstanceIds = [instance],
DocumentName = "AWS-RunShellScript",
Parameters = {
"commands": [
commands
]
}
)
command_id = r['Command']['CommandId']
while True:
time.sleep(0.2)
res = ssm.list_command_invocations(
CommandId = command_id,
Details = True
)
invocations = res['CommandInvocations']
status = invocations[0]['Status']
print ("Get status : " + status)
if status == "Failed":
return ("Command 실행 에러")
elif status == "Success":
print ("Success!! return command result at " + instance)
account = invocations[0]['CommandPlugins'][0]['Output']
return account
else:
print ("Waiting Lambda function for 0.2s")
def lambda_handler(event, context):
# TODO implement
print ("Start Script")
params = parse_qs(event['body'])
token = params['token'][0]
user = params['user_id'][0]
print ("Get Token from posted data : " + token)
if token != SLACKTOKEN:
print ("Invalied Token, terminate process")
return {
"statusCode": 400,
"headers": {
"Content-Type:": "application/json"
},
"body": json.dumps({
"response_type": "ephemeral",
"text": "Invalid Token"
})
}
retpost_url = params['response_url'][0]
print ("Get response_url from posted data : " + retpost_url)
str_command = "date;hostname;free -m;echo ''"
print ("Create boto3 client for ssm")
client = boto3.client(
'ssm',
region_name='ap-northeast-1',
)
comment=""
print ("Into the for Loop with array of Instances")
for str_instance in target_instances:
print ("Excute command with ssm :" + str_instance)
comment += command(client, str_instance, str_command)
print ("add codesnipet on result")
comment = "<@" + user + ">\r\n" + "```\r\n" + comment + "\r\n```"
print ("return result : \r\n" + comment)
print ("Making Object for posting to slack")
obj_slack = {
"response_type": "in_channel",
"text": comment
}
requests.post(retpost_url, data=json.dumps(obj_slack))
return {
"statusCode": 200,
"headers": {
"Content-Type": "application/json"
},
"body": json.dumps(obj_slack)
}
Slack 설정
slack slash command 어플리케이션이 이미 등록되어 있다고 가정하고, slash command에서 새로운 request를 작성한다.
- command 를 /free 로 지정
- URL은 API Gateway에서 확인한 Invoke URL을 지정
- Method는 Post
- token은 메모하여 Lambda코드에 삽입
- 그외에 적당한 내용을 지정하여 저장
최종확인
slack에서 /free 를 입력하여 지정한 ec2의 free 결과가 표시되는 것을 확인한다. 실행이 잘 안될 경우엔 cloudwatch logs를 확인하자.
- slash command의 timeout이 3초이기 때문에 여러대의 ec2에서 free -m 을 실행한 결과가 timeout되어 버린다. 어떻게 해결은 했는데 기억이.... 기억나는 대로 추가.
- 공유 링크 만들기
- 이메일
- 기타 앱
댓글
댓글 쓰기