Total Pageviews

2024/02/11

透過 AWS API Gateway (Rest API) 呼叫 Lambda

設定步驟如下

1.  在 Lambda 撰寫簡單的 Python 程式

def lambda_handler(event, context):
    return {
        'statusCode': 200,
        'body': 'Hello from Lambda!'
    }

2. 測試此程式是否如預期正確執行

Test Event Name
my-test

Response
{
  "statusCode": 200,
  "body": "Hello from Lambda!"
}

Function Logs
START RequestId: 131fa0f2-faa0-4ca3-a11d-4c63b6383b1c Version: $LATEST
END RequestId: 131fa0f2-faa0-4ca3-a11d-4c63b6383b1c
REPORT RequestId: 131fa0f2-faa0-4ca3-a11d-4c63b6383b1c	Duration: 1.37 ms	Billed Duration: 2 ms	Memory Size: 128 MB	Max Memory Used: 34 MB

Request ID
131fa0f2-faa0-4ca3-a11d-4c63b6383b1c

3.  至 API Gateway Build Rest API


4. 選擇「New API」、指定「API Name」、click「Create API」


5. click 「Create Resource」


6. 指定 「Resource name」 、click 「Create Resource」


7. create GET method 與指定 Lambda function name (i.e. HelloFunction)



8. Deploy API 與指定 stage name 為 dev



9. 回到原本的 Lambda function,click "Add tirgger"


10. 指定 API Gateway、選擇 "Use existing API"


11. 指定既有 API name (i.e. HelloFunction)、stage 設定為 "dev"、click "Add"


12. 設定成功,並取得 API endpoint



13. 測試成功




2024/02/10

使用 AWS API Gateway 整合 Lambda

以下是我的 Lambda code,他會取得網址的 name 參數值,並組成 "Hello World~"+name參數值的字串

def lambda_handler(event, context):
    # 從事件對象的 queryStringParameters 中獲取 "name" 參數
    name = event.get('queryStringParameters', {}).get('name', 'World')
    
    # 拼接字符串並返回結果
    return {
        'statusCode': 200,
        'headers': {
            'Content-Type': 'application/json'
        },
        'body': f'Hello World~{name}'
    }


以下是 API Gateway 的設定

1. 在 Create API 選擇 HTTP API => Click Build


2. 指定欲整合的 Lambda function name 與 API name


3. 指定 http method 與 resource path


4. 指定 stage name


5. 在 review page click Create button


Postman 測試畫面

1. 在 Stages 頁面取得 http url


2. 使用以下網址到 Postman 驗證結果

https://vo6ltr8xrl.execute-api.us-east-1.amazonaws.com/dev/hello?name=Mandy




2024/02/09

執行 AWS PutMetricData operation 出現錯誤:The parameter MetricData.member.1.Timestamp must specify a time no more than two hours in the future

Problem

AWS PutMetricData operation 內容如下

#!/bin/bash
aws cloudwatch put-metric-data --metric-name CriticalError --namespace MyService --value 1 --timestamp 2024-02-09T11:05:00.000Z 
aws cloudwatch put-metric-data --metric-name CriticalError --namespace MyService --value 1 --timestamp 2024-02-09T11:06:00.000Z 
aws cloudwatch put-metric-data --metric-name CriticalError --namespace MyService --value 1 --timestamp 2024-02-09T11:07:00.000Z
aws cloudwatch put-metric-data --metric-name CriticalError --namespace MyService --value 1 --timestamp 2024-02-09T11:08:00.000Z
aws cloudwatch put-metric-data --metric-name CriticalError --namespace MyService --value 1 --timestamp 2024-02-09T11:09:00.000Z
aws cloudwatch put-metric-data --metric-name CriticalError --namespace MyService --value 1 --timestamp 2024-02-09T11:10:00.000Z
aws cloudwatch put-metric-data --metric-name CriticalError --namespace MyService --value 1 --timestamp 2024-02-09T11:11:00.000Z 
aws cloudwatch put-metric-data --metric-name CriticalError --namespace MyService --value 1 --timestamp 2024-02-09T11:12:00.000Z 
aws cloudwatch put-metric-data --metric-name CriticalError --namespace MyService --value 1 --timestamp 2024-02-09T11:13:00.000Z 
aws cloudwatch put-metric-data --metric-name CriticalError --namespace MyService --value 1 --timestamp 2024-02-09T11:14:00.000Z 
aws cloudwatch put-metric-data --metric-name CriticalError --namespace MyService --value 1 --timestamp 2024-02-09T11:15:00.000Z
aws cloudwatch put-metric-data --metric-name CriticalError --namespace MyService --value 1 --timestamp 2024-02-09T11:16:00.000Z 
aws cloudwatch put-metric-data --metric-name CriticalError --namespace MyService --value 1 --timestamp 2024-02-09T11:17:00.000Z 
aws cloudwatch put-metric-data --metric-name CriticalError --namespace MyService --value 1 --timestamp 2024-02-09T11:18:00.000Z 
aws cloudwatch put-metric-data --metric-name CriticalError --namespace MyService --value 1 --timestamp 2024-02-09T11:19:00.000Z
aws cloudwatch put-metric-data --metric-name CriticalError --namespace MyService --value 1 --timestamp 2024-02-09T11:20:00.000Z
aws cloudwatch put-metric-data --metric-name CriticalError --namespace MyService --value 1 --timestamp 2024-02-09T11:21:00.000Z


