[AWS Health] AWS Health ์ด๋ฒคํŠธ๋ฅผ ์ž๋™์œผ๋กœ Airtable์— ์ €์žฅํ•˜๊ธฐ(+Amazon EventBridge, AWS Lambda)

2023. 3. 21. 18:41ใ†AWS

AWS Health Dashboard๋ž€?

AWS Health

AWS ์„œ๋น„์Šค๋‚˜ AWS ๊ณ„์ •์— ์˜ํ–ฅ์„ ์ฃผ๋Š” ์ด๋ฒคํŠธ๋ฅผ ์•Œ๋ ค์ค€๋‹ค.

AWS Health Dashboard๋Š” ๋ชจ๋“  AWS ๊ณ ๊ฐ์ด ์ดˆ๊ธฐ ์„ค์ • ๋ฐ ๋น„์šฉ ์—†์ด ์ด์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

Account health/Organization health๋Š” PHD(Personal Health Dashboard)๋ผ๊ณ ๋„ ํ•œ๋‹ค. Service Health Dashboard์—์„œ๋Š” ์ผ๋ฐ˜์ ์ธ AWS ์„œ๋น„์Šค ์ƒํƒœ๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ๊ณ , Personal Health Dashboard์—์„œ๋Š” ๊ตฌ์„ฑ๋œ ํŠน์ • AWS ํ™˜๊ฒฝ์— ๋Œ€ํ•ด ๋ถ„๋ช…ํ•œ ์•Œ๋ฆผ์„ ๋ฏธ๋ฆฌ ์ œ๊ณตํ•œ๋‹ค.

 


 

 

โ˜„๏ธ To-Be Architecture

AWS Health์—์„œ ์•Œ๋ ค์ฃผ๋Š” ์•Œ๋ฆผ์„ ๋†“์น˜์ง€ ์•Š๋„๋ก Amazon EventBridge, AWS Lambda๋ฅผ ํ†ตํ•ด ์ด๋ฒคํŠธ ์ •๋ณด๋ฅผ Airtable์— ์ €์žฅํ•˜์—ฌ ์ผ์ • ์‹œ๊ฐํ™” ๋ฐ ๊ด€๋ฆฌํ•ด ๋ณด์ž!

AWS Health์—์„œ event๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด python์œผ๋กœ ์ž‘์„ฑ๋œ Lambda ํ•จ์ˆ˜๊ฐ€ ์‹คํ–‰๋˜๋„๋ก EventBridge๋ฅผ ๊ตฌ์„ฑํ•œ๋‹ค. Lambda ํ•จ์ˆ˜๋Š” Airtable์˜ API๋ฅผ ์ด์šฉํ•˜์—ฌ Health event ์ •๋ณด๋ฅผ Airtable์˜ ํ…Œ์ด๋ธ”์— ์ €์žฅํ•œ๋‹ค.

 

 

1. Airtable ๊ตฌ์„ฑ

Airtable ๊ตฌ์„ฑ

  • Name (Single line text)
  • createdAt (Created time)
  • Account (Single line text)
  • Service (Single line text)
  • eventTypeCategory (Single select [accountNotification, issue, scheduledChange])
  • description (Long text)
  • startTime (Date)
  • endTime (Date)
  • affectedEntities (Single line text)

AWS Health์—์„œ ๋ฐœ์ƒํ•œ ์ด๋ฒคํŠธ๋ฅผ Airtable์— ์ €์žฅํ•˜๊ธฐ ์œ„ํ•ด ์œ„์™€ ๊ฐ™์ด base๋ฅผ ๊ตฌ์„ฑํ•˜์˜€๋‹ค.

์œ„ base์— ์ด๋ฒคํŠธ ๊ด€๋ จ ๋ ˆ์ฝ”๋“œ๋ฅผ ๋“ฑ๋กํ•˜๊ธฐ ์œ„ํ•ด Airtable์—์„œ ์ œ๊ณตํ•˜๋Š” ์•„๋ž˜ API๋ฅผ ์ด์šฉํ•  ๊ฒƒ์ด๋‹ค.

[Airtable Developers Web API Docs] Create records

์ธ์ฆ์„ ์œ„ํ•œ access token ๋ฐœ๊ธ‰์€ ๋‹ค์Œ์„ ์ฐธ์กฐํ•˜์ž.

