<?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: Evans Kiprotich</title>
    <description>The latest articles on Forem by Evans Kiprotich (@kevans254).</description>
    <link>https://forem.com/kevans254</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%2F1059966%2F0c1d2d3d-7536-4328-82cc-5ef0716de182.jpeg</url>
      <title>Forem: Evans Kiprotich</title>
      <link>https://forem.com/kevans254</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/kevans254"/>
    <language>en</language>
    <item>
      <title>Ditching the Access Key: Implementing IAM Roles Anywhere for Secure Edge and On-Prem Workloads</title>
      <dc:creator>Evans Kiprotich</dc:creator>
      <pubDate>Sun, 18 Jan 2026 08:10:29 +0000</pubDate>
      <link>https://forem.com/aws-builders/ditching-the-access-key-implementing-iam-roles-anywhere-for-secure-edge-and-on-prem-workloads-doi</link>
      <guid>https://forem.com/aws-builders/ditching-the-access-key-implementing-iam-roles-anywhere-for-secure-edge-and-on-prem-workloads-doi</guid>
      <description>&lt;p&gt;In the early days of AWS, if you had a server in your local data center that needed to upload logs to S3, you likely did the unthinkable: you created an IAM User, generated an access_key_id and secret_access_key, and hardcoded them into a config file.&lt;/p&gt;

&lt;p&gt;By 2026, this practice is not just outdated. It is a major compliance liability. Long-lived credentials are "static" targets for attackers. As AWS Community Builders, we should advocate for Zero Trust architectures. Enter IAM Roles Anywhere.&lt;/p&gt;

&lt;p&gt;This service allows your non-AWS workloads (servers, containers, or IoT devices) to use X.509 digital certificates to obtain temporary, short-lived AWS credentials. No more static keys. No more manual rotation.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Architecture of Trust
&lt;/h2&gt;

&lt;p&gt;The workflow relies on a Public Key Infrastructure (PKI) chain:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Trust Anchor: You tell AWS which Certificate Authority (CA) to trust.&lt;/li&gt;
&lt;li&gt;Profile: You define which IAM Roles can be assumed and for how long.&lt;/li&gt;
&lt;li&gt;Role: An IAM Role with a trust policy that allows the rolesanywhere.amazonaws.com service principal to assume it.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Step 1: Infrastructure as Code (CloudFormation)
&lt;/h2&gt;

&lt;p&gt;We'll start by defining the AWS-side resources. In this example, we assume you have a CA certificate (PEM format) from your internal PKI or a tool like OpenSSL.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;AWSTemplateFormatVersion: '2010-09-09'
Description: Deploy IAM Roles Anywhere for On-Premise Workloads

Parameters:
  CACertificateBody:
    Type: String
    Description: The PEM-encoded Public Key of your Certificate Authority.

Resources:
  EdgeTrustAnchor:
    Type: AWS::RolesAnywhere::TrustAnchor
    Properties:
      Name: OnPremDataCenterAnchor
      Enabled: true
      Source:
        SourceData:
          X509CertificateData: !Ref CACertificateBody
        SourceType: CERTIFICATE_BUNDLE

  EdgeWorkloadRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: EdgeBackupRole
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Principal:
              Service: rolesanywhere.amazonaws.com
            Action:
              - sts:AssumeRole
              - sts:TagSession
              - sts:SetSourceIdentity
            Condition:
              StringEquals:
                "aws:PrincipalTag/x509Subject/OU": "DataCenter-Northeast"
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/AmazonS3FullAccess # Scoped to your needs

  EdgeProfile:
    Type: AWS::RolesAnywhere::Profile
    Properties:
      Name: EdgeServerProfile
      DurationSeconds: 3600 # 1-hour temporary credentials
      Enabled: true
      RoleArns:
        - !GetAtt EdgeWorkloadRole.Arn
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 2: Preparing the Edge Server
&lt;/h2&gt;

&lt;p&gt;On your on-premise server, you need two things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A Client Certificate and Private Key signed by the CA you uploaded to the Trust Anchor.&lt;/li&gt;
&lt;li&gt;The AWS Signing Helper tool (provided by AWS).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The Credential Process Logic&lt;br&gt;
To make this seamless for the AWS CLI or SDKs, we use the credential_process feature in the AWS config file. This allows the CLI to call the signing helper automatically whenever credentials expire.&lt;/p&gt;

&lt;p&gt;Update your ~/.aws/config on the edge server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[profile edge-workload]
credential_process = /usr/local/bin/aws_signing_helper credential-process \
    --certificate /etc/pki/edge/server.crt \
    --private-key /etc/pki/edge/server.key \
    --trust-anchor-arn arn:aws:rolesanywhere:us-east-1:123456789012:trust-anchor/your-anchor-id \
    --profile-arn arn:aws:rolesanywhere:us-east-1:123456789012:profile/your-profile-id \
    --role-arn arn:aws:iam::123456789012:role/EdgeBackupRole
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 3: Python Implementation (Boto3)
&lt;/h2&gt;

&lt;p&gt;Once the config is set up, your application code doesn't even need to know IAM Roles Anywhere exists. It simply uses the profile.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import boto3
from botocore.exceptions import ClientError

def upload_to_s3(file_name, bucket):
    session = boto3.Session(profile_name='edge-workload')
    s3 = session.client('s3')

    try:
        s3.upload_file(file_name, bucket, file_name)
        print(f"Successfully uploaded {file_name} using temporary credentials!")
    except ClientError as e:
        print(f"Upload failed: {e}")

if __name__ == "__main__":
    upload_to_s3('daily_backup.tar.gz', 'my-secure-onprem-backups')
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Why This Matters
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Zero Static Secrets: If your edge server is stolen, there are no AWS keys to harvest from the disk.&lt;/li&gt;
&lt;li&gt;Instant Revocation: Revoke the certificate at your CA, and access is blocked immediately without touching AWS IAM.&lt;/li&gt;
&lt;li&gt;Auditability: Every session creation is logged in CloudTrail with the unique certificate serial number.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;Transitioning to IAM Roles Anywhere is a hallmark of a mature AWS architecture. It bridges the gap between on-premise stability and cloud security.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>security</category>
    </item>
    <item>
      <title>Beyond Chatbots: Building Autonomous Multi-Agent Workflows with Amazon Bedrock and Step Functions</title>
      <dc:creator>Evans Kiprotich</dc:creator>
      <pubDate>Sun, 18 Jan 2026 08:01:11 +0000</pubDate>
      <link>https://forem.com/aws-builders/beyond-chatbots-building-autonomous-multi-agent-workflows-with-amazon-bedrock-and-step-functions-472d</link>
      <guid>https://forem.com/aws-builders/beyond-chatbots-building-autonomous-multi-agent-workflows-with-amazon-bedrock-and-step-functions-472d</guid>
      <description>&lt;p&gt;I've witnessed the development of AI this year, especially on AWS. Simple prompt engineering and Retrieval Augmented Generation (RAG) have given way to autonomous agents. These programs are capable of reasoning, planning, and carrying out tasks, turning abstract objectives into tangible actions.&lt;/p&gt;

