In this section, We'll setup the building blocks for the backend infrastructure for the website using AWS services and Terraform.
1: Setting Up Terraform and AWS Provider
To begin, Terraform must be installed and configured on your machine.
otherwise, follow this link ---- Install Terraform | Terraform | HashiCorp Developer
Then, create a file named main.tf
and copy the following Terraform configuration into it:
This configuration sets up the required provider and specifies the AWS region for your resources.
Ok so, let's break down the Terraform code into simpler terms, explaining each part:
Terraform Configuration:
- Specifies the required Terraform version and AWS provider version.
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 4.0"
}
}
required_version = ">= 1.2.0"
}
provider "aws" {
AWS Provider Configuration:
- Specifies the AWS region where resources will be provisioned.
region = "eu-west-2"
# profile = "default"
}
IAM Role for Lambda Function:
Defines an IAM role allowing Lambda functions to be assumed, essential for their execution.
# IAM Role for Lambda function resource "aws_iam_role" "lambda_role" { name = "terraform_lambda_func_Role" assume_role_policy = jsonencode( { "Version" : "2012-10-17", "Statement" : [ { "Action" : "sts:AssumeRole", "Principal" : { "Service" : "lambda.amazonaws.com" }, "Effect" : "Allow", "Sid" : "" } ] }) }
IAM Policy for Lambda Function:
Defines an IAM policy detailing permissions required by the Lambda function, such as accessing CloudWatch logs and DynamoDB.
# IAM Policy for Lambda function resource "aws_iam_policy" "iam_policy_for_lambda" { name = "aws_iam_policy_for_terraform_lambda_func_role" path = "/" description = "AWS IAM Policy for managing aws lambda role" policy = jsonencode( { "Version" : "2012-10-17", "Statement" : [ { "Action" : [ "logs:CreateLogGroup", "logs:CreateLogStream", "logs:PutLogEvents" ], "Resource" : "arn:aws:logs:*:*:*", "Effect" : "Allow" }, { "Effect" : "Allow", "Action" : [ "dynamodb:UpdateItem", "dynamodb:GetItem", "dynamodb:PutItem" ], "Resource" : "arn:aws:dynamodb:eu-west-2:662406500657:table/visitor_count_ddb" }, ] }) }
Attachment of IAM Policy to IAM Role:
Attaches the previously defined IAM policy to the IAM role created for Lambda functions.
# Attach IAM Policy to IAM Role resource "aws_iam_role_policy_attachment" "attach_iam_policy_to_iam_role" { role = aws_iam_role.lambda_role.name policy_arn = aws_iam_policy.iam_policy_for_lambda.arn
IAM Role for Lambda Function:
Defines an IAM role allowing Lambda functions to be assumed, essential for their execution. While IAM Policy for Lambda Function defines an IAM policy detailing permissions required by the Lambda function, such as accessing CloudWatch logs and DynamoDB.
# Archive Python code into a zip file data "archive_file" "zip_the_python_code" { type = "zip" source_dir = "${path.module}/lambda/" output_path = "${path.module}/lambda/lambda_function.zip" } # Lambda Function resource "aws_lambda_function" "terraform_lambda_func" { filename = "${path.module}/lambda/lambda_function.zip" function_name = "terraform_lambda_func" role = aws_iam_role.lambda_role.arn handler = "lambda_function.lambda_handler" runtime = "python3.8" depends_on = [aws_iam_role_policy_attachment.attach_iam_policy_to_iam_role] environment { variables = { databaseName = "visitor_count_ddb" } } }
Archiving Python Code & Function Creation:
Archives Python code into a zip file, which will be uploaded to Lambda.
Lambda Function Creation:
Creates a Lambda function using the archived Python code.
Specifies the function's role, handler, runtime, and environment variables.
# Archive Python code into a zip file data "archive_file" "zip_the_python_code" { type = "zip" source_dir = "${path.module}/lambda/" output_path = "${path.module}/lambda/lambda_function.zip" }. # Lambda Function resource "aws_lambda_function" "terraform_lambda_func" { filename = "${path.module}/lambda/lambda_function.zip" function_name = "terraform_lambda_func" role = aws_iam_role.lambda_role.arn handler = "lambda_function.lambda_handler" runtime = "python3.8" depends_on = [aws_iam_role_policy_attachment.attach_iam_policy_to_iam_role] environment { variables = { databaseName = "visitor_count_ddb" } }
CloudWatch Log Group & API Gateway Creation:
Creates a CloudWatch log group for storing logs generated by the Lambda function.
API Gateway API Creation:
Creates an API Gateway API for triggering Lambda functions.
Specifies CORS configuration to allow cross-origin requests as discussed earlier.
# CloudWatch Log Group resource "aws_cloudwatch_log_group" "api_gw" { name = "visitor_count_log_group" retention_in_days = 30 } # API Gateway API resource "aws_apigatewayv2_api" "lambda" { name = "visitor_count_CRC" protocol_type = "HTTP" description = "Visitor count for Cloud Resume Challenge" cors_configuration { allow_origins = ["https://devtej.com"] } }
API Gateway Stage Creation:
Creates a stage for the API Gateway API, enabling deployment and access logging.
API Gateway Integration Creation:
Defines an integration between the API Gateway and the Lambda function, specifying the integration type and method.
# API Gateway Stage resource "aws_apigatewayv2_stage" "lambda" { api_id = aws_apigatewayv2_api.lambda.id name = "default" auto_deploy = true access_log_settings { destination_arn = aws_cloudwatch_log_group.api_gw.arn format = jsonencode({ requestId = "$context.requestId" sourceIp = "$context.identity.sourceIp" requestTime = "$context.requestTime" protocol = "$context.protocol" httpMethod = "$context.httpMethod" resourcePath = "$context.resourcePath" routeKey = "$context.routeKey" status = "$context.status" responseLength = "$context.responseLength" integrationErrorMessage = "$context.integrationErrorMessage" }) } tags = { Name = "Cloud Resume Challenge" } } # API Gateway Integration resource "aws_apigatewayv2_integration" "terraform_lambda_func" { api_id = aws_apigatewayv2_api.lambda.id integration_uri = aws_lambda_function.terraform_lambda_func.invoke_arn integration_type = "AWS_PROXY" integration_method = "POST" }
API Gateway Route Creation:
Creates a route in the API Gateway, associating it with the previously defined integration.
Lambda Permission for API Gateway:
Grants permission to the API Gateway to invoke the Lambda function.
# API Gateway Route resource "aws_apigatewayv2_route" "terraform_lambda_func" { api_id = aws_apigatewayv2_api.lambda.id route_key = "ANY /terraform_lambda_func" target = "integrations/${aws_apigatewayv2_integration.terraform_lambda_func.id}" } # Lambda Permission for API Gateway resource "aws_lambda_permission" "api_gw" { statement_id = "AllowExecutionFromAPIGateway" action = "lambda:InvokeFunction" function_name = aws_lambda_function.terraform_lambda_func.function_name principal = "apigateway.amazonaws.com" source_arn = "${aws_apigatewayv2_api.lambda.execution_arn}/*/*" }
DynamoDB Table Creation:
Creates a DynamoDB table for storing visitor count data.
Specifies the table's attributes, billing mode, and optional global secondary index.
DynamoDB Table Item Creation:
Adds an initial item to the DynamoDB table, setting the visitor count to 1.
# DynamoDB Table resource "aws_dynamodb_table" "visitor_count_ddb" { name = "visitor_count_ddb" billing_mode = "PAY_PER_REQUEST" hash_key = "id" attribute { name = "id" type = "S" } attribute { name = "visitor_count" type = "N" } global_secondary_index { name = "visitor_count_index" hash_key = "visitor_count" projection_type = "ALL" read_capacity = 10 write_capacity = 10 } tags = { Name = "Cloud Resume Challenge" } } # DynamoDB Table Item resource "aws_dynamodb_table_item" "visitor_count_ddb" { table_name = aws_dynamodb_table.visitor_count_ddb.name hash_key = aws_dynamodb_table.visitor_count_ddb.hash_key item = <<ITEM { "id": {"S": "Visits"}, "visitor_count": {"N": "1"} } ITEM lifecycle { ignore_changes = all } }
Creates a DynamoDB table for storing visitor count data.
Specifies the table's attributes, billing mode, and optional global secondary index.
DynamoDB Table Item Creation:
Adds an initial item to the DynamoDB table, setting the visitor count to 1.
2: Deployment
To guarantee the effective deployment of our Terraform backend infrastructure. We'll be using the following terraform commands;
terraform init, terraform plan, and terraform applyTerraform Init: The
terraform init
command ensures that your Terraform environment is properly configured and ready to use for planning, applying, and managing your infrastructure as code. It downloads and installs the necessary plugins of the provider and sets up the configuration for the backend.Terraform Plan: The
terraform plan
command examines the configuration files in your working directory and determines the changes that need to be made to achieve the desired state of your infrastructure. The plan gives an overview of the actions that Terraform will take to achieve the desired state, such as creating, modifying, or destroying resources. It assists in validating our configuration and comprehending the consequences of the alterations prior to implementation.Terraform Apply: The
terraform apply
command allows you to enact the changes defined in your Terraform configuration files, allowing you to provision and manage your infrastructure in a consistent and reproducible manner.
We can make sure that our Terraform backend infrastructure is correctly initialised, validated, and deployed by implementing these deployment processes into our workflow. By automating the deployment process, we can improve its consistency, dependability, and efficiency.