Introduction to AWS Lambda and Cognito

Understanding AWS Lambda, Cognito Identity Pool, and the validateToken Example

AWS Lambda

AWS Lambda is a serverless compute service that runs code in response to events. It eliminates the need to provision or manage servers, allowing you to focus on your application logic.

  • Supports multiple programming languages like Python, Node.js, and Java.
  • Triggers can include HTTP requests (via API Gateway), S3 uploads, DynamoDB changes, etc.
  • You pay only for the compute time you use.

Cognito Identity Pool

AWS Cognito Identity Pool allows you to grant AWS resource access to users, both authenticated and unauthenticated, using temporary credentials.

  • Identity Pools manage user identities and map them to IAM roles.
  • Unauthenticated identities are useful for guest access.
  • Integrated seamlessly with other AWS services like Lambda, S3, and DynamoDB.

For example, you can configure unauthenticated users to invoke a Lambda function through a role tied to the Cognito Identity Pool.

validateToken Sample

The validateToken example demonstrates how to:

  1. Use AWS Cognito Identity Pool to grant unauthenticated access.
  2. Invoke a Lambda function securely using the AWS SDK.
  3. Validate tokens and perform checks like route authorization.

Key Components:

  • Lambda Function: Implements token validation and page access authorization.
  • Cognito Identity Pool: Provides unauthenticated access permissions.
  • AWS SDK: Enables Lambda invocation from a browser or application.

Example workflow:

User -> AWS Cognito Identity Pool -> Temporary IAM Role -> Lambda Function

Using this setup, the validateToken Lambda function validates tokens and returns authorization responses for requested resources.

Objective

Below are the objective targeted

  • Capturing steps required for setting up aws Lambda function
  • Capturing steps required for setting up Cognito Identity Pool for unauthenticated access
  • Capturing script required for invoking Lambda function using AWS JS SDK

Lambda Function Code

The following is the source code of the `validateToken` Lambda function:

// index.mjs

const TOKEN_PAGE_MAPPING = {
    'token123': 'dashboard',
    'token456': 'profile',
    'token789': 'settings',
    'tokenABC': 'reports'
};

const createResponse = (statusCode, body) => ({
    statusCode,
    headers: {
        'Content-Type': 'application/json',
        'Access-Control-Allow-Origin': '*',
        'Access-Control-Allow-Credentials': true
    },
    body: JSON.stringify(body)
});

export const handler = async (event) => {
    try {
        const authHeader = event.headers?.Authorization;
        
        if (!authHeader?.startsWith('Bearer ')) {
            return createResponse(401, { error: 'No token provided or invalid format' });
        }
        
        const token = authHeader.substring(7);
        const requestedPage = event.queryStringParameters?.page;
        
        if (!requestedPage) {
            return createResponse(400, { error: 'Page parameter is required' });
        }
        
        const allowedPage = TOKEN_PAGE_MAPPING[token];
        
        if (!allowedPage) {
            return createResponse(401, { error: 'Invalid token' });
        }
        
        if (allowedPage !== requestedPage) {
            return createResponse(403, { error: `Token not authorized for page: ${requestedPage}` });
        }
        
        return createResponse(200, {
            status: 'success',
            message: `Token valid for page: ${allowedPage}`,
            page: allowedPage,
            timestamp: new Date().toISOString()
        });
    
    } catch (error) {
        console.error('Validation error:', error);
        return createResponse(500, { error: 'Internal server error', message: error.message });
    }
};
        

Shell Script Source Code

1. upload-aws-lambda.sh

#!/bin/bash
# upload-aws-lambda.sh

LAMBDA_FUNCTION_NAME="$1"
ZIP_FILE_NAME="$LAMBDA_FUNCTION_NAME.zip"

if [ -f "$ZIP_FILE_NAME" ]; then
    echo "Removing existing ZIP file: $ZIP_FILE_NAME"
    rm -f "$ZIP_FILE_NAME"
fi