執行後出現以下錯誤

An error occurred (InvalidParameterValue) when calling the PutMetricData operation: The parameter MetricData.member.1.Timestamp must specify a time no more than two hours in the future.

An error occurred (InvalidParameterValue) when calling the PutMetricData operation: The parameter MetricData.member.1.Timestamp must specify a time no more than two hours in the future.
An error occurred (InvalidParameterValue) when calling the PutMetricData operation: The parameter MetricData.member.1.Timestamp must specify a time no more than two hours in the future. An error occurred (InvalidParameterValue) when calling the PutMetricData operation: The parameter MetricData.member.1.Timestamp must specify a time no more than two hours in the future.


Root Cause

設定的 timestamp 時間不能比當前時間超過 2小時,此限制是為了防止因時間錯誤設置而導致數據不一致。


How-To

重新修正 timestamp

#!/bin/bash
aws cloudwatch put-metric-data --metric-name CriticalError --namespace MyService --value 1 --timestamp 2024-02-09T03:05:00.000Z 
aws cloudwatch put-metric-data --metric-name CriticalError --namespace MyService --value 1 --timestamp 2024-02-09T03:06:00.000Z 
aws cloudwatch put-metric-data --metric-name CriticalError --namespace MyService --value 1 --timestamp 2024-02-09T03:07:00.000Z
aws cloudwatch put-metric-data --metric-name CriticalError --namespace MyService --value 1 --timestamp 2024-02-09T03:08:00.000Z
aws cloudwatch put-metric-data --metric-name CriticalError --namespace MyService --value 1 --timestamp 2024-02-09T03:09:00.000Z
aws cloudwatch put-metric-data --metric-name CriticalError --namespace MyService --value 1 --timestamp 2024-02-09T03:10:00.000Z
aws cloudwatch put-metric-data --metric-name CriticalError --namespace MyService --value 1 --timestamp 2024-02-09T03:11:00.000Z 
aws cloudwatch put-metric-data --metric-name CriticalError --namespace MyService --value 1 --timestamp 2024-02-09T03:12:00.000Z 
aws cloudwatch put-metric-data --metric-name CriticalError --namespace MyService --value 1 --timestamp 2024-02-09T03:13:00.000Z 
aws cloudwatch put-metric-data --metric-name CriticalError --namespace MyService --value 1 --timestamp 2024-02-09T03:14:00.000Z 
aws cloudwatch put-metric-data --metric-name CriticalError --namespace MyService --value 1 --timestamp 2024-02-09T03:15:00.000Z
aws cloudwatch put-metric-data --metric-name CriticalError --namespace MyService --value 1 --timestamp 2024-02-09T03:16:00.000Z 
aws cloudwatch put-metric-data --metric-name CriticalError --namespace MyService --value 1 --timestamp 2024-02-09T03:17:00.000Z 
aws cloudwatch put-metric-data --metric-name CriticalError --namespace MyService --value 1 --timestamp 2024-02-09T03:18:00.000Z 
aws cloudwatch put-metric-data --metric-name CriticalError --namespace MyService --value 1 --timestamp 2024-02-09T03:19:00.000Z
aws cloudwatch put-metric-data --metric-name CriticalError --namespace MyService --value 1 --timestamp 2024-02-09T03:20:00.000Z
aws cloudwatch put-metric-data --metric-name CriticalError --namespace MyService --value 1 --timestamp 2024-02-09T03:21:00.000Z


