Tag: api

  • Deploying a Strands Agent on AWS Lambda using Terraform

    Recently I’ve been exploring the AI space a lot more as I’m sure a lot of you are doing as well. I’ve been looking at the Strands Agent SDK. I see this SDK as being very helpful in building out agents in the future (follow the blog to see what I come up with!).

    One thing that is not included in the SDK is the ability to deploy with Terraform. The SDK includes examples of how to package and deploy with Amazon Web Services CDK so I adapted that to utilize Terraform.

    I took my adaptation a step further and added an API Gateway layer so that you have the beginnings of a very simple AI agent deployed with the Strands SDK.

    Check out the code here: https://github.com/avansledright/terraform-strands-agent-api

    The code in the repository is fairly simple and includes everything you need to build an API Gateway, Lambda function, and some other useful resources just to help out.

    The key to all of this is packaging the required dependencies inside of the Lambda Layer. Without this the function will not work.

    File structure:
    terraform-strands-agent-api/
    └── lambda_code/
    │ ├── lambda_function.py # Your Strands agent logic
    │ └── requirements.txt # strands-agents + dependencies
    ├── api_gateway.tf # API Gateway configuration
    ├── iam.tf # IAM roles and policies
    ├── lambda.tf # Lambda function setup
    ├── locals.tf # Environment variables
    ├── logs.tf # CloudWatch logging
    ├── s3.tf # Deployment artifacts
    ├── variables.tf # Configurable inputs
    └── outputs.tf # API endpoints and resource IDs

    You shouldn’t have to change much in any of these files until you want to fully start customizing the actual functionality of the agent.

    To get started follow the instructions below!

    git clone https://github.com/avansledright/terraform-strands-agent-api
    cd terraform-strands-agent-api
    
    # Configure your settings. Add other values as needed
    echo 'aws_region = "us-west-2"' > terraform.tfvars
    
    # Deploy everything
    terraform init
    terraform plan
    terraform apply

    If everything goes as planned you should see the output of a curl command which will give you the ability to test the demo code.

    If you run into any issues feel free to let me know! I’d be happy to help you get this up and running.

    Github

    If this has helped you in any way, please share it on your social media and with any of your friends!

  • Creating a List of API Gateway resources using Terraform

    For some reason when you utilize Terraform with AWS, specifically when you want to get a list of API Gateway resources, that data element simply does not exist. Below is a relatively quick solution that will create a comma separated list of API Gateways so that you can iterate through them.

    In order to execute this element you need to have the AWS CLI setup within your preferred deployment method. Personally, I love GitHub Actions so I needed to add another stage in my deployment to install the CLI.

    The way this work is to create a data element that you can trigger as needed to execute a simple shell script.

    data "external" "apis" {
       program = ["sh", "-c", "aws apigateway get-rest-apis    --query 'items[?starts_with(name,`${var.prefix}`)].name' --output json | jq -r '{\"names\": (. | join(\",\"))}'"]
    }

    We also are creating a variable called “prefix” so that you can filter as required by your project. Personally, I used this to create Cloudwatch Dashboards so I can easily monitor my resources.

    If this is helpful for you, please share it on your social media!

  • API For Pre-signed URLs

    Pre-signed URL’s are used for downloading objects from AWS S3 buckets. I’ve used them many times in the past for various reasons but this idea was a new one. A proof of concept for an API that would create the pre-signed URL and return it to the user.

    This solution utilizes an API Gateway and an AWS Lambda function. The API Gateway takes two parameters “key” and “expiration”. Ultimately, you could add another parameter for “bucket” if you wanted the gateway to be able to get objects from multiple buckets.

    I used Terraform to create the infrastructure and Python to program the Lambda.

    Take a look at the Lambda code below:

    import boto3
    import json
    import os
    from botocore.exceptions import ClientError
    
    def lambda_handler(event, context):
        # Get the query parameters
        query_params = event.get('queryStringParameters', {})
        if not query_params or 'key' not in query_params:
            return {
                'statusCode': 400,
                'body': json.dumps({'error': 'Missing required parameter: key'})
            }
        
        object_key = query_params['key']
        expiration = int(query_params.get('expiration', 3600))  # Default 1 hour
        
        # Initialize S3 client
        s3_client = boto3.client('s3')
        bucket_name = os.environ['BUCKET_NAME']
        
        try:
            # Generate presigned URL
            url = s3_client.generate_presigned_url(
                'get_object',
                Params={
                    'Bucket': bucket_name,
                    'Key': object_key
                },
                ExpiresIn=expiration
            )
            
            return {
                'statusCode': 200,
                'headers': {
                    'Access-Control-Allow-Origin': '*',
                    'Content-Type': 'application/json'
                },
                'body': json.dumps({
                    'url': url,
                    'expires_in': expiration
                })
            }
            
        except ClientError as e:
            return {
                'statusCode': 500,
                'body': json.dumps({'error': str(e)})
            }

    The Terraform will also output a Postman collection JSON file so that you can immediately import it for testing. If this code and pattern is useful for you check it out on my GitHub below.

    Github

  • Updating AWS Managed Prefix Lists

    I was working with a customer the other day trying to come up with a way to import a bunch of IP addresses into a white list on AWS. We came up with the approach of using Managed Prefix Lists in VPC. I wrote some Python in order to grab some code from an API and then automatically put it into a prefix list.

    The code takes input from an API that is managed by a 3rd party. We first use that and parse the returned values into meaningful lists. After that, we pass the IPs to the function which it will check if the entry exists or not. If it does, it will pass the IP. If it doesn’t exist it will automatically add it.

    import requests
    import json
    import os
    import boto3
    from botocore.exceptions import ClientError
    import ipaddress
    
    def check_for_existing(list_id, ip):
        client = boto3.client("ec2", region_name="us-west-2")
        try:
            response = client.get_managed_prefix_list_entries(
                PrefixListId=list_id,
                MaxResults=100,
            )
            for entry in response['Entries']:
                if entry['Cidr'] == ip:
                    return True
                else:
                    pass
            return False
        except ClientError as e:
            print(e)
    
    
    
    def get_prefix_list_id(list_name):
        client = boto3.client("ec2", region_name="us-west-2")
        response = client.describe_managed_prefix_lists(
            MaxResults=100,
            Filters=[
                {
                    "Name": "prefix-list-name",
                    "Values": [list_name]
                }
            ]
        )
        for p_list in response['PrefixLists']:
            return {"ID": p_list['PrefixListId'], "VERSION": p_list['Version']}
    
    def update_managed_prefix_list(list_name, ip):
        client = boto3.client("ec2", region_name="us-west-2")
        if check_for_existing(get_prefix_list_id(list_name)['ID'], ip) == True:
            print("Rule already exists")
            return False
        else:
            try:
                response = client.modify_managed_prefix_list(
                            DryRun=False,
                            PrefixListId=get_prefix_list_id(list_name)['ID'],
                            CurrentVersion=get_prefix_list_id(list_name)['VERSION'],
                            AddEntries=[
                                {
                                    "Cidr": ip
                                }
                            ]
                        )
                return True
            except ClientError as e:
                print(e)
                print("Failed to update list")
    
    if __name__ == "__main__":
        url = "https://<my IP address URL>"
        headers = {}
        r = requests.get(url, headers=headers)
        json_ips = json.loads(r.content)
        ip = ""
        list_name = ""
        result = update_managed_prefix_list(list_name, ip)
        if result == True:
            print("Successfully Updates lists")
        else:
            print("Failed to update lists")

    If you are going to use this code it will need some modifications. I ultimately did not deploy this code but I had plans to run it as a Lambda function on a schedule so the lists would always be up to date.

    If this code is helpful to you please share it with your friends!

    Github

  • Where Is It 5 O’Clock Pt: 4

    As much as I’ve scratched my head working on this project it has been fun to learn some new things and build something that isn’t infrastructure automation. I’ve learned some frontend web development some backend development and utilized some new Amazon Web Services products.

    With all that nice stuff said I’m proud to announce that I have built a fully functioning project that is finally working the way I intended it. You can visit the website here:

    www.whereisitfiveoclock.net

    To recap, I bought this domain one night as a joke and thought “Hey, maybe one day I’ll build something”. I started off building a fully Python application backed by Flask. You can read about that in Part 1.This did not work out the way I intended as it did not refresh the timezones on page load. In part 3 I discussed how I was rearchitecting the project to include an API that would be called upon page load.

    The API worked great and delivered two JSON objects into my frontend. I then parsed the two JSON objects into two separate tables that display where you can be drinking and where you probably shouldn’t be drinking.

    This is a snippet of the JavaScript I wrote to iterate over the JSON objects while adding them into the appropriate table:

    function buildTable(someinfo){
                    var table1 = document.getElementById('its5pmsomewhere')
                    var table2 = document.getElementById('itsnot5here')
                    var its5_json = JSON.parse(someinfo[0]);
                    var not5_json = JSON.parse(someinfo[1]);
                    var its5_array = []
                    var not5_array = []
                    its5_json['its5'].forEach((value, index) => {
    
                        var row = `<tr>
                                    <td>${value}</td>
                                    <td></td>
                                    </tr>`
                    
                        table1.innerHTML += row
                    })  
                    not5_json['not5'].forEach((value, index) => {
    
                            var row = `<tr>
                                    <td></td>
                                    <td>${value}</td>
                                    </tr>`
                    
                        table2.innerHTML += row
                    })  

    First I reference two different HTML tables. I then parse the JSON from the API. I take both JSON objects and iterate over them adding the timezones into the table and then returning them into the HTML table.

    If you want more information on how I did this feel free to reach out.

    I want to continue iterating over this application to add new features. I need to do some standard things like adding Google Analytics so I can track traffic. I also want to add a search feature and a map that displays the different areas of drinking acceptability.

    I also am open to requests. One of my friends suggested that I add a countdown timer to each location that it is not yet acceptable to be drinking.

    Feel free to reach out in the comments or on your favorite social media platform! And as always, if you liked this project please share it with your friends.

  • Where Is It Five O’Clock Pt: 3

    So I left this project at a point where I felt it needed to be re-architected based on the fact that Flask only executes the function once and not every time the page loads.

    I re-architected the application in my head to include an API that calls the Lambda function and returns a list of places where it is and is not acceptable to be drinking based on the 5 O’Clock rules. These two lists will be JSON objects that have a single key with multiple values. The values will be the timezones appropriate to be drinking in.

    After the JSON objects are generated I can reference them through the web frontend and display them in an appropriate way.

    At this point I have the API built out and fully funcitoning the way I think I want it. You can use it by executing the following:
    curl https://5xztnem7v4.execute-api.us-west-2.amazonaws.com/whereisit5

    I will probably only have this publically accessible for a few days before locking it back down.

    Hopefully, in part 4 of this series, I will have a frontend demo to show!