[Airtable Developers Web API Docs] Personal access tokens

์•ก์„ธ์Šค ํ† ํฐ, base ID, ํ…Œ์ด๋ธ”๋ช…์„ ํ™•์ธํ•˜์ž.

 

 

2. AWS Secrets Manager ๊ตฌ์„ฑ

Lambda์— hard-coding์œผ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅํ•˜๋Š” ๊ฒƒ์„ ํ”ผํ•˜๊ธฐ ์œ„ํ•ด AWS Secrets Manager๋ฅผ ์ด์šฉํ•ด ๋ณด์ž.

1์—์„œ ํ™•์ธํ•œ Access Token, Base ID, ํ…Œ์ด๋ธ”๋ช…์„ AWS Secrets Manager๋ฅผ ํ†ตํ•ด ์ €์žฅํ•œ๋‹ค.

Secret ์ƒ์„ฑ์„ ์™„๋ฃŒํ•˜๋ฉด AWS๋Š” ํ•ด๋‹น Secret์— ์ €์žฅ๋œ ๊ฐ’์„ ๋ถˆ๋Ÿฌ์˜ค๋Š” ์ฝ”๋“œ๋„ ์ œ๊ณตํ•ด ์ค€๋‹ค. 3์—์„œ๋Š” ์•„๋ž˜ ์ฝ”๋“œ๋ฅผ ์ด์šฉํ•˜์—ฌ Lambda ํ•จ์ˆ˜๋ฅผ ๊ตฌ์„ฑํ•  ๊ฒƒ์ด๋‹ค.

 

 

3. Lambda ํ•จ์ˆ˜ ๊ตฌ์„ฑ

3-1. lambda_function.py

์ด๋ฒคํŠธ๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” ํ•จ์ˆ˜ ์ฝ”๋“œ์˜ ๋ฉ”์„œ๋“œ

# lambda_function.py
import create_records

def lambda_handler(event, context):
    return create_records.create_records(event)

3-2. create_records.py

Airtable API๋ฅผ ์ด์šฉํ•˜์—ฌ ์›ํ•˜๋Š” base์— ๋ ˆ์ฝ”๋“œ๋ฅผ ์ƒ์„ฑํ•œ๋‹ค. Airtable API๋ฅผ ์ด์šฉํ•˜๊ธฐ ์œ„ํ•ด requests ๋ชจ๋“ˆ์„ ์ด์šฉํ•œ๋‹ค. request ๋ชจ๋“ˆ์„ Lambda์— ์—…๋กœ๋“œํ•˜๊ธฐ ์œ„ํ•ด ์•„๋ž˜ ๋งํฌ๋ฅผ ์ฐธ์กฐํ•˜์˜€๋‹ค.

 AWS Docs: .zip ํŒŒ์ผ ์•„์นด์ด๋ธŒ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ Python Lambda ํ•จ์ˆ˜ ๋ฐฐํฌ

# create_records.py
import requests
import json
import get_secret

def create_records(event):
    endTime = event["detail"].get("endTime")
    affectedEntities = None
    if event["detail"].get("affectedEntities"):
        affectedEntities = event["detail"]["affectedEntities"][0]["entityValue"]
    
    fields = {
        "Name": event["detail"]["eventTypeCode"],
        "Account": event["account"],
        "Service": event["detail"]["service"],
        "eventTypeCategory": event["detail"]["eventTypeCategory"],
        "description": event["detail"]["eventDescription"][0]["latestDescription"],
        "startTime": event["detail"]["startTime"],
        "endTime": endTime,
        "affectedEntities": affectedEntities
    }
    
    secrets = json.loads(get_secret.get_secret())
    url = f"<https://api.airtable.com/v0/{secrets['baseID']}/{secrets['tableName']}>"
    authToken = secrets["authToken"]
    headers = {
        'Authorization': 'Bearer ' + authToken,
        'Content-Type': 'application/json'
        }
    payload = {
        "records":[
            {
                "fields": fields
                }
            ]
        }
        
    res = requests.post(url, headers=headers, json=payload)
    return(res.text)

3-3. get_secret.py

boto3์„ ์ด์šฉํ•˜์—ฌ AWS Secrets Manager์— ์ €์žฅํ•œ ๊ฐ’์„ ๊ฐ€์ ธ์˜จ๋‹ค.