在 CloudWatch Metrics 也如預期找到所執行的結果





AWS CloudWatch log group 未如預期出現所設定的 log group name

Problem

安裝 CloudWatch Agent 至 EC2,並將設定的 metrics 資料傳送至AWS CloudWatch log group,但在畫面未能找到所指定的 log group name。

config.json 的內容如下

{
        "agent": {
                "metrics_collection_interval": 1,
                "run_as_user": "cwagent"
        },
        "logs": {
                "logs_collected": {
                        "files": {
                                "collect_list": [
                                        {
                                                "file_path": "/var/log/messages",
                                                "log_group_class": "STANDARD",
                                                "log_group_name": "messages",
                                                "log_stream_name": "{instance_id}",
                                                "retention_in_days": 1
                                        }
                                ]
                        }
                }
        },
        "metrics": {
                "aggregation_dimensions": [
                        [
                                "InstanceId"
                        ]
                ],
                "append_dimensions": {
                        "AutoScalingGroupName": "${aws:AutoScalingGroupName}",
                        "ImageId": "${aws:ImageId}",
                        "InstanceId": "${aws:InstanceId}",
                        "InstanceType": "${aws:InstanceType}"
                },
                "metrics_collected": {
                        "cpu": {
                                "measurement": [
                                        "cpu_usage_idle",
                                        "cpu_usage_iowait",
                                        "cpu_usage_user",
                                        "cpu_usage_system"
                                ],
                                "metrics_collection_interval": 1,
                                "resources": [
                                        "*"
                                ],
                                "totalcpu": false
                        },
                        "disk": {
                                "measurement": [
                                        "used_percent",
                                        "inodes_free"
                                ],
                                "metrics_collection_interval": 1,
                                "resources": [
                                        "*"
                                ]
                        },
                        "diskio": {
                                "measurement": [
                                        "io_time"
                                ],
                                "metrics_collection_interval": 1,
                                "resources": [
                                        "*"
                                ]
                        },
                        "mem": {
                                "measurement": [
                                        "mem_used_percent"
                                ],
                                "metrics_collection_interval": 1
                        },
                        "statsd": {
                                "metrics_aggregation_interval": 10,
                                "metrics_collection_interval": 10,
                                "service_address": ":8125"
                        },
                        "swap": {
                                "measurement": [
                                        "swap_used_percent"
                                ],
                                "metrics_collection_interval": 1
                        }
                }
        }
}


Root Cause

到此目錄 /opt/aws/amazon-cloudwatch-agent/logs/ 檢查 CloudWatch Agent Log,發現是權限問題,我應該要把 run_as_user 改成 root

[inputs.logfile] Failed to tail file /var/log/messages with error: open /var/log/messages: permission denied


How-To

將 config.json 修改如下即可

{
        "agent": {
                "metrics_collection_interval": 1,
                "run_as_user": "root"
        },
        "logs": {
                "logs_collected": {
                        "files": {
                                "collect_list": [
                                        {
                                                "file_path": "/var/log/messages",
                                                "log_group_class": "STANDARD",
                                                "log_group_name": "messages",
                                                "log_stream_name": "{instance_id}",
                                                "retention_in_days": 1
                                        }
                                ]
                        }
                }
        },
        "metrics": {
                "aggregation_dimensions": [
                        [
                                "InstanceId"
                        ]
                ],
                "append_dimensions": {
                        "AutoScalingGroupName": "${aws:AutoScalingGroupName}",
                        "ImageId": "${aws:ImageId}",
                        "InstanceId": "${aws:InstanceId}",
                        "InstanceType": "${aws:InstanceType}"
                },
                "metrics_collected": {
                        "cpu": {
                                "measurement": [
                                        "cpu_usage_idle",
                                        "cpu_usage_iowait",
                                        "cpu_usage_user",
                                        "cpu_usage_system"
                                ],
                                "metrics_collection_interval": 1,
                                "resources": [
                                        "*"
                                ],
                                "totalcpu": false
                        },
                        "disk": {
                                "measurement": [
                                        "used_percent",
                                        "inodes_free"
                                ],
                                "metrics_collection_interval": 1,
                                "resources": [
                                        "*"
                                ]
                        },
                        "diskio": {
                                "measurement": [
                                        "io_time"
                                ],
                                "metrics_collection_interval": 1,
                                "resources": [
                                        "*"
                                ]
                        },
                        "mem": {
                                "measurement": [
                                        "mem_used_percent"
                                ],
                                "metrics_collection_interval": 1
                        },
                        "statsd": {
                                "metrics_aggregation_interval": 10,
                                "metrics_collection_interval": 10,
                                "service_address": ":8125"
                        },
                        "swap": {
                                "measurement": [
                                        "swap_used_percent"
                                ],
                                "metrics_collection_interval": 1
                        }
                }
        }
}


