星期一, 8月 30, 2021

使用 aws cli 建立 現有 EC2 status check alarm 小記

使用 aws cli 建立 現有 EC2 status check alarm 小記


aws-cli: 2.2.4

OS: container with openSUSE Leap 15.2



因為工作上面的需要, 雲端的資源如果是上線的服務大多需要設定告警機制.

如果是在 GCP 或是 Azure 的平台上, 是可以透過設定 Tag 的方式來進行告警條件的設定, 簡化設定告警的數量, 例如


AWS CloudWatch alarm 一次監控一個指標, 而指標的維度不同, 使得每個EC2 instance的指標都是唯一的, 因此目前並沒有直接的方法可以為相同指標的所有EC2 instance創建單一個通用的CloudWatch alarm.

所以解決的方式可能就是透過 aws cli 配合 shell script 方式來進行處理, 處理上大概有 2 個方向

  • 將目前現有的 EC2 建立 CloudWatch Alarm

  • 建立 EC2 時, 建立 CloudWatch Alarm


這一篇來寫將目前現有的 EC2 建立 CloudWatch Alarm


==== 將目前現有的 EC2 建立 CloudWatch Alarm ====


先來整理一下, 如果目前有現有的 EC2, 如何針對單一的 EC2 進行建立 CloudWatch Alarm


列出目前 Region 的 EC2 ID

測試 Region: us-east-2 (Ohio)


# aws  ec2 --region  us-east-2 --output  text  describe-instances --query  "Reservations[*].Instances[*].{Instances:InstanceId}"  --profile  default


i-0864c1f7405d84941

i-0339fb4f575b549f9


  • 因為我的環境有不同的  AWS ID, 所以利用 --profile 指定環境


因為我的 CloudWatch Alarm 有結合 SNS 進行通知, 等等建立 CloudWatch Alarm 要輸入 SNS ARN, 故使用 aws sns list-topics --region us-east-2 --profile xxxxx 列出 SNS 相關資訊


# aws  sns  list-topics  --region  us-east-2  --profile  default


{

    "Topics": [

        {

            "TopicArn": "arn:aws:sns:us-east-2:783127832903:Notity_EC2_CPU_50"

        }

    ]

}


接下來使用 aws cloudwatch put-metric-alarm 指令來建立



# aws  cloudwatch  put-metric-alarm  --alarm-name  healthcheck --alarm-description  "health check status failed"  --metric-name  StatusCheckFailed  --namespace  AWS/EC2  --statistic  Maximum  --period  60  --threshold  1  --comparison-operator  GreaterThanOrEqualToThreshold   --dimensions  "Name=InstanceId,Value=i-0864c1f7405d84941--evaluation-periods  2 --alarm-actions  arn:aws:sns:us-east-2:783127832903:Notity_EC2_CPU_50 --region  us-east-2 --profile  default


  • --dimensions 的 Value 請使用上面查詢到的 EC2 instance ID, SNS ARN 請換成要通知的 SNS Topic

  • 使用 EC2 的 StatusCheckFailed 這個 Metric 來判斷, 所以如果是自行停止 EC2  的狀況, 也只是資料不足

  • --period 60 - 60 秒採樣進行採樣

  • --evaluation-periods 2 - 配合 上面的 --period 60 秒, 就是 data point 為 60 秒 x2 就是 120 秒

  • --threshold 1 - 門檻值為 1, 

  • 配合比對方式 GreaterThanOrEqualToThreshod, 當 status check fail 達到 120 秒(驗證單位為 2 個 perion) (門檻值 1), 就滿足告警

  • --alarm-actions - 使用 SNS 來通知

  • 如果在使用 aws cloudwatch put-metric-alarm 指令建立的時候, 遇到同樣名稱的 alarm, 參考官網的說明是會覆蓋掉的, If an alarm with the same name already exists, it will be overwritten by the new alarm.


列出目前 Region 的 cloudwatch alarm 名稱來確認

# aws  cloudwatch  describe-alarms  --region  us-east-2  --profile default  |  grep  'AlarmName'


            "AlarmName": "healthcheck",


再來就是寫成 shell script 來針對所有的 EC2 建立 CloudWatch Alarm

建立一個 aws_create_CloudWatchAlarm_existEC2_statusCheck.sh 

內容如下


#!/bin/bash

# edit by sakana 2021/8/29

# 將特定的 Region 目前的 EC2 加上 CloudWatch 告警, 使用 StatusCheckFailed


echo ""

echo "將特定的 Region 目前的 EC2 加上 CloudWatch 告警, 使用 StatusCheckFailed"


# 設定使用 AWS 帳號

echo ""

echo "設定使用 AWS 帳號, 這邊請用您的 profile 名稱"

echo ""