(cd $LAMBDA_FUNCTION_NAME && zip -r ../$LAMBDA_FUNCTION_NAME.zip ./*)

aws lambda update-function-code \
    --function-name "$LAMBDA_FUNCTION_NAME" \
    --zip-file "fileb://$ZIP_FILE_NAME"

rm -f "$ZIP_FILE_NAME"

2. download-aws-lambda.sh

#!/bin/bash
# download-aws-lambda.sh

LAMBDA_FUNCTION_NAME="$1"
OUTPUT_ZIP_FILE="$LAMBDA_FUNCTION_NAME.zip"

DOWNLOAD_URL=$(aws lambda get-function --function-name "$LAMBDA_FUNCTION_NAME" --query "Code.Location" --output text)

curl -o "$OUTPUT_ZIP_FILE" "$DOWNLOAD_URL"

mkdir -p $LAMBDA_FUNCTION_NAME
unzip $OUTPUT_ZIP_FILE -d $LAMBDA_FUNCTION_NAME
rm $OUTPUT_ZIP_FILE

3. Upload the Function

#!/bin/bash
LAMBDA_FUNCTION_NAME="validateToken"

echo "Uploading function..."
./upload-aws-lambda.sh "$LAMBDA_FUNCTION_NAME"

4. Download the Function

#!/bin/bash
LAMBDA_FUNCTION_NAME="validateToken"

echo "Downloading function..."
./download-aws-lambda.sh "$LAMBDA_FUNCTION_NAME"

Steps to Deploy

  1. Save the Lambda function code as `index.mjs`.
  2. Upload the function using upload-aws-lambda.sh.
  3. Download the function using download-aws-lambda.sh for local development.

validateToken Lambda Function Setup

This section explains the creation, deployment, and testing of the `validateToken` Lambda function.

Test Lambda Function

#!/bin/bash
# test-aws-lambda.sh

LAMBDA_FUNCTION_NAME="$1"
REQUEST_FILE="test/$LAMBDA_FUNCTION_NAME/request.json"
RESPONSE_FILE="test/$LAMBDA_FUNCTION_NAME/response.json"

aws lambda invoke \
    --function-name "$LAMBDA_FUNCTION_NAME" \
    --cli-binary-format raw-in-base64-out \
    --payload file://"$REQUEST_FILE" \
    "$RESPONSE_FILE"

cat "$RESPONSE_FILE"
        

Testing with Request Payload

The following is the test request payload:

{
    "headers": {
        "Authorization": "Bearer token123"
    },
    "queryStringParameters": {
        "page": "dashboard"
    }
}

Steps to Test

  1. Test the function using test-aws-lambda.sh.

Setting Up AWS Guest Access

A step-by-step guide to configure unauthenticated access using Cognito Identity Pool

Overview

This guide walks through setting up guest access using AWS Cognito Identity Pool. We'll create:

  • A Cognito Identity Pool
  • An IAM Role for unauthenticated access
  • A policy allowing Lambda function invocation
  • Role attachment to the Identity Pool

Setup Script

Save the following script as setup-guest-access.sh:

#!/bin/bash

# Create Identity Pool
aws cognito-identity create-identity-pool \
    --identity-pool-name "GuestAccessPool" \
    --allow-unauthenticated-identities \
    --allow-classic-flow

# Store the Identity Pool ID
IDENTITY_POOL_ID=$(aws cognito-identity create-identity-pool \
    --identity-pool-name "GuestAccessPool" \
    --allow-unauthenticated-identities \
    --allow-classic-flow \
    --query 'IdentityPoolId' \
    --output text)

mkdir temp

# Create trust policy JSON
cat > temp/trust-policy.json << EOL
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "Federated": "cognito-identity.amazonaws.com"
            },
            "Action": "sts:AssumeRoleWithWebIdentity",
            "Condition": {
                "StringEquals": {
                    "cognito-identity.amazonaws.com:aud": "${IDENTITY_POOL_ID}"
                },
                "ForAnyValue:StringLike": {
                    "cognito-identity.amazonaws.com:amr": "unauthenticated"
                }
            }
        }
    ]
}
EOL

# Create the IAM role
aws iam create-role \
    --role-name CognitoUnAuthRole-001 \
    --assume-role-policy-document file://temp/trust-policy.json

# Store the Role ARN
ROLE_ARN=$(aws iam create-role \
    --role-name CognitoUnAuthRole-001 \
    --assume-role-policy-document file://temp/trust-policy.json \
    --query 'Role.Arn' \
    --output text)

# Create Lambda invoke policy
cat > temp/lambda-policy.json << EOL
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": "lambda:InvokeFunction",
            "Resource": "arn:aws:lambda:ap-southeast-2:xxxxxxxxxxxx:function:validateToken"
        }
    ]
}
EOL

# Attach policy to role
aws iam put-role-policy \
    --role-name CognitoUnAuthRole \
    --policy-name LambdaInvokePolicy \
    --policy-document file://temp/lambda-policy.json

# Create roles configuration
cat > temp/roles.json << EOL
    {
        "unauthenticated": "${ROLE_ARN}"
    }
EOL

# Attach role to Identity Pool
aws cognito-identity set-identity-pool-roles \
    --identity-pool-id ${IDENTITY_POOL_ID} \
    --roles file://temp/roles.json

# Clean up temporary files
rm -rf temp

Step-by-Step Instructions

1. Prepare the Environment

  • Ensure AWS CLI is installed and configured
  • Save the script as setup-guest-access.sh
  • Make the script executable: chmod +x setup-guest-access.sh

2. Run the Setup Script

./setup-guest-access.sh

3. Verify the Setup

Check the created resources:

# List Identity Pools
aws cognito-identity list-identity-pools --max-results 10

# Check IAM role
aws iam get-role --role-name CognitoUnAuthRole-001

# Verify role policy
aws iam get-role-policy \
    --role-name CognitoUnAuthRole-001 \
    --policy-name LambdaInvokePolicy

Script Components Explained

Identity Pool Creation

  • Creates a new Identity Pool named "GuestAccessPool"
  • Enables unauthenticated access
  • Allows classic flow

IAM Role Setup

  • Creates a role for unauthenticated users
  • Sets up trust relationship with Cognito
  • Configures conditions for the Identity Pool

Lambda Access Policy

  • Creates a policy allowing Lambda function invocation
  • Attaches policy to the IAM role

Role Association

  • Associates the IAM role with the Identity Pool
  • Configures role for unauthenticated access

Important Notes

  • The script creates temporary files in a 'temp' directory which is cleaned up after execution
  • Ensure you have sufficient IAM permissions to create these resources
  • The Lambda function ARN in the policy should be updated to match your function
  • Role names must be unique within your AWS account

Using AWS JS SDK to Invoke validateToken

Below is the complete code for invoking a Lambda function using the AWS JS SDK.

invoke-aws.html

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Lambda Invoker</title>
<script src="https://sdk.amazonaws.com/js/aws-sdk-2.1048.0.min.js"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/tailwindcss/2.2.19/tailwind.min.css" rel="stylesheet">
<style>
    body {
        background-color: #f3f4f6;
    }
    .card {
        background: white;
        border-radius: 8px;
        box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
    }
    .input-field {
        width: 100%;
        padding: 0.75rem;
        border: 1px solid #e5e7eb;
        border-radius: 6px;
        margin-top: 0.5rem;
        font-size: 0.975rem;
        transition: border-color 0.15s ease-in-out;
    }
    .input-field:focus {
        outline: none;
        border-color: #3b82f6;
        box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
    }
    .button {
        background-color: #3b82f6;
        color: white;
        padding: 0.75rem 1.5rem;
        border-radius: 6px;
        font-weight: 500;
        transition: background-color 0.15s ease-in-out;
    }
    .button:hover {
        background-color: #2563eb;
    }
    .success {
        background-color: #ecfdf5;
        border: 1px solid #10b981;
    }
    .error {
        background-color: #fef2f2;
        border: 1px solid #ef4444;
    }
</style>
</head>
<body class="min-h-screen py-8">
<div class="max-w-2xl mx-auto px-4">
    <div class="card p-6 mb-8">
        <h2 class="text-2xl font-bold text-gray-800 mb-6">AWS Lambda Invoker - validateToken</h2>
        
        <div>
            <div>
                <label for="token" class="block text-sm font-medium text-gray-700">Token</label>
                <input type="text" id="token" class="input-field" placeholder="Enter your token">
            </div>

            <div>
                <label for="page" class="block text-sm font-medium text-gray-700">Page</label>
                <select id="page" class="input-field">
                    <option value="dashboard">Dashboard</option>
                    <option value="profile">Profile</option>
                    <option value="settings">Settings</option>
                </select>
            </div>

            <button onclick="invokeLambda()" class="button w-full">Invoke validateToken</button>
        </div>
    </div>

    <div id="result" class="card p-6 hidden"></div>
</div>

<script>
    const REGION = 'ap-southeast-2';
    const IDENTITY_POOL_ID = 'ap-southeast-2:xxxxxxxx-xxxxxxxxx-xxxx-xxxxxxxxxxxx';
    const FUNCTION_NAME = 'validateToken';

    AWS.config.region = REGION;
    AWS.config.credentials = new AWS.CognitoIdentityCredentials({
        IdentityPoolId: IDENTITY_POOL_ID
    });

    const lambda = new AWS.Lambda();

    async function invokeLambda() {
        const token = document.getElementById('token').value;
        const page = document.getElementById('page').value;
        const resultDiv = document.getElementById('result');
        
        resultDiv.classList.remove('hidden');
        resultDiv.classList.remove('success', 'error');
        resultDiv.innerHTML = `
            <div class="flex items-center justify-center">
                <div class="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-500"></div>
                <span class="ml-3 text-gray-600">Processing...</span>
            </div>
        `;

        const payload = {
            headers: {
                Authorization: `Bearer ${token}`
            },
            queryStringParameters: {
                page: page
            }
        };

        const params = {
            FunctionName: FUNCTION_NAME,
            InvocationType: 'RequestResponse',
            Payload: JSON.stringify(payload)
        };

        try {
            const response = await lambda.invoke(params).promise();
            const result = JSON.parse(response.Payload);
            
            if (result.statusCode === 200) {
                resultDiv.classList.add('success');
                resultDiv.innerHTML = `
                    <h4 class="text-lg font-semibold text-green-800 mb-3">Success</h4>
                    <pre class="bg-white p-4 rounded-lg overflow-x-auto text-sm">${JSON.stringify(JSON.parse(result.body), null, 2)}</pre>
                `;
            } else {
                resultDiv.classList.add('error');
                resultDiv.innerHTML = `
                    <h4 class="text-lg font-semibold text-red-800 mb-3">Error</h4>
                    <pre class="bg-white p-4 rounded-lg overflow-x-auto text-sm">${JSON.stringify(JSON.parse(result.body), null, 2)}</pre>
                `;
            }
        } catch (error) {
            resultDiv.classList.add('error');
            resultDiv.innerHTML = `
                <h4 class="text-lg font-semibold text-red-800 mb-3">Error</h4>
                <p class="text-red-600">${error.message}</p>
            `;
            console.error('Lambda invocation error:', error);
        }
    }
</script>
</body>
</html>

Tested Results

1. Initial Test - Before setting up identity pool.

Result 1

2. Invalid Token - Token not recognized by the Lambda function.

Result 2

3. Not Authorized - Token provided does not grant access to the requested page.

Result 3

4. Successful Invocation - Token is valid and authorized for the page.

Result 4

AWS Identity Pool Stack Setup

Step-by-step setup for Cognito Identity Pool using CloudFormation.

Overview

This guide shows how to use AWS CloudFormation to set up an Identity Pool and configure IAM roles to allow Lambda function invocation.

CloudFormation allows you to automate the creation and management of AWS resources using templates. By defining your infrastructure as code, CloudFormation ensures consistency, reduces manual effort, and enables repeatable deployments.

CloudFormation Template

The CloudFormation template defines the Identity Pool, IAM roles, and a policy to allow Lambda invocation.

AWSTemplateFormatVersion: '2010-09-09'
AWSTemplateFormatVersion: '2010-09-09'
Description: 'CloudFormation template for creating an AWS Cognito Identity Pool with IAM Role'

Parameters:
  IdentityPoolName:
    Type: String
    Description: Name of the Identity Pool
    Default: GuestAccessPool
    MinLength: 1
    MaxLength: 128
    AllowedPattern: '[a-zA-Z0-9_. -]+'

  LambdaFunctionArn:
      Type: String
      Description: ARN of the Lambda function to invoke
      Default: arn:aws:lambda:ap-southeast-2:xxxxxxxxxxxx:function:validateToken
      AllowedPattern: '^arn:aws:lambda:[a-z0-9-]+:[0-9]{12}:function:.+$'
      ConstraintDescription: Must be a valid Lambda function ARN


Resources:
  CognitoIdentityPool:
    Type: 'AWS::Cognito::IdentityPool'
    Properties:
      IdentityPoolName: !Ref IdentityPoolName
      AllowUnauthenticatedIdentities: true
      AllowClassicFlow: true
      IdentityPoolTags: []

  # IAM Role for unauthenticated users
  CognitoUnAuthRole:
    Type: 'AWS::IAM::Role'
    Properties:
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Principal:
              Federated: cognito-identity.amazonaws.com
            Action: sts:AssumeRoleWithWebIdentity
            Condition:
              StringEquals:
                'cognito-identity.amazonaws.com:aud': !Ref CognitoIdentityPool
              'ForAnyValue:StringLike':
                'cognito-identity.amazonaws.com:amr': unauthenticated

  # Lambda invoke policy for the role
  LambdaInvokePolicy:
    Type: 'AWS::IAM::Policy'
    Properties:
      PolicyName: LambdaInvokePolicy
      PolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Action: lambda:InvokeFunction
            Resource: !Ref LambdaFunctionArn
      Roles: 
        - !Ref CognitoUnAuthRole

  # Attach the role to the Identity Pool
  IdentityPoolRoleAttachment:
    Type: 'AWS::Cognito::IdentityPoolRoleAttachment'
    Properties:
      IdentityPoolId: !Ref CognitoIdentityPool
      Roles:
        unauthenticated: !GetAtt CognitoUnAuthRole.Arn

Outputs:
  IdentityPoolId:
    Description: ID of the created Identity Pool
    Value: !Ref CognitoIdentityPool
  UnAuthRoleArn:
    Description: ARN of the unauthenticated IAM Role
    Value: !GetAtt CognitoUnAuthRole.Arn

Parameters File

The parameters file provides the dynamic inputs for the CloudFormation stack.

[
        {
            "ParameterKey": "IdentityPoolName",
            "ParameterValue": "MyPublicAccess"
        },
        {
            "ParameterKey": "LambdaFunctionArn",
            "ParameterValue": "arn:aws:lambda:ap-southeast-2:xxxxxxxxxxxx:function:validateToken"
        }
]

Stack Creation Script

Execute the following script to deploy the CloudFormation stack:

#!/bin/bash

aws cloudformation create-stack \
    --stack-name my-public-identity-pool \
    --template-body file://validateToken/identity-pool-stack-config.yaml \
    --capabilities CAPABILITY_IAM \
    --parameters file://validateToken/identity-pool-stack-params.json

aws cloudformation describe-stacks --stack-name my-public-identity-pool

aws cognito-identity list-identity-pools --max-results 10

Execution Results

Once the script runs successfully, verify the following:

  • The Identity Pool with name MyPublicAccess is created.
  • An IAM role is created and attached to the Identity Pool.
  • Lambda function permissions are applied to the role.

Complete Guide: Understanding and Implementing Terraform with AWS Lambda

A comprehensive guide to understanding Terraform and implementing it with AWS Lambda.

Part 1: Understanding Terraform

What is Terraform?

Terraform is an Infrastructure as Code (IaC) tool that allows you to define and provide data center infrastructure using a declarative configuration language. Instead of manually setting up cloud resources through a web interface, you define your infrastructure in code.

When to Use Terraform

  • Infrastructure Version Control: Track changes to your infrastructure just like source code using Git
  • Consistent Environments: Create identical development, staging, and production environments
  • Multi-Cloud Deployment: Manage resources across multiple cloud providers with the same workflow
  • Automated Infrastructure: Automate the creation and updates of infrastructure as part of CI/CD pipelines

Part 2: Installation and Setup

Installing Terraform

Run the following commands to install Terraform on Ubuntu:

curl -fsSL https://apt.releases.hashicorp.com/gpg | sudo apt-key add -
sudo apt-add-repository "deb [arch=amd64] https://apt.releases.hashicorp.com $(lsb_release -cs) main"
sudo apt update
sudo apt install terraform

AWS Configuration

Terraform will use your existing AWS configuration files:

  • Credentials file: ~/.aws/credentials
  • Configuration file: ~/.aws/config

Part 3: Implementation

variables.tf

variable "identity_pool_name" {
    default = "MyPublicAccess"
}

main.tf

provider "aws" {
  region = "ap-southeast-2"
}

resource "aws_iam_role" "lambda_validate_token_role" {
  name = "lambda_validate_token_role"
  description = "IAM role for validateToken Lambda function to execute and write logs to CloudWatch"

  assume_role_policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Action = "sts:AssumeRole"
        Effect = "Allow"
        Principal = {
          Service = "lambda.amazonaws.com"
        }
      }
    ]
  })
}

# Add basic Lambda execution permissions
resource "aws_iam_role_policy_attachment" "lambda_basic" {
  role       = aws_iam_role.lambda_validate_token_role.name
  policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
}

# AWS Lambda Function
resource "aws_lambda_function" "validate_token" {
  function_name    = "validateToken"
  runtime          = "nodejs18.x"
  role            = aws_iam_role.lambda_validate_token_role.arn  # Updated role
  handler          = "index.handler"

  filename         = "function.zip"
  source_code_hash = filebase64sha256("function.zip")

  depends_on = [
    aws_iam_role_policy_attachment.lambda_basic
  ]
}


# Cognito Identity Pool
resource "aws_cognito_identity_pool" "identity_pool" {
  identity_pool_name           = var.identity_pool_name
  allow_unauthenticated_identities = true
  allow_classic_flow           = true
}

# IAM Role for unauthenticated users
resource "aws_iam_role" "cognito_unauth_role" {
  name = "CognitoUnAuthRole"
  description = "IAM role for unauthenticated Cognito users to invoke Lambda validation function"


  assume_role_policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Effect = "Allow"
        Principal = {
          Federated = "cognito-identity.amazonaws.com"
        }
        Action = "sts:AssumeRoleWithWebIdentity"
        Condition = {
          StringEquals = {
            "cognito-identity.amazonaws.com:aud" = aws_cognito_identity_pool.identity_pool.id
          }
          "ForAnyValue:StringLike" = {
            "cognito-identity.amazonaws.com:amr" = "unauthenticated"
          }
        }
      }
    ]
  })
}

# IAM Policy for Lambda invocation
resource "aws_iam_policy" "lambda_invoke_policy" {
  name   = "LambdaInvokePolicy"
  description = "Allows invocation of validateToken Lambda function"
  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Effect = "Allow"
        Action = "lambda:InvokeFunction"
        Resource = aws_lambda_function.validate_token.arn
      }
    ]
  })

  depends_on = [
    aws_lambda_function.validate_token
  ]
}

# Attach policy to IAM role
resource "aws_iam_role_policy_attachment" "lambda_invoke_attachment" {
  role       = aws_iam_role.cognito_unauth_role.name
  policy_arn = aws_iam_policy.lambda_invoke_policy.arn
}

# Attach the role to the Identity Pool
resource "aws_cognito_identity_pool_roles_attachment" "identity_pool_roles" {
  identity_pool_id = aws_cognito_identity_pool.identity_pool.id

  roles = {
    "unauthenticated" = aws_iam_role.cognito_unauth_role.arn
  }
}

Part 4: Terraform Commands and Outputs

terraform plan

Preview the changes Terraform will make:

$ terraform plan
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
+ create

Terraform will perform the following actions:

# aws_cognito_identity_pool.identity_pool will be created
+ resource "aws_cognito_identity_pool" "identity_pool" {
    + allow_classic_flow               = true
    + allow_unauthenticated_identities = true
    + arn                              = (known after apply)
    + id                               = (known after apply)
    + identity_pool_name               = "MyPublicAccess"
    + tags_all                         = (known after apply)
}

# aws_cognito_identity_pool_roles_attachment.identity_pool_roles will be created
+ resource "aws_cognito_identity_pool_roles_attachment" "identity_pool_roles" {
    + id               = (known after apply)
    + identity_pool_id = (known after apply)
    + roles            = (known after apply)
}

# aws_iam_policy.lambda_invoke_policy will be created
+ resource "aws_iam_policy" "lambda_invoke_policy" {
    + arn              = (known after apply)
    + attachment_count = (known after apply)
    + description      = "Allows invocation of validateToken Lambda function"
    + id               = (known after apply)
    + name             = "LambdaInvokePolicy"
    + name_prefix      = (known after apply)
    + path             = "/"
    + policy           = (known after apply)
    + policy_id        = (known after apply)
    + tags_all         = (known after apply)
}

# aws_iam_role.cognito_unauth_role will be created
+ resource "aws_iam_role" "cognito_unauth_role" {
    + arn                   = (known after apply)
    + assume_role_policy    = (known after apply)
    + create_date           = (known after apply)
    + description           = "IAM role for unauthenticated Cognito users to invoke Lambda validation function"
    + force_detach_policies = false
    + id                    = (known after apply)
    + managed_policy_arns   = (known after apply)
    + max_session_duration  = 3600
    + name                  = "CognitoUnAuthRole"
    + name_prefix           = (known after apply)
    + path                  = "/"
    + tags_all              = (known after apply)
    + unique_id             = (known after apply)

    + inline_policy (known after apply)
}

# aws_iam_role.lambda_validate_token_role will be created
+ resource "aws_iam_role" "lambda_validate_token_role" {
    + arn                   = (known after apply)
    + assume_role_policy    = jsonencode(
        {
            + Statement = [
                + {
                    + Action    = "sts:AssumeRole"
                    + Effect    = "Allow"
                    + Principal = {
                        + Service = "lambda.amazonaws.com"
                    }
                },
            ]
            + Version   = "2012-10-17"
        }
    )
    + create_date           = (known after apply)
    + description           = "IAM role for validateToken Lambda function to execute and write logs to CloudWatch"
    + force_detach_policies = false
    + id                    = (known after apply)
    + managed_policy_arns   = (known after apply)
    + max_session_duration  = 3600
    + name                  = "lambda_validate_token_role"
    + name_prefix           = (known after apply)
    + path                  = "/"
    + tags_all              = (known after apply)
    + unique_id             = (known after apply)

    + inline_policy (known after apply)
}

# aws_iam_role_policy_attachment.lambda_basic will be created
+ resource "aws_iam_role_policy_attachment" "lambda_basic" {
    + id         = (known after apply)
    + policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
    + role       = "lambda_validate_token_role"
}

# aws_iam_role_policy_attachment.lambda_invoke_attachment will be created
+ resource "aws_iam_role_policy_attachment" "lambda_invoke_attachment" {
    + id         = (known after apply)
    + policy_arn = (known after apply)
    + role       = "CognitoUnAuthRole"
}

# aws_lambda_function.validate_token will be created
+ resource "aws_lambda_function" "validate_token" {
    + architectures                  = (known after apply)
    + arn                            = (known after apply)
    + code_sha256                    = (known after apply)
    + filename                       = "function.zip"
    + function_name                  = "validateToken"
    + handler                        = "index.handler"
    + id                             = (known after apply)
    + invoke_arn                     = (known after apply)
    + last_modified                  = (known after apply)
    + memory_size                    = 128
    + package_type                   = "Zip"
    + publish                        = false
    + qualified_arn                  = (known after apply)
    + qualified_invoke_arn           = (known after apply)
    + reserved_concurrent_executions = -1
    + role                           = (known after apply)
    + runtime                        = "nodejs18.x"
    + signing_job_arn                = (known after apply)
    + signing_profile_version_arn    = (known after apply)
    + skip_destroy                   = false
    + source_code_hash               = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
    + source_code_size               = (known after apply)
    + tags_all                       = (known after apply)
    + timeout                        = 3
    + version                        = (known after apply)

    + ephemeral_storage (known after apply)

    + logging_config (known after apply)

    + tracing_config (known after apply)
}

Plan: 8 to add, 0 to change, 0 to destroy.
        

terraform apply

Apply the planned changes:

$ terraform apply

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
+ create

Terraform will perform the following actions:

# aws_cognito_identity_pool.identity_pool will be created
+ resource "aws_cognito_identity_pool" "identity_pool" {
    + allow_classic_flow               = true
    + allow_unauthenticated_identities = true
    + arn                              = (known after apply)
    + id                               = (known after apply)
    + identity_pool_name               = "MyPublicAccess"
    + tags_all                         = (known after apply)
}

# aws_cognito_identity_pool_roles_attachment.identity_pool_roles will be created
+ resource "aws_cognito_identity_pool_roles_attachment" "identity_pool_roles" {
    + id               = (known after apply)
    + identity_pool_id = (known after apply)
    + roles            = (known after apply)
}

# aws_iam_policy.lambda_invoke_policy will be created
+ resource "aws_iam_policy" "lambda_invoke_policy" {
    + arn              = (known after apply)
    + attachment_count = (known after apply)
    + description      = "Allows invocation of validateToken Lambda function"
    + id               = (known after apply)
    + name             = "LambdaInvokePolicy"
    + name_prefix      = (known after apply)
    + path             = "/"
    + policy           = (known after apply)
    + policy_id        = (known after apply)
    + tags_all         = (known after apply)
}

# aws_iam_role.cognito_unauth_role will be created
+ resource "aws_iam_role" "cognito_unauth_role" {
    + arn                   = (known after apply)
    + assume_role_policy    = (known after apply)
    + create_date           = (known after apply)
    + description           = "IAM role for unauthenticated Cognito users to invoke Lambda validation function"
    + force_detach_policies = false
    + id                    = (known after apply)
    + managed_policy_arns   = (known after apply)
    + max_session_duration  = 3600
    + name                  = "CognitoUnAuthRole"
    + name_prefix           = (known after apply)
    + path                  = "/"
    + tags_all              = (known after apply)
    + unique_id             = (known after apply)

    + inline_policy (known after apply)
}

# aws_iam_role.lambda_validate_token_role will be created
+ resource "aws_iam_role" "lambda_validate_token_role" {
    + arn                   = (known after apply)
    + assume_role_policy    = jsonencode(
        {
            + Statement = [
                + {
                    + Action    = "sts:AssumeRole"
                    + Effect    = "Allow"
                    + Principal = {
                        + Service = "lambda.amazonaws.com"
                    }
                },
            ]
            + Version   = "2012-10-17"
        }
    )
    + create_date           = (known after apply)
    + description           = "IAM role for validateToken Lambda function to execute and write logs to CloudWatch"
    + force_detach_policies = false
    + id                    = (known after apply)
    + managed_policy_arns   = (known after apply)
    + max_session_duration  = 3600
    + name                  = "lambda_validate_token_role"
    + name_prefix           = (known after apply)
    + path                  = "/"
    + tags_all              = (known after apply)
    + unique_id             = (known after apply)

    + inline_policy (known after apply)
}

# aws_iam_role_policy_attachment.lambda_basic will be created
+ resource "aws_iam_role_policy_attachment" "lambda_basic" {
    + id         = (known after apply)
    + policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
    + role       = "lambda_validate_token_role"
}

# aws_iam_role_policy_attachment.lambda_invoke_attachment will be created
+ resource "aws_iam_role_policy_attachment" "lambda_invoke_attachment" {
    + id         = (known after apply)
    + policy_arn = (known after apply)
    + role       = "CognitoUnAuthRole"
}

# aws_lambda_function.validate_token will be created
+ resource "aws_lambda_function" "validate_token" {
    + architectures                  = (known after apply)
    + arn                            = (known after apply)
    + code_sha256                    = (known after apply)
    + filename                       = "function.zip"
    + function_name                  = "validateToken"
    + handler                        = "index.handler"
    + id                             = (known after apply)
    + invoke_arn                     = (known after apply)
    + last_modified                  = (known after apply)
    + memory_size                    = 128
    + package_type                   = "Zip"
    + publish                        = false
    + qualified_arn                  = (known after apply)
    + qualified_invoke_arn           = (known after apply)
    + reserved_concurrent_executions = -1
    + role                           = (known after apply)
    + runtime                        = "nodejs18.x"
    + signing_job_arn                = (known after apply)
    + signing_profile_version_arn    = (known after apply)
    + skip_destroy                   = false
    + source_code_hash               = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
    + source_code_size               = (known after apply)
    + tags_all                       = (known after apply)
    + timeout                        = 3
    + version                        = (known after apply)

    + ephemeral_storage (known after apply)

    + logging_config (known after apply)

    + tracing_config (known after apply)
}

Plan: 8 to add, 0 to change, 0 to destroy.

Do you want to perform these actions?
Terraform will perform the actions described above.
Only 'yes' will be accepted to approve.

Enter a value: yes

aws_iam_role.lambda_validate_token_role: Creating...
aws_cognito_identity_pool.identity_pool: Creating...
aws_cognito_identity_pool.identity_pool: Creation complete after 0s [id=ap-southeast-2:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx]
aws_iam_role.cognito_unauth_role: Creating...
aws_iam_role.lambda_validate_token_role: Creation complete after 2s [id=lambda_validate_token_role]
aws_iam_role_policy_attachment.lambda_basic: Creating...
aws_iam_role.cognito_unauth_role: Creation complete after 2s [id=CognitoUnAuthRole]
aws_cognito_identity_pool_roles_attachment.identity_pool_roles: Creating...
aws_cognito_identity_pool_roles_attachment.identity_pool_roles: Creation complete after 0s [id=ap-southeast-2:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx]
aws_iam_role_policy_attachment.lambda_basic: Creation complete after 1s [id=lambda_validate_token_role-xxxxxxxxxxxxxxxxxxxx]
aws_lambda_function.validate_token: Creating...
aws_lambda_function.validate_token: Still creating... [9s elapsed]
aws_lambda_function.validate_token: Creation complete after 14s [id=validateToken]
aws_iam_policy.lambda_invoke_policy: Creating...
aws_iam_policy.lambda_invoke_policy: Creation complete after 1s [id=arn:aws:iam::xxxxxxxxxxxx:policy/LambdaInvokePolicy]
aws_iam_role_policy_attachment.lambda_invoke_attachment: Creating...
aws_iam_role_policy_attachment.lambda_invoke_attachment: Creation complete after 1s [id=CognitoUnAuthRole-xxxxxxxxxxxxxxxxxxxx]

Apply complete! Resources: 8 added, 0 changed, 0 destroyed.

terraform destroy

Remove all resources created by Terraform:

$ terraform destroy
aws_iam_role.lambda_validate_token_role: Refreshing state... [id=lambda_validate_token_role]
aws_cognito_identity_pool.identity_pool: Refreshing state... [id=ap-southeast-2:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx]
aws_iam_policy.lambda_invoke_policy: Refreshing state... [id=arn:aws:iam::xxxxxxxxxxxx:policy/LambdaInvokePolicy]
aws_iam_role.cognito_unauth_role: Refreshing state... [id=CognitoUnAuthRole]
aws_iam_role_policy_attachment.lambda_basic: Refreshing state... [id=lambda_validate_token_role-xxxxxxxxxxxxxxxxxxxx]
aws_iam_role_policy_attachment.lambda_invoke_attachment: Refreshing state... [id=CognitoUnAuthRole-xxxxxxxxxxxxxxxxxxxx]
aws_cognito_identity_pool_roles_attachment.identity_pool_roles: Refreshing state... [id=ap-southeast-2:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx]
aws_lambda_function.validate_token: Refreshing state... [id=validateToken]

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
- destroy

Terraform will perform the following actions:

# aws_cognito_identity_pool.identity_pool will be destroyed
- resource "aws_cognito_identity_pool" "identity_pool" {
    - allow_classic_flow               = true -> null
    - allow_unauthenticated_identities = true -> null
    - arn                              = "arn:aws:cognito-identity:ap-southeast-2:xxxxxxxxxxxx:identitypool/ap-southeast-2:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" -> null
    - id                               = "ap-southeast-2:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" -> null
    - identity_pool_name               = "MyPublicAccess" -> null
    - openid_connect_provider_arns     = [] -> null
    - saml_provider_arns               = [] -> null
    - supported_login_providers        = {} -> null
    - tags                             = {} -> null
    - tags_all                         = {} -> null
    # (1 unchanged attribute hidden)
}

# aws_cognito_identity_pool_roles_attachment.identity_pool_roles will be destroyed
- resource "aws_cognito_identity_pool_roles_attachment" "identity_pool_roles" {
    - id               = "ap-southeast-2:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" -> null
    - identity_pool_id = "ap-southeast-2:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" -> null
    - roles            = {
        - "unauthenticated" = "arn:aws:iam::xxxxxxxxxxxx:role/CognitoUnAuthRole"
    } -> null
}

# aws_iam_policy.lambda_invoke_policy will be destroyed
- resource "aws_iam_policy" "lambda_invoke_policy" {
    - arn              = "arn:aws:iam::xxxxxxxxxxxx:policy/LambdaInvokePolicy" -> null
    - attachment_count = 1 -> null
    - id               = "arn:aws:iam::xxxxxxxxxxxx:policy/LambdaInvokePolicy" -> null
    - name             = "LambdaInvokePolicy" -> null
    - path             = "/" -> null
    - policy           = jsonencode(
        {
            - Statement = [
                - {
                    - Action   = "lambda:InvokeFunction"
                    - Effect   = "Allow"
                    - Resource = "arn:aws:lambda:ap-southeast-2:xxxxxxxxxxxx:function:validateToken"
                },
            ]
            - Version   = "2012-10-17"
        }
    ) -> null
    - policy_id        = "xxxxxxxxxxxxxxxxxxxxx" -> null
    - tags             = {} -> null
    - tags_all         = {} -> null
    # (2 unchanged attributes hidden)
}

# aws_iam_role.cognito_unauth_role will be destroyed
- resource "aws_iam_role" "cognito_unauth_role" {
    - arn                   = "arn:aws:iam::xxxxxxxxxxxx:role/CognitoUnAuthRole" -> null
    - assume_role_policy    = jsonencode(
        {
            - Statement = [
                - {
                    - Action    = "sts:AssumeRoleWithWebIdentity"
                    - Condition = {
                        - "ForAnyValue:StringLike" = {
                            - "cognito-identity.amazonaws.com:amr" = "unauthenticated"
                        }
                        - StringEquals             = {
                            - "cognito-identity.amazonaws.com:aud" = "ap-southeast-2:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
                        }
                    }
                    - Effect    = "Allow"
                    - Principal = {
                        - Federated = "cognito-identity.amazonaws.com"
                    }
                },
            ]
            - Version   = "2012-10-17"
        }
    ) -> null
    - create_date           = "2024-12-24T10:55:26Z" -> null
    - force_detach_policies = false -> null
    - id                    = "CognitoUnAuthRole" -> null
    - managed_policy_arns   = [
        - "arn:aws:iam::xxxxxxxxxxxx:policy/LambdaInvokePolicy",
    ] -> null
    - max_session_duration  = 3600 -> null
    - name                  = "CognitoUnAuthRole" -> null
    - path                  = "/" -> null
    - tags                  = {} -> null
    - tags_all              = {} -> null
    - unique_id             = "xxxxxxxxxxxxxxxxxxxx" -> null
    # (3 unchanged attributes hidden)
}

# aws_iam_role.lambda_validate_token_role will be destroyed
- resource "aws_iam_role" "lambda_validate_token_role" {
    - arn                   = "arn:aws:iam::xxxxxxxxxxxx:role/lambda_validate_token_role" -> null
    - assume_role_policy    = jsonencode(
        {
            - Statement = [
                - {
                    - Action    = "sts:AssumeRole"
                    - Effect    = "Allow"
                    - Principal = {
                        - Service = "lambda.amazonaws.com"
                    }
                },
            ]
            - Version   = "2012-10-17"
        }
    ) -> null
    - create_date           = "2024-12-24T11:36:59Z" -> null
    - force_detach_policies = false -> null
    - id                    = "lambda_validate_token_role" -> null
    - managed_policy_arns   = [
        - "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole",
    ] -> null
    - max_session_duration  = 3600 -> null
    - name                  = "lambda_validate_token_role" -> null
    - path                  = "/" -> null
    - tags                  = {} -> null
    - tags_all              = {} -> null
    - unique_id             = "xxxxxxxxxxxxxxxxxxxx" -> null
    # (3 unchanged attributes hidden)
}

# aws_iam_role_policy_attachment.lambda_basic will be destroyed
- resource "aws_iam_role_policy_attachment" "lambda_basic" {
    - id         = "lambda_validate_token_role-xxxxxxxxxxxxxxxxxxxx" -> null
    - policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" -> null
    - role       = "lambda_validate_token_role" -> null
}

# aws_iam_role_policy_attachment.lambda_invoke_attachment will be destroyed
- resource "aws_iam_role_policy_attachment" "lambda_invoke_attachment" {
    - id         = "CognitoUnAuthRole-xxxxxxxxxxxxxxxxxxxx" -> null
    - policy_arn = "arn:aws:iam::xxxxxxxxxxxx:policy/LambdaInvokePolicy" -> null
    - role       = "CognitoUnAuthRole" -> null
}

# aws_lambda_function.validate_token will be destroyed
- resource "aws_lambda_function" "validate_token" {
    - architectures                  = [
        - "x86_64",
    ] -> null
    - arn                            = "arn:aws:lambda:ap-southeast-2:xxxxxxxxxxxx:function:validateToken" -> null
    - code_sha256                    = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" -> null
    - filename                       = "function.zip" -> null
    - function_name                  = "validateToken" -> null
    - handler                        = "index.handler" -> null
    - id                             = "validateToken" -> null
    - invoke_arn                     = "arn:aws:apigateway:ap-southeast-2:lambda:path/2015-03-31/functions/arn:aws:lambda:ap-southeast-2:xxxxxxxxxxxx:function:validateToken/invocations" -> null
    - last_modified                  = "2024-12-24T11:37:09.868+0000" -> null
    - layers                         = [] -> null
    - memory_size                    = 128 -> null
    - package_type                   = "Zip" -> null
    - publish                        = false -> null
    - qualified_arn                  = "arn:aws:lambda:ap-southeast-2:xxxxxxxxxxxx:function:validateToken:$LATEST" -> null
    - qualified_invoke_arn           = "arn:aws:apigateway:ap-southeast-2:lambda:path/2015-03-31/functions/arn:aws:lambda:ap-southeast-2:xxxxxxxxxxxx:function:validateToken:$LATEST/invocations" -> null
    - reserved_concurrent_executions = -1 -> null
    - role                           = "arn:aws:iam::xxxxxxxxxxxx:role/lambda_validate_token_role" -> null
    - runtime                        = "nodejs18.x" -> null
    - skip_destroy                   = false -> null
    - source_code_hash               = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" -> null
    - source_code_size               = 891 -> null
    - tags                           = {} -> null
    - tags_all                       = {} -> null
    - timeout                        = 3 -> null
    - version                        = "$LATEST" -> null
    # (6 unchanged attributes hidden)

    - ephemeral_storage {
        - size = 512 -> null
    }

    - logging_config {
        - log_format            = "Text" -> null
        - log_group             = "/aws/lambda/validateToken" -> null
        # (2 unchanged attributes hidden)
    }

    - tracing_config {
        - mode = "PassThrough" -> null
    }
}

Plan: 0 to add, 0 to change, 8 to destroy.

Do you really want to destroy all resources?
Terraform will destroy all your managed infrastructure, as shown above.
There is no undo. Only 'yes' will be accepted to confirm.

Enter a value: yes

aws_iam_role_policy_attachment.lambda_invoke_attachment: Destroying... [id=CognitoUnAuthRole-xxxxxxxxxxxxxxxxxxxx]
aws_cognito_identity_pool_roles_attachment.identity_pool_roles: Destroying... [id=ap-southeast-2:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx]
aws_lambda_function.validate_token: Destroying... [id=validateToken]
aws_cognito_identity_pool_roles_attachment.identity_pool_roles: Destruction complete after 0s
aws_lambda_function.validate_token: Destruction complete after 1s
aws_iam_role_policy_attachment.lambda_basic: Destroying... [id=lambda_validate_token_role-xxxxxxxxxxxxxxxxxxxx]
aws_iam_role_policy_attachment.lambda_invoke_attachment: Destruction complete after 1s
aws_iam_policy.lambda_invoke_policy: Destroying... [id=arn:aws:iam::xxxxxxxxxxxx:policy/LambdaInvokePolicy]
aws_iam_role.cognito_unauth_role: Destroying... [id=CognitoUnAuthRole]
aws_iam_role_policy_attachment.lambda_basic: Destruction complete after 1s
aws_iam_role.lambda_validate_token_role: Destroying... [id=lambda_validate_token_role]
aws_iam_policy.lambda_invoke_policy: Destruction complete after 1s
aws_iam_role.lambda_validate_token_role: Destruction complete after 1s
aws_iam_role.cognito_unauth_role: Destruction complete after 2s
aws_cognito_identity_pool.identity_pool: Destroying... [id=ap-southeast-2:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx]
aws_cognito_identity_pool.identity_pool: Destruction complete after 1s

Destroy complete! Resources: 8 destroyed.

Part 5: Verification

Post-Destroy Verification

After running terraform destroy, verify resources are no longer accessible:

Function Not Found Error

Screenshot showing Lambda function is no longer accessible

Best Practices

  • Always review the plan before applying changes
  • Use version control for your Terraform configurations
  • Implement proper state management for team environments
  • Use variables for reusable configurations
  • Regularly backup your Terraform state files
  • Test your infrastructure code in a development environment first