2024/02/07

如何透過 AWS Lambda 呼叫 SES (Simple Email Service) 發信

實作步驟如下

1. 建立 Verified Identities來做為測試用途的 Sender 與 Receiver email address


2. 建立 Lambda Function 後,進入 Configuration => Permission,確認該 Role 具備 SES 角色權限


3. 呼叫 SES 的 Python code

import boto3
from botocore.exceptions import ClientError

def lambda_handler(event, context):
    # 建立 SES 客戶端
    ses_client = boto3.client('ses', region_name='us-east-1')  # 請根據需要替換為您的 SES 區域

    # 電子郵件參數
    SENDER = "hekarey795@giratex.com"  # 替換為您的發件人地址
    RECIPIENT = "hekarey795@giratex.com"  # 替換為您的收件人地址
    SUBJECT = "AWS SES Test Email from Lambda"
    BODY_TEXT = ("This is a test email sent from AWS Lambda using SES")
    CHARSET = "UTF-8"

    # 嘗試發送電子郵件
    try:
        response = ses_client.send_email(
            Destination={
                'ToAddresses': [
                    RECIPIENT,
                ],
            },
            Message={
                'Body': {
                    'Text': {
                        'Charset': CHARSET,
                        'Data': BODY_TEXT,
                    },
                },
                'Subject': {
                    'Charset': CHARSET,
                    'Data': SUBJECT,
                },
            },
            Source=SENDER,
        )
    except ClientError as e:
        print(e.response['Error']['Message'])
    else:
        print("Email sent! Message ID:"),
        print(response['MessageId'])

4. 執行結果


5. 檢查信箱






透過 AWS EC2 Connect 連入編輯檔案,出現 "index.html" E212: Can't open file for writing 錯誤

Problem

透過 AWS EC2 Connect 連入編輯檔案,出現 "index.html" E212: Can't open file for writing 錯誤


Root Cause

會出現上述問題是因為權限不足所導致


How-To

[ec2-user@ip-172-31-43-197 html]$ vi index.html 
[ec2-user@ip-172-31-43-197 html]$ sudo vi index.html


AWS CloudShell 無法成功執行指令

Problem

我在 CloudShell 執行以下指令,卻無法正常執行

aws cognito-identity set-identity-pool-roles \
--identity-pool-id "us-east-1:xxxx-xxxx-xxxx-xxxx-xxxxxx” \
--roles unauthenticated=arn:aws:iam::xxxx:role/Cognito_DynamoPoolUnauth --output json


Root Cause

第二行的第二個 double quote 有打錯

aws cognito-identity set-identity-pool-roles \
--identity-pool-id "us-east-1:xxxx-xxxx-xxxx-xxxx-xxxxxx \
--roles unauthenticated=arn:aws:iam::xxxx:role/Cognito_DynamoPoolUnauth --output json


How-To

修正為正確的 double quote 後即可正常執行

aws cognito-identity set-identity-pool-roles \
--identity-pool-id "us-east-1:xxxx-xxxx-xxxx-xxxx-xxxxxx" \
--roles unauthenticated=arn:aws:iam::xxxx:role/Cognito_DynamoPoolUnauth --output json


2024/02/06

AWS CloudFormation Error: TemplateURL must be a supported URL

Problem

當我要執行以下 CloudFormation yaml file:

