mirror of
https://github.com/Sea-Haven-Industries/afi-backup-monitor.git
synced 2026-09-30 07:13:11 +00:00
Merge pull request #47 from Sea-Haven-Industries/feature/hcp-terraform-migration
feat(iac): add hcp terraform config for prod migration (PLAT-56)
This commit is contained in:
commit
c35896e970
15 changed files with 421 additions and 34 deletions
32
.github/workflows/ci-terraform.yaml
vendored
Normal file
32
.github/workflows/ci-terraform.yaml
vendored
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
name: Terraform CI
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
branches: [main]
|
||||||
|
paths:
|
||||||
|
- "terraform/**"
|
||||||
|
- ".github/workflows/ci-terraform.yaml"
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
terraform:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
defaults:
|
||||||
|
run:
|
||||||
|
working-directory: terraform
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
|
||||||
|
|
||||||
|
- uses: hashicorp/setup-terraform@a1502cd9e758c50496cc9ac5308c4843bcd56d36 # v3.0.0
|
||||||
|
with:
|
||||||
|
terraform_version: "1.9.8"
|
||||||
|
|
||||||
|
- name: Terraform fmt
|
||||||
|
run: terraform fmt -check -recursive
|
||||||
|
|
||||||
|
- name: Terraform init
|
||||||
|
run: terraform init -backend=false
|
||||||
|
|
||||||
|
- name: Terraform validate
|
||||||
|
run: terraform validate
|
||||||
22
.github/workflows/deploy.yaml
vendored
22
.github/workflows/deploy.yaml
vendored
|
|
@ -1,22 +0,0 @@
|
||||||
name: Deploy
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: [main]
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
id-token: write
|
|
||||||
contents: read
|
|
||||||
|
|
||||||
concurrency:
|
|
||||||
group: deploy
|
|
||||||
cancel-in-progress: false
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
deploy:
|
|
||||||
uses: Sea-Haven-Industries/.github/.github/workflows/cd-sam.yaml@3f746774229d41770727e2e4fd63ed5f5555a8b3 # v1.0.3
|
|
||||||
with:
|
|
||||||
stack-name: afi-backup-monitor
|
|
||||||
cfn-role-arn: arn:aws:iam::328440206208:role/github-cfn-execution-role
|
|
||||||
secrets:
|
|
||||||
deploy-role-arn: ${{ secrets.AWS_DEPLOY_ROLE_ARN }}
|
|
||||||
parameter-overrides: ${{ secrets.SAM_PARAMETER_OVERRIDES }}
|
|
||||||
6
.gitignore
vendored
6
.gitignore
vendored
|
|
@ -3,3 +3,9 @@ __pycache__/
|
||||||
*.pyc
|
*.pyc
|
||||||
.env
|
.env
|
||||||
samconfig.toml
|
samconfig.toml
|
||||||
|
terraform/build/
|
||||||
|
terraform/.terraform/
|
||||||
|
*.tfvars
|
||||||
|
!terraform/*.tfvars.example
|
||||||
|
.terraform.tfstate*
|
||||||
|
crash.log
|
||||||
|
|
|
||||||
25
README.md
25
README.md
|
|
@ -2,10 +2,12 @@
|
||||||
|
|
||||||

|

|
||||||

|

|
||||||

|

|
||||||

|

|
||||||
|
|
||||||
AWS SAM stack with two Lambda functions that integrate the Afi.ai backup API with Slack.
|
HCP Terraform stack with two Lambda functions that integrate the Afi.ai backup API with Slack. Deployed to **seahaven-prod** via workspace `afi-backup-monitor-prod` (PLAT-56).
|
||||||
|
|
||||||
|
`template.yaml` remains as the historical SAM reference for the mgmt stack during cutover; deploy path is HCP Terraform only.
|
||||||
|
|
||||||
## Functions
|
## Functions
|
||||||
|
|
||||||
|
|
@ -16,14 +18,17 @@ AWS SAM stack with two Lambda functions that integrate the Afi.ai backup API wit
|
||||||
## Architecture
|
## Architecture
|
||||||
|
|
||||||
- **Runtime:** Python 3.12 (arm64)
|
- **Runtime:** Python 3.12 (arm64)
|
||||||
|
- **IaC:** Terraform under `terraform/` (HCP remote apply, Manual until sealed)
|
||||||
- **Shared Layer:** Afi API client + Slack webhook helper
|
- **Shared Layer:** Afi API client + Slack webhook helper
|
||||||
- **Secrets:** Afi API key and Slack webhook URL stored in AWS Secrets Manager
|
- **Secrets:** Afi API key and Slack webhook URL in Secrets Manager (ARNs as Terraform variables; values never in state)
|
||||||
- **Scheduling:** EventBridge cron rules (default: Mondays 10am ET)
|
- **Scheduling:** EventBridge cron rules (default: Mondays 10am ET)
|
||||||
|
- **IAM:** Execution roles under path `/tf-managed/` with `seahaven-lambda-execution-boundary`
|
||||||
|
|
||||||
## Repository Structure
|
## Repository Structure
|
||||||
|
|
||||||
```
|
```
|
||||||
template.yaml # SAM stack: Lambdas, shared layer, IAM, EventBridge schedules
|
terraform/ # HCP Terraform config (sole deploy path)
|
||||||
|
template.yaml # Historical SAM reference (mgmt cutover / rollback)
|
||||||
src/
|
src/
|
||||||
auto_protect/app.py # afi-auto-protect handler
|
auto_protect/app.py # afi-auto-protect handler
|
||||||
health_digest/app.py # afi-health-digest handler
|
health_digest/app.py # afi-health-digest handler
|
||||||
|
|
@ -32,8 +37,7 @@ src/
|
||||||
slack.py # Slack webhook helper
|
slack.py # Slack webhook helper
|
||||||
```
|
```
|
||||||
|
|
||||||
Deployment parameters (secret ARNs, tenant ID, policy ID, schedules) are defined in
|
Workspace Terraform variables: secret ARNs, tenant ID, policy ID (see `terraform/terraform.tfvars.example`).
|
||||||
`template.yaml` and supplied at deploy time via `samconfig.toml`.
|
|
||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
|
|
||||||
|
|
@ -43,18 +47,15 @@ The canonical map of Sea Haven's AWS infrastructure lives in Confluence. This pr
|
||||||
|
|
||||||
## Setup
|
## Setup
|
||||||
|
|
||||||
1. Store the Afi API key and Slack webhook URL in Secrets Manager:
|
1. Create secrets in seahaven-prod (exact ARNs are wired into the Lambda boundary and Terraform variables):
|
||||||
```bash
|
```bash
|
||||||
aws secretsmanager create-secret --name afi-api-key --secret-string "appkey-YOUR_KEY"
|
aws secretsmanager create-secret --name afi-api-key --secret-string "appkey-YOUR_KEY"
|
||||||
aws secretsmanager create-secret --name afi-slack-webhook --secret-string "https://hooks.slack.com/services/YOUR/WEBHOOK/URL"
|
aws secretsmanager create-secret --name afi-slack-webhook --secret-string "https://hooks.slack.com/services/YOUR/WEBHOOK/URL"
|
||||||
```
|
```
|
||||||
|
|
||||||
2. Update `samconfig.toml` with your Secret ARNs (`AfiApiKeySecretArn`, `SlackWebhookSecretArn`), Tenant ID, and Policy ID.
|
2. Set workspace variables in HCP (`afi-backup-monitor-prod`) from `terraform/terraform.tfvars.example`.
|
||||||
|
|
||||||
3. Build and deploy:
|
3. Apply from the HCP workspace (Manual apply until the stack is sealed). Do not use local `terraform apply` against prod.
|
||||||
```bash
|
|
||||||
sam build && sam deploy
|
|
||||||
```
|
|
||||||
|
|
||||||
## Manual Testing
|
## Manual Testing
|
||||||
|
|
||||||
|
|
|
||||||
46
terraform/.terraform.lock.hcl
generated
Normal file
46
terraform/.terraform.lock.hcl
generated
Normal file
|
|
@ -0,0 +1,46 @@
|
||||||
|
# This file is maintained automatically by "terraform init".
|
||||||
|
# Manual edits may be lost in future updates.
|
||||||
|
|
||||||
|
provider "registry.terraform.io/hashicorp/archive" {
|
||||||
|
version = "2.8.0"
|
||||||
|
constraints = "~> 2.0"
|
||||||
|
hashes = [
|
||||||
|
"h1:WB6H5ksIZiyq1lQlD/PWeh+tn4FLsbSjVnRW3+4xe2Y=",
|
||||||
|
"zh:0d14713fdc259fb377d0b899ad3c650a34194bd52194c863303ef22a65a580e2",
|
||||||
|
"zh:369b56040c7a8085d04e7e8ffac1e2b321a3170e502f788819bc34b868ec016f",
|
||||||
|
"zh:4d1a3b983ed6af5a52bfe12794674ae55cbadfa6021b37106ade68b433ad216a",
|
||||||
|
"zh:5c547549e26e083573c78a966ca68ce6d7df6bb8f3948f66a575f07da46b74ea",
|
||||||
|
"zh:6de093e62a975eb19a5e3017ce38e6e3cb639c17b79648d2000e0a8348f0e997",
|
||||||
|
"zh:7267936c2cdbc448efeb594d73e6b56a53d6a7ae14fe88cdd2a4133adc3302f0",
|
||||||
|
"zh:7482f023050ed426b4b45116e1761643bc33b1fd4ce4a6fab207ae2571f35940",
|
||||||
|
"zh:76bbd93b234e5a2927d98b511d86565700f549b570871a194c35f944b96cefb7",
|
||||||
|
"zh:78d5eefdd9e494defcb3c68d282b8f96630502cac21d1ea161f53cfe9bb483b3",
|
||||||
|
"zh:c6afc4bc1f002bac9c173007dd4da05fde788cd14c2916089f958c33fedb0dfa",
|
||||||
|
"zh:d3ba40bd806a3a08e9237dece679193c99afb2085de6b45d7f5d1f673cfcd368",
|
||||||
|
"zh:e1ad7ded53ecd6f0e5b473a3b44eae2b2e885653a56050ab583d387332be02e4",
|
||||||
|
"zh:e93e78575ce82be6084cc153c24ba8f385dc8d6880888ee66e918460c870953d",
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
provider "registry.terraform.io/hashicorp/aws" {
|
||||||
|
version = "5.100.0"
|
||||||
|
constraints = "~> 5.0"
|
||||||
|
hashes = [
|
||||||
|
"h1:Ijt7pOlB7Tr7maGQIqtsLFbl7pSMIj06TVdkoSBcYOw=",
|
||||||
|
"zh:054b8dd49f0549c9a7cc27d159e45327b7b65cf404da5e5a20da154b90b8a644",
|
||||||
|
"zh:0b97bf8d5e03d15d83cc40b0530a1f84b459354939ba6f135a0086c20ebbe6b2",
|
||||||
|
"zh:1589a2266af699cbd5d80737a0fe02e54ec9cf2ca54e7e00ac51c7359056f274",
|
||||||
|
"zh:6330766f1d85f01ae6ea90d1b214b8b74cc8c1badc4696b165b36ddd4cc15f7b",
|
||||||
|
"zh:7c8c2e30d8e55291b86fcb64bdf6c25489d538688545eb48fd74ad622e5d3862",
|
||||||
|
"zh:99b1003bd9bd32ee323544da897148f46a527f622dc3971af63ea3e251596342",
|
||||||
|
"zh:9b12af85486a96aedd8d7984b0ff811a4b42e3d88dad1a3fb4c0b580d04fa425",
|
||||||
|
"zh:9f8b909d3ec50ade83c8062290378b1ec553edef6a447c56dadc01a99f4eaa93",
|
||||||
|
"zh:aaef921ff9aabaf8b1869a86d692ebd24fbd4e12c21205034bb679b9caf883a2",
|
||||||
|
"zh:ac882313207aba00dd5a76dbd572a0ddc818bb9cbf5c9d61b28fe30efaec951e",
|
||||||
|
"zh:bb64e8aff37becab373a1a0cc1080990785304141af42ed6aa3dd4913b000421",
|
||||||
|
"zh:dfe495f6621df5540d9c92ad40b8067376350b005c637ea6efac5dc15028add4",
|
||||||
|
"zh:f0ddf0eaf052766cfe09dea8200a946519f653c384ab4336e2a4a64fdd6310e9",
|
||||||
|
"zh:f1b7e684f4c7ae1eed272b6de7d2049bb87a0275cb04dbb7cda6636f600699c9",
|
||||||
|
"zh:ff461571e3f233699bf690db319dfe46aec75e58726636a0d97dd9ac6e32fb70",
|
||||||
|
]
|
||||||
|
}
|
||||||
37
terraform/artifacts.tf
Normal file
37
terraform/artifacts.tf
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
# HCP plan and apply run on separate workers. archive_file paths from plan are
|
||||||
|
# not on the apply worker, so zip bytes are carried in the plan via
|
||||||
|
# content_base64 and uploaded to S3 at apply time for Lambda to consume.
|
||||||
|
|
||||||
|
resource "aws_s3_bucket" "artifacts" {
|
||||||
|
bucket = "afi-backup-monitor-artifacts-${local.account_id}"
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_s3_bucket_public_access_block" "artifacts" {
|
||||||
|
bucket = aws_s3_bucket.artifacts.id
|
||||||
|
|
||||||
|
block_public_acls = true
|
||||||
|
block_public_policy = true
|
||||||
|
ignore_public_acls = true
|
||||||
|
restrict_public_buckets = true
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_s3_object" "shared_layer" {
|
||||||
|
bucket = aws_s3_bucket.artifacts.id
|
||||||
|
key = "afi-shared-layer.zip"
|
||||||
|
content_base64 = filebase64(data.archive_file.shared_layer.output_path)
|
||||||
|
source_hash = data.archive_file.shared_layer.output_base64sha256
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_s3_object" "auto_protect" {
|
||||||
|
bucket = aws_s3_bucket.artifacts.id
|
||||||
|
key = "afi-auto-protect.zip"
|
||||||
|
content_base64 = filebase64(data.archive_file.auto_protect.output_path)
|
||||||
|
source_hash = data.archive_file.auto_protect.output_base64sha256
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_s3_object" "health_digest" {
|
||||||
|
bucket = aws_s3_bucket.artifacts.id
|
||||||
|
key = "afi-health-digest.zip"
|
||||||
|
content_base64 = filebase64(data.archive_file.health_digest.output_path)
|
||||||
|
source_hash = data.archive_file.health_digest.output_base64sha256
|
||||||
|
}
|
||||||
41
terraform/events.tf
Normal file
41
terraform/events.tf
Normal file
|
|
@ -0,0 +1,41 @@
|
||||||
|
resource "aws_cloudwatch_event_rule" "auto_protect" {
|
||||||
|
name = "afi-auto-protect-weekly"
|
||||||
|
description = "Weekly check for unprotected users"
|
||||||
|
schedule_expression = var.auto_protect_schedule
|
||||||
|
state = "ENABLED"
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_cloudwatch_event_target" "auto_protect" {
|
||||||
|
rule = aws_cloudwatch_event_rule.auto_protect.name
|
||||||
|
target_id = "afi-auto-protect"
|
||||||
|
arn = aws_lambda_function.auto_protect.arn
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_lambda_permission" "auto_protect_events" {
|
||||||
|
statement_id = "AllowExecutionFromEventBridge"
|
||||||
|
action = "lambda:InvokeFunction"
|
||||||
|
function_name = aws_lambda_function.auto_protect.function_name
|
||||||
|
principal = "events.amazonaws.com"
|
||||||
|
source_arn = aws_cloudwatch_event_rule.auto_protect.arn
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_cloudwatch_event_rule" "health_digest" {
|
||||||
|
name = "afi-health-digest-weekly"
|
||||||
|
description = "Weekly backup health digest to Slack"
|
||||||
|
schedule_expression = var.health_digest_schedule
|
||||||
|
state = "ENABLED"
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_cloudwatch_event_target" "health_digest" {
|
||||||
|
rule = aws_cloudwatch_event_rule.health_digest.name
|
||||||
|
target_id = "afi-health-digest"
|
||||||
|
arn = aws_lambda_function.health_digest.arn
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_lambda_permission" "health_digest_events" {
|
||||||
|
statement_id = "AllowExecutionFromEventBridge"
|
||||||
|
action = "lambda:InvokeFunction"
|
||||||
|
function_name = aws_lambda_function.health_digest.function_name
|
||||||
|
principal = "events.amazonaws.com"
|
||||||
|
source_arn = aws_cloudwatch_event_rule.health_digest.arn
|
||||||
|
}
|
||||||
47
terraform/iam.tf
Normal file
47
terraform/iam.tf
Normal file
|
|
@ -0,0 +1,47 @@
|
||||||
|
data "aws_iam_policy_document" "lambda_assume" {
|
||||||
|
statement {
|
||||||
|
effect = "Allow"
|
||||||
|
actions = ["sts:AssumeRole"]
|
||||||
|
|
||||||
|
principals {
|
||||||
|
type = "Service"
|
||||||
|
identifiers = ["lambda.amazonaws.com"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_iam_role" "auto_protect" {
|
||||||
|
name = "afi-auto-protect"
|
||||||
|
path = "/tf-managed/"
|
||||||
|
assume_role_policy = data.aws_iam_policy_document.lambda_assume.json
|
||||||
|
permissions_boundary = local.boundary_arn
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_iam_role_policy_attachment" "auto_protect_basic" {
|
||||||
|
role = aws_iam_role.auto_protect.name
|
||||||
|
policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_iam_role_policy" "auto_protect_secrets" {
|
||||||
|
name = "secretsmanager-get"
|
||||||
|
role = aws_iam_role.auto_protect.id
|
||||||
|
policy = local.secrets_policy_json
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_iam_role" "health_digest" {
|
||||||
|
name = "afi-health-digest"
|
||||||
|
path = "/tf-managed/"
|
||||||
|
assume_role_policy = data.aws_iam_policy_document.lambda_assume.json
|
||||||
|
permissions_boundary = local.boundary_arn
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_iam_role_policy_attachment" "health_digest_basic" {
|
||||||
|
role = aws_iam_role.health_digest.name
|
||||||
|
policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_iam_role_policy" "health_digest_secrets" {
|
||||||
|
name = "secretsmanager-get"
|
||||||
|
role = aws_iam_role.health_digest.id
|
||||||
|
policy = local.secrets_policy_json
|
||||||
|
}
|
||||||
85
terraform/lambda.tf
Normal file
85
terraform/lambda.tf
Normal file
|
|
@ -0,0 +1,85 @@
|
||||||
|
data "archive_file" "shared_layer" {
|
||||||
|
type = "zip"
|
||||||
|
source_dir = "${path.module}/../src/shared"
|
||||||
|
output_path = "${path.module}/build/afi-shared-layer.zip"
|
||||||
|
}
|
||||||
|
|
||||||
|
data "archive_file" "auto_protect" {
|
||||||
|
type = "zip"
|
||||||
|
source_dir = "${path.module}/../src/auto_protect"
|
||||||
|
output_path = "${path.module}/build/afi-auto-protect.zip"
|
||||||
|
excludes = ["requirements.txt"]
|
||||||
|
}
|
||||||
|
|
||||||
|
data "archive_file" "health_digest" {
|
||||||
|
type = "zip"
|
||||||
|
source_dir = "${path.module}/../src/health_digest"
|
||||||
|
output_path = "${path.module}/build/afi-health-digest.zip"
|
||||||
|
excludes = ["requirements.txt"]
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_lambda_layer_version" "shared" {
|
||||||
|
layer_name = "afi-shared"
|
||||||
|
description = "Shared Afi API client and utilities"
|
||||||
|
s3_bucket = aws_s3_bucket.artifacts.id
|
||||||
|
s3_key = aws_s3_object.shared_layer.key
|
||||||
|
source_code_hash = data.archive_file.shared_layer.output_base64sha256
|
||||||
|
compatible_runtimes = ["python3.12"]
|
||||||
|
compatible_architectures = ["arm64"]
|
||||||
|
|
||||||
|
depends_on = [aws_s3_object.shared_layer]
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_lambda_function" "auto_protect" {
|
||||||
|
function_name = "afi-auto-protect"
|
||||||
|
role = aws_iam_role.auto_protect.arn
|
||||||
|
handler = "app.handler"
|
||||||
|
runtime = "python3.12"
|
||||||
|
architectures = ["arm64"]
|
||||||
|
memory_size = 256
|
||||||
|
timeout = 120
|
||||||
|
|
||||||
|
s3_bucket = aws_s3_bucket.artifacts.id
|
||||||
|
s3_key = aws_s3_object.auto_protect.key
|
||||||
|
source_code_hash = data.archive_file.auto_protect.output_base64sha256
|
||||||
|
|
||||||
|
layers = [aws_lambda_layer_version.shared.arn]
|
||||||
|
|
||||||
|
environment {
|
||||||
|
variables = merge(local.common_env, {
|
||||||
|
AFI_POLICY_ID = var.afi_policy_id
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
depends_on = [
|
||||||
|
aws_s3_object.auto_protect,
|
||||||
|
aws_iam_role_policy_attachment.auto_protect_basic,
|
||||||
|
aws_iam_role_policy.auto_protect_secrets,
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_lambda_function" "health_digest" {
|
||||||
|
function_name = "afi-health-digest"
|
||||||
|
role = aws_iam_role.health_digest.arn
|
||||||
|
handler = "app.handler"
|
||||||
|
runtime = "python3.12"
|
||||||
|
architectures = ["arm64"]
|
||||||
|
memory_size = 256
|
||||||
|
timeout = 120
|
||||||
|
|
||||||
|
s3_bucket = aws_s3_bucket.artifacts.id
|
||||||
|
s3_key = aws_s3_object.health_digest.key
|
||||||
|
source_code_hash = data.archive_file.health_digest.output_base64sha256
|
||||||
|
|
||||||
|
layers = [aws_lambda_layer_version.shared.arn]
|
||||||
|
|
||||||
|
environment {
|
||||||
|
variables = local.common_env
|
||||||
|
}
|
||||||
|
|
||||||
|
depends_on = [
|
||||||
|
aws_s3_object.health_digest,
|
||||||
|
aws_iam_role_policy_attachment.health_digest_basic,
|
||||||
|
aws_iam_role_policy.health_digest_secrets,
|
||||||
|
]
|
||||||
|
}
|
||||||
24
terraform/locals.tf
Normal file
24
terraform/locals.tf
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
locals {
|
||||||
|
account_id = "011934824531"
|
||||||
|
boundary_arn = "arn:aws:iam::${local.account_id}:policy/seahaven-lambda-execution-boundary"
|
||||||
|
|
||||||
|
common_env = {
|
||||||
|
AFI_API_KEY_SECRET_ARN = var.afi_api_key_secret_arn
|
||||||
|
AFI_TENANT_ID = var.afi_tenant_id
|
||||||
|
SLACK_WEBHOOK_SECRET_ARN = var.slack_webhook_secret_arn
|
||||||
|
}
|
||||||
|
|
||||||
|
secrets_policy_json = jsonencode({
|
||||||
|
Version = "2012-10-17"
|
||||||
|
Statement = [
|
||||||
|
{
|
||||||
|
Effect = "Allow"
|
||||||
|
Action = ["secretsmanager:GetSecretValue"]
|
||||||
|
Resource = [
|
||||||
|
var.afi_api_key_secret_arn,
|
||||||
|
var.slack_webhook_secret_arn,
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
}
|
||||||
14
terraform/outputs.tf
Normal file
14
terraform/outputs.tf
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
output "auto_protect_function_arn" {
|
||||||
|
description = "Auto-protect Lambda ARN"
|
||||||
|
value = aws_lambda_function.auto_protect.arn
|
||||||
|
}
|
||||||
|
|
||||||
|
output "health_digest_function_arn" {
|
||||||
|
description = "Health digest Lambda ARN"
|
||||||
|
value = aws_lambda_function.health_digest.arn
|
||||||
|
}
|
||||||
|
|
||||||
|
output "shared_layer_arn" {
|
||||||
|
description = "Shared Lambda layer ARN"
|
||||||
|
value = aws_lambda_layer_version.shared.arn
|
||||||
|
}
|
||||||
11
terraform/providers.tf
Normal file
11
terraform/providers.tf
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
provider "aws" {
|
||||||
|
region = var.aws_region
|
||||||
|
|
||||||
|
default_tags {
|
||||||
|
tags = {
|
||||||
|
Project = "afi-backup-monitor"
|
||||||
|
ManagedBy = "terraform"
|
||||||
|
Workspace = "afi-backup-monitor-prod"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
6
terraform/terraform.tfvars.example
Normal file
6
terraform/terraform.tfvars.example
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
# Wire these as HCP workspace Terraform variables (never commit real .tfvars).
|
||||||
|
# Prod ARNs created 2026-08-05 (PLAT-56):
|
||||||
|
afi_api_key_secret_arn = "arn:aws:secretsmanager:us-east-1:011934824531:secret:afi-api-key-w0E02a"
|
||||||
|
slack_webhook_secret_arn = "arn:aws:secretsmanager:us-east-1:011934824531:secret:afi-slack-webhook-T4oR3G"
|
||||||
|
afi_tenant_id = "01KNHTNY89N7ZM6YETP4R083PB"
|
||||||
|
afi_policy_id = "01KNHTNY8GCQ0988BFSY6DR13B"
|
||||||
37
terraform/variables.tf
Normal file
37
terraform/variables.tf
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
variable "aws_region" {
|
||||||
|
type = string
|
||||||
|
description = "AWS region for all resources"
|
||||||
|
default = "us-east-1"
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "afi_api_key_secret_arn" {
|
||||||
|
type = string
|
||||||
|
description = "Secrets Manager ARN for the Afi API key (exact ARN, including suffix)"
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "slack_webhook_secret_arn" {
|
||||||
|
type = string
|
||||||
|
description = "Secrets Manager ARN for the Slack webhook URL (exact ARN, including suffix)"
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "afi_tenant_id" {
|
||||||
|
type = string
|
||||||
|
description = "Afi tenant ID for Google Workspace"
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "afi_policy_id" {
|
||||||
|
type = string
|
||||||
|
description = "Afi backup policy ID to assign to new users"
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "auto_protect_schedule" {
|
||||||
|
type = string
|
||||||
|
description = "EventBridge schedule for auto-protect (default Monday 10am ET)"
|
||||||
|
default = "cron(0 14 ? * MON *)"
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "health_digest_schedule" {
|
||||||
|
type = string
|
||||||
|
description = "EventBridge schedule for health digest (default Monday 10am ET)"
|
||||||
|
default = "cron(0 14 ? * MON *)"
|
||||||
|
}
|
||||||
22
terraform/versions.tf
Normal file
22
terraform/versions.tf
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
terraform {
|
||||||
|
required_version = ">= 1.7.0"
|
||||||
|
|
||||||
|
required_providers {
|
||||||
|
aws = {
|
||||||
|
source = "hashicorp/aws"
|
||||||
|
version = "~> 5.0"
|
||||||
|
}
|
||||||
|
archive = {
|
||||||
|
source = "hashicorp/archive"
|
||||||
|
version = "~> 2.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cloud {
|
||||||
|
organization = "seahaven"
|
||||||
|
|
||||||
|
workspaces {
|
||||||
|
name = "afi-backup-monitor-prod"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Reference in a new issue