&lt;p&gt;Instead of using monolithic models, generative AI can solve complex problems by coordinating a number of specialized agents. Using AWS Step Functions for orchestration and Amazon Bedrock Agents for task execution, this article will walk you through creating such a workflow.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Transition to Agentic AI: Why It's Important Now
&lt;/h2&gt;

&lt;p&gt;Conventional applications of generative AI frequently include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A prompt is user input.&lt;/li&gt;
&lt;li&gt;RAG (Optional): Knowledge base context.&lt;/li&gt;
&lt;li&gt;LLM Call: Producing an answer.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is useful for responding to enquiries, but what if the user wants to take action? "Summarize the last quarter's sales data, identify the top 3 underperforming products, and create a draft email to their respective product managers suggesting a review" is an example of what might happen.&lt;/p&gt;

&lt;p&gt;This necessitates:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Information retrieval: Getting sales information.&lt;/li&gt;
&lt;li&gt;Analysis: Finding underachievers.&lt;/li&gt;
&lt;li&gt;Writing emails is the action.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This multi-step, action-oriented process is difficult for a single LLM call to handle. Bedrock Agents excel in this situation. An agent can be given a high-level objective, evaluate it, decide on a course of action, and then employ predefined "tools" (APIs) to accomplish that objective.&lt;/p&gt;

&lt;p&gt;However, what happens if you require the cooperation of several agents? What if their tasks require human approval, conditional logic, error handling, and a particular sequence? This is the point at which AWS Step Functions become essential.&lt;/p&gt;

&lt;h2&gt;
  
  
  Overview of Architecture: Managing Independent Workflows
&lt;/h2&gt;

&lt;p&gt;An "Order Processing" workflow will be used in our example scenario:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A fresh order arrives.&lt;/li&gt;
&lt;li&gt;An "Order Validation Agent" uses an API to compare order details to the product catalogue.&lt;/li&gt;
&lt;li&gt;A "Shipping Agent" communicates with a shipping provider API if it is legitimate.&lt;/li&gt;
&lt;li&gt;An alert is sent by a "Notification Agent" if any problems occur (validation failed, shipping error, etc.).&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Parts:
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Order Ingestion: New order requests are sent to an API Gateway endpoint or an Amazon SQS queue.&lt;/li&gt;
&lt;li&gt;Step Functions on AWS The central orchestrator is the State Machine. It manages conditional logic, parallel execution, retries, and flow definition.&lt;/li&gt;
&lt;li&gt;Bedrock Agents on Amazon:

&lt;ul&gt;
&lt;li&gt;Order Validation Agent: Verifies product IDs, stock, and prices by interacting with a Product Catalogue API (such as a Lambda function).&lt;/li&gt;
&lt;li&gt;Shipping Agent: Creates shipments by interacting with an API from an external shipping provider.&lt;/li&gt;
&lt;li&gt;Notification Agent: A more straightforward agent that can log to DynamoDB based on event details or send alerts via SNS.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;AWS Lambda: Provides the "tools" (APIs) for Bedrock Agents, serving as a safe bridge between the agent and internal data stores or external services.&lt;/li&gt;

&lt;li&gt;Amazon CloudWatch: For tracking and recording every step of the process.&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Methodical Execution
&lt;/h2&gt;

&lt;p&gt;Let's dissect the implementation using Python/Boto3 for agent definitions and interactions and CloudFormation for infrastructure.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Describe the AWS Lambda Functions Agent Tools.
Every Bedrock Agent must have access to "tools"—API functions that it can use to carry out tasks. These will be implemented as AWS Lambda functions.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;a) Lambda Product Catalogue (for Order Validation Agent) Checking a product catalogue is simulated by this Lambda.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# product_catalog_lambda.py
import json

def lambda_handler(event, context):
    print(f"Received event: {json.dumps(event)}")
    action_group = event['actionGroup']
    api_path = event['apiPath']
    http_method = event['httpMethod']
    parameters = event['parameters']

    response_body = {}

    if api_path == '/products/{productId}':
        product_id = next(p['value'] for p in parameters if p['name'] == 'productId')
        if product_id == 'PROD123':
            response_body = {
                "productName": "AWS Widget Pro",
                "price": 99.99,
                "inStock": True
            }
        elif product_id == 'PROD456':
             response_body = {
                "productName": "Cloud Gadget",
                "price": 49.99,
                "inStock": False 
            }
        else:
            response_body = {
                "message": "Product not found"
            }
    else:
        response_body = {
            "message": f"Unknown API path: {api_path}"
        }

    return {
        'body': json.dumps(response_body),
        'statusCode': 200,
        'applicationContentType': 'application/json'
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;b) Shipping Provider Lambda (for Shipping Agent) This Lambda simulates interacting with an external shipping API.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import json
import random

def lambda_handler(event, context):
    print(f"Received event: {json.dumps(event)}")
    action_group = event['actionGroup']
    api_path = event['apiPath']
    http_method = event['httpMethod']
    parameters = event['parameters']
    request_body = json.loads(event['requestBody']['content']['application/json']['properties']['requestBody'])

    response_body = {}

    if api_path == '/shipments':
        # Extract details from request_body, e.g., request_body['orderId'], request_body['items']
        order_id = request_body.get('orderId', 'UNKNOWN')
        # Simulate an external API call
        if random.random() &amp;lt; 0.1: # 10% chance of failure
            response_body = {
                "message": f"Failed to create shipment for Order {order_id}",
                "status": "FAILED",
                "errorCode": "EXTERNAL_API_ERROR"
            }
            status_code = 500
        else:
            tracking_number = f"TRACK-{order_id}-{random.randint(1000, 9999)}"
            response_body = {
                "message": f"Shipment created successfully for Order {order_id}",
                "trackingNumber": tracking_number,
                "status": "CREATED"
            }
            status_code = 200
    else:
        response_body = {
            "message": f"Unknown API path: {api_path}"
        }
        status_code = 404

    return {
        'body': json.dumps(response_body),
        'statusCode': status_code,
        'applicationContentType': 'application/json'
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Create the Bedrock Agents
Each agent requires:&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;An Agent Role with permissions to invoke the LLM and the Lambda tools.&lt;/li&gt;
&lt;li&gt;An Agent Definition specifying the foundation model, a description, and the action groups (tools).&lt;/li&gt;
&lt;li&gt;Action Groups: Link to the Lambda functions and their OpenAPI schemas.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;a) CloudFormation for Agent Roles and Lambda Permissions&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;AWSTemplateFormatVersion: '2010-09-09'
Description: AWS Bedrock Agents and supporting Lambda functions

