skip to Main Content

I currently have a Terraform file that is used to set up a website with s3 and CloudFront. My website has a dynamic component with Lambda Function URL that adds to a Dynamo DB table when my website is viewed. Everything works good, but my problem is I don’t know how to get the function URL after Terraform is complete to automatically update in my javascript file, if this is possible or any other way. Right now, I have to go into the Javascript file and copy and paste the function URL from Lambda after my Terraform has ran, but I would like everything to be fully automated.

My javascript file(XXX= the function url that I have to hardcode):

const counter = document.querySelector(".counter-number"); 
async function updateCounter() {
    let response = await fetch("XXX");
    let data = await response.json();
    counter.innerHTML = `Website View Count: ${data}`;
} 
updateCounter();

The portion of my Terraform where the Javascript file is uploaded to S3 – I currently have it to where any change in my website files is picked up through Terraform:

resource "aws_s3_object" "website_contents" {
  for_each     = fileset("/Users/andrea/Documents/AWS/CRC/my_website", "**") 
  bucket       = data.aws_s3_bucket.main_bucket.id
  key          = each.key
  source       = "/Users/andrea/Documents/AWS/CRC/my_website/${each.value}"
  content_type = each.value
  etag = filemd5("/Users/andrea/Documents/AWS/CRC/my_website/${each.value}")
}

Please let me know if there is any thing else I can provide, I appreciate any sort of help and assistance- thank you!

2

Answers


  1. What you could try is to save the function_url in a variable and then use this variable to edit a local file using sed with a local-exec provisioner. Hope this helps!

    The resource lambda_function_url exports three attributes:

    • function_arn The Amazon Resource Name (ARN) of the function.
    • function_url The HTTP URL endpoint for the function in the format https://<url_id&gt;.lambda-url..on.aws.
    • url_id A generated ID for the endpoint.

    This means that the resource provides certain pieces of information that can be used by other parts of your Terraform configuration. These attributes can be referenced and used in other resources or data blocks.

    Assuming you have something like this in your .tf files:

    resource "aws_lambda_function" "test" {
      filename      = "lambda_function_payload.zip"
      function_name = "my_lambda_function_name"
      # . . .
      # etc.
    }
    
    resource "aws_lambda_function_url" "my_lambda_function_url" {
      function_name      = aws_lambda_function.test.function_name
      authorization_type = "NONE"
    }
    

    You can define a variable myURL

    # Use the exported function_url attribute in a variable
    variable "myURL" {
      value = aws_lambda_function_url.my_lambda_function_url.function_url
    }
    

    And here’s the code to edit the file ./my_website/myfile.js

    resource "null_resource" "edit_file" {
      provisioner "local-exec" {
        command = <<EOF
          sed -i.bak "s|^const apiUrl =.*|const apiUrl = "$URL";|" ./my_website/myfile.js
        EOF
        environment = {
          URL = var.myURL 
        }
      }
      # this is needed to have script run every time
      triggers = {
        always_run = timestamp()
      }
    }
    
    # Optional: Output the content of the edited file
    data "local_file" "edited_file" {
      depends_on = [null_resource.edit_file]
      filename   = "./my_website/myfile.js"
    }
    
    output "edited_file_content" {
      value = data.local_file.edited_file.content
    }
    
    
    variable "myURL" {
      type = string
      default = "https://example.comx/api?parameter=xyz"
    }
    

    This will substitute the URL using sed in a file myfile.js looking like this:

    cat >./my_website/myfile.js <<EOF
    // filename: myfile.js
    const apiUrl = "abc";
    console.log(apiUrl);
    EOF
    
    Login or Signup to reply.
  2. Beside the option given in the previous question, let me give you another option you can consider.

    Injecting views count as a query parameter

    Since you’ve already using CloudFront, you may consider to convert your Web-facing Lambda function into Lambda.Edge function and connect it to the CloudFront Distribution you have as a viewer-request handler.

    This way you will be able to rewrite request before it gets processed by the origin.

    The code for your Lambda.Edge might look like the following:

    'use strict';
    
    exports.handler = (evt, context, cb) => {
      const { request } = evt.Records[0].cf;
    
      // CUT: Get and update views count with DynamoDB
      // const viewsCount = 'xxx';
    
      request.querystring += `&viewsCount=${viewsCount}`;
    
      cb(null, request);
    }
    

    Your frontend code can be simplified, since views count will be provided as a query parameter, so no need to call a Lambda:

    const urlParams = new URLSearchParams(window.location.search);
    const counter = document.querySelector(".counter-number"); 
    counter.innerHTML = urlParams['viewsCount'] || '<default_value>';
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search