read -e -p "Please enter aws account name: " -i "sakana" account_name


# 設定使用 AWS Region

echo ""

echo "設定使用 AWS Region"

echo "N.Virginia: us-east-1"

echo "Tokyo: ap-northeast-1"

echo ""

read -e -p "Please enter aws region name: " -i "ap-northeast-1" region_name


# 設定使用 SNS Topic

echo ""

echo "==== 列出目前的 SNS Topic ARN ===="

echo ""

aws sns list-topics --region $region_name --profile $account_name

echo "================================"

echo "設定使用 SNS Topic"

echo ""

read -e -p "Please enter sns arn: " -i "arn:aws:sns:xxxxxxxxx" sns_arn

echo ""


# 執行 新增 CloudWatch Alarm

echo "執行 新增 CloudWatch Alarm"

for InstanceId in `aws ec2 --region $region_name --output text describe-instances --query "Reservations[*].Instances[*].{Instances:InstanceId}" --profile $account_name`

do

  echo  ""

  aws cloudwatch put-metric-alarm --alarm-name EC2_healthcheck_$InstanceId --alarm-description "health check status failed" --metric-name StatusCheckFailed --namespace AWS/EC2 --statistic Maximum --period 60 --threshold 1 --comparison-operator GreaterThanOrEqualToThreshold  --dimensions "Name=InstanceId,Value=$InstanceId" --evaluation-periods 2 --alarm-actions $sns_arn --region $region_name --profile $account_name

  echo  ""

done


這樣應該會把目前 Region 上面的 EC2 都建立 CloudWatch Alarm :)


後續思考

  • 這個 script 的好處是目前 Region 的 EC2 都加上 StatusCheckFailed 的 Alarm, 但是如果 EC2 被移除後的管理是後續要思考的點


不過也算是又向 AWS 前進一步


~ enjoy it



Reference

星期二, 8月 10, 2021

使用 aws cloudtrail lookup-events 指令搜尋事件

使用 aws cloudtrail lookup-events 指令搜尋事件

aws cli : 2.2.4


因為工作上的需求, 可能需要定時去查詢或是紀錄特定事件

例如  RDS 上面密碼變更的行為, 但是因為 CloudTrail 預設只保留 90 天的 log


如果不想要將相關 log 轉到 S3 或是其他的地方. 目前想到 2 種方式

  1. 定時使用 aws cli 去進行相關查詢. ( 如果有特定對象的話 )

  2. 使用 AWS CloudWatch Event Rule 結合 SNS 發送通知儲存


今天來嘗試的是方式 1


AWS Resource Target:  RDS Aurora with MySQL

針對行為: 更改 masterUserPassword


實驗做法

  • 建立 RDS database

  • 更改 master User Password

  • 去 CloudTrail 的 Event history 進行查詢



以上的方式可以在 Console 查詢到 RDS 進行 master 使用者密碼變更


那如果想要使用 aws cli 應該如何進行呢? 目前使用 aws cloudtrail lookup-events 指令來進行


首先嘗試直接下指令, 但是你會發現這樣的輸出實在太多, 效果不彰

# aws  cloudtrail  lookup-events


接下來加上限制輸出的輸量

# aws  cloudtrail  lookup-events  --max-results 1


  • 這樣好多了, 因為資料很短, 所以有助於了解輸出資料的格式



再來嘗試加上起始時間

# aws  cloudtrail  lookup-events  --start-time  2021-07-19  --end-time  2021-07-20


然後試試看加上我們要找的屬性, 使用 eventname 來進行過濾


# aws  cloudtrail  lookup-events --lookup-attributes  AttributeKey=EventName,AttributeValue=ModifyDBCluster  --region  us-east-1


  • 這邊要注意你要查詢的 Region 是否符合你的資源所在區域

  • 另外不同的 DB, 有可能是出現再 ModifyDBCluster 也有可能是 ModifyDBInstance, 這點請依照實際的狀況調整


這樣的輸出, 只要是 ModifyDBCluster 都會被列入, 但是我想要針對有變更 master password 才納入, 所以使用 jq 來進行處理


輸出的結果我們只想取出

  • userIdentity.UserName - 來知道是誰設定

  • eventTime - 事件時間

  • eventName - 事件名稱

  • awsRegion - 發生區域

  • sourceIPAddress - 來源 IP

  • requestParameters.materUserPassword - 用來識別有變更密碼行為

  • dBClusterIdentifier  - DB 名稱



例如輸出的資料為 (部份)