Resources:
  ProductCatalogLambda:
    Type: AWS::Lambda::Function
    Properties:
      FunctionName: ProductCatalogLambda
      Handler: product_catalog_lambda.lambda_handler
      Runtime: python3.9
      Code:
        ZipFile: |
          import json
          def lambda_handler(event, context):
              print(f"Received event: {json.dumps(event)}")
              action_group = event['actionGroup']
              api_path = event['apiPath']
              http_method = event['httpMethod']
              parameters = event['parameters']
              response_body = {}
              if api_path == '/products/{productId}':
                  product_id = next(p['value'] for p in parameters if p['name'] == 'productId')
                  if product_id == 'PROD123':
                      response_body = {
                          "productName": "AWS Widget Pro",
                          "price": 99.99,
                          "inStock": True
                      }
                  elif product_id == 'PROD456':
                      response_body = {
                          "productName": "Cloud Gadget",
                          "price": 49.99,
                          "inStock": False
                      }
                  else:
                      response_body = {
                          "message": "Product not found"
                      }
              else:
                  response_body = {
                      "message": f"Unknown API path: {api_path}"
                  }
              return {
                  'body': json.dumps(response_body),
                  'statusCode': 200,
                  'applicationContentType': 'application/json'
              }
      Role: !GetAtt LambdaExecutionRole.Arn
      Timeout: 30

  ShippingProviderLambda:
    Type: AWS::Lambda::Function
    Properties:
      FunctionName: ShippingProviderLambda
      Handler: shipping_provider_lambda.lambda_handler
      Runtime: python3.9
      Code:
        ZipFile: |
          import json
          import random
          def lambda_handler(event, context):
              print(f"Received event: {json.dumps(event)}")
              action_group = event['actionGroup']
              api_path = event['apiPath']
              http_method = event['httpMethod']
              parameters = event['parameters']
              request_body = json.loads(event['requestBody']['content']['application/json']['properties']['requestBody'])
              response_body = {}
              if api_path == '/shipments':
                  order_id = request_body.get('orderId', 'UNKNOWN')
                  if random.random() &amp;lt; 0.1:
                      response_body = {
                          "message": f"Failed to create shipment for Order {order_id}",
                          "status": "FAILED",
                          "errorCode": "EXTERNAL_API_ERROR"
                      }
                      status_code = 500
                  else:
                      tracking_number = f"TRACK-{order_id}-{random.randint(1000, 9999)}"
                      response_body = {
                          "message": f"Shipment created successfully for Order {order_id}",
                          "trackingNumber": tracking_number,
                          "status": "CREATED"
                      }
                      status_code = 200
              else:
                  response_body = {
                      "message": f"Unknown API path: {api_path}"
                  }
                  status_code = 404
              return {
                  'body': json.dumps(response_body),
                  'statusCode': status_code,
                  'applicationContentType': 'application/json'
              }
      Role: !GetAtt LambdaExecutionRole.Arn
      Timeout: 30

  LambdaExecutionRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Principal:
              Service: lambda.amazonaws.com
            Action: sts:AssumeRole
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole

  BedrockAgentServiceRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Principal:
              Service: bedrock.amazonaws.com
            Action: sts:AssumeRole
      Policies:
        - PolicyName: BedrockAgentPermissions
          PolicyDocument:
            Version: '2012-10-17'
            Statement:
              - Effect: Allow
                Action:
                  - bedrock:InvokeModel # Allow agents to use Bedrock LLMs
                Resource:
                  - !Sub "arn:aws:bedrock:${AWS::Region}::foundation-model/anthropic.claude-v2" # or your preferred model
              - Effect: Allow
                Action:
                  - lambda:InvokeFunction # Allow agents to call their tool Lambdas
                Resource:
                  - !GetAtt ProductCatalogLambda.Arn
                  - !GetAtt ShippingProviderLambda.Arn
              - Effect: Allow
                Action:
                  - sns:Publish
                  - dynamodb:PutItem
                Resource: "*"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;b) Agent Creation (Boto3/CLI) Since Bedrock Agents are often managed programmatically due to their complexity (OpenAPI schemas, etc.), we'll use Boto3.&lt;/p&gt;

&lt;p&gt;First, define the OpenAPI schemas for our Lambda functions:&lt;/p&gt;

&lt;p&gt;product_catalog_openapi.yaml&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;openapi: 3.0.0
info:
  title: ProductCatalogAPI
  version: 1.0.0
paths:
  /products/{productId}:
    get:
      summary: Get product details
      description: Retrieves details for a specific product ID.
      operationId: getProductDetails
      parameters:
        - name: productId
          in: path
          required: true
          schema:
            type: string
          description: The ID of the product to retrieve.
      responses:
        '200':
          description: Product details
          content:
            application/json:
              schema:
                type: object
                properties:
                  productName:
                    type: string
                  price:
                    type: number
                  inStock:
                    type: boolean
        '404':
          description: Product not found
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;shipping_provider_openapi.yaml&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;openapi: 3.0.0
info:
  title: ShippingProviderAPI
  version: 1.0.0
paths:
  /shipments:
    post:
      summary: Create a new shipment
      description: Creates a new shipment with the specified order details.
      operationId: createShipment
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                orderId:
                  type: string
                  description: The ID of the order to ship.
                items:
                  type: array
                  items:
                    type: object
                    properties:
                      productId:
                        type: string
                      quantity:
                        type: integer
                      price:
                        type: number
              required:
                - orderId
                - items
      responses:
        '200':
          description: Shipment created successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  message:
                    type: string
                  trackingNumber:
                    type: string
                  status:
                    type: string
        '500':
          description: Failed to create shipment
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, create the agents using Boto3 (Python):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import boto3
import json

bedrock_agent_client = boto3.client('bedrock-agent')
bedrock_agent_runtime_client = boto3.client('bedrock-agent-runtime')

agent_role_arn = "arn:aws:iam::&amp;lt;YOUR_ACCOUNT_ID&amp;gt;:role/BedrockAgentServiceRole" # From CloudFormation Output
product_catalog_lambda_arn = "arn:aws:lambda:&amp;lt;REGION&amp;gt;:&amp;lt;YOUR_ACCOUNT_ID&amp;gt;:function:ProductCatalogLambda"
shipping_provider_lambda_arn = "arn:aws:lambda:&amp;lt;REGION&amp;gt;:&amp;lt;YOUR_ACCOUNT_ID&amp;gt;:function:ShippingProviderLambda"

with open('product_catalog_openapi.yaml', 'r') as f:
    product_catalog_schema = f.read()

def create_order_validation_agent():
    try:
        response = bedrock_agent_client.create_agent(
            agentName='OrderValidationAgent',
            agentResourceRoleArn=agent_role_arn,
            description='Agent for validating product orders against a product catalog.',
            foundationModel='anthropic.claude-v2', # Or your preferred model
            idleSessionTTLInSeconds=1800,
            instruction="You are an order validation assistant. Your goal is to verify product details using the provided product catalog tool. If a product is not found or out of stock, report it as an error."
        )
        agent_id = response['agent']['agentId']
        print(f"Created OrderValidationAgent with ID: {agent_id}")

        response = bedrock_agent_client.create_agent_action_group(
            agentId=agent_id,
            agentVersion='DRAFT', # Always create on DRAFT
            actionGroupName='ProductCatalogActionGroup',
            description='Provides operations to check product details.',
            actionGroupExecutor={'lambda': product_catalog_lambda_arn},
            apiSchema={'payload': product_catalog_schema}
        )
        print(f"Created action group for ProductCatalogLambda: {response['agentActionGroup']['actionGroupId']}")

        response = bedrock_agent_client.prepare_agent(agentId=agent_id)
        print(f"Prepared OrderValidationAgent: {response['agent']['agentStatus']}")
        return agent_id
    except Exception as e:
        print(f"Error creating Order Validation Agent: {e}")
        return None

