# Creating Custom AMI using EC2 Image Builder

## What is AWS EC2 Image Builder

EC2 AMI တစ်ခုကို manual ပြုလုပ်တဲ့အခါမှာ Creating, Custimizing, testing and Deploying အစရှိတဲ့ အဆင့်ဆင့်ကို ကျော်ဖြတ်ရပါတယ်။ ဒီလိုအဆင့်ဆင့် ပြုလုပ်ထားပြီးသား image တစ်ခုကို တစ်ခုခု update လုပ်စရာရှိတိုင်းမှာ ဒီအဆင့်တွေကို အကုန် ပြန်လုပ်ဖို့ အချိန်တွေ အများကြီးပေးရပါတယ်။ ဒီလို လုပ်ပြီးသားအလုပ်တွေကို အကြိမ်အကြိမ်ပြန်လုပ်ရတဲ့ ဒုက္ခတွေ ကင်းဝေးချင်ရင်တော့ EC2 Image Builder ကို အသုံးပြုလို့ရပါတယ်။

![Windows Image Builder Tool - EC2 Image Builder - AWS](https://d1.awsstatic.com/re19/image-builder/Product-Page-Diagram_Image-Factory.cbf8db591ca6de1c5d9149f3cd6ccfe6c6a64f33.png align="left")

EC2 Image Builder ရဲ့ image pipeline ထဲမှာ ဒီအဆင့်တွေအကုန်ပါဝင်ပြီးတော့ တစ်ကြိမ် တည်ဆောက်ထားလိုက်တာနဲ့ အကြိမ်ကြိမ်ပြန်အသုံးပြုလို့ရနိုင်ပါတယ်။ ကုန်ကျစရိတ်အနေနဲ့လည်း သူ့အတွက်သီးသန့်ပေးစရာမလိုပါဘူး၊ pipeline run တဲ့အချိန်မှာ အသုံးပြုသွားတဲ့ resource တွေအတွက် ကုန်ကျစရိတ်နဲ့ image တွေသိမ်းတဲ့အတွက် ကုန်ကျစရိတ်လောက်ပဲပေးရတာဖြစ်ပါတယ်။ automation နဲ့ လိုအပ်တဲ့ stage တွေကို run သွားတာက manual လုပ်တာထက် ပိုမြန်တာကြောင့် manual create လုပ်တာထက်တောင် ကုန်ကျစားရိတ် သက်သာနိုင်ပါသေးတယ်။

ကျွန်တော်တို့ EC2 Image Builder စမ်းကြည့်ဖို့အတွက် Larvel application တစ်ခု deploy ဖို့ လိုအပ်တဲ့ package တွေ ပါဝင်ပြီးသား ubuntu image တစ်ခုလောက် စမ်းပြီးလုပ်ကြည့်ကြရအောင်။

## EC2 Image Builder | creating ubuntu image ready for laravel

1. ### Create IAM Role for Image Builder
    
    အရင်ဆုံး Image Builder အတွက် လိုအပ်တဲ့ permission တွေ ပါဝင်တဲ့ IAM Role တစ်ခု create လုပ်ဖို့လိုပါတယ်။ Role Name ကို `EC2ImageBuilderRole` လို့ပေးပြီး သူ့ထဲမှာ လိုအပ်တဲ့ Permission တွေဖြစ်တဲ့ `AmazonS3ReadOnlyAccess`, `AmazonSSMManagedInstanceCore`, `EC2InstanceProfileForImageBuilder` တို့ကို attach တွဲပေးလိုက်ပါ။
    
    ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1710412814177/086dc2e1-c543-4832-92aa-5fd341dddc95.png align="center")
    
    Permission တွေ တွဲထားပေးပြီးသား `EC2ImageBuilderRole` ကို ပြန်ဖွင့်ကြည့်မယ်ဆိုရင် ဒီအောက်ကပုံထဲကလိုလေး မြင်ရပါမယ်။
    
    ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1710416770396/e823d163-bad0-415e-9f68-a64b7edce270.png align="center")
    