{
    "AWSTemplateFormatVersion" : "2010-09-09",
    "Resources" : {
        "myStack" : {
	       "Type" : "AWS::CloudFormation::Stack",
	       "Properties" : {
              "TemplateURL" : "https://s3.amazonaws.com/nested-demo-531193295833/s3static.json",
              "TimeoutInMinutes" : "60"
	       }
        },
        "myStack2" : {
            "Type" : "AWS::CloudFormation::Stack",
            "Properties" : {
               "TemplateURL" : "https://s3.amazonaws.com/nested-demo-531193295833/noretain.json",
               "TimeoutInMinutes" : "60"
            }
         }    
    }
}


出現以下錯誤



Root Cause

因為 TemplateURL 輸入有誤,故出現上述錯誤

{
    "AWSTemplateFormatVersion" : "2010-09-09",
    "Resources" : {
        "myStack" : {
	       "Type" : "AWS::CloudFormation::Stack",
	       "Properties" : {
              "TemplateURL" : "https://s3.amazonaws.com/nested-demo-531193295833/s3static.json",
              "TimeoutInMinutes" : "60"
	       }
        },
        "myStack2" : {
            "Type" : "AWS::CloudFormation::Stack",
            "Properties" : {
               "TemplateURL" : "https://s3.amazonaws.com/nested-demo-531193295833/noretain.json",
               "TimeoutInMinutes" : "60"
            }
         }    
    }
}


How-To

重新修正 YAML 內容中的 TemplateURL,即可成功執行

{
    "AWSTemplateFormatVersion" : "2010-09-09",
    "Resources" : {
        "myStack" : {
	       "Type" : "AWS::CloudFormation::Stack",
	       "Properties" : {
              "TemplateURL" : "https://nested-demo-531193295833.s3.amazonaws.com/s3static.json",
              "TimeoutInMinutes" : "60"
	       }
        },
        "myStack2" : {
            "Type" : "AWS::CloudFormation::Stack",
            "Properties" : {
               "TemplateURL" : "https://nested-demo-531193295833.s3.amazonaws.com/noretain.json",
               "TimeoutInMinutes" : "60"
            }
         }    
    }
}


執行結果



2024/02/05

AWS CloudFormation Error Code: InvalidAMIID.NotFound

Problem

當我透過已經編輯好的 CloudFormation yaml file 來建立相關資源,出現以下錯誤訊息



Root Cause

因為 yaml file 中的 image id 在該 region 不存在,所以會出現上述錯誤


How-To

Fix image id 即可成功建立資源

AWSTemplateFormatVersion: 2010-09-09

Description: Template to create an EC2 instance and enable SSH

Parameters: 
  KeyName:
    Description: Name of SSH KeyPair
    Type: 'AWS::EC2::KeyPair::KeyName'
    ConstraintDescription: Provide the name of an existing SSH key pair

Resources:
  MyEC2Instance:
    Type: 'AWS::EC2::Instance'
    Properties:
      InstanceType: t2.micro
      ImageId: ami-0277155c3f0ab2930
      KeyName: !Ref KeyName
      SecurityGroups:
       - Ref: InstanceSecurityGroup
      Tags:
        - Key: Name
          Value: My CF Instance
  InstanceSecurityGroup:
    Type: 'AWS::EC2::SecurityGroup'
    Properties:
      GroupDescription: Enable SSH access via port 22
      SecurityGroupIngress:
        IpProtocol: tcp
        FromPort: 22
        ToPort: 22
        CidrIp: 0.0.0.0/0

Outputs: 
  InstanceID:
    Description: The Instance ID
    Value: !Ref MyEC2Instance