with open('shipping_provider_openapi.yaml', 'r') as f:
    shipping_provider_schema = f.read()

def create_shipping_agent():
    try:
        response = bedrock_agent_client.create_agent(
            agentName='ShippingAgent',
            agentResourceRoleArn=agent_role_arn,
            description='Agent for creating shipments using an external shipping provider API.',
            foundationModel='anthropic.claude-v2',
            idleSessionTTLInSeconds=1800,
            instruction="You are a shipping assistant. Your goal is to create shipments for orders using the shipping provider tool. If the shipment creation fails, report the error."
        )
        agent_id = response['agent']['agentId']
        print(f"Created ShippingAgent with ID: {agent_id}")

        response = bedrock_agent_client.create_agent_action_group(
            agentId=agent_id,
            agentVersion='DRAFT',
            actionGroupName='ShippingProviderActionGroup',
            description='Provides operations to create shipments.',
            actionGroupExecutor={'lambda': shipping_provider_lambda_arn},
            apiSchema={'payload': shipping_provider_schema}
        )
        print(f"Created action group for ShippingProviderLambda: {response['agentActionGroup']['actionGroupId']}")

        response = bedrock_agent_client.prepare_agent(agentId=agent_id)
        print(f"Prepared ShippingAgent: {response['agent']['agentStatus']}")
        return agent_id
    except Exception as e:
        print(f"Error creating Shipping Agent: {e}")
        return None

if __name__ == "__main__":
    validation_agent_id = create_order_validation_agent()
    shipping_agent_id = create_shipping_agent()

    print(f"\nOrder Validation Agent ID: {validation_agent_id}")
    print(f"Shipping Agent ID: {shipping_agent_id}")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Orchestrate with AWS Step Functions
Now, the magic of orchestration. Step Functions allows us to define the entire workflow visually and programmatically using Amazon States Language (ASL).
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;AWSTemplateFormatVersion: '2010-09-09'
Description: AWS Step Functions State Machine for Autonomous Order Processing

Parameters:
  OrderValidationAgentId:
    Type: String
    Description: The ID of the Bedrock Order Validation Agent.
  ShippingAgentId:
    Type: String
    Description: The ID of the Bedrock Shipping Agent.

Resources:
  AutonomousOrderProcessingStateMachine:
    Type: AWS::StepFunctions::StateMachine
    Properties:
      StateMachineName: AutonomousOrderProcessingWorkflow
      DefinitionString: !Sub |
        {
          "Comment": "Autonomous Order Processing Workflow using Bedrock Agents",
          "StartAt": "InitializeOrder",
          "States": {
            "InitializeOrder": {
              "Type": "Pass",
              "Result": {
                "orderId.$": "$.orderId",
                "items.$": "$.items",
                "validationResult": null,
                "shippingResult": null
              },
              "Next": "InvokeOrderValidationAgent"
            },
            "InvokeOrderValidationAgent": {
              "Type": "Task",
              "Resource": "arn:aws:states:::bedrock:invokeAgent", 
              "Parameters": {
                "AgentId": "${OrderValidationAgentId}",
                "AgentAliasId": "TSTALIAS", # Use TSTALIAS for the DRAFT version
                "InputText.$": "States.Format('Validate the following order details: Order ID {}. Items: {}.', $.orderId, States.JsonToString($.items))",
                "SessionState": {
                  "sessionAttributes": {
                    "orderId.$": "$.orderId"
                  }
                },
                "EnableTrace": true
              },
              "ResultPath": "$.validationResult",
              "Catch": [
                {
                  "ErrorEquals": ["States.TaskFailed", "Bedrock.InvokeAgent.Failed"],
                  "Next": "OrderValidationFailed"
                }
              ],
              "Next": "CheckValidationStatus"
            },
            "CheckValidationStatus": {
              "Type": "Choice",
              "Choices": [
                {
                  "Variable": "$.validationResult.output.text",
                  "StringContains": "Validation successful",
                  "Next": "InvokeShippingAgent"
                },
                {
                  "Variable": "$.validationResult.output.text",
                  "StringContains": "Product not found",
                  "Next": "OrderValidationFailed"
                },
                {
                  "Variable": "$.validationResult.output.text",
                  "StringContains": "out of stock",
                  "Next": "OrderValidationFailed"
                }
              ],
              "Default": "OrderValidationFailed" # Catch-all for unexpected validation results
            },
            "OrderValidationFailed": {
              "Type": "Task",
              "Resource": "arn:aws:states:::sns:publish", # Example Notification Action
              "Parameters": {
                "TopicArn": "arn:aws:sns:${AWS::Region}:${AWS::AccountId}:OrderAlertsTopic", 
                "Message": !Sub "Order ${$.orderId} failed validation. Reason: ${$.validationResult.output.text}"
              },
              "End": true
            },
            "InvokeShippingAgent": {
              "Type": "Task",
              "Resource": "arn:aws:states:::bedrock:invokeAgent",
              "Parameters": {
                "AgentId": "${ShippingAgentId}",
                "AgentAliasId": "TSTALIAS",
                "InputText.$": "States.Format('Create a shipment for order {} with the following items: {}.', $.orderId, States.JsonToString($.items))",
                "SessionState": {
                  "sessionAttributes": {
                    "orderId.$": "$.orderId",
                    "items.$": "$.items"
                  }
                },
                "EnableTrace": true
              },
              "ResultPath": "$.shippingResult",
              "Catch": [
                {
                  "ErrorEquals": ["States.TaskFailed", "Bedrock.InvokeAgent.Failed"],
                  "Next": "ShippingFailed"
                }
              ],
              "Next": "CheckShippingStatus"
            },
            "CheckShippingStatus": {
              "Type": "Choice",
              "Choices": [
                {
                  "Variable": "$.shippingResult.output.text",
                  "StringContains": "Shipment created successfully",
                  "Next": "CompleteOrder"
                }
              ],
              "Default": "ShippingFailed"
            },
            "ShippingFailed": {
              "Type": "Task",
              "Resource": "arn:aws:states:::sns:publish",
              "Parameters": {
                "TopicArn": "arn:aws:sns:${AWS::Region}:${AWS::AccountId}:OrderAlertsTopic",
                "Message": !Sub "Order ${$.orderId} failed shipping. Reason: ${$.shippingResult.output.text}"
              },
              "End": true
            },
            "CompleteOrder": {
              "Type": "Succeed"
            }
          }
        }
      RoleArn: !GetAtt StepFunctionsExecutionRole.Arn

  StepFunctionsExecutionRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Principal:
              Service: states.${AWS::Region}.amazonaws.com
            Action: sts:AssumeRole
      Policies:
        - PolicyName: StepFunctionsPermissions
          PolicyDocument:
            Version: '2012-10-17'
            Statement:
              - Effect: Allow
                Action:
                  - bedrock:InvokeAgent
                Resource:
                  - !Sub "arn:aws:bedrock:${AWS::Region}:${AWS::AccountId}:agent/${OrderValidationAgentId}"
                  - !Sub "arn:aws:bedrock:${AWS::Region}:${AWS::AccountId}:agent/${ShippingAgentId}"
                  - !Sub "arn:aws:bedrock:${AWS::Region}:${AWS::AccountId}:agent-alias/${OrderValidationAgentId}/*" # Allow invoking specific agent aliases
                  - !Sub "arn:aws:bedrock:${AWS::Region}:${AWS::AccountId}:agent-alias/${ShippingAgentId}/*"
              - Effect: Allow # For SNS notifications
                Action:
                  - sns:Publish
                Resource: "arn:aws:sns:${AWS::Region}:${AWS::AccountId}:OrderAlertsTopic" # Ensure this topic exists or create it
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Kicking off the Workflow
You can start a Step Functions execution via the AWS CLI or SDK:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;aws stepfunctions start-execution \
    --state-machine-arn "arn:aws:states:&amp;lt;REGION&amp;gt;:&amp;lt;YOUR_ACCOUNT_ID&amp;gt;:stateMachine:AutonomousOrderProcessingWorkflow" \
    --input '{
        "orderId": "ORD789",
        "items": [
            {"productId": "PROD123", "quantity": 1, "price": 99.99},
            {"productId": "PROD456", "quantity": 2, "price": 49.99}
        ]
    }'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Benefits of this Approach
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Modularity: Each agent is a specialist, reducing the cognitive load on a single LLM and allowing for easier updates and maintenance.&lt;/li&gt;
&lt;li&gt;Observability: Step Functions provides a visual workflow, detailed execution history, and CloudWatch Logs integration, making it easy to debug complex AI processes.&lt;/li&gt;
&lt;li&gt;Reliability: Built-in retry mechanisms, error handling, and parallel execution capabilities of Step Functions ensure resilience.&lt;/li&gt;
&lt;li&gt;Scalability: Both Bedrock Agents and Step Functions are fully managed, scaling automatically to meet demand.&lt;/li&gt;
&lt;li&gt;Security: IAM roles control permissions granularly, ensuring agents only access the resources they need.&lt;/li&gt;
&lt;li&gt;Cost-Effectiveness: You only pay for the state transitions in Step Functions and the Bedrock agent inv
ocations.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Considerations for Production
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Version Control: Manage Bedrock agent versions carefully. Step Functions should typically invoke a specific, published agent version, not DRAFT for production.&lt;/li&gt;
&lt;li&gt;Input Validation: Implement robust input validation at the Step Functions entry point and within your Lambda tools.&lt;/li&gt;
&lt;li&gt;Human-in-the-Loop: For critical workflows, consider adding a human approval step in Step Functions, perhaps by sending a task to an Amazon SQS queue that a human operator monitors.&lt;/li&gt;
&lt;li&gt;Prompt Engineering: The instruction given to your Bedrock agents is crucial for their performance. Experiment with clear, concise instructions and few-shot examples if necessary.&lt;/li&gt;
&lt;li&gt;Tool Design: Design your Lambda tools to be idempotent and handle various edge cases, as the LLM might call them with unexpected inputs.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;As we push the boundaries of Generative AI, the ability to build and orchestrate autonomous agents becomes a cornerstone of truly intelligent applications. By combining the reasoning and task execution capabilities of Amazon Bedrock Agents with the robust workflow management of AWS Step Functions, you can construct complex, multi-step processes that move far beyond simple conversational AI.&lt;/p&gt;