# get_secret.py
import boto3
from botocore.exceptions import ClientError

def get_secret():
    secret_name = "hyeonju-airtable-secrets"
    region_name = "ap-northeast-2"

    # Create a Secrets Manager client
    session = boto3.session.Session()
    client = session.client(
        service_name='secretsmanager',
        region_name=region_name
    )

    try:
        get_secret_value_response = client.get_secret_value(
            SecretId=secret_name
        )
    except ClientError as e:
        raise e

    # Decrypts secret using the associated KMS key.
    secret = get_secret_value_response['SecretString']

    return secret

 

 

 

4. IAM Policy ์ถ”๊ฐ€

Secrets Manager์— ์ €์žฅ๋œ ๊ฐ’์„ ์กฐํšŒํ•˜๊ธฐ ์œ„ํ•ด Lambda์— ์—ฐ๊ฒฐ๋˜์–ด ์žˆ๋Š” IAM Role์— secretsmanager:GetSecretValue๋ฅผ ํ—ˆ์šฉํ•˜๋Š” Policy๋ฅผ ์ถ”๊ฐ€ํ•œ๋‹ค.

(Lambda ํ•จ์ˆ˜์— ์—ฐ๊ฒฐ๋˜์–ด ์žˆ๋Š” Execution Role์€ [AWS Lambda ์ฝ˜์†” > Configuration ํƒญ > Permissions ์„น์…˜]์—์„œ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.)

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": "secretsmanager:GetSecretValue",
            "Resource": "arn:aws:secretsmanager:ap-northeast-2:############:secret:{ secret-name }-######"
        }
    ]
}

 

 

5. ์ค‘๊ฐ„ ์ ๊ฒ€