AWS CloudFormation 建立失敗:The key pair 'irkp' does not exist (Service: AmazonEC2; Status Code: 400; Error Code: InvalidKeyPair.NotFound

Problem

當我透過 CLI 建立相關 AWS 資源

aws cloudformation create-stack --stack-name CodeDeployDemoStack-2 \
--template-url https://my-cf-template-347854199521.s3.amazonaws.com/CF_Template.json \
--parameters ParameterKey=InstanceCount,ParameterValue=1 \
ParameterKey=InstanceType,ParameterValue=t3.micro \
ParameterKey=KeyPairName,ParameterValue=irkp \
ParameterKey=OperatingSystem,ParameterValue=Linux \
ParameterKey=SSHLocation,ParameterValue=0.0.0.0/0 \
ParameterKey=TagKey,ParameterValue=Name \
ParameterKey=TagValue,ParameterValue=CodeDeployDemo \
--capabilities CAPABILITY_IAM


出現以下錯誤


Root Cause

因為指令列中的 key pair 不存在


How-To

修正 AWS CLI 為存在的的 key pair 名字

aws cloudformation create-stack --stack-name CodeDeployDemoStack-3 \
--template-url https://my-cf-template-347854199521.s3.amazonaws.com/CF_Template.json \
--parameters ParameterKey=InstanceCount,ParameterValue=1 \
ParameterKey=InstanceType,ParameterValue=t3.micro \
ParameterKey=KeyPairName,ParameterValue=nvkp \
ParameterKey=OperatingSystem,ParameterValue=Linux \
ParameterKey=SSHLocation,ParameterValue=0.0.0.0/0 \
ParameterKey=TagKey,ParameterValue=Name \
ParameterKey=TagValue,ParameterValue=CodeDeployDemo \
--capabilities CAPABILITY_IAM


執行結果



2024/02/04

AWS CodeDploy 失敗:Zip end of central directory signature not found

Problem

我已經 upload source code 到指定 S3 bucket,並規劃透過 CodeDploy 從 S3 bucket 上傳 source code 到指定的 EC2 instances,但發生錯誤



Root Cause

經查 Deployment lifecycle events,發現是在 DownloadBundle 時發生錯誤

錯誤訊息為:Zip end of central directory signature not found

此錯誤訊息的成因:
在 AWS CodeDeploy 的過程中,試圖從 S3 下載並解壓 ZIP 壓縮檔作為部署包時,無法正確解壓縮檔案,此錯誤通常為檔案毀損。



Solution

重新上傳 source code 至 S3,即可成功透過 CodeDepoly 佈署程式


2024/01/14

"errorMessage": "require is not defined in ES module scope, you can use import instead"

Problem

我在跟著 cloudguru 的 lab,學習如何寫一個簡單的 Node.js Lambda function


const https = require('https');
let url = "https://www.amazon.com";

exports.handler = async function(event) {
    let statusCode;
    await new Promise(function(resolve, reject) {
        https.get(url, (res) => {
            statusCode = res.statusCode;
            resolve(statusCode);
        }).on("error", (e) => {
            reject(Error(e));
        });
    });
    console.log(statusCode);
    return statusCode;
};

執行 Test Event 出現以下錯誤訊息
Test Event Name
MyTestEvent

Response
{
  "errorType": "ReferenceError",
  "errorMessage": "require is not defined in ES module scope, you can use import instead",
  "trace": [
    "ReferenceError: require is not defined in ES module scope, you can use import instead",
    "    at file:///var/task/index.mjs:1:15",
    "    at ModuleJob.run (node:internal/modules/esm/module_job:218:25)",
    "    at async ModuleLoader.import (node:internal/modules/esm/loader:329:24)",
    "    at async _tryAwaitImport (file:///var/runtime/index.mjs:1008:16)",
    "    at async _tryRequire (file:///var/runtime/index.mjs:1057:86)",
    "    at async _loadUserApp (file:///var/runtime/index.mjs:1081:16)",
    "    at async UserFunction.js.module.exports.load (file:///var/runtime/index.mjs:1119:21)",
    "    at async start (file:///var/runtime/index.mjs:1282:23)",
    "    at async file:///var/runtime/index.mjs:1288:1"
  ]
}

Root Cause

當您在 AWS Lambda 中使用 Node.js 20 版本,並且選擇使用 .mjs 附檔名時,Lambda 函數被視為 ES module。在 ECMAScript module 中,標準的 require 語法不能使用,因為它是 CommonJS 模塊系統的一部分,連帶上述 export 語法也需稍微改寫


Solution

程式碼需修改如下

import https from 'https';
let url = "https://www.amazon.com";

export async function handler(event) {
    let statusCode;
    await new Promise(function(resolve, reject) {
        https.get(url, (res) => {
            statusCode = res.statusCode;
            resolve(statusCode);
        }).on("error", (e) => {
            reject(Error(e));
        });
    });
    console.log(statusCode);
    return statusCode;
};


執行結果