&lt;p&gt;This architecture empowers developers to build sophisticated, resilient, and scalable AI solutions that can perform real-world actions, marking a significant leap forward in the journey of cloud-native AI. Dive in, experiment, and unleash the full potential of agentic AI on AWS!&lt;/p&gt;

</description>
      <category>aws</category>
      <category>ai</category>
    </item>
    <item>
      <title>🚀 Deploying a Sample Web Application on AWS Using CloudFormation and EC2 💻🌐</title>
      <dc:creator>Evans Kiprotich</dc:creator>
      <pubDate>Fri, 08 Mar 2024 09:41:13 +0000</pubDate>
      <link>https://forem.com/kevans254/deploying-a-sample-web-application-on-aws-using-cloudformation-and-ec2-5d5f</link>
      <guid>https://forem.com/kevans254/deploying-a-sample-web-application-on-aws-using-cloudformation-and-ec2-5d5f</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;If you're an AWS Cloud Engineer, and you're looking to automate infrastructure deployment on AWS using IAC principles, you've come to the right place. In this guide, I'll walk you through the steps to deploy a sample application stack using AWS CloudFormation and AWS CLI. The goal is to automate the deployment of a multi-tier application stack and configure it to run smoothly on AWS. By following the steps in this guide, you'll be able to demonstrate your knowledge of AWS services and tools and how you can leverage them to streamline the deployment of complex applications on AWS. Let's get started!&lt;/p&gt;

&lt;h2&gt;
  
  
  AWS CLI
&lt;/h2&gt;

&lt;p&gt;Developers and system administrators can connect with AWS services and manage resources via the command line using the robust and flexible AWS CLI (Command Line Interface). The AWS CLI can be modified to match certain use cases and workflows because it is made to be highly flexible. It is also simple to combine with other tools and services thanks to the variety of output formats it offers, including JSON, YAML, and text.&lt;/p&gt;

&lt;p&gt;The AWS CLI can be installed following the guide in this &lt;a href="https://aws.amazon.com/cli/"&gt;AWS documentation&lt;/a&gt;. Once installed, you can set up access keys to use as credentials. In this scenario, I am going to use AWS CloudShell to run the AWS CLI commands from the browser.&lt;/p&gt;

&lt;h2&gt;
  
  
  CLoudFormation Template
&lt;/h2&gt;

&lt;p&gt;The CloudFormation template to set up the VPC, subnets, security groups and EC2 instances is shown below. The code can be saved as IACTest.yaml and uploaded to AWS CloudShell. For the key name, The &lt;code&gt;3tier&lt;/code&gt; specified is an already existing key pair.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;---
AWSTemplateFormatVersion: "2010-09-09"
Resources:
  VPC:
    Type: "AWS::EC2::VPC"
    Properties:
      CidrBlock: "10.0.0.0/16"
      EnableDnsHostnames: true
      Tags:
        - Key: "Name"
          Value: "LabVPC"
  PublicSubnet:
    Type: "AWS::EC2::Subnet"
    Properties:
      AvailabilityZone: "us-east-1a"
      CidrBlock: "10.0.0.0/24"
      MapPublicIpOnLaunch: true
      VpcId: !Ref VPC
      Tags:
        - Key: "Name"
          Value: "MyPublicSubnet"
  PrivateSubnet:
    Type: "AWS::EC2::Subnet"
    Properties:
      AvailabilityZone: "us-east-1b"
      CidrBlock: "10.0.1.0/24"
      MapPublicIpOnLaunch: false
      VpcId: !Ref VPC
      Tags:
        - Key: "Name"
          Value: "MyPrivateSubnet"
  SecurityGroup:
    Type: "AWS::EC2::SecurityGroup"
    Properties:
      GroupDescription: "Allow inbound traffic to the EC2 instances"
      VpcId: !Ref VPC
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 80
          ToPort: 80
          CidrIp: 0.0.0.0/0
  Instance:
    Type: "AWS::EC2::Instance"
    Properties:
      InstanceType: t2.micro
      ImageId: ami-0c94855ba95c71c99
      KeyName: "3tier"
      NetworkInterfaces:
        - DeviceIndex: "0"
          SubnetId: !Ref PublicSubnet
          GroupSet:
            - !Ref SecurityGroup

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