2. ### Creating SNS Topic
    
    ပြီးရင်တော့ Image Builder Pipeline ထဲမှာ အသုံးပြုဖို့ SNS Topic တစ်ခု create လုပ်ပြီး ကျွန်တော့ရဲ့အီးမေးနဲ့ subscribe လုပ်ထားလိုက်ပါတယ်။
    
    ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1710427714598/918c20bc-a7e0-4206-a71d-78e53833ea2c.png align="center")
    
3. ### Create S3 Bucket and upload the build script to S3.
    
    S3 Bucket အသစ်တစ်ခုလုပ်ပြီး ကျွန်တော်တို့ရဲ့ AMI ထဲမှာ လိုအပ်တဲ့ setup တွေ run ဖို့ လိုအပ်တဲ့ script ကို အဲ့ဒီ Bucket ထဲတင်ပေးထားလိုက်ပါမယ်။
    
    ကျွန်တော်ကတော့ Bucket name ကို `kalaung-ami-builder` လို့ပေးပြီး Script file name ကို `amiBuilderScript.sh` လို့ပေးပြီး ဖိုင်ထဲမှာ အောက်က script တွေကို ထည့်ပေးလိုက်ပါတယ်။
    
    ```bash
    # Update the system
    sudo apt update -y &&
    
    # Install PHP 8.1
    sudo apt install software-properties-common -y &&
    sudo add-apt-repository ppa:ondrej/php -y &&
    sudo apt update -y &&
    sudo apt install php8.1 php8.1-fpm php8.1-mysql php8.1-xml php8.1-mbstring php8.1-gd php8.1-curl php8.1-zip php8.1-imagick php8.1-redis php8.1-bcmath php8.1-exif php8.1-ctype php8.1-fileinfo php8.1-tokenizer php8.1-xml -y &&
    sudo systemctl enable --now php8.1-fpm &&
    
    # Install Composer
    curl -sS https://getcomposer.org/installer -o composer-setup.php &&
    sudo php composer-setup.php --install-dir=/usr/local/bin --filename=composer &&
    rm composer-setup.php &&
    
    # Install Nginx
    sudo apt install nginx -y &&
    sudo systemctl enable --now nginx &&
    
    # Install Node.js
    curl -sL https://deb.nodesource.com/setup_20.x | sudo -E bash - &&
    sudo apt install -y nodejs &&
    sudo apt install -y build-essential &&
    sudo npm install -g npm@latest &&
    
    # Add the ubuntu user to the www-data group
    sudo usermod -aG www-data ubuntu &&
    
    # Install Supervisor
    sudo apt install supervisor -y &&
    sudo systemctl enable --now supervisor &&
    
    # Over write the default Nginx Welcome page
    sudo rm /var/www/html/*.html &&
    echo "<!doctype html><html><body><h1>This Server is running PHP 8.1, Node.js 20.x, Nginx, and Supervisor on Ubuntu 22.04 </h1></body></html>" > index.html &&
    sudo mv index.html /var/www/html/index.html
    ```
    
    ပြီးရင်တော့ ကျွန်တော်တို့ရဲ့ Script file လေးကို bucket အသစ်လေးထဲ upload တင်ပေးလိုက်ပါမယ်။
    
    ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1710429334395/48910cbd-537b-43e3-bfce-b68e36e90c42.png align="center")
    
    လိုအပ်တာတွေပြင်ဆင်ပြီးပြီဆိုတော့ ကျွန်တော်တို့ EC2 Image Builder Console ထဲမှာ တဆင့်ခြင်း Setup လုပ်လို့ပါပြီ။
    