{
  "version": "0",
  "id": "1######-0###-2###-2d84-#########",
  "detail-type": "AWS Health Event",
  "source": "aws.health",
  "account": "#########",
  "time": "2022-11-02T07:30:00Z",
  "region": "ap-northeast-2",
  "resources": [
    "##############"
  ],
  "detail": {
    "eventArn": "arn:aws:health:ap-northeast-2::event/CLOUDFORMATION/AWS_CLOUDFORMATION_OPERATIONAL_NOTIFICATION/AWS_CLOUDFORMATION_OPERATIONAL_NOTIFICATION_##############################",
    "service": "CLOUDFORMATION",
    "eventTypeCode": "AWS_CLOUDFORMATION_OPERATIONAL_NOTIFICATION",
    "eventTypeCategory": "accountNotification",
    "startTime": "Wed, 2 Nov 2022 07:30:00 GMT",
    "eventDescription": [
      {
        "language": "en_US",
        "latestDescription": "English follows Korean | ํ•œ๊ตญ์–ด๋ฒ„์ „ ๋’ค์— ์˜์–ด๋ฒ„์ „์ด ์žˆ์Šต๋‹ˆ๋‹ค \\n\\n์ž์ฒด ๊ด€๋ฆฌํ˜• AWS CloudFormation StackSets ๋กœ์˜ ์˜จ๋ณด๋”ฉ์„ ๊ฐ„์†Œํ™”ํ•˜๊ธฐ ์œ„ํ•ด ๋ฐฑ์—”๋“œ ๊ถŒํ•œ ์ •์ฑ…์„ ์—…๋ฐ์ดํŠธํ–ˆ์Šต๋‹ˆ๋‹ค. 2022๋…„ 10์›” 31์ผ๋ถ€ํ„ฐ ์ž์ฒด ๊ด€๋ฆฌํ˜• StackSets ๋ฅผ ์‚ฌ์šฉ ์‹œ์ž‘ํ•˜๊ธฐ ์œ„ํ•œ ์ „์ œ ์กฐ๊ฑด์œผ๋กœ AWSCloudFormationStackSetExecutionRole ๋‚ด sns* ๊ถŒํ•œ์ด ๋” ์ด์ƒ ํ•„์š”ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.\\n\\nSNS ์ž์›์„ ์‚ฌ์šฉํ•  ๊ณ„ํš์ด ์—†์œผ์‹  ๊ณ ๊ฐ์€ AWSCloudFormationStackSetExecutionRole ์—์„œ ๊ธฐ์กด sns* ๊ถŒํ•œ์„ ์ œ๊ฑฐํ•ด๋„ ๋ฌด๋ฐฉํ•ฉ๋‹ˆ๋‹ค. ์ด ๊ถŒํ•œ์€ Amazon SNS ์ž์›์„ ํ”„๋กœ๋น„์ €๋‹ํ•  ๋•Œ๋งŒ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.\\n\\nStackSets๋ฅผ ์‚ฌ์šฉํ•˜๋ ค๋ฉด ํ•ด๋‹น ๊ณ„์ •๋‚ด ์‚ฌ์šฉ์ž ๋งž์ถคํ˜• ๊ด€๋ฆฌ์ž ์—ญํ• ์„ ์œ„์ž„๋ฐ›์„ ์ˆ˜ ์žˆ๋Š” AWSCloudFormationStacksetExecutionRole์ด๋ผ๋Š” ์„œ๋น„์Šค ์—ญํ• ์„ ์ƒ์„ฑํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ด์ „์—๋Š” StackSets๊ฐ€ ์‚ฌ์šฉ์ž๋ฅผ ๋Œ€์‹ ํ•˜์—ฌ ์ž์›์„ ๊ด€๋ฆฌํ•˜๊ณ  ํ”„๋กœ๋น„์ €๋‹ํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•˜๊ธฐ ์œ„ํ•ด AWS CloudFormationStackSetExecutionRole์— ๋Œ€ํ•œ sns: *, s3: * ๋ฐ cloudformation: * ๊ถŒํ•œ์„ ์ œ๊ณตํ•ด์•ผ ํ–ˆ์Šต๋‹ˆ๋‹ค. ์ด์ œ๋Š” StackSets๊ฐ€ ์ž‘๋™ํ•˜๊ธฐ ์œ„ํ•ด AWSCloudFormationStackSetExecutionRole๋‚ด sns: * ๊ถŒํ•œ์ด ๋” ์ด์ƒ ํ•„์ˆ˜์กฐ๊ฑด์ด ์•„๋‹™๋‹ˆ๋‹ค. AWSCloudFormationStackSetExecutionRole์—๋Š” ์—ฌ์ „ํžˆ s3: * ๋ฐ cloudformation: * ๊ถŒํ•œ์ด ํ•„์š”ํ•œ ์  ์ฐธ๊ณ  ๋ถ€ํƒ๋“œ๋ฆฝ๋‹ˆ๋‹ค.\\n\\n์‚ฌ์šฉ์ž ์„ค๋ช…์„œ [1] ์—์„œ StackSets์— ์ž์ฒด ๊ด€๋ฆฌํ˜• ๊ถŒํ•œ์„ ๋ถ€์—ฌํ•˜๋Š” ๋ฐฉ๋ฒ•์— ๋Œ€ํ•ด ์ž์„ธํžˆ ์•Œ์•„๋ณด์‹ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.\\n\\n์งˆ๋ฌธ์ด๋‚˜ ์šฐ๋ ค ์‚ฌํ•ญ์ด ์žˆ๋Š” ๊ฒฝ์šฐ, AWS Support [2] ์— ๋ฌธ์˜ํ•ด์ฃผ์‹œ๊ธฐ ๋ฐ”๋ž๋‹ˆ๋‹ค.\\n\\n[1] <https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/stacksets-prereqs-self-managed.html\\n[2]> <https://aws.amazon.com/support\\n\\n---\\n\\nWe> have updated our back-end permissions policy to simplify on-boarding to self-managed AWS CloudFormation StackSets. Starting October 31, 2022, AWS CloudFormation StackSets no longer requires sns* permissions in AWSCloudFormationStackSetExecutionRole as a prerequisite for getting started with self-managed StackSets.\\n\\nCustomers not planning to use any SNS resources can safely remove their existing sns* permissions from their AWSCloudFormationStackSetExecutionRole. These are only required when provisioning Amazon SNS resources.\\n\\nStackSets requires you to create a service role named AWSCloudFormationStackSetExecutionRole that trusts the customized administration role for each target account. Previously, you needed to provide sns:*, s3:*, and cloudformation:* permissions to AWSCloudFormationStackSetExecutionRole as prerequisites to allow StackSets to manage and provision resources on your behalf. Now, StackSets has removed the explicit need for sns:* permissions as prerequisites in AWSCloudFormationStackSetExecutionRole. Please note, you will still need to provide s3:* and cloudformation:* permissions in AWSCloudFormationStackSetExecutionRole.\\n\\nLearn more about how to grant self-managed permissions for StackSets in the User Guide [1].\\n\\nIf you have any questions or concerns, please reach out to AWS Support [2].\\n\\n[1] <https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/stacksets-prereqs-self-managed.html\\n[2]> <https://aws.amazon.com/support>"
      }
    ],
    "affectedEntities": [
      {
        "entityValue": "##############"
      }
    ]
  }
}