&lt;/div&gt;



&lt;p&gt;You can then run the command &lt;code&gt;aws cloudformation create-stack --stack-name my-stack --template-body file://IACTest.yaml --region us-east-1&lt;/code&gt; to deploy the CloudFormation stack. &lt;/p&gt;

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

&lt;p&gt;We can configure the EC2 instances to run a sample web application by adding user data section to the template and then updating the template. This modifies the Instance section of the template to look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  Instance:
    Type: "AWS::EC2::Instance"
    Properties:
      InstanceType: t2.micro
      ImageId: ami-0c94855ba95c71c99
      KeyName: "3tier"
      NetworkInterfaces:
        - DeviceIndex: "0"
          SubnetId: !Ref PublicSubnet
          GroupSet:
            - !Ref SecurityGroup
      UserData:
        Fn::Base64: !Sub |
          #!/bin/bash
          yum update -y
          yum install -y httpd
          systemctl start httpd
          systemctl enable httpd
          echo "&amp;lt;html&amp;gt;&amp;lt;head&amp;gt;&amp;lt;title&amp;gt;EC2 Instance Metadata&amp;lt;/title&amp;gt;&amp;lt;/head&amp;gt;&amp;lt;body&amp;gt;&amp;lt;h1&amp;gt;EC2 Instance ID:&amp;lt;/h1&amp;gt;" &amp;gt;&amp;gt; /var/www/html/index.html
          curl http://169.254.169.254/latest/meta-data/instance-id &amp;gt;&amp;gt; /var/www/html/index.html
          echo "&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;" &amp;gt;&amp;gt; /var/www/html/index.html
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can the update the CloudFormation stack by running the following command: &lt;code&gt;aws cloudformation update-stack --stack-name my-stack --template-body file://IACTest.yaml --region us-east-1&lt;/code&gt;.&lt;/p&gt;

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

</description>
    </item>
    <item>
      <title>🌐💻 Building a Multi-AZ Three-Tier Architecture on AWS: A Comprehensive Guide</title>
      <dc:creator>Evans Kiprotich</dc:creator>
      <pubDate>Fri, 21 Apr 2023 08:37:06 +0000</pubDate>
      <link>https://forem.com/aws-builders/building-a-multi-az-three-tier-architecture-on-aws-a-comprehensive-guide-2506</link>
      <guid>https://forem.com/aws-builders/building-a-multi-az-three-tier-architecture-on-aws-a-comprehensive-guide-2506</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;As a budding cloud engineer, I was recently given the task of creating a three-tier architecture for a web application that previously had a straightforward one-tier architecture. A fault-tolerant, scalable design that could accommodate increasing traffic and demand was the aim. I successfully designed and implemented a three-tier architecture on AWS using multiple availability zones for high availability and fault tolerance after some research and testing. Anyone who wishes to carry out a similar task can use this blog post as a guide.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;LongPostAlert!&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Architectural Diagram
&lt;/h2&gt;

&lt;p&gt;The first step is to create a three-tier architecture. There are several tools available for this purpose. This &lt;a href="https://aws.amazon.com/architecture/icons/"&gt;AWS webpage&lt;/a&gt; provides a summary of several websites that can be used, and I suggest &lt;a href="https://www.draw.io/?splash=0&amp;amp;libs=aws4"&gt;draw.io&lt;/a&gt;. A sample architectural plan created is shown below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--kmoIEnO8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/a4bfe3j6khm753oq2enm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--kmoIEnO8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/a4bfe3j6khm753oq2enm.png" alt="Three-Tier Architecture" width="800" height="752"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Deployment
&lt;/h2&gt;

&lt;p&gt;The resources needed can be divided into various sections which will be discussed below.&lt;/p&gt;

&lt;h3&gt;
  
  
  VPC
&lt;/h3&gt;

&lt;p&gt;The VPC can be created as shown in the following figure. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--lGq4hCJA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/61r73mtoew8cjuh4c0ed.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--lGq4hCJA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/61r73mtoew8cjuh4c0ed.png" alt="VPC Creation" width="800" height="696"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next is to edit VPC settings to enable DNS resolution and hostnames.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--m40wzR8F--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/a74tfutx1d8o82n9ja3m.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--m40wzR8F--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/a74tfutx1d8o82n9ja3m.png" alt="Edit VPC Settings" width="800" height="583"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Subnets
&lt;/h3&gt;

&lt;p&gt;The architecture requires six subnets, with three in each availability zone. Two subnets are private while only one is public in each availability zone.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--o0tNxPrT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/56vxgyu5gbgnx7e2m6qi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--o0tNxPrT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/56vxgyu5gbgnx7e2m6qi.png" alt="Subnet Creation 1" width="800" height="1628"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--b6TDYRUW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cr33xnbpw08pj5341q0z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--b6TDYRUW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cr33xnbpw08pj5341q0z.png" alt="Subnet Creation 2" width="800" height="2395"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For the public subnets, their settings can be edited to enable auto-assign public IPv4 settings.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--rxWeeu4a--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zr90mwt02oy7jrgmpuc7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--rxWeeu4a--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zr90mwt02oy7jrgmpuc7.png" alt="Edit Subnet 1 Settings" width="800" height="634"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--EZNX4mee--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yn6ne4u39p04k12wgzwk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--EZNX4mee--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yn6ne4u39p04k12wgzwk.png" alt="Edit Subnet 2 Settings" width="800" height="634"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Security Groups
&lt;/h3&gt;

&lt;p&gt;Next is to create the security groups. &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The first is the &lt;code&gt;web-elb-sg&lt;/code&gt; which allows HTTP access from all sources.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--FjC6yPdY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/v9uicuoiyksn6wne0eb2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--FjC6yPdY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/v9uicuoiyksn6wne0eb2.png" alt="Web ELB SG" width="800" height="425"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The &lt;code&gt;web-sg&lt;/code&gt; is created next which allows HTTP access from &lt;code&gt;web-elb-sg&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--UjTBUoNA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/i3xmvoq4jpkoghnhicdb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--UjTBUoNA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/i3xmvoq4jpkoghnhicdb.png" alt="Web SG" width="800" height="877"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The &lt;code&gt;app-elb-sg&lt;/code&gt; is created next which allows HTTP access from &lt;code&gt;web-sg&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--BiT5e5Up--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/x9jid29dlb0szsg8obj5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--BiT5e5Up--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/x9jid29dlb0szsg8obj5.png" alt="App ELB SG" width="800" height="857"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The &lt;code&gt;app-sg&lt;/code&gt; is next which allows HTTP access from &lt;code&gt;app-elb-sg&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--qXbOtxle--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/h21nrkq585ibd1caymzr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--qXbOtxle--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/h21nrkq585ibd1caymzr.png" alt="App SG" width="800" height="877"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Finally, &lt;code&gt;db-sg&lt;/code&gt; is created which allows MySQL access from &lt;code&gt;app-sg&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--dtrl6M3u--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8l37tk983lj5vmrc7cq3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--dtrl6M3u--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8l37tk983lj5vmrc7cq3.png" alt="DB SG" width="800" height="877"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Internet Gateway
&lt;/h3&gt;

