AWS AMI - 20260119.1.0 #4
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Build AWS AMI | |
| run-name: AWS AMI - ${{ inputs.image_version }} | |
| on: | |
| workflow_dispatch: | |
| inputs: | |
| image_types: | |
| description: "Comma-separated list of image types to build" | |
| required: true | |
| default: ubuntu-22.04,ubuntu-24.04 | |
| type: string | |
| image_version: | |
| description: "Image version in semver format" | |
| required: true | |
| type: string | |
| concurrency: "${{ inputs.image_types }}-${{ inputs.image_version }}" | |
| permissions: | |
| id-token: write | |
| contents: read | |
| env: | |
| AWS_REGION: "eu-central-1" | |
| INSTANCE_TYPE: "r7a.large" | |
| jobs: | |
| generate-matrix: | |
| runs-on: ubuntu-latest | |
| outputs: | |
| matrix: ${{ steps.set-matrix.outputs.matrix }} | |
| steps: | |
| - name: Check out code | |
| uses: actions/checkout@v4 | |
| - name: Set matrix | |
| id: set-matrix | |
| run: | | |
| selected_images="$(echo '${{ inputs.image_types }}' | jq -R -c 'split(",")')" | |
| images="$(cat .github/images-aws.json)" | |
| matrix="$(echo $images | jq --argjson selecteds "$selected_images" '{include: .include | map(select(.image_type | IN($selecteds[])))}')" | |
| echo "matrix<<EOF"$'\n'"$matrix"$'\n'EOF >> "$GITHUB_OUTPUT" | |
| build: | |
| needs: generate-matrix | |
| name: build ${{ matrix.image_type }} | |
| runs-on: ubicloud-standard-4 | |
| strategy: | |
| fail-fast: false | |
| matrix: ${{ fromJson(needs.generate-matrix.outputs.matrix) }} | |
| steps: | |
| - name: Check out code | |
| uses: actions/checkout@v4 | |
| - name: Configure AWS credentials | |
| uses: aws-actions/configure-aws-credentials@v4 | |
| with: | |
| aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} | |
| aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} | |
| aws-region: ${{ env.AWS_REGION }} | |
| - name: Create VPC | |
| id: create-vpc | |
| run: | | |
| echo "Creating VPC for Packer build..." | |
| VPC_ID=$(aws ec2 create-vpc \ | |
| --cidr-block 10.0.0.0/16 \ | |
| --tag-specifications 'ResourceType=vpc,Tags=[{Key=Name,Value=packer-build-vpc-${{ github.run_number }}-${{ matrix.image_type }}},{Key=Purpose,Value=AMI-Build},{Key=CreatedBy,Value=GitHub-Actions}]' \ | |
| --query 'Vpc.VpcId' --output text) | |
| echo "vpc_id=$VPC_ID" >> $GITHUB_OUTPUT | |
| echo "VPC created: $VPC_ID" | |
| - name: Create Internet Gateway | |
| id: create-igw | |
| run: | | |
| echo "Creating Internet Gateway..." | |
| IGW_ID=$(aws ec2 create-internet-gateway \ | |
| --tag-specifications 'ResourceType=internet-gateway,Tags=[{Key=Name,Value=packer-build-igw-${{ github.run_number }}-${{ matrix.image_type }}},{Key=Purpose,Value=AMI-Build},{Key=CreatedBy,Value=GitHub-Actions}]' \ | |
| --query 'InternetGateway.InternetGatewayId' --output text) | |
| echo "igw_id=$IGW_ID" >> $GITHUB_OUTPUT | |
| echo "Internet Gateway created: $IGW_ID" | |
| # Attach Internet Gateway to VPC | |
| aws ec2 attach-internet-gateway \ | |
| --internet-gateway-id $IGW_ID \ | |
| --vpc-id ${{ steps.create-vpc.outputs.vpc_id }} | |
| echo "Internet Gateway attached to VPC" | |
| - name: Create Subnet | |
| id: create-subnet | |
| run: | | |
| echo "Creating public subnet..." | |
| # Get the first availability zone in the region | |
| AZ=$(aws ec2 describe-availability-zones \ | |
| --query 'AvailabilityZones[0].ZoneName' --output text) | |
| echo "Using availability zone: $AZ" | |
| SUBNET_ID=$(aws ec2 create-subnet \ | |
| --vpc-id ${{ steps.create-vpc.outputs.vpc_id }} \ | |
| --cidr-block 10.0.1.0/24 \ | |
| --availability-zone $AZ \ | |
| --tag-specifications 'ResourceType=subnet,Tags=[{Key=Name,Value=packer-build-subnet-${{ github.run_number }}-${{ matrix.image_type }}},{Key=Purpose,Value=AMI-Build},{Key=CreatedBy,Value=GitHub-Actions}]' \ | |
| --query 'Subnet.SubnetId' --output text) | |
| echo "subnet_id=$SUBNET_ID" >> $GITHUB_OUTPUT | |
| echo "Subnet created: $SUBNET_ID" | |
| # Enable auto-assign public IP | |
| aws ec2 modify-subnet-attribute \ | |
| --subnet-id $SUBNET_ID \ | |
| --map-public-ip-on-launch | |
| - name: Create Route Table | |
| id: create-route-table | |
| run: | | |
| echo "Creating route table..." | |
| ROUTE_TABLE_ID=$(aws ec2 create-route-table \ | |
| --vpc-id ${{ steps.create-vpc.outputs.vpc_id }} \ | |
| --tag-specifications 'ResourceType=route-table,Tags=[{Key=Name,Value=packer-build-rt-${{ github.run_number }}-${{ matrix.image_type }}},{Key=Purpose,Value=AMI-Build},{Key=CreatedBy,Value=GitHub-Actions}]' \ | |
| --query 'RouteTable.RouteTableId' --output text) | |
| echo "route_table_id=$ROUTE_TABLE_ID" >> $GITHUB_OUTPUT | |
| echo "Route table created: $ROUTE_TABLE_ID" | |
| # Add route to Internet Gateway | |
| aws ec2 create-route \ | |
| --route-table-id $ROUTE_TABLE_ID \ | |
| --destination-cidr-block 0.0.0.0/0 \ | |
| --gateway-id ${{ steps.create-igw.outputs.igw_id }} | |
| echo "Route to Internet Gateway added" | |
| # Associate route table with subnet | |
| aws ec2 associate-route-table \ | |
| --route-table-id $ROUTE_TABLE_ID \ | |
| --subnet-id ${{ steps.create-subnet.outputs.subnet_id }} | |
| echo "Route table associated with subnet" | |
| - name: Create Security Group | |
| id: create-sg | |
| run: | | |
| echo "Creating security group..." | |
| SG_ID=$(aws ec2 create-security-group \ | |
| --group-name packer-build-sg-${{ github.run_number }}-${{ matrix.image_type }} \ | |
| --description "Security group for Packer AMI build" \ | |
| --vpc-id ${{ steps.create-vpc.outputs.vpc_id }} \ | |
| --tag-specifications 'ResourceType=security-group,Tags=[{Key=Name,Value=packer-build-sg-${{ github.run_number }}-${{ matrix.image_type }}},{Key=Purpose,Value=AMI-Build},{Key=CreatedBy,Value=GitHub-Actions}]' \ | |
| --query 'GroupId' --output text) | |
| echo "sg_id=$SG_ID" >> $GITHUB_OUTPUT | |
| echo "Security group created: $SG_ID" | |
| # Add SSH inbound rule (Packer needs SSH access) | |
| aws ec2 authorize-security-group-ingress \ | |
| --group-id $SG_ID \ | |
| --protocol tcp \ | |
| --port 22 \ | |
| --cidr 0.0.0.0/0 | |
| echo "SSH access rule added to security group" | |
| - name: Setup Packer | |
| uses: hashicorp/setup-packer@main | |
| with: | |
| version: 1.9.4 | |
| - name: Initialize Packer plugins | |
| run: | | |
| packer init images/ubuntu/templates | |
| env: | |
| PACKER_GITHUB_API_TOKEN: ${{ secrets.PACKER_GITHUB_API_TOKEN }} | |
| - name: Validate Packer template | |
| run: | | |
| packer validate \ | |
| -only "${{ matrix.build_template_name }}.amazon-ebs.image" \ | |
| -var "ami_name=${{ matrix.ami_name }}-${{ inputs.image_version }}" \ | |
| -var "ami_description=${{ matrix.ami_description }} - Version ${{ inputs.image_version }}" \ | |
| -var "region=${{ env.AWS_REGION }}" \ | |
| -var "instance_type=${{ env.INSTANCE_TYPE }}" \ | |
| -var "vpc_id=${{ steps.create-vpc.outputs.vpc_id }}" \ | |
| -var "subnet_id=${{ steps.create-subnet.outputs.subnet_id }}" \ | |
| -var "security_group_id=${{ steps.create-sg.outputs.sg_id }}" \ | |
| -var "image_os=${{ matrix.image_os }}" \ | |
| -var "source_ami_name=${{ matrix.source_ami_name }}" \ | |
| images/ubuntu/templates | |
| - name: Build AMI | |
| run: | | |
| packer build \ | |
| -only "${{ matrix.build_template_name }}.amazon-ebs.image" \ | |
| -var "ami_name=${{ matrix.ami_name }}-${{ inputs.image_version }}" \ | |
| -var "ami_description=${{ matrix.ami_description }} - Version ${{ inputs.image_version }}" \ | |
| -var "region=${{ env.AWS_REGION }}" \ | |
| -var "instance_type=${{ env.INSTANCE_TYPE }}" \ | |
| -var "vpc_id=${{ steps.create-vpc.outputs.vpc_id }}" \ | |
| -var "subnet_id=${{ steps.create-subnet.outputs.subnet_id }}" \ | |
| -var "security_group_id=${{ steps.create-sg.outputs.sg_id }}" \ | |
| -var "image_os=${{ matrix.image_os }}" \ | |
| -var "source_ami_name=${{ matrix.source_ami_name }}" \ | |
| -var "image_version=${{ inputs.image_version }}" \ | |
| images/ubuntu/templates | |
| env: | |
| PACKER_LOG: 1 | |
| PACKER_LOG_PATH: "/tmp/packer-log.txt" | |
| PACKER_GITHUB_API_TOKEN: ${{ secrets.PACKER_GITHUB_API_TOKEN }} | |
| - name: Upload Packer logs | |
| if: always() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: packer-logs-${{ github.run_number }}-${{ matrix.image_type }} | |
| path: /tmp/packer-log.txt | |
| retention-days: 30 | |
| - name: Get AMI ID | |
| id: get-ami-id | |
| run: | | |
| # Extract AMI ID from Packer output | |
| AMI_ID=$(grep -o 'ami-[a-z0-9]*' /tmp/packer-log.txt | tail -1) | |
| echo "ami_id=$AMI_ID" >> $GITHUB_OUTPUT | |
| echo "AMI ID: $AMI_ID" | |
| - name: Tag AMI | |
| if: steps.get-ami-id.outputs.ami_id != '' | |
| run: | | |
| aws ec2 create-tags \ | |
| --resources ${{ steps.get-ami-id.outputs.ami_id }} \ | |
| --tags Key=Name,Value="${{ env.AMI_NAME }}" \ | |
| Key=Description,Value="${{ env.AMI_DESCRIPTION }}" \ | |
| Key=CreatedBy,Value="GitHub Actions" \ | |
| Key=Version,Value="${{ inputs.image_version }}" \ | |
| Key=BuildDate,Value="$(date -u +%Y-%m-%dT%H:%M:%SZ)" | |
| - name: Cleanup Infrastructure | |
| if: always() | |
| run: | | |
| echo "Cleaning up infrastructure..." | |
| # Delete Security Group | |
| if [ -n "${{ steps.create-sg.outputs.sg_id }}" ]; then | |
| aws ec2 delete-security-group \ | |
| --group-id ${{ steps.create-sg.outputs.sg_id }} || true | |
| echo "Security Group deleted" | |
| fi | |
| # Disassociate route table from subnet before deleting | |
| if [ -n "${{ steps.create-route-table.outputs.route_table_id }}" ] && [ -n "${{ steps.create-subnet.outputs.subnet_id }}" ]; then | |
| # Get the association ID | |
| ASSOCIATION_ID=$(aws ec2 describe-route-tables \ | |
| --route-table-ids ${{ steps.create-route-table.outputs.route_table_id }} \ | |
| --query 'RouteTables[0].Associations[?SubnetId==`${{ steps.create-subnet.outputs.subnet_id }}`].RouteTableAssociationId' \ | |
| --output text) || true | |
| if [ -n "$ASSOCIATION_ID" ] && [ "$ASSOCIATION_ID" != "None" ]; then | |
| aws ec2 disassociate-route-table \ | |
| --association-id $ASSOCIATION_ID || true | |
| echo "Route table disassociated from subnet" | |
| fi | |
| fi | |
| # Delete Subnet | |
| if [ -n "${{ steps.create-subnet.outputs.subnet_id }}" ]; then | |
| aws ec2 delete-subnet \ | |
| --subnet-id ${{ steps.create-subnet.outputs.subnet_id }} || true | |
| echo "Subnet deleted" | |
| fi | |
| # Delete Route Table | |
| if [ -n "${{ steps.create-route-table.outputs.route_table_id }}" ]; then | |
| aws ec2 delete-route-table \ | |
| --route-table-id ${{ steps.create-route-table.outputs.route_table_id }} || true | |
| echo "Route Table deleted" | |
| fi | |
| # Delete Internet Gateway | |
| if [ -n "${{ steps.create-igw.outputs.igw_id }}" ] && [ -n "${{ steps.create-vpc.outputs.vpc_id }}" ]; then | |
| aws ec2 detach-internet-gateway \ | |
| --internet-gateway-id ${{ steps.create-igw.outputs.igw_id }} \ | |
| --vpc-id ${{ steps.create-vpc.outputs.vpc_id }} || true | |
| aws ec2 delete-internet-gateway \ | |
| --internet-gateway-id ${{ steps.create-igw.outputs.igw_id }} || true | |
| echo "Internet Gateway deleted" | |
| fi | |
| # Delete VPC | |
| if [ -n "${{ steps.create-vpc.outputs.vpc_id }}" ]; then | |
| aws ec2 delete-vpc \ | |
| --vpc-id ${{ steps.create-vpc.outputs.vpc_id }} || true | |
| echo "VPC deleted" | |
| fi | |
| echo "Infrastructure cleanup completed" | |
| - name: Output AMI Information | |
| run: | | |
| echo "## AMI Build Complete 🎉" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "**AMI ID:** \`${{ steps.get-ami-id.outputs.ami_id }}\`" >> $GITHUB_STEP_SUMMARY | |
| echo "**AMI Name:** ${{ matrix.ami_name }}-${{ inputs.image_version }}" >> $GITHUB_STEP_SUMMARY | |
| echo "**Region:** ${{ env.AWS_REGION }}" >> $GITHUB_STEP_SUMMARY | |
| echo "**Instance Type:** ${{ env.INSTANCE_TYPE }}" >> $GITHUB_STEP_SUMMARY | |
| echo "**Version:** ${{ inputs.image_version }}" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY |