Skip to content

Always-on / fixed infrastructure (Terraform)

Source: examples/always-on-infrastructure.yaml

To check and price this model from a clone of the repository:

infra-cost-model validate examples/always-on-infrastructure.yaml
infra-cost-model compute --time-basis monthly examples/always-on-infrastructure.yaml
# Example: Always-on / fixed infrastructure (Terraform)
# Demonstrates Issue #196:
#   - Per-metric `fixed` flags: one node carries BOTH a fixed monthly dimension
#     and a usage-driven dimension (NAT gateway hours + GB processed; ALB-hours
#     + LCUs) without splitting into two nodes.
#   - Always-on nodes: a fixed resource (Secrets Manager secret) is costed
#     without a synthetic incoming edge and emits no unreachable warning.
#   - No spurious DP#9 conflict: a mixed node may receive DAG edges; the warning
#     fires only for a fully-fixed node that also receives edges.
#
# Issue: https://github.com/elecnix/infra-cost-model/issues/196

version: "1.0"

workflow:
  name: always-on-web-api
  entry: aws_lb.main
  frequency:
    unit: perMinute
    value: 1000

nodes:
  # Entry: Application Load Balancer. Mixed — fixed ALB-hours plus usage-driven
  # LCUs. Being the entry node it has no incoming edge, so no DP#9 conflict.
  aws_lb.main:
    nodeType: routing
    resourceAddress: aws_lb.main
    provider: aws
    service: AmazonALB
    region: us-east-1
    usageMetrics:
      albHours: { unit: hours, value: 730, fixed: true }   # always-on, $/month
      # One LCU-hour covers 25 new connections a second for an hour, or
      # 90,000 connections. One request opens one connection: 1/90,000.
      newConnections: { unit: LCU-hours, value: 0.0000111111 }

  aws_lambda_function.api:
    nodeType: compute
    resourceAddress: aws_lambda_function.api
    provider: aws
    service: AWSLambda
    region: us-east-1
    usageMetrics:
      invocations: { unit: requests, value: 1 }
      avgDurationMs: { unit: ms, value: 500 }
      memoryMb: { unit: MB, value: 256 }

  aws_dynamodb_table.data:
    nodeType: storage
    resourceAddress: aws_dynamodb_table.data
    provider: aws
    service: AmazonDynamoDB
    region: us-east-1
    usageMetrics:
      readRequests: { unit: requests, value: 1 }

  # NAT gateway: mixed — fixed gateway-hours plus usage-driven GB processed.
  # It DOES receive an edge (egress flows through it), and because only one of
  # its metrics is fixed there is no DP#9 conflict.
  aws_nat_gateway.main:
    nodeType: routing
    resourceAddress: aws_nat_gateway.main
    provider: aws
    service: AmazonVPC
    region: us-east-1
    usageMetrics:
      # The NAT gateway handler maps these names to the catalog metrics
      # NAT-Gateway-Hour and NAT-Gateway-DataProcessed.
      natHours: { unit: hours, value: 730, fixed: true }       # always-on, $/month
      dataProcessedGb: { unit: GB, value: 0.00005 }            # ~50KB/req, scales with traffic

  # Secrets Manager secret: a purely fixed, always-on resource. It has NO
  # incoming edge — it is costed anyway and emits no unreachable warning.
  aws_secretsmanager_secret.api_key:
    nodeType: storage
    resourceAddress: aws_secretsmanager_secret.api_key
    provider: aws
    service: AWSSecretsManager
    region: us-east-1
    usageMetrics:
      # The handler maps secretsCount to the catalog metric SecretsManager-Secret.
      secretsCount: { unit: secrets, value: 1, fixed: true }

edges:
  - { from: aws_lb.main, to: aws_lambda_function.api, rate: 1.0 }
  - { from: aws_lambda_function.api, to: aws_dynamodb_table.data, rate: 1.0, type: read }
  - { from: aws_lambda_function.api, to: aws_nat_gateway.main, rate: 1.0 }