<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Pakawat (Tle) Teerawattanasuk</title>
    <description>The latest articles on Forem by Pakawat (Tle) Teerawattanasuk (@riteru).</description>
    <link>https://forem.com/riteru</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F374541%2F1d913f03-2717-4406-a7de-3bb23e00a914.jpeg</url>
      <title>Forem: Pakawat (Tle) Teerawattanasuk</title>
      <link>https://forem.com/riteru</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/riteru"/>
    <language>en</language>
    <item>
      <title>การมอบอำนาจการจัดการ SubDomain ไปยัง Hostzone ใหม่ข้าม Account ผ่าน Route53</title>
      <dc:creator>Pakawat (Tle) Teerawattanasuk</dc:creator>
      <pubDate>Mon, 28 Mar 2022 08:24:56 +0000</pubDate>
      <link>https://forem.com/awscommunity-asean/kaarmbamnaacchkaarcchadkaar-subdomain-aipyang-hostzone-aihmkhaam-account-phaan-route53-59hc</link>
      <guid>https://forem.com/awscommunity-asean/kaarmbamnaacchkaarcchadkaar-subdomain-aipyang-hostzone-aihmkhaam-account-phaan-route53-59hc</guid>
      <description>&lt;p&gt;Public Domain Name ถือเป็นส่วนหนึ่งของ Brand Identify ที่สำคัญมาก ในการทำ Website หรือ Application ต่าง ๆ ซึ่งมีผลต่อการที่จะทำให้ผู้ใช้งานรับทราบได้ว่า Website นั้น ๆ เป็นของทีมของเราหรือไม่ รวมถึงเป็นหนึ่งในจุดตรวจสอบว่า Link ที่กดเป็น Phishing หรือไม่&lt;/p&gt;

&lt;p&gt;สำหรับทีมที่มีขนาดใหญ่ขึ้น มีระบบต่าง ๆ เพิ่มขึ้นมา จำเป็นต้องใช้ public domain name แยกย่อยออกไปจำนวนมาก ไม่ว่าจะเป็นของผลิตภัณฑ์หรือของทีมต่าง ๆ ภายในการจะจัดการ DNS ที่จุดเดียว อาจจะต้องมีขั้นตอนการขออนุญาตหรือรอเพื่อให้ฝ่ายที่ดูแลเป็นคนจัดการให้ เพื่อความปลอดภัยหรือความเหมาะสมของการใช้งานชื่อที่เป็น identity ขององค์กร ซึ่งส่งผลให้การคิดหรือทำสิ่งใหม่ ๆ ทำได้ยากขึ้นไปด้วย ดังนั้นบทความนี้จะเสนอทางเลือกง่าย ๆ ของการจัดการ Domain Name ภายในองค์กร&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;หมายเหตุ&lt;/strong&gt; การจัดการ Domain ที่กล่าวถึงในทบความนี้จะยกเว้นเรื่องของ Policy ของแต่ละองค์กร โดยคำนึงถึงเรื่องความสะดวกในการพัฒนาเป็นหลัก&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;เมื่อองค์กรมีขนาดใหญ่ขึ้น มีการแยกทีมย่อยออกไป สำหรับองค์กรที่ใช้ AWS มักจะมีการสร้าง Organization และสร้าง Account แยกจากกันออกไปตามทีมต่าง ๆ  ซึ่งในสถานการณ์ที่มีองค์กรมี public domain เดียว การที่จะจัดการโดยส่วนมากจะมีวิธีดังต่อไปนี้&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Domain อยู่ภายใต้การควบคุมของ Account หลักที่เดียว ซึ่งการจัดการจะมีรูปแบบที่นิยมกันดังนี้

&lt;ul&gt;
&lt;li&gt;มีคนหรือทีมที่จัดการดูแล DNS แยกออกมาเพื่อให้ทีมอื่นขอให้จัดการให้&lt;/li&gt;
&lt;li&gt;สร้าง Assume Role เพื่อให้แต่ละทีมเข้ามาจัดการ DNS โดยตรง&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Subdomain delegation เป็นการมอบอำนาจการควบคุม subdomain ตั้งแต่ level 3 เป็นต้นไปให้กับ DNS อื่น เพื่อให้สามารถควบคุม subdomain ย่อยลงไปได้อีก
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fse3ix50l9wytn3030xya.png" alt="Domain Level"&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;ซึ่งวิธีที่บทความนี้กล่าวถึงคือ Subdomain delegation ด้วย Route53 ระหว่าง 2 AWS Accounts ซึ่งวิธีการดังกล่าวเป็นมาตรฐานของ DNS สามารถทำได้กับ DNS อื่นด้วยเช่นกัน&lt;/p&gt;

&lt;h2&gt;
  
  
  การเตรียมพร้อม
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;2 AWS Accounts คือ Domain Account และ Subdomain Account&lt;/li&gt;
&lt;li&gt;Domain name ที่ใช้บริการ DNS บน hosted zone ของ Route53 บน Domain Account ซึ่งตัวอย่างนี้จะใช้ &lt;code&gt;lawlity.com&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;หน้า HTML เพื่อใช้ทดสอบบน Subdomain Account โดยสร้าง S3 static website ชื่อ bucket &lt;code&gt;dns.sub.lawlity.com&lt;/code&gt; ไว้เพื่อทดสอบ &lt;a href="https://gist.github.com/teer823/df98f4047d3d0eff5940e42fcc8ae017" rel="noopener noreferrer"&gt;ใช้อันนี้ก็ได้&lt;/a&gt;
ในการสร้าง S3 Static Website เพื่อเชื่อมกับ DNS record ด้วย Alias จะต้องสร้าง bucket name ให้ตรงกับ dns record ที่ต้องการ เช่น กรณีตัวอย่างจะสร้าง S3 bucket ชื่อ &lt;code&gt;dns.sub.lawlity.com&lt;/code&gt;
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpdaj7dmsewc5fxccq8f8.png" alt="S3 Bucket"&gt;
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdpzjhci70mht53jybazh.png" alt="Sample Static Site"&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  ค่าใช้จ่ายเพิ่มเติม
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;ค่า Hosted zone บน Subdomain Account 0.50 USD ต่อเดือน &lt;/li&gt;
&lt;li&gt;ค่า Query 0.40 USD ต่อ 1 ล้าน Queries ต่อเดือน&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  ขั้นตอนการสร้าง Subdomain Delegation
&lt;/h2&gt;

&lt;p&gt;ในตัวอย่างจะเป็นการสร้างมอบการควบคุม &lt;code&gt;sub.lawlity.com&lt;/code&gt; จาก Domain Account ไปให้ Subdomain Account และสร้าง A record alias เพื่อแสดง Static Website ที่สร้างไว้&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;สร้าง Public hosted zone บน Route53 ใน Subdomain Account
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5adm7c3twaxhxr2llis2.png" alt="Create public hosted zone"&gt;
&lt;/li&gt;
&lt;li&gt;เมื่อสร้างเสร็จแล้วจะมี NS และ SOA record โดยค่า NS Record คือค่าที่เราจะนำไปใช้ต่อ บน Domain Account ในขั้นตอนที่ 3
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1w9qok9vx470ftd3mubk.png" alt="Default record for hosted zone"&gt;
&lt;/li&gt;
&lt;li&gt;เปลี่ยนไป Route53 ของ Domain Account และสร้าง Record ใหม่ประเภท NS โดยใส่ค่าเป็น NS ของ public hosted zone ที่สร้างไว้ในขั้นตอนที่ 2
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgfkvh6i4qvd4u0s55dg8.png" alt="Add NS Record to domain account"&gt;
&lt;/li&gt;
&lt;li&gt;เปลี่ยนกลับมา Route53 ของ Subdomain Account เพื่อทำการสร้าง Alias Record เพื่อชี้ไปยัง S3 Static Website เตรียมไว้
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fllvw98k3y14u5n6evfv4.png" alt="Create Alias record"&gt;
&lt;/li&gt;
&lt;li&gt;ทำการทดสอบเข้า lv4 domain &lt;code&gt;dns.sub.lawlity.com&lt;/code&gt; ที่สร้างไว้ในขั้นตอนที่ 4 (ในกรณีที่ยังเข้าไม่ได้ต้องรอซักพักเพื่อให้ DNS ทำการอัพเดทก่อน)
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx0v03kjvfxrgzi0qi390.png" alt="Visit new subdomain"&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  สรุปประโยชน์ของการทำ Subdomain Delegation
&lt;/h2&gt;

&lt;p&gt;ด้วยวิธีการง่าย ๆ ข้างต้นโดยใช้ความสามารถพื้นฐานของ DNS ที่หลาย ๆ คนยังไม่เคยใช้งานอย่างการทำ Subdomain Delegation จะช่วยให้สามารถกระจายการจัดการ Subdomain ให้กับทีม หรือ App ต่าง ๆ ในองค์กร ทำให้การติดตั้งระบบ การปรับเปลียนแก้ไขหรือการพัฒนาระบบใหม่ ๆ สามารถทำได้รวดเร็วขึ้น โดยที่การควบคุม Domain หลักที่เป็น identity ขององค์กร ยังอยู่ภายใต้ Account หลักตามเดิมได้&lt;/p&gt;

</description>
      <category>route53</category>
      <category>dns</category>
      <category>management</category>
    </item>
    <item>
      <title> เล่นกับ AWS X-Ray เบื้องต้น</title>
      <dc:creator>Pakawat (Tle) Teerawattanasuk</dc:creator>
      <pubDate>Thu, 21 Oct 2021 07:54:55 +0000</pubDate>
      <link>https://forem.com/awscommunity-asean/elnkab-aws-x-ray-ebuuengtn-1fkb</link>
      <guid>https://forem.com/awscommunity-asean/elnkab-aws-x-ray-ebuuengtn-1fkb</guid>
      <description>&lt;p&gt;&lt;em&gt;Level: Overview - Basic&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;งานสำคัญอย่างนึงในการพัฒนาระบบคือการตรวจสอบและติดตามหา Root cause ของ error หรือ bug ในโปรแกรม ซึ่งโดยปกติจะมีการนั่งดู log ของ service ต่างๆที่เกี่ยวข้อง แต่สำหรับกรณีที่เราใช้ Serverless หรือ Service ใน AWS นอกจากที่จะเข้าไปตรวจสอบ Log ใน Cloudwatch หรือจุดอื่นๆแล้ว AWS มี Service อีกตัวที่หลายๆคนอาจจะไม่เคยได้ลองเล่น คือ AWS X-Ray ที่ช่วยให้เราสามารถติดตามการทำงานของระบบได้สะดวกมาก และมีประโยชน์ในการวิเคราะห์ว่าการทำงานมีส่วนไปนที่ทำงานผิดพลาดหรือมีคอขวดตรงไหนหรือไม่&lt;/p&gt;

&lt;p&gt;โดยในตัวอย่างที่ยกมาจะลองเล่นกับ AWS X-Ray เบื้องต้นซึ่งจะมี Service ต่างๆที่เกี่ยวข้องดังนี้&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;API Gateway&lt;/li&gt;
&lt;li&gt;Lambda Function&lt;/li&gt;
&lt;li&gt;S3&lt;/li&gt;
&lt;li&gt;DynamoDB&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Architecture
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb4kzhunm1mlaqakwhu5g.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb4kzhunm1mlaqakwhu5g.png" alt="Architecture"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;นอกจากการทดสอบระหว่าง API Gateway กับ Lambda แล้ว ในตัวอย่างจะเพิ่มการเชื่อมต่อกับ DynamoDB และ S3 รวมไปถึงการสร้าง DynamoDB Stream และ S3 Event Notification เพื่อเรียก Lambda เพื่อทดสอบดูว่า Trace ไปได้ถึงไหน ในเบื้องต้น&lt;/p&gt;

&lt;h2&gt;
  
  
  Setup
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;สร้าง DynamoDB Table ชื่อ &lt;strong&gt;xray-demo&lt;/strong&gt; โดยมี partition key ชื่อ &lt;strong&gt;id&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feas64jifgiz6pw690vsg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feas64jifgiz6pw690vsg.png" alt="DynamoDB xray-demo"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;สร้าง S3 Bucket ชื่อ &lt;strong&gt;xray-demo-20211020&lt;/strong&gt; (ถ้าไปทำตามเปลี่ยนชื่อและเปลี่ยนชื่อใน code function xray-demo-s3 ด้วยนะครับ)&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuh3crptx21fqlyvo5rb3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuh3crptx21fqlyvo5rb3.png" alt="S3 xray-demo-20211020"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;สร้าง AWS Lambda Function &lt;strong&gt;xray-demo-dynamo&lt;/strong&gt; ทำการเขียนข้อมูลลงบน dynamoDB (อย่าลืมเพิ่ม DynamoDB Policy ให้ LambdaExecution Role)&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;uuid&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;lambda_handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;dynamodb&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;table&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Table&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;xray-demo&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;table&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;put_item&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;Item&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; 
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;uuid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;uuid4&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt; 
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;data&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;statusCode&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;body&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Wrote to DynamoDB&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;สร้าง AWS Lambda Function &lt;strong&gt;xray-demo-s3&lt;/strong&gt; ทำการเขียนข้อมูลลงบน s3 (อย่าลืมเพิ่ม S3 Policy ให้ LambdaExecution Role)
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;uuid&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;lambda_handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;filename&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;uuid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;uuid4&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="n"&gt;s3&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s3&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;s3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;xray-demo-20211020&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
        &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;.json&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;Body&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;UTF-8&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;statusCode&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;body&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Wrote to S3&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;สร้าง AWS Lambda Function &lt;strong&gt;xray-demo-logging&lt;/strong&gt; ทำการเขียน Log บน CloudWatch
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;lambda_handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Status&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Done&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;สร้าง API Gateway 2 Methods แล้วทำการ Deploy เพื่อใช้ทดสอบ

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;GET /dynamodb&lt;/strong&gt; เพื่อเรียก Lambda Proxy Integrationไปยัง Function &lt;strong&gt;xray-demo-dynamo&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GET /s3&lt;/strong&gt; เพื่อเรียก Lambda Proxy Integrationไปยัง Function &lt;strong&gt;xray-demo-s3&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy0wgxazz1gutmzf7omv2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy0wgxazz1gutmzf7omv2.png" alt="API Gateway Setup"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;เปิดใช้งาน DynamoDB Stream แล้วให้ Trigger เป็น Lambda Function &lt;strong&gt;xray-demo-loging&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F61gu05fhcyekfwvvj11s.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F61gu05fhcyekfwvvj11s.png" alt="Enable DynamoDB Stream"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;เปิดใช้งาน S3 Event Notification แล้วให้เรียก Lambda Function &lt;strong&gt;xray-demo-logging&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmswfc4tmhlhohfesbzw2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmswfc4tmhlhohfesbzw2.png" alt="Enable S3 Event Notification"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  การเปิดใช้ X-Ray สำหรับ AWS Lambda
&lt;/h2&gt;

&lt;p&gt;สำหรับ AWS Lambda ทั้ง 3 Functions จะต้องทำการเปิดใช้ X-Ray บน AWS Lambda ก่อน โดยสามารถเปิดใช้งานได้ที่หน้า Configuration &amp;gt; Monitoring and operations tools โดยตัว X-Ray จะใช้ชื่อว่า Active Tracing ซึ่งปกติจะขึ้น Not enabled อยู่ และสามารถกด Edit เพื่อเข้าไปเปิดได้&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fchk5cfon3bgw51w5uu1v.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fchk5cfon3bgw51w5uu1v.png" alt="Lambda Config"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;โดยในการเปิด Active tracing ตัว Lambda จะเพิ่ม Permission ให้โดยอัตโนมัติ&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcicf5xxcg51w11kq01ti.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcicf5xxcg51w11kq01ti.png" alt="Enable Active tracing"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  การเปิดใช้ X-Ray สำหรับ API Gateway
&lt;/h2&gt;

&lt;p&gt;สำหรับ API Gateway เมื่อทำการ Deploy API แล้วสามารถเปิดใช้งาน AWS X-Ray ได้ที่หน้า Logs/Tracing ของ Stage นั้นๆ&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fiizxaq0agn1xqx39lgx6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fiizxaq0agn1xqx39lgx6.png" alt="Enable API Gateway X-Ray"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;สำหรับ X-Ray Sampling Rule เพื่อให้ง่ายต่อการทดสอบเฉยๆจะใช้ Default ที่มีอยู่แล้ว (จริงๆคือยังไม่ได้ลอง แหะๆ) ถ้าสนใจรายละเอียดอ่านที่เอกสารของ AWS &lt;a href="https://docs.aws.amazon.com/xray/latest/devguide/xray-console-sampling.html?icmpid=docs_xray_console" rel="noopener noreferrer"&gt;ที่นี่&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkl5ls59puhgj4pguqagv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkl5ls59puhgj4pguqagv.png" alt="X-Ray Sampling"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  ทดลองเรียกใช้งานรอบที่ 1
&lt;/h2&gt;

&lt;p&gt;ลองเรียกทั้ง 2 Function ดูแล้วเข้าไปดูที่ AWS X-Ray เพื่อดูผลลัพธ์ดู โดยจะเรียกจาก Postman และดู Header ที่ตอบกลับมา&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;เรียก &lt;strong&gt;API GET /dynamodb&lt;/strong&gt; &lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fya1pgpxb78zekbw609gm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fya1pgpxb78zekbw609gm.png" alt="Postman GET /dynamodb"&gt;&lt;/a&gt;&lt;br&gt;
จากรูปข้างบนจะเห็นว่าจะมี Header &lt;strong&gt;X-Amzn-Trace-Id&lt;/strong&gt; กลับมาด้วย ซึ่งตัวข้อมูลนี้คือค่าที่ X-Ray ใช้ Trace ข้อมูล Request/Response ต่างๆในระบบ ซึ่งเมื่อมีการส่งต่อเลขนี้ไปด้วย&lt;br&gt;
เมื่อเข้าไปดูที่หน้าจอ X-Ray จะสามารถดูรายการ Trace ที่เกิดขึ้นในช่วงระยะเวลาที่เราเลือกได้ (ขวาบน) ซึ่งจะสังเกตได้ว่าเลขอ้างอิง หรือ ID ที่แสดงใน Trace List จะเป็นเลขเดียวกับค่าของ &lt;strong&gt;X-Amzn-Trace-Id&lt;/strong&gt; โดยสามารถเข้าไปดูรายละเอียดของ Request นั้นๆและ Service Map ที่แสดงภาพรวมระยะเวลาในการทำงานของ Node ต่างๆได้ด้วย&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxxdbjk1b5b6am7xryg1u.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxxdbjk1b5b6am7xryg1u.png" alt="Dynamo Trace"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fme35av1fbrksjx2dceil.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fme35av1fbrksjx2dceil.png" alt="Dynamo Trace Detail"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmz71s7n3ric7h9agr9v9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmz71s7n3ric7h9agr9v9.png" alt="Dynamo Service Map"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;เรียก &lt;strong&gt;API GET /s3&lt;/strong&gt;&lt;br&gt;
เพื่อให้เห็นตัวอย่างที่ชัดเจนขึ้นจะทำการแก้ Lambda Function ให้ทำงานผิดพลาด (Lambda Error และ Return 502 จาก API Gateway) 1 รอบ และแก้กลับให้ทำงานปกติ ซึ่งเมื่อดู Trace และ Service Map บน X-Ray จะขึ้นสี เหลือง - Errors (4XX) และ สีแดง - Faults (5XX) ตามลำดับ สำหรับรายการที่ไม่สำเร็จ&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy18rp0xtizxcnh6rt9mi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy18rp0xtizxcnh6rt9mi.png" alt="Trace S3"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0i990jmdca261gjzaog9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0i990jmdca261gjzaog9.png" alt="Service Map S3"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  การเพิ่มรายละเอียดการ Trace ด้วย Code
&lt;/h2&gt;

&lt;p&gt;จากตัวอย่างการเรียกทั้ง 2 แบบด้านบน จะเห็นว่าข้อมูลการ Trace จบที่ AWS Lambda ไม่แสดงรายละเอียดการเรียกไปยัง DynamoDB และ S3 รวมไปถึงไม่สามารถแสดงกรณีที่ S3 Event หรือ DynamoDB Stream เป็นผู้ Trigger Lambda &lt;strong&gt;xray-demo-logging&lt;/strong&gt; ได้ ซึ่งการจะแสดงผลให้ละเอียดขึ้นจะต้องทำการเรียกใช้ &lt;strong&gt;aws-xray-sdk&lt;/strong&gt; เพื่อให้ทำการเพิ่ม Trace ID ลงบน Header ของการเรียกใช้งาน Service อื่นๆด้วย&lt;br&gt;
ในตัวอย่างจะแสดงการเรียกใช้แบบง่าย ซึ่งถ้าต้องการรายละเอียดเชิงลึกเพิ่มเติมสามารถดูได้ที่เอกสารของ AWS &lt;a href="https://docs.aws.amazon.com/xray/latest/devguide/xray-sdk-python.html" rel="noopener noreferrer"&gt;ที่นี่&lt;/a&gt; ส่วนถ้าใช้ภาษาอื่นๆนอกเหนือจาก Python สามารถหาข้อมูลต่อได้จาก Link ข้างต้นเหมือนกัน&lt;/p&gt;

&lt;p&gt;ในการเรียก &lt;strong&gt;aws-xray-sdk&lt;/strong&gt; บน Lambda เนื่องจาก AWS ไม่ได้ include library ตัวนี้ไว้ใน default runtime ดังนั้นในเบื้องต้นจะมี 2 ทางเลือกคือ&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;เขียน Code บนเครื่องพร้อม install package แล้ว upload .zip ขึ้นมายัง Lambda&lt;/li&gt;
&lt;li&gt;เพิ่ม Lambda Layer ที่ติดตั้ง &lt;strong&gt;aws-xray-sdk&lt;/strong&gt; ไว้
ซึ่งในตัวอย่างจะใช้การทำงานแบบที่ 2 ซึ่งวิธีการสร้าง Lambda Layer ง่ายๆอ่านได้ &lt;a href="https://towardsdatascience.com/python-packages-in-aws-lambda-made-easy-8fbc78520e30" rel="noopener noreferrer"&gt;ที่นี่&lt;/a&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;เมื่อทำการสร้าง Lambda Layer แล้ว ให้ทำการ Attach Layer และแก้ Lambda function สำหรับ dynamodb และ s3 โดยเพิ่ม 3 บรรทัดด้านบนดังนี้&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;aws_xray_sdk.core&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;xray_recorder&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;aws_xray_sdk.core&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;patch_all&lt;/span&gt;
&lt;span class="nf"&gt;patch_all&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ซึ่งเมื่อแก้ไขแล้วจะได้ Code ของ. Function ที่แก้ไขแล้ว&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;แก้ไข AWS Lambda Function &lt;strong&gt;xray-demo-dynamo&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;uuid&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;aws_xray_sdk.core&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;xray_recorder&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;aws_xray_sdk.core&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;patch_all&lt;/span&gt;
&lt;span class="nf"&gt;patch_all&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;lambda_handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;dynamodb&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;table&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Table&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;xray-demo&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;table&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;put_item&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;Item&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; 
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;uuid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;uuid4&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt; 
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;data&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;statusCode&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;body&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Wrote to DynamoDB&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;แก้ไข Function &lt;strong&gt;xray-demo-s3&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;uuid&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;aws_xray_sdk.core&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;xray_recorder&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;aws_xray_sdk.core&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;patch_all&lt;/span&gt;
&lt;span class="nf"&gt;patch_all&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;lambda_handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;filename&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;uuid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;uuid4&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="n"&gt;s3&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s3&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;s3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;xray-demo-20211020&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
        &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;.json&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;Body&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;UTF-8&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;statusCode&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;body&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Wrote to S3&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ซึ่งการทำงานของคำสั่ง &lt;code&gt;patch_all()&lt;/code&gt; จะไปทำการเพิ่ม Trace Id ใน Header ของทุกคำสั่งที่ X-Ray รองรับ เพื่อให้สามารถ Trace การทำงานได้&lt;/p&gt;