AWS Health Event์˜ ํ˜•์‹์€ ์œ„์™€ ๊ฐ™๋‹ค.

3์—์„œ ๊ตฌ์„ฑํ•œ Lambda ํ•จ์ˆ˜๋ฅผ ํ…Œ์ŠคํŠธํ•ด ๋ณด์ž. Test event์˜ Event JSON์— ์œ„ ๋‚ด์šฉ์„ ๋„ฃ๊ณ  test๋ฅผ ์ˆ˜ํ–‰ํ—€์„ ๋•Œ, ๋ชฉํ‘œ๋กœ ํ–ˆ๋˜ base์— ๋ฐ์ดํ„ฐ๊ฐ€ ์ž˜ ์ž…๋ ฅ๋˜๋ฉด ์„ฑ๊ณต. ๋งŒ์•ฝ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•˜๊ฑฐ๋‚˜ ๋ฐ์ดํ„ฐ๊ฐ€ ์ž…๋ ฅ๋˜์ง€ ์•Š์€ ๊ฒฝ์šฐ, 1๋ฒˆ๋ถ€ํ„ฐ ๋Œ์•„๊ฐ€ ๋‹ค์‹œ ์‚ดํŽด๋ณด์ž.

 

 

 

6. EventBridge Rule ์ƒ์„ฑ

โš ๏ธ ์ฃผ์˜ โš ๏ธ
EventBrdige Rule์ด ์ƒ์„ฑ๋œ ๋ฆฌ์ „์— ๋Œ€ํ•œ aws health event๋งŒ ์ ์šฉ์ด ๋œ๋‹ค.
์—ฌ๋Ÿฌ ๋ฆฌ์ „์˜ Health Event๋ฅผ Airtable๋กœ ๋ฐ›์•„๋ณด๊ณ  ์‹ถ์€ ๊ฒฝ์šฐ, ๊ฐ ๋ฆฌ์ „์—์„œ ์•„๋ž˜์™€ ๊ฐ™์€ EventBridge Rule์„ ์ƒ์„ฑํ•˜์ž.
  • Rule type: Rule with an event pattern (standard rule)
  • Event source: AWS events or EventBridge partner events
  • Event pattern: Health์—์„œ ๋ฐœ์ƒํ•˜๋Š” ๋ชจ๋“  ์ด๋ฒคํŠธ๋ฅผ ์ˆ˜์‹ ํ•˜๊ณ  ์‹ถ๊ธฐ ๋•Œ๋ฌธ์— ์•„๋ž˜์™€ ๊ฐ™์€ Event pattern์„ ์„ค์ •ํ•œ๋‹ค.
{
  "source": ["aws.health"]
}

 

  • Target: 3์—์„œ ์ƒ์„ฑํ•œ Lambda function์˜ arn ์ž…๋ ฅ

EventBridge๋ฅผ ์œ„์™€ ๊ฐ™์ด ์„ค์ •ํ•˜๋ฉด Lambda Console์—์„œ ์œ„์™€ ๊ฐ™์ด EventBridge๊ฐ€ ํŠธ๋ฆฌ๊ฑฐ ์†Œ์Šค๋กœ ์ถ”๊ฐ€๋์Œ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

 

 

 

7. ํ™•์ธ

AWS Health Dashboard > Event log์—์„œ ํ™•์ธ๋˜๋Š” ์ •๋ณด๊ฐ€ Airtable ํ…Œ์ด๋ธ”์— ์ž˜ ์ €์žฅ๋˜๋Š”์ง€ ํ™•์ธํ•ด ๋ณด์ž.

 

 

 

 

๐Ÿ”— ์ฐธ๊ณ  ๋งํฌ