4. ### Component creating in Image Builder
    
    Image Builder Component ကို crteate လုပ်ဖို့ `Crteate component` ကိုနိပ်ပြီး အောက်ကအချက်လေးတွေထည့်ပေးလိုက်ပါ။
    
    Component Type: Build နဲ့ Test နှစ်ခုရှိတဲ့အထဲက Build ကို ရွေးပါမယ်
    
    Component Detail :
    
    ```yaml
    Image operating system (OS): Linux
    Compatible OS Versions: Ubuntu 22.04
    Component version: 1.0.0
    Component name: Ubuntu2204-laravel10
    ```
    
    Definition document : မှာတော့ Define document content ကိုရွေးပြီး Content ထဲမှာ အောက်က YAML script တွေထည့်ပေးလိုက်ပါမယ်။ YAML content ထဲက `kalaung-ami-builder` ဆိုတဲ့ နေရာမှာတော့ ကိုယ့်ရဲ့ s3 bucket name ကို ထည့်ပေးဖို့လိုပါမယ်။
    
    ```yaml
     name: EC2ImageBuilder-laravel10
     description: 'This Image Builder component will install PHP 8.1, Node.js 20.x, Nginx, and Supervisor on Ubuntu 22.04'
     schemaVersion: 1.0
     phases:
       - name: build
         steps:
           - name: DownloadScript
             action: S3Download
             onFailure: Abort
             maxAttempts: 3
             inputs:
               - source: s3://kalaung-ami-builder/amiBuilderScript.sh
                 destination: /tmp/amiBuilderScript.sh
           - name: RunScript
             action: ExecuteBash
             onFailure: Abort
             maxAttempts: 3
             inputs:
               commands:
                 - 'chmod +x {{ build.DownloadScript.inputs[0].destination }}'
                 - 'bash {{ build.DownloadScript.inputs[0].destination }}'
           - name: InstanceCleanUp
             action: ExecuteBash 
             onFailure: Abort
             maxAttempts: 3
             inputs:
               commands:
                 - 'rm {{ build.DownloadScript.inputs[0].destination }}'
    ```
    
    ပြီးရင်တော့ အောက်ဆုံးက Create component ဆိုတာကို နှိပ်ပြီး save လိုက်ပါ။ ကိုယ့်ရဲ့ component ကို ပြန်ဖွင့်ကြည့်ရင် အောက်က ပုံထဲကလို ဖြစ်နေရပါမယ်။
    
    ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1710431610346/82a4659d-80a0-4fd8-ab2e-d5fa03558c9c.png align="center")
    
5. ### Recipe creating in EC2 Image Builder
    
    နောက်တဆင့်အနေနဲ့ recipe တစ်ခု create လုပ် ဖို့ `Image recipes` ထဲသွားပြီး `Create new recipe` ဆိုတာကို နှိပ်ပါမယ်။
    
    **Name**: UbuntuLaravelRecipe
    
    **Version**: 1.0.0  
    **Base image:** Select managed images
    
    **Image Operating System (OS)**: Ubuntu
    
    **Image origin:** Quick start (Amazon-managed)
    
    **Image name**: Ubuntu Server 22 LTS x86
    
    **Auto-versioning options**: Use latest available OS version
    
    **Working directory: /tmp**
    
    **Components**: ထဲက `Build components - Ubuntu` မှာတော့ ခုဏက ကျွန်တော်တို့ လုပ်ခဲ့တဲ့ component ပြန်ရွေးပေးပြီး `Test components -` တော့ မထည့်တော့ပါဘူး။
    
    ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1710432837463/dd86a581-81b1-4b0d-89bf-00d11f70b4a1.png align="center")
    
    storage မှာတော့ သူ့ default အတိုင်းပဲ ထားပြီး Save ဖို့အတွက် `Create recipe` ကိုနှိပ်လိုက်ပါမယ်။
    
    ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1710433370757/b0c0098d-a250-477d-8a7d-6de2f3a0f9bf.png align="center")
    
    လိုအပ်တဲ့ recipe လည်းရှိပြီဆိုတော့ ကျွန်တော်တို့ ImagePipeline create လုပ်လို့ရပါပြီ။
    
