Skip to content

Commit

Permalink
Deploy Lambda with Terraform
Browse files Browse the repository at this point in the history
Includes IAM permissions, X-Ray, function URL and records the environment deployment in GitHub.

For VEGA-1942 #minor
  • Loading branch information
gregtyler committed Oct 9, 2023
1 parent d0a8a6f commit 712433a
Show file tree
Hide file tree
Showing 15 changed files with 296 additions and 12 deletions.
22 changes: 11 additions & 11 deletions .github/workflows/env-deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@ on:
jobs:
terraform_environment_workflow:
runs-on: ubuntu-latest
# environment:
# name: ${{ inputs.workspace_name }} popup environment
# url: ${{ steps.terraform_outputs.outputs.url }}
environment:
name: ${{ inputs.workspace_name }} popup environment
url: ${{ steps.terraform_outputs.outputs.url }}
steps:
- uses: actions/checkout@v4
with:
Expand Down Expand Up @@ -73,11 +73,11 @@ jobs:
terraform apply -lock-timeout=300s -input=false -auto-approve -parallelism=30
working-directory: ./terraform/environment

# - name: Terraform Outputs
# id: terraform_outputs
# env:
# TF_WORKSPACE: ${{ inputs.workspace_name }}
# TF_VAR_app_version: ${{ inputs.version_tag }}
# run: |
# echo "url=$(terraform output -raw app_fqdn)" >> $GITHUB_OUTPUT
# working-directory: ./terraform/environment
- name: Terraform Outputs
id: terraform_outputs
env:
TF_WORKSPACE: ${{ inputs.workspace_name }}
TF_VAR_app_version: ${{ inputs.version_tag }}
run: |
echo "url=$(terraform output -raw lambda_url)" >> $GITHUB_OUTPUT
working-directory: ./terraform/environment
3 changes: 2 additions & 1 deletion terraform/environment/dynamodb.tf
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ resource "aws_dynamodb_table" "deeds_table" {
name = "deeds-${local.environment_name}"
billing_mode = "PAY_PER_REQUEST"
deletion_protection_enabled = local.environment.is_production
stream_enabled = false
stream_enabled = true
stream_view_type = "NEW_AND_OLD_IMAGES"
hash_key = "uid"

server_side_encryption {
Expand Down
4 changes: 4 additions & 0 deletions terraform/environment/outputs.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
output "lambda_url" {
description = "Public URL of 'create' Lambda function"
value = module.eu_west_1.lambda_url
}
72 changes: 72 additions & 0 deletions terraform/environment/region/kms.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
resource "aws_kms_key" "cloudwatch" {
description = "CloudWatch ${terraform.workspace} ${data.aws_region.current.name}"
deletion_window_in_days = 10
policy = data.aws_iam_policy_document.cloudwatch_kms.json
enable_key_rotation = true

provider = aws.region
}

resource "aws_kms_alias" "cloudwatch_standard_alias" {
name = "alias/cloudwatch-${var.environment_name}"
target_key_id = aws_kms_key.cloudwatch.key_id

provider = aws.region
}

data "aws_iam_policy_document" "cloudwatch_kms" {
statement {
sid = "Enable Root account permissions on Key"
effect = "Allow"
actions = ["kms:*"]
resources = ["*"]
principals {
type = "AWS"
identifiers = [
"arn:aws:iam::${data.aws_caller_identity.current.account_id}:root",
]
}
}
statement {
sid = "Allow Key to be used for Encryption"
effect = "Allow"
resources = ["*"]
actions = [
"kms:Encrypt",
"kms:Decrypt",
"kms:ReEncrypt*",
"kms:GenerateDataKey*",
"kms:DescribeKey",
]
principals {
type = "Service"
identifiers = ["logs.${data.aws_region.current.name}.amazonaws.com"]
}
}
statement {
sid = "Key Administrator"
effect = "Allow"
resources = ["*"]
actions = [
"kms:Create*",
"kms:Describe*",
"kms:Enable*",
"kms:List*",
"kms:Put*",
"kms:Update*",
"kms:Revoke*",
"kms:Disable*",
"kms:Get*",
"kms:Delete*",
"kms:TagResource",
"kms:UntagResource",
"kms:ScheduleKeyDeletion",
"kms:CancelKeyDeletion"
]

principals {
type = "AWS"
identifiers = ["arn:aws:iam::${data.aws_caller_identity.current.account_id}:role/breakglass"]
}
}
}
27 changes: 27 additions & 0 deletions terraform/environment/region/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
locals {
functions = toset([
"create",
"get",
"update",
])
}

module "lambda" {
for_each = local.functions
source = "../../modules/lambda"

environment_name = var.environment_name
lambda_name = each.key
ecr_image_uri = "${data.aws_ecr_repository.lambda[each.key].repository_url}:${var.app_version}"
cloudwatch_kms_key_id = aws_kms_key.cloudwatch.arn

providers = {
aws = aws.region
}
}

data "aws_ecr_repository" "lambda" {
for_each = local.functions
name = "lpa-store/lambda/api-${each.key}"
provider = aws.management
}
4 changes: 4 additions & 0 deletions terraform/environment/region/outputs.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
output "lambda_url" {
description = "Public URL of 'create' Lambda function"
value = module.lambda["create"].function_url
}
25 changes: 25 additions & 0 deletions terraform/environment/region/terraform.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
terraform {
required_version = ">= 1.4.0"

required_providers {
aws = {
source = "hashicorp/aws"
configuration_aliases = [
aws.region,
aws.management,
]
}
}
}

data "aws_region" "current" {
provider = aws.region
}

data "aws_caller_identity" "current" {
provider = aws.region
}

data "aws_default_tags" "current" {
provider = aws.region
}
9 changes: 9 additions & 0 deletions terraform/environment/region/variables.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
variable "environment_name" {
description = "The name of the environment the region is deployed to"
type = string
}

variable "app_version" {
description = "Version of application to deploy"
type = string
}
23 changes: 23 additions & 0 deletions terraform/environment/regions.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
module "eu_west_1" {
source = "./region"

environment_name = local.environment_name
app_version = var.app_version

providers = {
aws.region = aws.eu_west_1
aws.management = aws.management
}
}

module "eu_west_2" {
source = "./region"

environment_name = local.environment_name
app_version = var.app_version

providers = {
aws.region = aws.eu_west_2
aws.management = aws.management
}
}
6 changes: 6 additions & 0 deletions terraform/environment/variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,9 @@ variable "management_role" {
type = string
default = "lpa-store-ci"
}

variable "app_version" {
description = "Version of application to deploy"
type = string
default = "latest"
}
55 changes: 55 additions & 0 deletions terraform/modules/lambda/iam.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
resource "aws_iam_role" "lambda" {
name = "lambda-${var.lambda_name}-${var.environment_name}-${data.aws_region.current.name}"
assume_role_policy = data.aws_iam_policy_document.lambda_assume.json

lifecycle {
create_before_destroy = true
}
}

data "aws_iam_policy_document" "lambda_assume" {
statement {
actions = ["sts:AssumeRole"]

principals {
type = "Service"
identifiers = ["lambda.amazonaws.com"]
}
}
}

resource "aws_iam_role_policy_attachment" "aws_xray_write_only_access" {
role = aws_iam_role.lambda.name
policy_arn = "arn:aws:iam::aws:policy/AWSXrayWriteOnlyAccess"
}

resource "aws_iam_role_policy_attachment" "vpc_access_execution_role" {
role = aws_iam_role.lambda.name
policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole"
}

resource "aws_iam_role_policy" "lambda" {
name = "lambda"
role = aws_iam_role.lambda.id
policy = data.aws_iam_policy_document.lambda.json
}

data "aws_iam_policy_document" "lambda" {
statement {
sid = "allowLogging"
effect = "Allow"
resources = [aws_cloudwatch_log_group.lambda.arn]
actions = [
"logs:CreateLogStream",
"logs:PutLogEvents",
"logs:DescribeLogStreams"
]
}
}

resource "aws_lambda_permission" "allow_lambda_execution_operator" {
statement_id = "AllowExecutionOperator"
action = "lambda:InvokeFunction"
function_name = aws_lambda_function.main.function_name
principal = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:role/operator"
}
22 changes: 22 additions & 0 deletions terraform/modules/lambda/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
resource "aws_cloudwatch_log_group" "lambda" {
name = "/aws/lambda/${var.lambda_name}-${var.environment_name}"
kms_key_id = var.cloudwatch_kms_key_id
}

resource "aws_lambda_function" "main" {
function_name = "${var.lambda_name}-${var.environment_name}"
image_uri = var.ecr_image_uri
package_type = "Image"
role = aws_iam_role.lambda.arn
timeout = 5
depends_on = [aws_cloudwatch_log_group.lambda]

tracing_config {
mode = "Active"
}
}

resource "aws_lambda_function_url" "main" {
function_name = aws_lambda_function.main.function_name
authorization_type = "NONE"
}
4 changes: 4 additions & 0 deletions terraform/modules/lambda/outputs.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
output "function_url" {
description = "Public URL of Lambda function"
value = aws_lambda_function_url.main.function_url
}
13 changes: 13 additions & 0 deletions terraform/modules/lambda/terraform.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
terraform {
required_version = ">= 1.4.0"

required_providers {
aws = {
source = "hashicorp/aws"
}
}
}

data "aws_region" "current" {}

data "aws_caller_identity" "current" {}
19 changes: 19 additions & 0 deletions terraform/modules/lambda/variables.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
variable "environment_name" {
description = "The name of the environment the lambda is deployed to"
type = string
}

variable "lambda_name" {
description = "The name of the lambda function"
type = string
}

variable "ecr_image_uri" {
description = "The URI of the image lambda should use"
type = string
}

variable "cloudwatch_kms_key_id" {
description = "KMS key used to encrypt CloudWatch logs"
type = string
}

0 comments on commit 712433a

Please sign in to comment.