&lt;p&gt;Since this is a new VPC, an internet gateway has to be created and attached to the VPC as shown below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--d5tf0jgc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kqiqbfzfaq8tgi4puipd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--d5tf0jgc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kqiqbfzfaq8tgi4puipd.png" alt="Create Internet Gateway" width="800" height="470"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--RrmzJR7h--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/uzbmijcu15sx4d6ocq14.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--RrmzJR7h--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/uzbmijcu15sx4d6ocq14.png" alt="Attach IGW To VPC" width="800" height="366"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Route Tables
&lt;/h3&gt;

&lt;p&gt;When a VPC is created, a default route table is created. Any subnets that have no explicit route table associations will be associated with the default route table. &lt;/p&gt;

&lt;p&gt;A &lt;code&gt;web-RT&lt;/code&gt; route table can then be created with a route with &lt;code&gt;0.0.0.0/0&lt;/code&gt; destination with the internet gateway as the target. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--be0d1mWd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/j3e0upxtq8tpw8bhb9dp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--be0d1mWd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/j3e0upxtq8tpw8bhb9dp.png" alt="Create Route Table" width="800" height="470"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--6_8nHKaY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bug5xfe2r5p0ylqz44h9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--6_8nHKaY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bug5xfe2r5p0ylqz44h9.png" alt="Edit Route Table" width="800" height="366"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The public subnets can then be associated with this route table.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--KIjY-zTI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/a626kihfnerozpp1rkt2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--KIjY-zTI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/a626kihfnerozpp1rkt2.png" alt="Edit Subnet Associations" width="800" height="426"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Launch Templates
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Web Tier Launch Template
A launch template is then created to be used in the web tier. This is associated with the &lt;code&gt;web-sg&lt;/code&gt; security group, with auto-assign public IP enabled. &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s---As-Tpbh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/br5o6qubvzgxoqs6pn1s.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s---As-Tpbh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/br5o6qubvzgxoqs6pn1s.png" alt="Web Tier Launch Template 1" width="800" height="1872"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Some user data can be included to enable Apache web server to run in the instances after creation.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--G2T75wpJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hqf1oz5kwiz6ff0hwugx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--G2T75wpJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hqf1oz5kwiz6ff0hwugx.png" alt="Web Tier Launch Template 2" width="800" height="2167"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;App Tier Launch Template
A launch template is then created to be used in the app tier. This is associated with the &lt;code&gt;app-sg&lt;/code&gt; security group. &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--FjtCj6Uk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sjvabjtkosc1qba3777y.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--FjtCj6Uk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sjvabjtkosc1qba3777y.png" alt="App Tier Launch Template" width="800" height="1432"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Target Groups
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Web Tier Target Group
A target group is then created to be used for the web tier. &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--sm5wYSOn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xmdux5p0g6ytqrtsuprt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--sm5wYSOn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xmdux5p0g6ytqrtsuprt.png" alt="Web Tier Target Group 1" width="800" height="1869"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;No instances are registered at the moment, but they will be registered automatically once they will be launched by the Auto Scaling Group.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--pY3TEpGL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jlejl0ehjoi5n146j20r.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--pY3TEpGL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jlejl0ehjoi5n146j20r.png" alt="Web Tier Target Group 2" width="800" height="778"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;App Tier Target Group.
This is a similar process to the web tier target group.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--oeCSZ5zn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/uoux154eyebf1w3yycgr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--oeCSZ5zn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/uoux154eyebf1w3yycgr.png" alt="App Tier Target Group 1" width="800" height="1492"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--dtbCimWO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/47v9n9bfpeq3ifhqanjt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--dtbCimWO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/47v9n9bfpeq3ifhqanjt.png" alt="App Tier Target Group 2" width="800" height="806"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Application Load Balancers
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Web Tier
This is set up with the internet-facing scheme. Under mappings, the two public subnets in the two AZs are selected. This is associated with the &lt;code&gt;web-elb-sg&lt;/code&gt; security group. &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--1eqpfGRc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wnsmqudgdncdrpc79ooo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--1eqpfGRc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wnsmqudgdncdrpc79ooo.png" alt="Web Tier ALB 1" width="800" height="1127"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A &lt;code&gt;HTTP:80&lt;/code&gt; listener is then associated with the &lt;code&gt;WebTierTargetGroup&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--SdScWCp3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7hrlcvs03xzk16xvc47o.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--SdScWCp3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7hrlcvs03xzk16xvc47o.png" alt="Web Tier ALB 2" width="800" height="1059"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;App Tier
This is set up with the internal scheme. Under mappings, the two app private subnets in the two AZs are selected. This is then associated with the &lt;code&gt;app-elb-sg&lt;/code&gt; security group. &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--J0juqPjg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fpehwtuyk185vnj4yano.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--J0juqPjg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fpehwtuyk185vnj4yano.png" alt="App Tier ALB 1" width="800" height="1115"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A &lt;code&gt;HTTP:80&lt;/code&gt; listener is associated with the &lt;code&gt;AppTierTargetGroup&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--lziD3iBs--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9dh7stt03wqm7pj962ue.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--lziD3iBs--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9dh7stt03wqm7pj962ue.png" alt="App Tier ALB 2" width="800" height="1054"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Auto Scaling Groups
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Web Tier
The &lt;code&gt;WebTierASG&lt;/code&gt; is created and associated with the &lt;code&gt;WebTierLaunchTemplate&lt;/code&gt;. &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ttuYI_uk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ce7dknoyi9ruu8ie02c0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ttuYI_uk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ce7dknoyi9ruu8ie02c0.png" alt="Web Tier ASG 1" width="800" height="712"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The two web public subnets are selected to be used by the ASG. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--3eKt7RlS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/75m39noa5o2fhy6u23qo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--3eKt7RlS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/75m39noa5o2fhy6u23qo.png" alt="Web Tier ASG 2" width="800" height="686"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is then associated with the &lt;code&gt;WebTierELB&lt;/code&gt;, with ELB health checks enabled. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--KHcY-rsC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4nk3jkt5dbayd65tvr86.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--KHcY-rsC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4nk3jkt5dbayd65tvr86.png" alt="Web Tier ASG 3" width="800" height="1229"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The group size is selected as shown, with a target tracking scaling policy using the &lt;code&gt;Average CPU utilization&lt;/code&gt; metric.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--STAgmO3a--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bybf8q84x3u4c8tm1y48.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--STAgmO3a--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bybf8q84x3u4c8tm1y48.png" alt="Web Tier ASG 4" width="800" height="890"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--mWqEjNJD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rcmruyln14svmouoxee8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--mWqEjNJD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rcmruyln14svmouoxee8.png" alt="Web Tier ASG Review" width="800" height="1809"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;App Tier
The &lt;code&gt;AppTierASG&lt;/code&gt; is created and associated with the &lt;code&gt;AppTierLaunchTemplate&lt;/code&gt;. &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--yU4b1-qx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/c0481i9igoubjk6wjgi1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--yU4b1-qx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/c0481i9igoubjk6wjgi1.png" alt="App Tier ASG 1" width="800" height="712"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The two app private subnets are selected to be used by the ASG.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--9FCB5FSe--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6evydd7wlky9cxup7jra.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--9FCB5FSe--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6evydd7wlky9cxup7jra.png" alt="App Tier ASG 2" width="800" height="686"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is then associated with the &lt;code&gt;AppTierELB&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--sFOVbl20--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/k14jrcxz8dfgvdtycpsw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--sFOVbl20--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/k14jrcxz8dfgvdtycpsw.png" alt="App Tier ASG 3" width="800" height="1165"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The group size is selected as shown, with a target tracking scaling policy using the &lt;code&gt;Average CPU utilization&lt;/code&gt; metric.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--isUxrsQz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2hwra22c7gwvi8430nry.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--isUxrsQz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2hwra22c7gwvi8430nry.png" alt="App Tier ASG 4" width="800" height="890"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--yvdfg5Vx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/72vev0s4elnnyd36t3cw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--yvdfg5Vx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/72vev0s4elnnyd36t3cw.png" alt="App Tier ASG Review" width="800" height="1809"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Web Tier Test
&lt;/h2&gt;

&lt;p&gt;After the auto scaling groups are set, it kicks in and creates the instances. Two instances are created initially in each tier since the desired capacity is set to two. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--fikzPpdd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/flr7wwp0j91dcvz1pgsw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--fikzPpdd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/flr7wwp0j91dcvz1pgsw.png" alt="Desired EC2 Capacity" width="800" height="181"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Upon visiting the ELB endpoint, it can be seen that we get responses from both instances as shown below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--uJX97aoy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/p5gvnkbkobftebfhpy29.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--uJX97aoy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/p5gvnkbkobftebfhpy29.png" alt="Web ELB Returning Traffic From Instance 1" width="800" height="121"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Q0Q_I7yx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hcc35si0jntdf2074cne.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Q0Q_I7yx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hcc35si0jntdf2074cne.png" alt="Web ELB Returning Traffic From Instance 2" width="800" height="118"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;However, since the minimum capacity is set to one, once the target tracking policy kicks in and the web servers are idle, the capacity is reduced to one in each tier.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--9B1h62_8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/libt52jbmtfoc15yiy9j.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--9B1h62_8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/libt52jbmtfoc15yiy9j.png" alt="Minimum EC2 Capacity" width="800" height="178"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  DB Subnet Group
&lt;/h3&gt;

&lt;p&gt;The DB subnet group is created specifying the two availability zones and the two DB subnets created earlier on.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--B8d22uVw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2xsw6oh6kxz34sv84tbd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--B8d22uVw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2xsw6oh6kxz34sv84tbd.png" alt="DB Subnet Group" width="800" height="780"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  RDS Database Creation
&lt;/h3&gt;

&lt;p&gt;This is created using RDS using the Aurora(MySQL Compatible) engine. Multi-AZ deployment is enabled. The subnet group created in the previous section is selected, specifying the &lt;code&gt;db-sg&lt;/code&gt; as the security group to be used.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--_NHcnxQh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/trn45crx7o012drtyri0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--_NHcnxQh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/trn45crx7o012drtyri0.png" alt="RDS 1" width="800" height="1355"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--nMA9vWmL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/69xhc6oswn0zj3f992r7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--nMA9vWmL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/69xhc6oswn0zj3f992r7.png" alt="RDS 2" width="800" height="1878"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This creates two DB instances, one in each AZ. One is set as the writer instance in &lt;code&gt;us-east-1b&lt;/code&gt; and the other as the reader instance in &lt;code&gt;us-east-1a&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--5LjUcSGm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/elw6onuinub5l08lmtfp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--5LjUcSGm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/elw6onuinub5l08lmtfp.png" alt="RDS Instances" width="800" height="238"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In order to test failover to see what happens if the writer fails, we can see that the writer instance switches to &lt;code&gt;us-east-1a&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--UEYMV2UI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9td30jolmthn7hh7jz2z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--UEYMV2UI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9td30jolmthn7hh7jz2z.png" alt="RDS Failover" width="800" height="366"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--gaf3NhnQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ir79qkkkc0ecmxg58cw5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--gaf3NhnQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ir79qkkkc0ecmxg58cw5.png" alt="RDS Instances After Failover" width="800" height="238"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  CloudFront Distribution
&lt;/h3&gt;

&lt;p&gt;A CloudFront distribution is created with with the web tier ELB as the origin domain using the HTTP protocol. A custom header is added so that requests coming through the distribution will include it. This will be essential to distinguish traffic that come via the distribution and those that go directly to the ELB.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--etfI8XCx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mpv3v5vogk8w5jdic3sa.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--etfI8XCx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mpv3v5vogk8w5jdic3sa.png" alt="CloudFront Configuration" width="800" height="1086"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  WAF Web ACL
&lt;/h3&gt;

&lt;p&gt;A WAF web ACL is used to block any traffic to the ELB that do not contain the custom header, implying that only traffic that go through CloudFront will be allowed to the ELB.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;CustomHeaderACL&lt;/code&gt; is created and associated with the &lt;code&gt;WebTierELB&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--lS8_rnTt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/did4q3sf6tou0aqnjz4e.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--lS8_rnTt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/did4q3sf6tou0aqnjz4e.png" alt="Web ACL 1" width="800" height="619"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A rule is added to block any request that do not contain the custom header specified in the previous step. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--wKHKamjj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2e4krmr9iwnfhbovwo41.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--wKHKamjj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2e4krmr9iwnfhbovwo41.png" alt="Web ACL 2" width="800" height="1160"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The default action is left as &lt;code&gt;Allow&lt;/code&gt; to permit any request that does not match the set rule.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--NMeVGSr9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2inv9cxgha5jeyi8ok20.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--NMeVGSr9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2inv9cxgha5jeyi8ok20.png" alt="Web ACL 3" width="800" height="652"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--iszPCDhv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tbt0qszlg58j8tq0vt7j.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--iszPCDhv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tbt0qszlg58j8tq0vt7j.png" alt="Web ACL Review" width="800" height="980"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  CloudFront Test
&lt;/h2&gt;

&lt;p&gt;Traffic that is sent via the CloudFront endpoint is allowed as shown below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--tMMSG8OE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cppc5em54gigzmb27g8w.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--tMMSG8OE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cppc5em54gigzmb27g8w.png" alt="CloudFront Traffic Success" width="800" height="121"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Traffic sent using the ALB endpoint is now denied.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--5OYK7LKG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/l9gdrw084w575fow6mrx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--5OYK7LKG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/l9gdrw084w575fow6mrx.png" alt="Web ELB Direct Traffic Denied" width="800" height="134"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Outro
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;Think of deleting unused AWS resources like eating your vegetables - it may not be fun, but it's necessary for a healthy and cost-efficient cloud environment 🥦💰💪&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Experimenting with these resources can be a fantastic experience, giving you the thrill when you get the desired outcome and even more fun when you have to troubleshoot and figure out why something went wrong.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>webdev</category>
      <category>cloud</category>
      <category>architecture</category>
    </item>
  </channel>
</rss>