6. ### Creating Image Pipeline
    
    Image pipelines  ထဲကိုသွားပြီး `Create image pipeline` ကိုနှိပ်ပါမယ်။
    
    ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1710433677493/fd65cb41-495c-4b37-8655-86226cd8d280.png align="center")
    
    Pipeline Name  နဲ့ Description ပေးပြီးရင် Build schedule မှာ Manual ကိုရွေးပြီး Next နှိပ်ပါမယ်။
    
    ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1710433775230/3878cfd7-0e18-4d56-a282-dfa4dac6c5be.png align="center")
    
    Chose recipe မှာ `Use existing recipe` ကိုရွေးပြီး ကျွန်တော်တို့ ခုဏက လုပ်ထားတဲ့ recipe ကို ရွေးပြီး `Next` ထပ်နှိပ်ပါမယ်။
    
    ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1710433897295/45b1d86c-7555-483a-8525-3399c3c84251.png align="center")
    
    ဒီတဆင့်မှာတော့ Default workflows ကိုပဲ ရွေးပြီး `Next` ထပ်နှိပ်ပါ။
    
    ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1710434078770/e66c46f8-f54a-4124-9597-8d44641c24d8.png align="center")
    
    ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1710434051920/2bd03b84-2953-4dce-b054-d7a1d31e05d6.png align="center")
    
    Define infrastructure configuration   မှာ `Create a new infrastructure` ကိုရွေး ပြီး Name တစ်ခုပေးလိုက်ပါ။ ပြီးရင်တော့ Infra setting ထဲက IMA role နဲ့ SNS Topic ကို ကျွန်တော်တို့ ပထမဆုံးလုပ်ခဲ့တာတွေပြန်ရွေးပေးလိုက်ပြီး Instance Type ကိုတော့ t3.medium လောက် ရွေးပေးလိုက်ပါ။
    
    ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1710434796378/aa72b6a8-139e-4cfb-992b-d63c073e0360.png align="center")
    
    VPC, subnet and security groups ထဲမှာတော့ Default VPC, subnet  နဲ့ Default Security group တွေပဲ ရွေးပေးလိုက်ပါ။
    
    ပြီးရင်တော့ Next ထပ်နှိပ်လိုက်ပါ။
    
    ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1710435014251/20fdd4a4-7f45-42a7-a4cd-f5244fb9c5cf.png align="center")
    
    Define distribution settings မှာဘာမှ မပြင်ပဲ `Next` ပဲ နှိပ်လိုက်ရင် Review state ကိုရောက်သွားပါမယ်။
    
    ကိုယ်လုပ်ခဲ့တဲ့ Pipeline ကိုတစ်ချက် review လုပ်ပြီး အဆင်ပြေရင် `Create pipeline` ကိုနှိပ်ပြီး Save လိုက်လို့ရပါပြီ။
    
    ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1710435297702/b4657261-5fd3-480e-8d4d-db74320ecc47.png align="center")
    
    Pipeline လေး create လုပ်တာ success ဖြစ်သွားပြီဆိုရင်တော့ ကိုယ့်ရဲ့ pipeline အသစ်လေးကို ရွေးပြီး `Actions` ထဲက `Run pipeline` ဆိုတာကို နှိပ်လိုက်ပါ။
    
    Pipeline Run  ပြီးသွားပြီဆိုရင်တော့ `Images` ထဲကို သွားကြည့်လိုက်ပါ။
    
    ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1710435519788/7b2e98f6-7f6f-4417-b850-9af327d09600.png align="center")
    
    ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1710435537855/ad7a9667-db69-46a0-b857-8fede5ab3800.png align="center")
    
    Images ထဲရောက်လို့ ကိုယ့်ရဲ့ image/version ကို နှိပ်လိုက်ရင် Building state မှာရှိနေတဲ့ image ကို အပေါ်ပုံထဲကလို မြင်ရမှာ ဖြစ်ပါတယ်။ ဆယ်မိနစ်လောက် စောင့်ပြီး refresh လုပ်လိုက်ရင် Testing stage ကို ရောက်သွားပါလိမ့်မယ်။
    
    ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1710436542787/bdb0d88c-f905-4fd1-b36f-ae99aecc4606.png align="center")
    
    ခဏထပ်စောင့်ပြီး refresh လုပ်ကြည့်လို့ Image status က Available ဖြစ်သွားရင်တော့ ကိုယ့်ရဲ့ image လေးကို ထပ်ဖွင့်ကြည့်ပြီး အသုံးပြုဖို့ ready ဖြစ်နေပါပြီ။
    
    ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1710436611138/77e20273-9ed9-4f3f-b23c-98422e41f928.png align="center")
    
    SNS topic subscribe လုပ်ထားတဲ့ ကိုယ့်ရဲ့ email ထဲကိုလည်း အောက်ပါပုံထဲကလိုမျိုး notification လေးရောက်နေပါလိမ့်မယ်။
    
    ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1710436801404/16b01adb-1db3-45ab-91ce-12570a0b7664.png align="center")
    
    ကျွန်တော်တို့လုပ်ထားတဲ့ Pipeline ထဲက create လုပ်ပေးလိုက်တဲ့ AMI လေး တစ်ကယ် အလုပ်မလုပ်သိချင်ရင်တော့ EC2 Console ထဲမှာ Instance အသစ်တစ်ခု create လုပ်ကြည့်နိုင်ပါတယ်၊ AMI နေရာမှာတော့ MyAMIs &gt;&gt; Ownd By Me ထဲက ကျွန်တော်တို့ရဲ့ UbuntuLaravel AMI လေးကို ရွေးပြီး Lauch လုပ်ကြည့်လိုက်ပါ။
    
    Instance အသစ်လေး ready ဖြစ်လာပြီဆိုရင်တော့ instance အသစ်ရဲ့ public IP ကို browser ကနေဖွင့်ကြည့်လိုက်ရင် အောက်ပါပုံထဲကလိုမျိုးပေါ်နေပါလိမ့်မယ်။
    
    ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1710438379368/c61e6667-65c8-4c9c-a61b-7d45df688bab.png align="center")
    
    Instance အသစ်ထဲကို SSH နဲ့ဝင်ပြီး စစ်ကြည့်ရင်လည်း ကျွန်တော်တို့ install ထားတဲ့ software တွေရှိနေမှာဖြစ်ပါတယ်။
    
    ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1710438552071/2ee37af0-70ed-4f86-8bc5-02312c0fc340.png align="center")
    