&lt;h2&gt;
  
  
  ทดลองเรียกใช้งานรอบที่ 2
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;เรียก &lt;strong&gt;API GET /dynamodb&lt;/strong&gt;&lt;br&gt;
เมื่อเรียก API และเปิดดู​ Service Map จะเห็นว่ามี Node การทำงานของ DynamoDB Table เพิ่มขึ้นมา ซึ่งถ้าดูใน Trace จะแสดง API ที่ถูกเรียกด้วย คือ คำสั่ง PutItem แต่การแสดงข้อมูลการเรียก &lt;strong&gt;xray-demo-logging&lt;/strong&gt; จะถูกแยกออกจาก Trace ของ API ที่เรียก&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fya56d2m945px0a1z6ogc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fya56d2m945px0a1z6ogc.png" alt="X-Ray with Dynamo Table"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvvx86lhhxa5vmc0irnlk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvvx86lhhxa5vmc0irnlk.png" alt="X-Ray Dynamo PutItem"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;เรียก &lt;strong&gt;API GET /s3&lt;/strong&gt;&lt;br&gt;
เมื่อเรียก API และเปิดดู Service Map จะเห็นว่ามี Node การทำงานของ S3 Bucket เพิ่มขึ้นมา ซึ่งถ้าดูใน Trace จะแสดง API ที่ถูกเรียกด้วย คือ คำสั่ง PutObject และการเรียก &lt;strong&gt;xray-demo-logging&lt;/strong&gt; จะแสดงเป็นการถูกเรียกโดย Lambda function &lt;strong&gt;xray-demo-s3&lt;/strong&gt; แทน โดยไม่แยกออกจากกันเหมือนกรณี DynamoDB&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fafnqhxljdn3am7lx9jr8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fafnqhxljdn3am7lx9jr8.png" alt="X-Ray With S3 Bucket"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F790qyy7cdy1vll67s1u4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F790qyy7cdy1vll67s1u4.png" alt="X-Ray S3 PutObject"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Limitation
&lt;/h2&gt;

&lt;p&gt;อย่างไรก็ตาม จากตัวอย่างข้างบน 2 รูปแบบ จะเห็นว่าการเรียก xray-demo-logging ซึ่งถูกเรียกโดย DynamoDB Stream จะขึ้น Trace Id แยกออกจากการคำสั่งชุดแรกที่ผ่าน API แต่การเรียกผ่าน S3 Event Notification จะมีการส่งต่อ TraceID เพื่อให้เห็นว่าที่มาของการเกิด Trigger เกิดจากที่ใด &lt;br&gt;
ซึ่งรูปแบบที่แตกต่างกันนี้เกิดขึ้นเนื่องจาก X-Ray ยังไม่รองรับการส่ง Trace Id ผ่าน Dynamo DB แต่สำหรับ S3 มีการรองรับการส่งต่อ Trace ID ไปยังคำสั่งต่อไปแล้ว เพียงแต่จะไม่เห็นว่าต้นทางที่เรียกมาจาก S3 Event Notification โดยรายละเอียดการรองรับการทำงานร่วมกับ X-Ray สามารถอ่านเพิ่มเติมได้ที่เอกสารของ AWS &lt;a href="https://docs.aws.amazon.com/xray/latest/devguide/xray-services.html" rel="noopener noreferrer"&gt;ที่นี่&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Cost
&lt;/h2&gt;

&lt;p&gt;AWS X-Ray เปิดให้ใช้ฟรีสำหรับการเก็บข้อมูลเดือนละ 100,000 trace และฟรีสำหรับการเรียกข้อมูลหรือสแกนข้อมูลที่บันทึกไว้ 1,000,000 trace หลังจากนั้นจึงคิดเงินเพิ่มตามรูปด้านล่าง&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fexbktf39qsrj2ks123uh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fexbktf39qsrj2ks123uh.png" alt="X-Ray Pricing"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;Software developers spend 35-50 percent of their time validating and debugging software. The cost of debugging, testing, and verification is estimated to account for 50-75 percent of the total budget of software development projects&lt;br&gt;
&lt;em&gt;Devon H. O'Dell&lt;/em&gt; &lt;a href="https://queue.acm.org/detail.cfm?id=3068754" rel="noopener noreferrer"&gt;ACMQueue&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;งาน Monitor &amp;amp; Debugging เป็นงานที่สำคัญและกินเวลาของ Developer ค่อนข้างมาก ซึ่งการเลือกเครื่องมือต่างๆมาช่วยให้สามารถตรวจสอบหรือติดตามผลการทำงานของ Software ที่พัฒนาขึ้นมาได้อย่างเหมาะสมจะช่วยลดเวลาและค่าใช้จ่ายในการพัฒนาระบบได้ด้วย ดังนั้น AWS X-Ray จึงเป็นหนึ่งในตัวเลือกที่นักพัฒนาบน AWS ควรจะศึกษาไว้เพื่อให้การทำงานง่ายขึ้น&lt;/p&gt;

</description>
      <category>awsthai</category>
      <category>awsxray</category>
      <category>monitoring</category>
    </item>
    <item>
      <title>เก็บตก AWS Gameday Security</title>
      <dc:creator>Pakawat (Tle) Teerawattanasuk</dc:creator>
      <pubDate>Wed, 06 Oct 2021 11:14:35 +0000</pubDate>
      <link>https://forem.com/awscommunity-asean/aws-gameday-security-1iin</link>
      <guid>https://forem.com/awscommunity-asean/aws-gameday-security-1iin</guid>
      <description>&lt;p&gt;เพิ่งเล่นจบเลยอยากมาเขียนไว้เป็นที่ระลึกนิดนึง สนุกและได้ประสบการณ์ + ความรู้หลายๆอย่างดีครับ ถ้ามีอีกแนะนำให้ ลองสมัครมาแข่งกันดู&lt;/p&gt;

&lt;p&gt;ข้างล่างนี่ไม่ได้เฉลยอะไรนะคับ แค่ Note ไว้เผื่อจะได้ไปศึกษาเพิ่มเติม และแนะนำให้คนที่ได้มาอ่านลองมาหาประสบการณ์ในรอบหน้าดูนะคับ&lt;/p&gt;

&lt;h2&gt;
  
  
  Secret Manager &amp;amp; DB Password Rotation
&lt;/h2&gt;

&lt;p&gt;ในกรณีที่มีการเชื่อมต่อกับ Database ปกติถ้าทำงานกันแบบไวๆ หลายๆคนน่าจะเคยไปใส่ Password ไว้ใน Code บ้าง ใน ENV บ้าง แต่ Practice ในกรณีนี้ที่ AWS ให้ลองเล่นคือ ใช้ Secret Manager แล้วก็ใช้ Code ไปอ่านและ decrypt/parse ค่าที่ใช้ในการเชื่อมต่อกับ DB ออกมา &lt;/p&gt;

&lt;p&gt;ส่วนการ Rotate DB Password สามารถตั้งได้ใน Secret Manager แล้วใช้ Lambda Function เข้าไปแก้ Password database แล้วบันทึกเข้า Secret Manager อีกที&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuh8b5fq4kmrzc3xl9pek.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuh8b5fq4kmrzc3xl9pek.png" alt="Secret Manager"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  AWS Inspector + EC2 Image Builder
&lt;/h2&gt;

&lt;p&gt;ส่วนตัวชอบ AWS Inspector ที่มี Managed Rule ที่ช่วยหาว่า EC2 ของเรามี Vulnerabilities อะไรบ้าง ซึ่งโดยมาก ถ้าตัวที่ใช้ &lt;code&gt;Common Vulnerabilities and Exposures-1.1&lt;/code&gt; นี่ส่วนมากจะแก้ได้ด้วยการ update OS &lt;/p&gt;

&lt;p&gt;ซึ่งจากข้างบนถ้าเราน่าจะมี 2 ทางเลือกคือ เข้าไป update OS เรื่อยๆ (แล้วพัง 555) หรือสร้าง EC2 Image ที่ Update ไว้เรียบร้อยแล้ว ลงโปรแกรมพร้อม เสร็จด้วย EC2 Image Builder แล้วเอา Image นั้นมาใช้งาน&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;จริงๆ ข้อนี้น่าจะสนุกขึ้นถ้าต้องตั้งให้เปลี่ยน Instance ใหม่ด้วยแบบอัตโนมัติด้วย แต่กรณีนี้น่าจะไม่ใช่เรื่อง Security เลยตัดทิ้งไป (ทำไงหว่า 555)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Secure Database (RDS Encryption + AWS Config)
&lt;/h2&gt;

&lt;p&gt;เสียคะแนนไปตรงนี้นิสนึง &amp;gt;_&amp;lt; หลักๆเป็นเรื่องของการ Encryption at rest ของฐานข้อมูลที่รันอยู่แล้ว โดยการ Copy Snapshot เพิ่มการ Encrypt แล้ว Restore ขึ้นมาใหม่ ซึ่งตรงนี้เพิ่งสังเกตว่า db.t2 ไม่รองรับ Encryption at rest ด้วย ต้องใช้ db.t3 แทน&lt;/p&gt;

&lt;p&gt;ส่วน AWS Config ซึ่งจะคอย detect ให้ว่าเราตั้งค่าอะไรผิดไปรึเปล่า เป็นอีกตัวที่ชอบมากพอๆกับ inspector โดยเราไปกำหนด compliance rule ต่างๆ (เลือกจากที่ aws สร้างไว้ให้แหละ ซะส่วนใหญ่) ซึ่งส่วนมากจากตัวอย่างที่เห็นมักจะเห็นใช้กับ RDS และ EC2 ซะเยอะ ถ้ามีโอกาสน่าจะลอง explore กับ service อื่นๆดู&lt;/p&gt;