{

  "eventVersion": "1.08",

  "userIdentity": {

    "type": "IAMUser",

    "principalId": "AIDAJ7LK4OH5WD54F4P5U",

    "arn": "arn:aws:iam::78212783195:user/1303067",

    "accountId": "782127831905",

    "accessKeyId": "ASIA4MGTTTNQN75FOXWD",

    "userName": "1303067",

    "sessionContext": {

      "sessionIssuer": {},

      "webIdFederationData": {},

      "attributes": {

        "mfaAuthenticated": "true",

        "creationDate": "2021-07-19T14:08:35Z"

      }

    }

  },

  "eventTime": "2021-07-19T15:01:45Z",

  "eventSource": "rds.amazonaws.com",

  "eventName": "ModifyDBCluster",

  "awsRegion": "us-east-1",

  "sourceIPAddress": "59.126.193.27",

  "userAgent": "aws-internal/3 aws-sdk-java/1.11.975 Linux/4.9.230-0.1.ac.224.84.332.metal1.x86_64 OpenJDK_64-Bit_Server_VM/25.242-b08 java/1.8.0_242 vendor/Oracle_Corporation cfg/retry-mode/legacy",

  "requestParameters": {

    "dBClusterIdentifier": "test20210719",

    "applyImmediately": true,

    "masterUserPassword": "****",

    "allowMajorVersionUpgrade": false

  },

.... (恕略)



最後的指令為


# aws  cloudtrail  lookup-events  --lookup-attributes  AttributeKey=EventName,AttributeValue=ModifyDBCluster  --region  us-east-1  --query  'Events[*].CloudTrailEvent'  |  jq  -r  '.[]'  |  jq  -c  '. | select(.requestParameters.masterUserPassword) | {username: .userIdentity.userName, eventTime: .eventTime, eventName: .eventName, awsRegion: .awsRegion, sourceIPAddress: .sourceIPAddress, masterUserPassword: .requestParameters.masterUserPassword, dBClusterIdentifier: .requestParameters.dBClusterIdentifier}' | jq .


  • jq 的部份

    • -r               output raw strings, not JSON texts;

    • -c               compact instead of pretty-printed output;

  • 這邊要讓自己記住的 jq 用法

    • 用 select (  ) 選取我們希望要符合的資料, 以這個例子來說就是 .requestParameters.masterUserPassword 

    • 使用  | pipe 串接後, 用 大括號  {  }, 然後用冒號為區隔, 定義輸出欄為名稱與 value, 例如 輸出名稱 username: 內容去對應 .userIdentity.userName


輸出參考


{

  "username": "1303067",

  "eventTime": "2021-07-19T14:28:03Z",

  "eventName": "ModifyDBCluster",

  "awsRegion": "us-east-1",

  "sourceIPAddress": "59.127.193.27",

  "masterUserPassword": "****",

  "dBClusterIdentifier": "test20210719"

}


這樣又跟 AWS 靠近一步

~ enjoy it


Reference:



星期二, 8月 03, 2021

使用 aws cli 建立 CloudWatch Event Rule 與 Target - 使用現有的 Rule 為範本

使用 aws cli 建立 CloudWatch Event Rule 與 Target - 使用現有的 Rule 為範本


上一篇文章是使用 Console 的方式定義 Input Transformer 輸出到 CloudTrail Event


這一篇文章要來寫如何使用 aws cli 來建立 CloudWatch Event Rule 與 Target - 在已經有現有的 Rule 下.


環境假設

  • 已經建立 CloudWatch Event Rule, 名稱為 Notify_Create_Delete_IAM_User

  • 已經建立 SNS 通知方式 - 使用 Email 通知, 且已經完成訂閱

  • 使用 Input Transformer 設定 Notify_Create_Delete_IAM_User 這個 Rule 的 Target 為 SNS 然後設定 Input Path 與 Input Template


首先觀察先前建立的 CloudWatch Rule "Notify_Create_Delete_IAM_User" 的內容



Event pattern 為


{

  "source": [

    "aws.iam"

  ],

  "detail-type": [

    "AWS API Call via CloudTrail"

  ],

  "detail": {

    "eventSource": [

      "iam.amazonaws.com"

    ],

    "eventName": [

      "CreateUser",

      "DeleteUser"

    ]

  }

}


  • 這邊是 JSON 格式


將內容儲存為 Notify_Create_Delete_IAM_User.json


因為等等要使用到的 aws 指令輸入只接受 string 格式

所以使用 jq 指令轉為 string


# cat  Notify_Create_Delete_IAM_User.json |  jq  tostring


"{\"source\":[\"aws.iam\"],\"detail-type\":[\"AWS API Call via CloudTrail\"],\"detail\":{\"eventSource\":[\"iam.amazonaws.com\"],\"eventName\":[\"CreateUser\",\"DeleteUser\"]}}"


  • 這個之前真的沒有注意到這個功能, jq 真是太好用了 


當然也可以反向使用 ( string to JSON)


# echo  "{\"source\":[\"aws.iam\"],\"detail-type\":[\"AWS API Call via CloudTrail\"],\"detail\":{\"eventSource\":[\"iam.amazonaws.com\"],\"eventName\":[\"CreateUser\",\"DeleteUser\"]}}"  |  jq


{

  "source": [

    "aws.iam"

  ],

  "detail-type": [

    "AWS API Call via CloudTrail"

  ],

  "detail": {

    "eventSource": [

      "iam.amazonaws.com"

    ],

    "eventName": [

      "CreateUser",

      "DeleteUser"

    ]

  }

}




使用 aws 指令建立 AWS CloudWatch Event Rule


# aws  events  put-rule  --name "test-2021-08-02"  --region  us-east-1  --event-pattern  

"{\"source\":[\"aws.iam\"],\"detail-type\":[\"AWS API Call via CloudTrail\"],\"detail\":{\"eventSource\":[\"iam.amazonaws.com\"],\"eventName\":[\"CreateUser\",\"DeleteUser\"]}}"


  • 可以到 Console 觀察有建立一個 CloudWatch Event Rule, 名稱為 test-2021-08-02

  • 使用剛剛既有的 Rule 的 Event Pattern


接下來要觀察先前建立的 CloudWatch Event  Rule Target 內容


# aws  events  list-targets-by-rule  --rule  "Notify_Create_Delete_IAMuser"  --region  us-east-1


{

    "Targets": [

        {

            "Id": "1",

            "Arn": "arn:aws:sns:us-east-1:783147821205:Notify_CreateIAMUser_20210714",

            "InputTransformer": {

                "InputPathsMap": {

                    "AccountID": "$.detail.userIdentity.accountId",

                    "Create-userName": "$.detail.responseElements.user.userName",

                    "Region": "$.detail.awsRegion",

                    "Staff-userName": "$.detail.userIdentity.userName",

                    "eventName": "$.detail.eventName",

                    "eventTime": "$.detail.eventTime",

                    "sourceIPAddress": "$.detail.sourceIPAddress"

                },

                "InputTemplate": "\"這是來自雲端課的通知,偵測到Assume Role行為\"\n\"AccountID: <AccountID>\"\n\"Region:<Region>\"\n\"建立者: <Staff-userName>\"\n\"EventTime: <eventTime>\"\n\"SourceIP: <sourceIPAddress>\"\n\"EventName: <eventName>\"\n\"建立帳號名稱: <Create-userName>\""

            }

        }

    ]

}


  • Id 的部份可以換成自訂的 ID, 使用 Console 建立的會很長

  • 我是透過 SNS 通知, 所以 SNS ARN 請換成自己的 SNS ARN


使用輸出導向的方式將內容存成 notify_create_delete_iam_user_target.json


# aws  events  list-targets-by-rule  --rule  "Notify_Create_Delete_IAMuser" --region  us-east-1   > notify_create_delete_iam_user_target.json


因為之後要套用 Rule, 所以要編輯檔案


{

    "Rule":"test-2021-08-02",

    "Targets": [

        {

            "Id": "1",

            "Arn": "arn:aws:sns:us-east-1:783147821205:Notify_CreateIAMUser_20210714",

            "InputTransformer": {

                "InputPathsMap": {

                    "AccountID": "$.detail.userIdentity.accountId",

                    "Create-userName": "$.detail.responseElements.user.userName",

                    "Region": "$.detail.awsRegion",

                    "Staff-userName": "$.detail.userIdentity.userName",

                    "eventName": "$.detail.eventName",

                    "eventTime": "$.detail.eventTime",

                    "sourceIPAddress": "$.detail.sourceIPAddress"

                },

                "InputTemplate": "\"這是來自雲端課的通知,偵測到Assume Role行為\"\n\"AccountID: <AccountID>\"\n\"Region:<Region>\"\n\"建立者: <Staff-userName>\"\n\"EventTime: <eventTime>\"\n\"SourceIP: <sourceIPAddress>\"\n\"EventName: <eventName>\"\n\"建立帳號名稱: <Create-userName>\""

            }

        }

    ]

}


  • 加上 Rule 設定, 後面請接上剛剛新建立的 Rule, 然後記得加上 逗號 ,

  • Id 的部份可以換成自訂的 ID, 使用 Console 建立的會很長

  • 我是透過 SNS 通知, 所以 SNS ARN 請換成自己的 SNS ARN


完成準備檔案


使用 aws 指定套用 Rule 的 Target

我的 notify_create_delete_iam_user_target.json 在目前的目錄下


# aws  events  put-targets  --region  us-east-1  --cli-input-json  file://./notify_create_delete_iam_user_target.json


{

    "FailedEntryCount": 0,

    "FailedEntries": []

}


回 Console 驗證是否完成 :)


又向 AWS 前進一步


~ enjoy it



Reference: