Tag: api

  • 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!