&lt;p&gt;ในรูปข้างล่างน่าจะเอาไปรวมกับข้อแรกว่าต้องตั้ง Rotation ด้วยเพิ่มความยาก 555&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frxp89m18pqeiuxi97vgv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frxp89m18pqeiuxi97vgv.png" alt="AWS Config SecretManager Rotation"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;สุดท้ายความรู้ใหม่จริงๆ ที่ AWS ใจดีแถม Diagram มาให้ด้วยคือการใช้ AWS Config ร่วมกับ Cloudwatch Event Rule เพื่อไป Trigger Lambda อีกที ซึ่งเคยรู้ว่าทำได้แต่ไม่เคยได้ลอง Hand-on จริงๆ&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzywq7kezei69qr76awxf.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzywq7kezei69qr76awxf.jpg" alt="Automate Delete Snapshot"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;ตรงนี้สงสัยว่า การที่ snapshot จะเป็น public ได้มันต้องตั้งยังไง เพราะเหมือนวิธีแก้คือให้ลบออก แต่ Root cause จริงๆน่าจะเป็นการตั้งให้เวลาสร้าง snapshot ของ DB ตัวอย่างแล้วเป็น public อัตโนมัติ (ไปสร้างทิ้งไว้ตั้งนาน -.- จน AWS Config ขึ้นว่า Solve แล้วแล้วกลับมาใหม่เพราะไปสร้างเพิ่มเอง&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  IAM Access Analyzer (IAM Access Analyzer, S3 Bucket Policy)
&lt;/h2&gt;

&lt;p&gt;ข้อนี้ค่อนข้างตรงไปตรงมา คือใช้ IAM Access Analyzer (ซึ่งยอมรับว่าไม่เคยใช้มาก่อน) ก็จะบอกให้รู้ว่าเรามีการตั้ง permission ตรงไหนอนุญาตให้ Account อื่นสามารถเข้ามาใช้ได้ โดยจากใน Gameday จะมี IAM Role, S3 และ KMS ซึ่งถ้าเราไม่ได้ตั้ง Security ไว้ให้ดีตั้งแต่แรก มีโอกาสที่อาจจะเจอ Developer มักง่ายตั้งค่าใช้ Account ส่วนตัว ไม่ต้อง Switch ไปมาเพื่อเข้ามาจัดการได้ (เมื่อไหร่ AWS จะให้ login multiple account ซะที -.-)&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;IAM Role จะมีการตั้งให้ Account อื่นสามารถ AssumeRole ได้ผ่าน Trust Relation&lt;/li&gt;
&lt;li&gt;S3 และ KMS จะเป็น Resource Based Policy ที่อนุญาตให้ Account อื่นเข้ามาใช้หรือจัดการ Resource แต่ละตัวได้&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmfwyoe4fm4ox33n4wcj3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmfwyoe4fm4ox33n4wcj3.png" alt="IAM Access Analyzer"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;ข้อสงสัยค้างคาใจสำหรับกรณีนี้คือ ถ้า Rule ไหนที่เรา Archived ไปแล้ว เพราะว่า ณ​ เวลานั้นๆเราตั้งใจอนุญาต แล้วอนาคตไม่อนุญาตแล้วเราก็จะไม่เจอ Finding นั้นๆรึเปล่า&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Event-Driven Security (Amazon GuardDuty + Event Bridge + Lambda + NACL)
&lt;/h2&gt;

&lt;p&gt;อันนี้น่าจะเป็นข้อที่น่าสนใจสุด เพราะเน้นการทำ Automate จริงๆจังๆมาก โดยจะคล้ายๆกับ กรณีทำ Automate ของข้อ Secure Database ข้างบน แต่ว่าใช้ Event Bridge เป็นตัวกลางแทน ซึ่งก็ไม่เคยลองทำมาก่อนเหมือนกัน &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;ข้อสงสัยหลักที่อยู่ตรงนี้คือ NACL เนี่ยมี Soft Limit ที่ 20 ต้องไปขอเพิ่ม แต่จากตัวอย่างใน Gameday แป๊บเดียว Lambda ก็ Error &lt;code&gt;NetworkAclEntryLimitExceeded&lt;/code&gt; แล้ว เลยไม่รู้ว่าจริงๆแล้วในกรณีแบบนี้้้จะ Automate ในรูปแบบไหนได้บ้างนอกจากแก้ NACL เนื่องจากถ้าเจอเคสนี้จริงๆ น่าจะต้อง block เป็น CIDR หรือไม่ก็ไปแก้ที่ต้นเหตุด้วยการลบ Instance นั้นหรือ block outbound ให้หมดเพราะน่าจะมี Malware ที่คอยยิง outbound แปลกๆอยู่&lt;/p&gt;

&lt;p&gt;จากข้างบนพอดีทาง AWS แนะนำมาว่าสามารถเพิ่ม NACL ไปที่ 40 ได้ หรือใช้การ Route Traffic ผ่าน custom NAT Instance แล้ว block ที่ NAT แทน หรือ ไม่ก็ Isolate ตัว Instance ด้วย Security Group ไปเลย&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;สุดท้ายนี้ขอบคุณทีม AWS Thailand สำหรับกิจกรรมที่ได้ทั้งความสนุก ความรู้ และประสบการณ์ Hand-on หลายๆอย่างด้วยนะคับ ถ้ามีอีกแล้วไม่ติดอะไร น่าจะขอเข้าไปร่วมเล่นด้วยอีก ถ้าใครผ่านมาตรงนี้มีโอกาสแนะนำให้ลองดูนะคับ น่าจะได้เรียนรู้อะไรไม่มากก็น้อย &lt;/p&gt;

&lt;p&gt;ปล. จริงๆ สำหรับคนที่ประสบการณ์น้อย ถ้าจบการแข่งแล้วทางทีม AWS มาแนะนำวิธีทำด้วยจะดีมากครับ จะคล้ายๆกับของ JAM ที่ถ้าไม่รู้จริงๆลองกดแล้วทำตามดูด้วย&lt;/p&gt;

</description>
      <category>awsthai</category>
      <category>awsgameday</category>
      <category>security</category>
    </item>
    <item>
      <title>วิธีแก้ปัญหาเมื่อทำ Keypair EC2 หาย (Linux)</title>
      <dc:creator>Pakawat (Tle) Teerawattanasuk</dc:creator>
      <pubDate>Sun, 03 Oct 2021 18:55:50 +0000</pubDate>
      <link>https://forem.com/awscommunity-asean/keypair-ec2-linux-47bl</link>
      <guid>https://forem.com/awscommunity-asean/keypair-ec2-linux-47bl</guid>
      <description>&lt;p&gt;ในกรณีที่เราทำ Keypair ที่ใช้ Access EC2 หายสามารถแก้ปัญหาอย่างไรได้บ้าง&lt;/p&gt;

&lt;h2&gt;
  
  
  Setup
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;สร้าง Instance ที่ 1 ด้วย Keypair ชุดที่ 1 ซึ่งจะสมมุติให้เครื่องนี้เป็นเครื่องที่ Keypair หายไป และนำสร้างไฟล์ ImportantFile.txt ไว้ เพื่อเป็นตัวอย่าง&lt;/li&gt;
&lt;li&gt;สร้าง Instance ที่ 2 ด้วย Keypair ชุดที่ 2 ซึ่งจะเป็นเครื่องที่ใช้เพื่อการแก้ปัญหาเมื่อเราไม่สามารถเข้าถึงเครื่องที่ 1 ได้ (ในในกรณีที่ 3)
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhyt680ksvru9rzwsprez.png" alt="2 Instances"&gt;
&lt;/li&gt;
&lt;li&gt;Keypair ชุดใหม่สำหรับเครื่องที่ 1 โดยจะต้องนำ public key ของ keypair ไปใส่ไว้ใน ~/.ssh/authorized_keys แทนที่อันเดิม&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  วิธีแก้ปัญหาแบบที่ 1 ใช้ Session Manager
&lt;/h2&gt;

&lt;p&gt;วิธีนี้ใช้สำหรับกรณีที่ OS ที่เราใช้มีการติดตั้ง System Manager Agent ไว้ไม่ว่าจะเป็นการติดตั้งแบบ Manual หรือเราเลือก AMI ของ OS ที่มี SSM Agent อยู่แล้ว เช่น&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Amazon Linux , Amazon Linux 2&lt;/li&gt;
&lt;li&gt;Ubuntu 16.04, 18.04 และ 20.04&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;โดยปกติแล้วถ้าเราไม่ได้สร้าง Instance Profile ที่มี Policy ที่เหมาะสมไว้ จะไม่สามารถใช้งาน Session Manager ได้ &lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkacyb4rom4wsc951ry1j.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkacyb4rom4wsc951ry1j.png" alt="Cannot use Session Manager without SSM Policy"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;โดยในเบื้องต้นสามารถสร้าง Role ที่มี Policy AmazonSSMManagedInstanceCore ใน IAM&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6q99xaa67wggrr51ocf1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6q99xaa67wggrr51ocf1.png" alt="AmazonSSMManagedInstanceCore Policy"&gt;&lt;/a&gt;&lt;br&gt;
และทำการ Attach Role ให้กับเครื่อง EC2 ที่เราทำ Keypair หายไป โดยเลือก Action &amp;gt; Security &amp;gt; Modify IAM Role&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9ra5uv9uxd9nmj0wtzuy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9ra5uv9uxd9nmj0wtzuy.png" alt="Attach Role"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;หลังจาก Attach Role แล้วรอซักพัก (10 นาทีโดยประมาณ) จะสามารถกด Connect &amp;gt; Session Manager แล้วเชื่อมต่อเข้าไปที่เครื่องได้&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwbu5xqj99pv8tf9d2kyw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwbu5xqj99pv8tf9d2kyw.png" alt="Session Manager Connectable"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;เมื่อเข้าไปแล้วเราจะเป็น user ssm-user สามารถเปลี่ยนเป็น user อื่นๆด้วยคำสั่ง sudo su  แล้วเข้าไปเปลี่ยน keypair ด้วย keypair ที่เตรียมไว้ได้เลย (/home//.ssh/authorized_keys)&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbg3otraotmb4nq1oja3d.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbg3otraotmb4nq1oja3d.png" alt="Session Manager"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  วิธีแก้ปัญหาแบบที่ 2 ใช้ EC2 Serial Console
&lt;/h2&gt;

&lt;p&gt;เมื่อช่วงต้อนปี 2021 AWS เปิดให้ EC2 Serial Console เป็น GA แล้ว (&lt;a href="https://aws.amazon.com/about-aws/whats-new/2021/03/introducing-ec2-serial-console/" rel="noopener noreferrer"&gt;Link&lt;/a&gt;) ดังนั้นถ้าเครื่องที่ Keypair หายมีการตั้ง password root user ไว้ (&lt;code&gt;sudo passwd root&lt;/code&gt;) จะสามารถที่จะเข้าเครื่อง EC2 ที่เป็น Nitro ผ่านหน้า Console ได้เลย&lt;/p&gt;

&lt;p&gt;สำหรับใครที่ยังไม่เคยเปิดใช้งาน EC2 Serial Console สามารถเข้าไปเปิดก่อนได้ โดยจะขึ้นข้อความแจ้งไว้ตามรูป &lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9tbk99rtufvix0dmdo7v.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9tbk99rtufvix0dmdo7v.png" alt="EC2 Serial Access Disabled"&gt;&lt;/a&gt;&lt;br&gt;
เมื่อกด Manage Access ให้ไปเลือก Allow เพื่อเปิดใช้ EC2 Serial Console&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcdx8gicelc2qqr4mw4ot.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcdx8gicelc2qqr4mw4ot.png" alt="Allow EC2 Serial Console"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;จากเครื่องตัวอย่างที่สร้างไว้เป็น t2.micro ซึ่งไม่ใช่ Nitro จะไม่สามารถใช้งาน EC2 Serial Console ได้ ดังนั้นเราจะต้องเปลี่ยน Instance Type ก่อน&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl2m8ryrtupnfqk0hh946.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl2m8ryrtupnfqk0hh946.png" alt="T2 Cannot Access via Serial"&gt;&lt;/a&gt;&lt;br&gt;
จากตัวอย่างจะเปลี่ยน Instance Type เป็น T3.Micro แทน&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi0gs4espdlim3e030mqp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi0gs4espdlim3e030mqp.png" alt="Change to T3"&gt;&lt;/a&gt;&lt;br&gt;
เมื่อเปลี่ยน Instance Type เป็น T3 จะสามารถใช้งาน Serial Console ได้&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsngnq4vj9ok76iufz9ki.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsngnq4vj9ok76iufz9ki.png" alt="T3 can use serial console"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;เมื่อต่อเข้ามาครั้งแรกจะเจอ จอดำๆ ไม่ขึ้นอะไรเลย ไม่ต้องตกใจ ถ้าใครเคยใช้ Linux แบบเครื่องเปล่าๆ จะรู้ว่าเครื่องกำลังถาม user อยู่ (ลองกด Enter ดูสักทีจะขึ้น Message ให้ใส่ Username ตามรูป)&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffqkxl7d0r1koo3e6fihr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffqkxl7d0r1koo3e6fihr.png" alt="Login to EC2"&gt;&lt;/a&gt;&lt;br&gt;
เมื่อใส่ Username และ Password แล้วก็สามารถเข้าไปเปลี่ยน Keypair ที่ authorized_keys ได้ตามสะดวก&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcjrt0vm06q3mnbjo9lkk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcjrt0vm06q3mnbjo9lkk.png" alt="Change authorized_keys"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  แบบที่ 3 แก้ผ่านเครื่องอื่นด้วยการ mount volume EBS
&lt;/h2&gt;

&lt;p&gt;ในกรณีที่ไม่สามารถตั้งค่า SSM ได้ และไม่สามารถใช้ EC2 Serial Console ด้านบนได้ ถ้าเครื่องที่ทำ Keypair หายมี Root Volume เป็น EBS สามารถที่จะใช้วิธีการ Detach EBS Volume ของเครื่องนั้นๆ ไป Attach เข้ากับอีกเครื่อง (เครื่องที่ 2) และทำการแก้ keypair ได้ โดยมีขั้นตอนต่อไปนี้&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Stop Instance ของเครื่องที่ Keypair หายไป แล้วทำการ Detach Volume
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3kyyglpd645h35pbqnrp.png" alt="Detach Volume"&gt;
&lt;/li&gt;
&lt;li&gt;นำ Volume ที่ถูก Detached ออกมาไป Attach เป็น Volume ที่ 2 ของเครื่องใหม่ (ในตัวอย่าง เครื่องที่ 2 ไม่ได้ปิดเครื่องอยู่)
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fepbwmyq9iz1bg8y4pvb5.png" alt="Attach Volume"&gt;
*เมื่อเครื่องที่ 2 มี 2 Volume แล้วให้ทำการ SSH เข้าไปที่เครื่องที่ 2 
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fav3lsrsn2fkzk4tq20u2.png" alt="2 Volumes"&gt;
&lt;/li&gt;
&lt;li&gt;ใช้คำสั่ง &lt;code&gt;lsblk -f&lt;/code&gt; ดูข้อมูล Volume ซึ่งจากตัวอย่างจะมี Volume /dev/xvdf เพิ่มขึ้นมา โดยมี partition xvdf1 อยู่แล้ว แต่ถ้าสังเกตุช่อง UUID จะเห็นว่ามี UUID ซ้ำกัน เนื่องจากทั้ง 2 Volume ถูกสร้างขึ้นมาจาก AMI ตัวเดียวกัน (Amazon Linux2) ดังนั้นการ mount จะต้องใช้ option nouuid ด้วย
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7llkzka7y0un3hx04mwl.png" alt="lsblk -f"&gt;
&lt;/li&gt;
&lt;li&gt;ทำการสร้าง Folder และ mount volume ด้วยคำสั่ง &lt;code&gt;sudo mount -t xfs -o nouuid /dev/&amp;lt;partition_name&amp;gt; &amp;lt;mount_point&amp;gt;&lt;/code&gt; และทำการตรวจสอบด้วย &lt;code&gt;lsblk -f&lt;/code&gt; อีกครั้ง
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F45ya6icbkboxggtj56u5.png" alt="Success mount"&gt;
&lt;/li&gt;
&lt;li&gt;เข้าไปที่ //.ssh เพื่อแก้ไข authorized_keys 
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq9hjrpt7ajpta4mnehag.png" alt="Change keypair"&gt;
&lt;/li&gt;
&lt;li&gt;ทำการ Unmount ด้วยคำสั่ง `sudo umount 
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2m5emoxja6vin2smudd4.png" alt="Umount"&gt;
&lt;/li&gt;
&lt;li&gt;ทำการ Detach และ Attach กลับไปยังเครื่องที่ 1 ที่ Keypair หายไป โดยในขั้นตอนการ Attach ให้เลือก Device เป็น /dev/xvda เพื่อให้ตรงกับ Root Volume เดิม และทำการตรวจสอบใน Instance Summary &amp;gt; Storage
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwan0vj9k81s3uvz3z15g.png" alt="Reattach Volume to old instance"&gt;
&lt;/li&gt;
&lt;li&gt;ทำการ Start Instance ของเครื่องที่ 1 กลับขึ้นมาเพื่อใช้งาน&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  ทดสอบ SSH ด้วย Keypair ใหม่
&lt;/h2&gt;

&lt;p&gt;ไม่ว่าจะแก้ไขด้วยวิธีการใดใน 3 ขั้นตอนด้านบน เมื่อเปลี่ยน authorized_keys แล้วเราจะสามารถ SSH เข้าไปใหม่ด้วย Keypair ใหม่ได้&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feu840n5nz9tx6ded6c7j.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feu840n5nz9tx6ded6c7j.png" alt="SSH back to Instance"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>awsthai</category>
      <category>ec2</category>
      <category>keypair</category>
    </item>
    <item>
      <title>การตั้งค่า HTTP Response สำหรับ API Gateway ในการทำงานร่วมกับ AWS Lambda</title>
      <dc:creator>Pakawat (Tle) Teerawattanasuk</dc:creator>
      <pubDate>Fri, 24 Sep 2021 10:33:11 +0000</pubDate>
      <link>https://forem.com/awscommunity-asean/http-response-api-gateway-aws-lambda-2bg5</link>
      <guid>https://forem.com/awscommunity-asean/http-response-api-gateway-aws-lambda-2bg5</guid>
      <description>&lt;p&gt;&lt;strong&gt;หมายเหตุ: บทความนี้กล่าวถึง Lambda custom integration เท่านั้น โดยผลลัพธ์เมื่อเลือกเป็น Lambda proxy integration จะได้ผลลัพธ์อีกแบบ&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;หลายๆคนน่าจะเคยสร้าง AWS Lambda Function ขึ้นมาแล้วสร้าง REST API บน API Gateway มาเชื่อมต่อกับ Function ที่สร้างขึ้น ซึ่งขั้นตอนการสร้างดังกล่าวค่อนข้างง่ายและรวดเร็ว แต่ว่าในขั้นตอนง่ายๆนี้หลายคนอาจจะมองข้ามการตั้งค่าที่สำคัญที่อาจจะทำให้การทำงานผิดพลาดได้ โดยเฉพาะถ้า Developer คนนั้นคุ้นเคยกับการเขียนโปรแกรมบนเครื่องส่วนตัว เช่นการเขียนบน Framework หรือการใช้ Express.js ในการจัดการ Web server.&lt;/p&gt;

&lt;p&gt;เพื่อให้เห็นภาพของความผิดพลาดนี้มากขึ้น เราจะเริ่มทดลองจากการสร้าง Function ง่ายๆขึ้นมาและทำการทดสอบเรียกด้วย Postman&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;handler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// TODO implement&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;statusCode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;// Change from 200 to 400&lt;/span&gt;
        &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Hello from Lambda!&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ตัวอย่างแรก ใน Template Nodejs ที่ AWS ให้มาเมื่อสร้าง Function แบบ "Author from scratch" บน AWS Lambda แล้วลองแก้ค่า statusCode จาก 200 เป็น 400 ซึ่งถ้าดูผ่านๆจะเข้าใจว่าเมื่อเรียก Function นี้จะได้ค่า HTTP 400 มาแทน 200 ที่ตั้งไว้เดิม แต่ว่าเมื่อลองยิงแล้วเราจะได้ HTTP 200 ตามเดิมมา แต่ในเนื้อหาจะตั้งค่า &lt;code&gt;statusCode: 400&lt;/code&gt; ไว้ ซึ่งตรงนี้เข้าใจได้ง่ายๆว่าเพราะเรา return content ไม่ใช่ตั้งค่า statuscode โดยตรง.&lt;/p&gt;

&lt;p&gt;(ในกรณี Lambda proxy integration จะได้ ผลลัพธ์ HTTP 400 Bad Request ถูกต้อง)&lt;/p&gt;

&lt;p&gt;&lt;em&gt;( ถ้าไม่คุ้นเคยกับ HTTP Status &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Status" rel="noopener noreferrer"&gt;อ่านที่นี่&lt;/a&gt; )&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe3olqmdqyjjf3njx7c7a.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe3olqmdqyjjf3njx7c7a.png" alt="HTTP 200 with statusCode 400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;เพื่อให้เห็นภาพชัดเจนขึ้น ลองเปลี่ยน Code ใหม่ให้มีการ Throw Error อยู่ใน Code ดูบ้าง&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;handler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// TODO implement&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Internal Error - Intentionally throw this error&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;เมื่อลองเรียกดูแล้วจะพบว่าผลลัพธ์ที่ได้ คือ HTTP 200 (OK) ที่มาพร้อมกับ Error message&lt;br&gt;
(ในกรณี Lambda proxy integration จะได้ ผลลัพธ์ HTTP 502 Bad Gateway ถูกต้อง)&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft1b78al18zltk0tosksb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft1b78al18zltk0tosksb.png" alt="HTTP 200 with Error"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;กรณีแบบนี้เหมือนกับว่า Lambda ไม่ Ok นะแต่ API Gateway บอกว่ามัน Ok &lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fylit5lchgulqxj0wgmy2.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fylit5lchgulqxj0wgmy2.jpg" alt="It's Okay to not be Okay"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;(ภาพจาก &lt;a href="https://www.beartai.com/lifestyle/449363" rel="noopener noreferrer"&gt;Beartai.com&lt;/a&gt; Seo Ye-ji สวยมว๊ากกกก ซีรีย์ดีแนะนำครับ)&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;ถ้าใครเคยพัฒนา NodeJS บน ExpressJS หรือใช้ Web Framework อื่นๆ น่าจะคุ้นเคยดี ว่ากับการ throw Error ใน Code แล้วได้ HTTP 500 โดยอัตโนมัติ ซึ่เหตุผลที่ได้แบบนั้นเพราะว่า Framework ทำการ Handle Error ต่างๆให้แล้วสร้าง HTTP Response ที่เหมาะสม เนื่องจากเราทำงานอยู่ใต้ Framework นั้นๆ แต่สำหรับ AWS Lambda กับ API Gateway ซึ่งเป็นคนละระบบกันโดยสิ้นเชิง แต่รองรับการเชื่อมต่อ (Integrate) ระหว่างกัน ดังนั้นสิ่งที่ตอบกลับมา HTTP200 ซึ่งเป็นค่าปกติของ API Gateway นั้นจึงมีความหมายประมาณว่า &lt;strong&gt;Code ของคุณถูกเรียกใช้แล้ว&lt;/strong&gt; &lt;/p&gt;
&lt;h2&gt;
  
  
  ตั้งค่า Method Response บน API Gateway
&lt;/h2&gt;

&lt;p&gt;อันดับแรกจะต้องกำหนด HTTP Status ต่างๆที่เป็นไปได้ที่ API Gateway จะตอบกลับไปยังผู้เรียก โดยการตั้งค่าทำได้ที่หัวข้อ Method Response บน API Gateway โดยในกรณีตัวอย่างจะมีการตอบ Error 2 แบบคือ&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;HTTP 500 (Internal Server Error) สำหรับกรณีที่มีการสร้าง Error ขึ้นด้วยการเรียกคำสั่ง &lt;code&gt;throw Error()&lt;/code&gt; (ตัวอย่างกรณีที่ 2) โดยในเอกสารของ AWS จะเรียก Error ประเภทนี้ว่า &lt;strong&gt;Standard Lambda Error&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;HTTP 400 (Bad Request) สำหรับกรณีที่มีการกำหนด Response แบบมีโครงสร้างข้อมูล Error ที่เรากำหนดไว้ โดยอาจจะมีการสร้างเป็น Class หรือ Object ที่มีรูปแบบตายตัวและทำการกำหนดตัวเลข Error Code ไว้ใน Object ดังกล่าว (ตัวอย่างกรณีที่ 1) โดยในเอกสารของ AWS จะเรียก Error ประเภทนี้ว่า &lt;strong&gt;Custom Lambda Error&lt;/strong&gt;
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fum91vldcu7g1kxpw0izx.png" alt="Method Response"&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  ตั้งค่า Integration Response บน API Gateway
&lt;/h2&gt;

&lt;p&gt;เมื่อเรากำหนด HTTP Status ที่จะทำการตอบกลับแล้วในขั้นตอนถัดไปจะเป็นการกำหนดให้ API Gateway อ่านค่าที่ตอบกลับมาจาก AWS Lambda และแปลงข้อมูลเป็น Status ตามที่กำหนด โดยในการอ่านค่า API Gateway จะใช้ Regular Expression ในการทดสอบข้อความในส่วน errorMessage ที่ตอบกลับมา ซึ่งถ้า Regular Expression Pattern ที่กำหนดตรงกับข้อมูลที่ได้รับ API Gateway จะเลือกตอบกลับด้วย HTTP Status ตาที่กำหนดคู่กับ Pattern นั้นๆ นอกจากนี้สำหรับการตังค่า Body Content ที่ API Gateway ตอบกลับมาจะสามารถตั้งค่าได้ด้วย Mapping Template เพื่อกำหนดรูปแบบข้อมูลที่ส่งกลับได้ด้วย&lt;/p&gt;
&lt;h3&gt;
  
  
  กรณี Standard Lambda Error (500)
&lt;/h3&gt;

&lt;p&gt;ในกรณี Error ที่เกิดจากการเรียก &lt;code&gt;throw Error()&lt;/code&gt; ซึ่ง Lambda จะสร้าง Error Object ตอบกลับไปยัง API Gateway โดยอัตโนมัติและมี &lt;code&gt;errorMessage&lt;/code&gt; เป็นส่วนประกอบตามรูปผลการทดสอบในกรณีที่ 2 ข้างต้น&lt;br&gt;
โดยในตัวอย่าง เราจะทำการกำหนดข้อความให้ขึ้นต้นด้วยคำว่า 'Internal Error' ไว้หน้ารายละเอียดของ Error ที่ถูกสร้างขึ้น เพื่อให้ง่ายต่อการกำหนด regular expression ที่จะใช้ทดสอบ ซึ่งจะสามารถกำหนด Lambda Error Regexเป็น &lt;code&gt;^Internal Error.*&lt;/code&gt; เพื่อให้ Match กับข้อความ Error ได้ อย่างไรก็ตามจากการทดสอบ กรณีที่เป็น partial match หรือ เป็นการที่ regular expression pattern ตรงกับ errorMessage เพียงบางส่วน จะไม่ถือว่าเป็นกรณีที่กำหนด และ API Gateway จะตอบกลับ 200 ตามค่ามาตรฐาน&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1epym7pvbwwwfpvbz14u.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1epym7pvbwwwfpvbz14u.png" alt="Config HTTP 500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;เมื่อทดสอบด้วย Postman จะได้ผลลัพธ์ถูกต้องโดย ได้รับข้อมูล HTTP 500 (Internal Server Error) พร้อมกับข้อมูล Error object ต้นฉบับ&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxiv1qvl4yw3iims260xi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxiv1qvl4yw3iims260xi.png" alt="Postman Test HTTP 500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;เนื่องจากการแสดง Stack trace ส่งไปให้ผู้เรียกใช้ อาจจะไม่ค่อยเหมาะสม เราสามารถกำหนดเนื้อหาของข้อมูลที่จะส่งกลับไปยังผู้เรียกใช้ได้ โดยการตั้งค่า Mapping Template และกำหนดชนิดของข้อมูลเป็น &lt;code&gt;application/json&lt;/code&gt; และตั้งค่าให้ตอบกลับเฉพาะ errorMessage เท่านั้น&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqd3noygm1be0vf2ydkkb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqd3noygm1be0vf2ydkkb.png" alt="Config Mapping for HTTP 500"&gt;&lt;/a&gt;&lt;br&gt;
(อย่าลืมกดปุ่ม &lt;strong&gt;Save&lt;/strong&gt; สีเทาด้านล่าง เพื่อบันทึก Template แทนการกด &lt;strong&gt;Save&lt;/strong&gt; สีน้ำเงินด้านบน ที่เป็นการบันทึกเมื่อแก้ Lambda Error Regex - ส่วนตัวคิดว่าการออกแบบ UX ตรงนี้แปลกๆไปหน่อย ทำให้สับสนได้ง่ายมาก และกดผิดไปหลายครั้งมาก)&lt;/p&gt;

&lt;p&gt;เมื่อทำการ Deploy และทดสอบ จะได้ข้อมูลตอบกลับที่ถูกต้องตามที่ต้องการ โดยได้ สถานะ HTTP500 และแสดงเฉพาะข้อความ errorMessage ที่กำหนด&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6r6e9ejuxjut0jzq1oag.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6r6e9ejuxjut0jzq1oag.png" alt="Correct HTTP 500 Response"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  กรณี Custom Lambda Error (400)
&lt;/h3&gt;

&lt;p&gt;ในกรณีที่มีการกำหนดโครงสร้างข้อมูลตอบกลับไว้ โดยอาจจะเป็นการกำหนด Class หรือ Response Object ตามที่ต้องการ โดยการตอบ Error Response จะใช้โครงสร้างข้อมูลแบบเดียวกับ Success Response ตามตัวอย่างกรณีที่ 1&lt;br&gt;
วิธีการที่ง่ายที่สุดที่จะทำให้ API Gateway ทำการตรวจสอบข้อความที่ส่งออกไปและทำการเลือกส่ง HTTP Status ได้อย่างเหมาะสมคือการทำให้เกิดการ Error ด้วยคำสั่ง &lt;code&gt;context.fail()&lt;/code&gt; แทนการ return ใน Code ตัวอย่างข้างต้น โดยส่งข้อมูล response ไปในรูปแบบ JSON string ไปยัง Error ที่ถูกสร้างขึ้นเมื่อเรียก &lt;code&gt;context.fail()&lt;/code&gt; ซึ่งเมื่อทำการแก้ไขแล้วจะได้ Code ดังนี้&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;handler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// TODO implement&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;statusCode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;// Change from 200 to 400&lt;/span&gt;
        &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Hello from Lambda!&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;เมื่อ Function ถูกเรียก Lambda จะสร้าง Error Object แบบเดียวกับกรณี HTTP 500 ข้างต้น โดยนำข้อมูล Response Object ที่เรากำหนดไปเป็น errorMessage เพื่อให้ API Gateway ตรวจสอบ ซึ่งเราสามารถกำหนด Regular expression pattern คือ &lt;code&gt;.*"statusCode":400.*&lt;/code&gt; เพื่อให้ Match กับ statusCode ที่อยู่ใน Response object ได้ และแสดง สถานะตอบกลับได้อย่างถูกต้อง&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjs4qbkurun09oqnq7xs7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjs4qbkurun09oqnq7xs7.png" alt="Config HTTP400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;เมื่อทำการทดสอบแล้วจะได้ผลลัพธ์เป็น HTTP Status 400 (Bad Request) ที่ส่งมาพร้อมกับ Lambda error object ที่ถูกสร้างขึ้น&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk1gjr1l00p1b723gpwnh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk1gjr1l00p1b723gpwnh.png" alt="HTTP400 with Error Object"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;ในขั้นตอนสุดท้ายเพื่อที่จะทำให้แสดงผล Response content เป็น JSON ตามที่กำหนดไว้ จะต้องทำการสร้าง Mapping Template เพื่อนำ errorMessage กลับมาแสดงเป็น Response Content ตามเดิม&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbu65s4i8qiid4tw7iwsd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbu65s4i8qiid4tw7iwsd.png" alt="HTTP400 Mapping Template"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;เมื่อทดสอบอีกครั้งจะได้ Response Object ที่เรากำหนดไว้เพร้อมกับสถานะ HTTP400 ที่ถูกต้องตามที่กำหนด&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj20vfy7hu1dr4su4exaa.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj20vfy7hu1dr4su4exaa.png" alt="HTTP400 with response data"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  บทสรุป
&lt;/h2&gt;

&lt;p&gt;สำหรับคนที่เพิ่งเริ่มหัดใช้ Lambda และ API Gateway อาจจะเคยเจอปัญหานี้ ซึ่งหวังว่าจะมีประโยชน์กับคนที่ผ่านเข้ามาดูบ้างไม่มากก็น้อยนะครับ ทั้งนี้โดยส่วนตัวเองจะชอบใช้งาน Lambda proxy integration มากการเพราะสามารถควบคุมการทำงานต่างจาก code ได้โดยตรงมากกว่าการใช้งานแบบ Lambda custom integration ตามตัวอย่างในบทความนี้ &lt;/p&gt;

&lt;p&gt;สุดท้ายนี้ถ้ามีคำถาม คำแนะนำ หรือข้อเสนอแนะใดๆ ผมยินดีเป็นอย่างยิ่งที่จะได้สนทนาแลกเปลี่ยนกับทุกๆท่านที่ผ่านเข้ามาคับ ขอให้สนุกกับการพัฒนาซอฟแวร์นะครับ&lt;/p&gt;

&lt;h2&gt;
  
  
  เอกสารอ้างอิง
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/apigateway/latest/developerguide/handle-errors-in-lambda-integration.html#handle-standard-errors-in-lambda-integration" rel="noopener noreferrer"&gt;Handle Lambda errors in API Gateway&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>awsthai</category>
      <category>lambda</category>
      <category>apigateway</category>
      <category>aws</category>
    </item>
    <item>
      <title>API Gateway integration response setup for AWS Lambda</title>
      <dc:creator>Pakawat (Tle) Teerawattanasuk</dc:creator>
      <pubDate>Fri, 24 Sep 2021 10:33:08 +0000</pubDate>
      <link>https://forem.com/awscommunity-asean/api-gateway-integration-response-setup-for-aws-lambda-25km</link>
      <guid>https://forem.com/awscommunity-asean/api-gateway-integration-response-setup-for-aws-lambda-25km</guid>
      <description>&lt;p&gt;&lt;strong&gt;Disclaimer: This article cover Lambda custom integration only, Lambda proxy integration will work differently&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It's a very common strategy to create your function in AWS Lambda and create REST API on API Gateway and integrate them together, however, there's one common and simple mistake that many developer overlook when doing so, especially if you move from local development to Lambda. Let's see the example below&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="nx"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;handler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// TODO implement&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;statusCode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;// Change from 200 to 400&lt;/span&gt;
        &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Hello from Lambda!&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The above code blocks is a little modification from pre created code when choose "Author from scratch" when creating Lambda with NodeJS. The only change is from statusCode 200 to 400. Then create a new REST API method point to this lambda function... deploy and done. This is how simple it look like to create REST API, When looking at the code, many developer will say that this will return HTTP Status 400 right? but it's not. You will get response 200 with data &lt;code&gt;statusCode: 400&lt;/code&gt;. This case can easily interpret that it's because we return content not HTTP Status.&lt;/p&gt;

&lt;p&gt;(For Lambda proxy integration this function will response correctly with HTTP 400 Bad Request) &lt;/p&gt;

&lt;p&gt;&lt;em&gt;( For more information about HTTP Status &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Status" rel="noopener noreferrer"&gt;Read Here&lt;/a&gt; )&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe3olqmdqyjjf3njx7c7a.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe3olqmdqyjjf3njx7c7a.png" alt="HTTP 200 with statusCode 400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's try another example which is more obvious, this time intentionally throw Error in Lambda.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="nx"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;handler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// TODO implement&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Internal Error - Intentionally throw this error&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Try calling it and get HTTP 200 again with error message as content.&lt;/p&gt;

&lt;p&gt;(For Lambda proxy integration this function will response correctly with HTTP 502 Bad Gateway)&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft1b78al18zltk0tosksb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft1b78al18zltk0tosksb.png" alt="HTTP 200 with Error"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Look like It's Okay to Not Be Okay&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi9y1ee0mqg2h59jfkbbz.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi9y1ee0mqg2h59jfkbbz.jpg" alt="It's Okay to not be Okay"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;(Image from &lt;a href="https://www.kdramalove.com/ItsOkayToNotBeOkay.html" rel="noopener noreferrer"&gt;Kdramalove.com&lt;/a&gt; - Yes... I like Korean series.. and Seo Ye-ji is gorgeous)&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If you have experience throwing error and get HTTP 500 when developing locally with express or any framework. That happen because framework handle that error for you and generate correct HTTP Status, while Lambda and API Gateway is totally different services and what API Gateway looking for, by default, is that Lambda answer something and HTTP 200 you get mean &lt;strong&gt;yes, your code was executed.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Setup Method Response in API Gateway
&lt;/h2&gt;

&lt;p&gt;First we need to define which HTTP Status we want to send back to client. We can do this in Method Response in API Gateway. So let's add the following error&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;HTTP 500 (Internal Server Error) for error that has been generated when we call &lt;code&gt;throw Error()&lt;/code&gt; (Second case above). According to AWS Documentation, this type of error call &lt;strong&gt;Standard Lambda Error&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;HTTP 400 (Bad Request) for error that we intentionally response with specific formatted string (First case above). According to AWS Documentation, this type of error call &lt;strong&gt;Custom Lambda Error&lt;/strong&gt;
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fum91vldcu7g1kxpw0izx.png" alt="Method Response"&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Setup Integration Response in API Gateway
&lt;/h2&gt;

&lt;p&gt;Next, move to Integration Response to configure each case. The general idea for configuring this is to set a pattern (regular expression) that will be tested against response from Lambda function, if the pattern matched, we will get HTTP Response governing that pattern. If no patterns matched, we will get the default HTTP response. (configured to 200 by default or customize by configured with pattern &lt;code&gt;.*&lt;/code&gt;)&lt;br&gt;
Furthermore, we can setup response body using Mapping Template.&lt;/p&gt;

&lt;h3&gt;
  
  
  Handle Standard Lambda Error (500)
&lt;/h3&gt;

&lt;p&gt;For this type of error, when we call &lt;code&gt;throw Error()&lt;/code&gt;, Lambda will automatically generate response as shown in screenshot above. In order to test pattern against this type of error, API Gateway will test configured pattern against &lt;code&gt;errorMessage&lt;/code&gt; field instead of whole error object. &lt;/p&gt;

&lt;p&gt;In our case, we send our error message when generating this error by adding prefix 'Internal Error' in front of any detail. Let's setup using regular expression &lt;code&gt;^Internal Error.*&lt;/code&gt; which will match whole errorMessage. According to my test, if we use partial match (e.g. Internal*) it will not work and we will still get HTTP 200 instead of 500.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1epym7pvbwwwfpvbz14u.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1epym7pvbwwwfpvbz14u.png" alt="Config HTTP 500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Testing on postman again, now we will get HTTP 500 (Internal Server Error) with the same previous content because we use Passthrough for content handling above.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxiv1qvl4yw3iims260xi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxiv1qvl4yw3iims260xi.png" alt="Postman Test HTTP 500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now that we got correct error, but it's strange to stack trace to client. Let's set Mapping Template for &lt;code&gt;application/json&lt;/code&gt; type and return only errorMessage to client.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqd3noygm1be0vf2ydkkb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqd3noygm1be0vf2ydkkb.png" alt="Config Mapping for HTTP 500"&gt;&lt;/a&gt;&lt;br&gt;
(There's a strange UX behavior here, when editing Mapping Template you have to grey &lt;strong&gt;Save&lt;/strong&gt; down below text editor instead of clicking blue &lt;strong&gt;Save&lt;/strong&gt; button above, which is more attractive to click.)&lt;/p&gt;

&lt;p&gt;Deploy and test it to get the response we want!&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6r6e9ejuxjut0jzq1oag.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6r6e9ejuxjut0jzq1oag.png" alt="Correct HTTP 500 Response"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Handle Custom Lambda Error (400)
&lt;/h3&gt;

&lt;p&gt;For this case where we want to create our own response with error message and we know that this response is gonna be an error. The easiest way is to forcefully make this function failed and return and error using &lt;code&gt;context.fail&lt;/code&gt; instead of &lt;code&gt;return&lt;/code&gt;. Additionally, in order for API Gateway to parse error message, we need to convert JSON response to text and the code will be change to the following:-&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="nx"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;handler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// TODO implement&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;statusCode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;// Change from 200 to 400&lt;/span&gt;
        &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Hello from Lambda!&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Now that we cause error to occurred, Lambda will return and error object similar to standard error in previous case and we can use regex &lt;code&gt;.*"statusCode":400.*&lt;/code&gt; to detect which statusCode we send and return correct HTTP Response.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjs4qbkurun09oqnq7xs7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjs4qbkurun09oqnq7xs7.png" alt="Config HTTP400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After testing, you will get HTTP Status 400 (Bad Request) with generated Lambda error object&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk1gjr1l00p1b723gpwnh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk1gjr1l00p1b723gpwnh.png" alt="HTTP400 with Error Object"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Last step is to setup Mapping Template to return our intended object&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbu65s4i8qiid4tw7iwsd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbu65s4i8qiid4tw7iwsd.png" alt="HTTP400 Mapping Template"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Finally, we will get our same old object but with HTTP Status 400 instead of 200 as intended.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj20vfy7hu1dr4su4exaa.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj20vfy7hu1dr4su4exaa.png" alt="HTTP400 with response data"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;There's a lot more to explore and configure when working with AWS service, this one is just an example of a common mistake mostly happen when start using Lambda and API Gateway or when in need of a quick api creation. Anyway, I personally prefer using Lambda proxy integration which let me have more control. I hope this article help anyway to happen to pass through here or looking for this problem. &lt;/p&gt;

&lt;p&gt;Lastly, I would be very appreciate for any comment, knowledge sharing and feedback. Happy coding!&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/apigateway/latest/developerguide/handle-errors-in-lambda-integration.html#handle-standard-errors-in-lambda-integration" rel="noopener noreferrer"&gt;Handle Lambda errors in API Gateway&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>aws</category>
      <category>awsthai</category>
      <category>lambda</category>
      <category>apigateway</category>
    </item>
    <item>
      <title>WooCommerce Webhook and AWS Lambda</title>
      <dc:creator>Pakawat (Tle) Teerawattanasuk</dc:creator>
      <pubDate>Sun, 12 Sep 2021 02:32:36 +0000</pubDate>
      <link>https://forem.com/riteru/woocommerce-webhook-and-aws-lambda-2948</link>
      <guid>https://forem.com/riteru/woocommerce-webhook-and-aws-lambda-2948</guid>
      <description>&lt;p&gt;WooCommerce is very popular platform for e-commerce, while it's in PHP and usually self-hosted on self-managed servers or shared host, I got some question from my friend to extend WooCommerce functionality. It's possible to write plugin but for my use cases, using WC Webhook and AWS Lambda is more convenient. In this example, I will handle Webhook call when order is updated (order.updated).&lt;/p&gt;

&lt;p&gt;More information about WC Webhook&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://woocommerce.github.io/woocommerce-rest-api-docs/?javascript#delivery-payload" rel="noopener noreferrer"&gt;API Document&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://gist.github.com/teer823/b0670117038250379e2d860368e90f7d" rel="noopener noreferrer"&gt;Sample payload&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Pre-requisites
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Pre installed Wordpress &amp;amp; WooCommerce (And your host IP)&lt;/li&gt;
&lt;li&gt;Basic knowledge of creating lambda function with NodeJS runtime&lt;/li&gt;
&lt;li&gt;Basic knowledge about to create REST API on API Gateway&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Architecture
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyuy08lbnhuxi1278wlot.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyuy08lbnhuxi1278wlot.png" alt="Architecture"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  Workflow
&lt;/h3&gt;

&lt;p&gt;When receiving Webhook event, it's important to check for integrity of Webhook content to prevent malicious request. In order to do so we can calculate HMAC using Webhook content and secret which store in both side. After validated event integrity, we can proceed to process event.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdxn9jti40cpdj47y9q8w.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdxn9jti40cpdj47y9q8w.png" alt="Workflow"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  Setup AWS Lambda
&lt;/h3&gt;

&lt;p&gt;First, let create Lambda function that will process our webhook event.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Create lambda function with Node runtime&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fft5fpjh5b7o8tbfx5m81.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fft5fpjh5b7o8tbfx5m81.png" alt="Setup Lambda"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Set up Configuration &amp;gt; Environment Variables &amp;gt; Add WC_SECRET (this same value should be used when configure WooCommerce Webhook later)&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc7nqcsk9phtowpqxluea.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc7nqcsk9phtowpqxluea.png" alt="Environment Variable"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Add The following Code to Lambda &lt;code&gt;index.js&lt;/code&gt; file&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;For Testing, if you set WC_SECRET to "woocommerce", you can use the following test event in Lambda&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Deploy and testing Lambda function will give you the success screen with event log (&lt;code&gt;OnOrderCompleted&lt;/code&gt; in screenshot below)&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fui4hlprt7amxd6plgq11.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fui4hlprt7amxd6plgq11.png" alt="Lambda Test Result"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  Setup API Gateway
&lt;/h3&gt;

&lt;p&gt;Next step is to create API Gateway to receive webhook. For some unknown reason, WooCommerce Webhook don't fire pre-flight request (OPTION) and always skip &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS" rel="noopener noreferrer"&gt;CORS&lt;/a&gt;, so I will skip detail about enabling CORS here.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Create REST API&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa6ao0u0ey02m8dkfd74x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa6ao0u0ey02m8dkfd74x.png" alt="Create REST API"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Create Resource (order)&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9irf79i2576b3hbkimy7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9irf79i2576b3hbkimy7.png" alt="Create Order Resource"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Create POST method point to the Lambda function created earlier. Note that we need to enable Lambda Proxy Integration in order to forward header to Lambda Function.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9nv1lbrjspoa3mo7fvmo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9nv1lbrjspoa3mo7fvmo.png" alt="Create POST Method"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;In order to only allow API call which initiate from our WooCommerce server. Create Resource Policy with explicit Deny on call from IP outside our own servers.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5hxqxnb8ecgku762ezn7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5hxqxnb8ecgku762ezn7.png" alt="Resource Policy"&gt;&lt;/a&gt;&lt;br&gt;
Using Resource Policy below, replace &lt;code&gt;REGION&lt;/code&gt;, &lt;code&gt;ACCOUNT_ID&lt;/code&gt;, &lt;code&gt;API_ID&lt;/code&gt; and &lt;code&gt;SOURCE_CIDR&lt;/code&gt; with your own configuration.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nl"&gt;"Version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2012-10-17"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nl"&gt;"Statement"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"Effect"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Allow"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"Principal"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"*"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"Action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"execute-api:Invoke"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"Resource"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"arn:aws:execute-api:{{REGION}}:{{ACCOUNT_ID}}:{{API_ID}}/*/*/*"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"Effect"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Deny"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"Principal"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"*"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"Action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"execute-api:Invoke"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"Resource"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"arn:aws:execute-api:{{REGION}}:{{ACCOUNT_ID}}:{{API_ID}}/*/*/*"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"Condition"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"NotIpAddress"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"aws:SourceIp"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"{{SOURCE_CIDR}}"&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Finally, Deploy API and get your Webhook target URL (don't forget to add your resource name when calling it)&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbw9dmqetojlxn62ydtx4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbw9dmqetojlxn62ydtx4.png" alt="Deploy"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  Setup WooCommerce Webhook
&lt;/h3&gt;

&lt;p&gt;Go to your WooCommerce (in my case i use ngrok to host it locally) and config Webhook&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;To config webhook go to &lt;code&gt;Setting &amp;gt; Advanced &amp;gt; Webhooks&lt;/code&gt; and select &lt;code&gt;Add Webhook&lt;/code&gt; &lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F26bsugaq1mxxiumd4n5v.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F26bsugaq1mxxiumd4n5v.png" alt="Webhook Setting"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Config new webhook as we plan&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Status&lt;/code&gt; : &lt;code&gt;Active&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Topic&lt;/code&gt; : &lt;code&gt;Order Updated&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Delivery URL&lt;/code&gt; : API Gateway's &lt;code&gt;Invoke URL&lt;/code&gt; following by &lt;code&gt;/order&lt;/code&gt; (or any resource name, created in API Gateway)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Secret&lt;/code&gt; : same value with &lt;code&gt;WC_SECRET&lt;/code&gt; configured in AWS Lambda's Environment Variable.
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5db4s3jbxs1qcrz6k06l.png" alt="Webhook Configuration"&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;&lt;p&gt;Hit Save, you may likely get error 400, like the picture below, because when we setup webhook, woocommerce try to send POST request to our API but we didn't handle it above. However, our webhook will work anyway, let's go to test our webhook.&lt;br&gt;&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg0cm1m3qn3pbottm4k9f.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg0cm1m3qn3pbottm4k9f.png" alt="Error 400"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;

&lt;/ul&gt;




&lt;h3&gt;
  
  
  Test it!
&lt;/h3&gt;

&lt;p&gt;Let's test our webhook, create an order and update it to &lt;code&gt;processing&lt;/code&gt; or &lt;code&gt;completed&lt;/code&gt; and let's check the logs.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;WooCommerce Log at &lt;code&gt;Status &amp;gt; Logs&lt;/code&gt; and select log file starting with &lt;code&gt;webhook-delivery-&lt;/code&gt; and click &lt;code&gt;view&lt;/code&gt;. In &lt;code&gt;Response&lt;/code&gt; value, check http response &lt;code&gt;Code&lt;/code&gt; and &lt;code&gt;Body&lt;/code&gt; value to see success webhook.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0so2uzyvl531k6vv461g.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0so2uzyvl531k6vv461g.png" alt="WC Log"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Alternately, you can check CloudWatch Log groups for AWS Lambda log.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fytx76zvja5koqpr9jz1u.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fytx76zvja5koqpr9jz1u.png" alt="CloudWatch"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;There're many things we can do with WooCommerce Webhook e.g. Save your information for further processing or trigger automation. I write this article aiming for those who want to start explore and expand your e-commerce business, so if you happen to come across this, I hope you found it useful and help you realize your idea in business!&lt;/p&gt;

</description>
      <category>woocommerce</category>
      <category>wordpress</category>
      <category>aws</category>
      <category>awslambda</category>
    </item>
    <item>
      <title>Tag-based monorepo docker image build on AWS</title>
      <dc:creator>Pakawat (Tle) Teerawattanasuk</dc:creator>
      <pubDate>Tue, 07 Sep 2021 09:09:22 +0000</pubDate>
      <link>https://forem.com/awscommunity-asean/tag-based-monorepo-docker-image-build-on-aws-1bja</link>
      <guid>https://forem.com/awscommunity-asean/tag-based-monorepo-docker-image-build-on-aws-1bja</guid>
      <description>&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6dxe1y23s46sc9y4ysqq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6dxe1y23s46sc9y4ysqq.png" alt="Monorepo to AWS ECR"&gt;&lt;/a&gt;&lt;br&gt;
My team adopted &lt;a href="https://www.atlassian.com/git/tutorials/monorepos" rel="noopener noreferrer"&gt;monorepo&lt;/a&gt; for our backend microservices and we need a simple solution to build docker images so we can use it for test or deploy anywhere. Because we use &lt;a href="https://aws.amazon.com/ecr/" rel="noopener noreferrer"&gt;Amazon Elastic Container Repository (ECR)&lt;/a&gt; to store docker images, I decided to use git tag to trigger docker image build on &lt;a href="https://aws.amazon.com/codebuild/" rel="noopener noreferrer"&gt;AWS CodeBuild&lt;/a&gt; and push it to ECR.&lt;/p&gt;

&lt;p&gt;The condition to trigger build is adding tag &lt;code&gt;service-name/version-number&lt;/code&gt; and push it to GitHub or Bitbucket. AWS CodeBuild will receive webhook and start building new docker image using &lt;code&gt;version-number&lt;/code&gt; provided in tag and push it to ECR. And lastly.... store build artifact in S3.&lt;/p&gt;
&lt;h3&gt;
  
  
  The REPO
&lt;/h3&gt;

&lt;p&gt;Firstly, we need a monorepo on GitHub or Bitbucket, I prepared a sample repo with 2 projects at &lt;a href="https://github.com/teer823/monorepo-aws-docker" rel="noopener noreferrer"&gt;https://github.com/teer823/monorepo-aws-docker&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Each projects is just a simple express service with Dockerfile ready to build docker image and include buildspec.yml for AWS CodeBuild to use, however, I also added 2 buildspec file inside buildspec folder to demonstrate that it's possible to separate buildspec.yml file per project as needed.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The buildspec.yml file is an instruction for AWS CodeBuild and separated to phases. Here's what I do at each phase.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;pre_build&lt;/strong&gt; extract TAG_VERSION_NUMBER from tag and create version.txt file to store as artifact then login to ECR&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;build&lt;/strong&gt; as the name say... build and tag docker image. Please note that I also add &lt;code&gt;latest&lt;/code&gt; tag here as well, doing so prevent me from using ECR's tag immutability feature.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;post_build&lt;/strong&gt; push docker image to ECR
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;version: 0.2

phases:
  pre_build:
    commands:
      - echo Pre Build Phase...
      - export TAG_VERSION_NUMBER=$(echo $CODEBUILD_WEBHOOK_TRIGGER | sed 's/.*\///')
      - echo $TAG_VERSION_NUMBER
      - &amp;gt;
        PACKAGE_VERSION=$(cat $REPO_FOLDER/package.json | grep version | head -1 | awk -F: '{print $2}' | sed 's/[",[:space:]]//g')
      - echo $PACKAGE_VERSION &amp;gt; version.txt
      - aws ecr get-login-password --region $AWS_DEFAULT_REGION | docker login --username AWS --password-stdin $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com
  build:
    commands:
      - echo Build Phase...
      - cd $REPO_FOLDER
      - docker build -t $IMAGE_REPO_NAME:$TAG_VERSION_NUMBER -t $IMAGE_REPO_NAME:latest .
      - docker tag $IMAGE_REPO_NAME:$TAG_VERSION_NUMBER $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$IMAGE_REPO_NAME:$TAG_VERSION_NUMBER
      - docker tag $IMAGE_REPO_NAME:latest $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$IMAGE_REPO_NAME:latest 
  post_build:
    commands:
      - echo Post Build Phase...
      - docker push $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$IMAGE_REPO_NAME:$TAG_VERSION_NUMBER
      - docker push $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$IMAGE_REPO_NAME:latest
artifacts:
  files:
    - '$REPO_FOLDER/**/*'
    - 'version.txt'
  name: '$IMAGE_REPO_NAME-$TAG_VERSION_NUMBER'
  discard-paths: no
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;For more info about &lt;code&gt;buildspec.yml&lt;/code&gt; please refer to &lt;a href="https://docs.aws.amazon.com/codebuild/latest/userguide/build-spec-ref.html" rel="noopener noreferrer"&gt;HERE&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  AWS ECR
&lt;/h3&gt;

&lt;p&gt;As I want to push docker image to ECR, I need to pre-create container repositories for each service beforehand. Just go and create a private repository and please don't enable &lt;strong&gt;Tag immutability&lt;/strong&gt; or build will failed as &lt;code&gt;latest&lt;/code&gt; tag cannot be overwritten. (Or just change the build spec and remove &lt;code&gt;latest&lt;/code&gt; tag)&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flvhlsxf3bnrnpksphkfa.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flvhlsxf3bnrnpksphkfa.png" alt="AWS ECR Create Repo"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  AWS S3
&lt;/h3&gt;

&lt;p&gt;We want CodeBuild to store build artifact on AWS S3, so we need to prepare a bucket in advance. Let's create a bucket! (Leave the rest of configuration default or up to you.., i recommend turn on server-side encryption for &lt;em&gt;BEST PRACTICE&lt;/em&gt; reason)&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftknd6gyz7byfhhjmuoov.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftknd6gyz7byfhhjmuoov.png" alt="S3 create bucket"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  AWS CODEBUILD
&lt;/h3&gt;

&lt;p&gt;Now we have Code Repo , Container Repo, Artifact bucket.. let's create AWS CodeBuild project.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Project configuration&lt;/strong&gt; your choices..
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpsqynyfbr11c92ie1ql1.png" alt="Config Project"&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Source&lt;/strong&gt; connect to your GitHub or Bitbucket and select &lt;em&gt;repository in my GitHub account&lt;/em&gt; and select our monorepo repository. Please note that we need to rely on webhook so public repo won't do.
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpakgclklg40w9uziw7kn.png" alt="Config Sourdce"&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Primary source webhook events&lt;/strong&gt; Here's what important. Enable webhook by checking the box. And config build condition

&lt;ul&gt;
&lt;li&gt;Build type - Single build&lt;/li&gt;
&lt;li&gt;Event type - PUSH and PULL_REQUEST_MERGED&lt;/li&gt;
&lt;li&gt;Condition - Start build when receiving tag, in HEAD_REF add a regular expression &lt;code&gt;^refs/tags/service-name/([\w\.-]+)$&lt;/code&gt;, don't forget to replace &lt;code&gt;service-name&lt;/code&gt; accordingly.
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq6ohu3r6mlywhswi79bs.png" alt="Config Webhook"&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;If you want to use other method to trigger build, here's the place to try and modify build condition according to your usecases.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Environment&lt;/strong&gt; I personally prefer Ubuntu, so it's up to you how you want to config your build machine, however what's important for me here are the following&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;Privileged&lt;/em&gt; Enable the flag because we want to build docker images
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuxrrwiq4aorxaomtkrwr.png" alt="Priviledge"&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Service role&lt;/em&gt; Select your preferred name, I personally like to reuse the role in same project so I name it with project instead of service and next time I can just select it from existing service role. However, please remember the Role name because we will need it later.
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fowyh4il0gat2d78a0b18.png" alt="Service Role"&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;em&gt;Environment Variable&lt;/em&gt; If you noticed in &lt;code&gt;buildspec.yml&lt;/code&gt; earlier, I used some environment variable. you will need to set it here&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AWS_DEFAULT_REGION - your ECR region&lt;/li&gt;
&lt;li&gt;AWS_ACCOUNT_ID - your ECR account Id&lt;/li&gt;
&lt;li&gt;IMAGE_REPO_NAME - name of ECR remove created above&lt;/li&gt;
&lt;li&gt;REPO_FOLDER - folder name for service we're building&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F13yqokgf1efra9oh4c6b.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F13yqokgf1efra9oh4c6b.png" alt="Environment Variable"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Buildspec&lt;/strong&gt; when using root's &lt;code&gt;buildspec.yml&lt;/code&gt; just select &lt;em&gt;Use a buildspec file&lt;/em&gt; will do. &lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe78cdxo7gxz3d8x6vyfg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe78cdxo7gxz3d8x6vyfg.png" alt="Default buildspec"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;In case you want to try using individual buildspec file, add path to project-buildspec file in Buildspec name field&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9xlydegsrx2zdhzilvln.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9xlydegsrx2zdhzilvln.png" alt="Custom buildspec file"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Artifacts&lt;/strong&gt; Select Amazon S3 and bucket created above, use Zip packaging and use &lt;code&gt;service-name&lt;/code&gt; as path.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwjmtyvm00ljqeeua8eem.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwjmtyvm00ljqeeua8eem.png" alt="Build Artifact"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Hit &lt;em&gt;Create build project&lt;/em&gt; and we're done...then add another one for 2nd service.&lt;/p&gt;
&lt;h3&gt;
  
  
  PERMISSION
&lt;/h3&gt;

&lt;p&gt;One last step before start tagging and building. our service role don't have permission to push docker image to ECR yet. so we need to add it in IAM. Go to your Role created above and Attach Managed Policy name &lt;code&gt;AmazonEC2ContainerRegistryPowerUser&lt;/code&gt; to our role and Done!&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fadj73us7a8114avqkn9a.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fadj73us7a8114avqkn9a.png" alt="IAM Role Allow ECR"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  TAGGING TIME!
&lt;/h3&gt;

&lt;p&gt;Now it's time to add tag to monorepo and push it to origin&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;❯ git tag ingest-service/0.1.0
❯ git push origin ingest-service/0.1.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Go back to CodeBuild project and see it work!&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn7vs2c1wtmu7sop4xbcj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn7vs2c1wtmu7sop4xbcj.png" alt="Build in progress"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When building complete, find your new docker image in ECR!&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7nt1362uv86p0rd3iv3t.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7nt1362uv86p0rd3iv3t.png" alt="New Docker Image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;That's all about it. I hope this is useful for anyone looking to make a simple build pipeline like my team :)&lt;/p&gt;

</description>
      <category>aws</category>
      <category>monorepo</category>
      <category>devops</category>
      <category>awsthai</category>
    </item>
  </channel>
</rss>
