Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
87191f0
lambda with dynamodb read-access
Karthikeyannhs May 6, 2025
cb09443
fixed handler name
Karthikeyannhs May 7, 2025
80abf68
kms for lambda & added tags for kms in dynamodb and lambda modules
Karthikeyannhs May 7, 2025
ac9a9bd
lint fixed for temporary app
Karthikeyannhs May 7, 2025
e8a0a6c
lambda logs integration, terraform format
Karthikeyannhs May 7, 2025
0909523
lambda runtime, timeout, memory_size & formatting
Karthikeyannhs May 7, 2025
89edf15
clean up
Karthikeyannhs May 7, 2025
8a434f9
lambda s3 policies
Karthikeyannhs May 7, 2025
32d31cd
lambda vpc integration
Karthikeyannhs May 8, 2025
418e10b
fix for state issue
Karthikeyannhs May 8, 2025
8173a28
fix for state issue in api-layer
Karthikeyannhs May 8, 2025
ef61e96
tf code cleanup
Karthikeyannhs May 8, 2025
f382f49
lambda zip output path changed
Karthikeyannhs May 8, 2025
4ba1c69
created variables for the lambda resource params
Karthikeyannhs May 8, 2025
090b65a
Replace dummy Lambda with real function; increase timeout to 30s for …
Karthikeyannhs May 9, 2025
784af81
give lambda role to decrypt kms key
Karthikeyannhs May 9, 2025
9922fa3
NACL restrictions relaxed: allow all outbound traffic from private su…
Karthikeyannhs May 9, 2025
68674bd
decoupled api layer from networking, passed req env var to lambda
Karthikeyannhs May 12, 2025
885db77
updated config to pick cred&URL by env
Karthikeyannhs May 12, 2025
028ce37
Merge branch 'main' into feature/ash-kt-eli-205-configure-provision-l…
Karthikeyannhs May 12, 2025
b306288
decoupled api-layer from iam-developer-roles
Karthikeyannhs May 12, 2025
c4c7e73
cleaned iam-developer roles - after api-layer decoupling
Karthikeyannhs May 12, 2025
ca192bb
Update src/eligibility_signposting_api/config.py
Karthikeyannhs May 13, 2025
e6b10d3
Update tests/unit/repos/test_factory.py
Karthikeyannhs May 13, 2025
8f0935a
Update tests/unit/repos/test_factory.py
Karthikeyannhs May 13, 2025
ccfb489
Update tests/unit/repos/test_factory.py
Karthikeyannhs May 13, 2025
d780ffa
Update tests/unit/repos/test_factory.py
Karthikeyannhs May 13, 2025
4479101
tf code cleanup - clearing out the TODOs
Karthikeyannhs May 12, 2025
4adc53b
config, factory test fixed
Karthikeyannhs May 13, 2025
23efa11
explained config and envs in README.md
Karthikeyannhs May 13, 2025
0a3b8fa
code cleanup
Karthikeyannhs May 13, 2025
595b234
added dynamodb:BatchWriteItem to external role
Karthikeyannhs May 13, 2025
54f059f
code formating
Karthikeyannhs May 13, 2025
90e414c
markdown fix
Karthikeyannhs May 13, 2025
cba485c
markdown fix
Karthikeyannhs May 13, 2025
adb247b
Merge branch 'main' into feature/ash-kt-eli-205-configure-provision-l…
Karthikeyannhs May 13, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 18 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ The software will only be used for signposting an individual to an appropriate s
- [Setup](#setup)
- [Prerequisites](#prerequisites)
- [Configuration](#configuration)
- [Environment variables](#environment-variables)
- [Environment variables - Local](#environment-variables---local)
- [Environment variables - DEV, PROD or PRE-PROD](#environment-variables---dev-prod-or-pre-prod)
- [Usage](#usage)
- [Testing](#testing)
- [Sandbox and Specification](#sandbox-and-specification)
Expand Down Expand Up @@ -67,18 +68,32 @@ The following software packages, or their equivalents, are expected to be instal

### Configuration

#### Environment variables
#### Environment variables - Local

| Variable | Default | Description |
|--------------------------|------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `AWS_ACCESS_KEY_ID` | `dummy_key` | AWS Access Key |
| `AWS_DEFAULT_REGION` | `eu-west-1` | AWS Region |
| `AWS_SECRET_ACCESS_KEY` | `dummy_secret` | AWS Secret Access Key |
| `DYNAMODB_ENDPOINT` | `http://localhost:4566` | Endpoint for the app to access DynamoDB |
| `S3_ENDPOINT` | `http://localhost:4566` | Endpoint for the app to access S3 |
| `ELIGIBILITY_TABLE_NAME` | `test_eligibility_datastore` | AWS DynamoDB table for person data. |
| `LOG_LEVEL` | `WARNING` | Logging level. Must be one of `DEBUG`, `INFO`, `WARNING`, `ERROR` or `CRITICAL` as per [Logging Levels](https://docs.python.org/3/library/logging.html#logging-levels) |
| `LOG_LEVEL` | `WARNING` | Logging level. Must be one of `DEBUG`, `INFO`, `WARNING`, `ERROR` or `CRITICAL` as per [Logging Levels](https://docs.python.org/3/library/logging.html#logging-levels) |
| `RULES_BUCKET_NAME` | `test-rules-bucket` | AWS S3 bucket from which to read rules. |

#### Environment variables - DEV, PROD or PRE-PROD

| Variable | Default | Description | Comments |
|--------------------------|------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------|
| `AWS_DEFAULT_REGION` | `eu-west-1` | AWS Region | |
| `AWS_ACCESS_KEY_ID` | None | AWS Access Key | **AWS_ACCESS_KEY_ID** is set to None, <br/>because it is provided by the AWS environment automatically. |
| `AWS_SECRET_ACCESS_KEY` | None | AWS Secret Access Key | **AWS_SECRET_ACCESS_KEY** is set to None, <br/>because it is provided by the AWS environment automatically. |
| `DYNAMODB_ENDPOINT` | None | Endpoint for the app to access DynamoDB | **DYNAMODB_ENDPOINT** are set to None, <br/>since we are using aws service default endpoints which are provided automatically. |
| `S3_ENDPOINT` | None | Endpoint for the app to access S3 | **S3_ENDPOINT** are set to None, <br/>since we are using aws service default endpoints which are provided automatically. |
| `ELIGIBILITY_TABLE_NAME` | `test_eligibility_datastore` | AWS DynamoDB table for person data. | |
| `LOG_LEVEL` | `WARNING` | Logging level. Must be one of `DEBUG`, `INFO`, `WARNING`, `ERROR` or `CRITICAL` as per [Logging Levels](https://docs.python.org/3/library/logging.html#logging-levels) | |
| `RULES_BUCKET_NAME` | `test-rules-bucket` | AWS S3 bucket from which to read rules. | |

## Usage

After a successful installation, provide an informative example of how this project can be used. Additional code snippets, screenshots and demos work well in this space. You may also link to the other documentation resources, e.g. the [User Guide](./docs/user-guide.md) to demonstrate more use cases and to show more features.
Expand Down
1 change: 1 addition & 0 deletions infrastructure/modules/dynamodb/kms.tf
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ resource "aws_kms_key" "dynamodb_cmk" {
deletion_window_in_days = 14
is_enabled = true
enable_key_rotation = true
tags = var.tags
}

resource "aws_kms_alias" "dynamodb_cmk" {
Expand Down
9 changes: 9 additions & 0 deletions infrastructure/modules/dynamodb/outputs.tf
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,12 @@ output "arn" {
output "table_name" {
value = aws_dynamodb_table.dynamodb_table.name
}

output "dynamodb_kms_key_arn" {
value = aws_kms_key.dynamodb_cmk.arn
}

output "dynamodb_kms_key_id" {
value = aws_kms_key.dynamodb_cmk.id
}

1 change: 1 addition & 0 deletions infrastructure/modules/lambda/default_variables.tf
12 changes: 12 additions & 0 deletions infrastructure/modules/lambda/kms.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
resource "aws_kms_key" "lambda_cmk" {
description = "${terraform.workspace == "default" ? "" : "${terraform.workspace}-"}${var.lambda_func_name} Master Key"
deletion_window_in_days = 14
is_enabled = true
enable_key_rotation = true
tags = var.tags
}

resource "aws_kms_alias" "lambda_cmk" {
name = "alias/${terraform.workspace == "default" ? "" : "${terraform.workspace}-"}${var.lambda_func_name}-cmk"
target_key_id = aws_kms_key.lambda_cmk.key_id
}
26 changes: 26 additions & 0 deletions infrastructure/modules/lambda/lambda.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
resource "aws_lambda_function" "eligibility_signposting_lambda" {
# If the file is not in the current working directory you will need to include a
# path.module in the filename.
filename = var.file_name
function_name = var.lambda_func_name
role = var.eligibility_lambda_role_arn
handler = var.handler

source_code_hash = filebase64sha256(var.file_name)

runtime = "python3.13"
timeout = 30
memory_size = 128 # Default

environment {
variables = {
ELIGIBILITY_TABLE_NAME = var.eligibility_status_table_name,
RULES_BUCKET_NAME = var.eligibility_rules_bucket_name,
ENV = var.environment
}
}
vpc_config {
subnet_ids = var.vpc_intra_subnets
security_group_ids = var.security_group_ids
}
}
6 changes: 6 additions & 0 deletions infrastructure/modules/lambda/outputs.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
output "aws_lambda_function_id" {
value = aws_lambda_function.eligibility_signposting_lambda.id
}
output "aws_lambda_function_arn" {
value = aws_lambda_function.eligibility_signposting_lambda.arn
}
45 changes: 45 additions & 0 deletions infrastructure/modules/lambda/variables.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
variable "workspace" {
description = "Usually the developer short code or the name of the environment."
type = string
}

variable "eligibility_lambda_role_arn" {
description = "lambda read role arn for dynamodb"
type = string
}

variable "lambda_func_name" {
description = "Name of the Lambda function"
type = string
}

variable "vpc_intra_subnets" {
description = "vpc private subnets for lambda"
type = list(string)
}

variable "security_group_ids" {
description = "security groups for lambda"
type = list(string)
}

variable "file_name" {
description = "path of the the zipped lambda"
type = string
}

variable "handler" {
description = "lambda handler name"
type = string
}

variable "eligibility_rules_bucket_name" {
description = "campaign config rules bucket name"
type = string
}

variable "eligibility_status_table_name" {
description = "eligibility datastore table name"
type = string
}

8 changes: 8 additions & 0 deletions infrastructure/modules/s3/outputs.tf
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,11 @@ output "storage_bucket_access_logs_arn" {
output "storage_bucket_access_logs_id" {
value = aws_s3_bucket.storage_bucket_access_logs.id
}

output "storage_bucket_arn" {
value = aws_s3_bucket.storage_bucket.arn
}

output "storage_bucket_name" {
value = aws_s3_bucket.storage_bucket.bucket
}
6 changes: 3 additions & 3 deletions infrastructure/stacks/_shared/locals.tf
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,13 @@ locals {
}

sso_role_patterns = {
dev = "AWSReservedSSO_vdselid_dev_*"
test = "AWSReservedSSO_vdselid_test_*"
dev = "AWSReservedSSO_vdselid_dev_*"
test = "AWSReservedSSO_vdselid_test_*"
preprod = "AWSReservedSSO_vdselid_preprod_*"
}

terraform_state_bucket_name = "eligibility-signposting-api-${var.environment}-tfstate"
terraform_state_bucket_arn = "arn:aws:s3:::eligibility-signposting-api-${var.environment}-tfstate"
terraform_state_bucket_arn = "arn:aws:s3:::eligibility-signposting-api-${var.environment}-tfstate"

account_ids = {
dev = "448049830832"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ bucket = "eligibility-signposting-api-dev-tfstate"
key = "tfstate/api-layer.tfstate"
region = "eu-west-2"
encrypt = true
use_lockfile = true
10 changes: 10 additions & 0 deletions infrastructure/stacks/api-layer/cloudwatch.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# CloudWatch Log Group for lambda Flow Logs
resource "aws_cloudwatch_log_group" "lambda_logs" {
name = "/aws/lambda/${module.eligibility_signposting_lambda_function.aws_lambda_function_id}"
retention_in_days = 14

tags = {
Name = "lambda-execution-logs"
Stack = local.stack_name
}
}
130 changes: 114 additions & 16 deletions infrastructure/stacks/api-layer/iam_policies.tf
Original file line number Diff line number Diff line change
@@ -1,33 +1,31 @@
# Read-only policy for DynamoDB
data "aws_iam_policy_document" "dynamodb_read_policy" {
data "aws_iam_policy_document" "dynamodb_read_policy_doc" {
statement {
actions = ["dynamodb:GetItem", "dynamodb:Query", "dynamodb:Scan"]
resources = [module.eligibility_status_table.arn]
}
}

# Attach dynamoDB read policy to Lambda role
resource "aws_iam_role_policy" "lambda_dynamodb_read_policy" {
name = "DynamoDBReadAccess"
role = aws_iam_role.eligibility_lambda_role.id
policy = data.aws_iam_policy_document.dynamodb_read_policy_doc.json
}

# Write-only policy for DynamoDB
data "aws_iam_policy_document" "dynamodb_write_policy" {
data "aws_iam_policy_document" "dynamodb_write_policy_doc" {
statement {
actions = ["dynamodb:PutItem", "dynamodb:UpdateItem", "dynamodb:DeleteItem"]
actions = ["dynamodb:PutItem", "dynamodb:UpdateItem", "dynamodb:DeleteItem", "dynamodb:BatchWriteItem"]
resources = [module.eligibility_status_table.arn]
}
}

# Attach read policy to Lambda role (only in IAM default workspace)
resource "aws_iam_role_policy" "lambda_read_policy" {
count = local.is_iam_owner ? 1 : 0
name = "DynamoDBReadAccess"
role = local.lambda_read_role
policy = data.aws_iam_policy_document.dynamodb_read_policy.json
}

# Attach write policy to external write role (only in IAM default workspace)
resource "aws_iam_role_policy" "external_write_policy" {
count = local.is_iam_owner ? 1 : 0
# Attach dynamoDB write policy to external write role
resource "aws_iam_role_policy" "external_dynamodb_write_policy" {
name = "DynamoDBWriteAccess"
role = local.write_access_role
policy = data.aws_iam_policy_document.dynamodb_write_policy.json
role = aws_iam_role.write_access_role.id
policy = data.aws_iam_policy_document.dynamodb_write_policy_doc.json
}


Expand Down Expand Up @@ -63,4 +61,104 @@ resource "aws_s3_bucket_policy" "storage_bucket_access_logs_policy" {
policy = data.aws_iam_policy_document.storage_bucket_access_logs_policy.json
}

# Policy doc for S3 Rules bucket
data "aws_iam_policy_document" "s3_rules_bucket_policy" {
statement {
sid = "AllowSSLRequestsOnly"
actions = [
"s3:GetObject",
"s3:ListBucket",
]
resources = [
module.s3_rules_bucket.storage_bucket_arn,
"${module.s3_rules_bucket.storage_bucket_arn}/*",
]
condition {
test = "Bool"
values = ["true"]
variable = "aws:SecureTransport"
}
}
}


# Attach s3 read policy to Lambda role
resource "aws_iam_role_policy" "lambda_s3_read_policy" {
name = "S3ReadAccess"
role = aws_iam_role.eligibility_lambda_role.id
policy = data.aws_iam_policy_document.s3_rules_bucket_policy.json
}

# Attach AWSLambdaVPCAccessExecutionRole to Lambda
resource "aws_iam_role_policy_attachment" "AWSLambdaVPCAccessExecutionRole" {
role = aws_iam_role.eligibility_lambda_role.id
policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole"
}

#Attach AWSLambdaBasicExecutionRole to Lambda
resource "aws_iam_role_policy_attachment" "lambda_logs_policy_attachment" {
policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
role = aws_iam_role.eligibility_lambda_role.name
}

# Policy doc for S3 Audit bucket
data "aws_iam_policy_document" "s3_audit_bucket_policy" {
statement {
sid = "AllowSSLRequestsOnly"
actions = ["s3:*"]
resources = [
module.s3_audit_bucket.storage_bucket_arn,
"${module.s3_audit_bucket.storage_bucket_arn}/*",
]
condition {
test = "Bool"
values = ["true"]
variable = "aws:SecureTransport"
}
}
}

# Attach s3 write policy to external write role
resource "aws_iam_role_policy" "external_s3_write_policy" {
name = "S3WriteAccess"
role = aws_iam_role.eligibility_lambda_role.id
policy = data.aws_iam_policy_document.s3_audit_bucket_policy.json
}

## KMS
data "aws_iam_policy_document" "kms_key_policy" {
statement {
sid = "EnableIamUserPermissions"
effect = "Allow"
principals {
type = "AWS"
identifiers = ["arn:aws:iam::${data.aws_caller_identity.current.account_id}:root"]
}
actions = ["kms:*"]
resources = ["*"]
}
statement {
sid = "Allow lambda role"
effect = "Allow"
principals {
type = "AWS"
identifiers = [
aws_iam_role.eligibility_lambda_role.arn
]
}
actions = [
"kms:Decrypt"
]
resources = [
module.eligibility_status_table.dynamodb_kms_key_arn
]
}
}

# attach kms decrypt policy kms key
resource "aws_kms_key_policy" "kms_key" {
key_id = module.eligibility_status_table.dynamodb_kms_key_id
policy = data.aws_iam_policy_document.kms_key_policy.json
}


Loading
Loading