7. ### Conclusion
    
    လုပ်ခဲ့တာတွေကို ပြန်ပြီး အနှစ်ချုပ်ရရင်တော့
    
    **IAM Role**: Image Builder pipeline run တဲ့အခါ လိုအပ်တဲ့ permision ပေးဖို့သုံးပါတယ်။
    
    **SNS Topic**: Image Builder pipeline ရဲ့ notification တွေပို့ဖို့ လိုအပ်ပါတယ်။  
    **S3 Bucket**: Component ထဲမှာသုံးမဲ့ script ကို host လုပ်ပေးဖို့ပါ။  
    **Component**: ကတော့ Build နဲ့ Test Component ဆိုပြီးနှစ်မျိုးရှိပါတယ်၊ သူက image build/test လုပ်ဖို့ configuration document တစ်ခုပါပဲ။ ကျွန်တော်ကတော့ Docker layer နဲ့ အလုပ်လုပ်ပုံခြင်းတူတယ်လို့ ပြောချင်ပါတယ်။ Component တွေက reusable ဖြစ်ပြီး pipeline တစ်ခုမှာ Component အများကြီးထည့်သုံးလို့ရနိုင်ပါတယ်။
    
    **Recipe**: ကတော့ image build လုပ်ဖို့ instruction တွေကို စုစည်းပေးတာပါ။ recipe ထဲမှာ component တွေနဲ့ သူတို့ရဲ့ configurations တွေပါဝင်ပြီး။ ဘယ် Component ကို execute လုပ်ပြီးရင် ဘယ် Component ကိုဆက် execute လုပ်မယ်ဆိုတာမျိုး စီမံပေးပါတယ်။ Recipe က version support လုပ်ပြီး အခြား aws account တွေကိုလည်း share သုံးလို့ရပါတယ်။
    
8. ### Reference:
    
    ```basic
    https://catalog.us-east-1.prod.workshops.aws/workshops/d6c7ecdc-c75f-4ad1-910f-fdd994cc4aed/en-US/100-ec2-ib/120-create-ec2-image-builder-pipeline/121-creating-an-image-pipeline
    ```
    
    **အချိန်ပေးပြီး ဖတ်ရှုပေးတဲ့အတွက် ကျေးဇူးတင်ပါတယ်။**
