{"id":39494163,"url":"https://github.com/infrahouse/terraform-aws-website-pod","last_synced_at":"2026-01-28T05:37:04.110Z","repository":{"id":188115707,"uuid":"678134164","full_name":"infrahouse/terraform-aws-website-pod","owner":"infrahouse","description":"Terraform module for web application deployment with ALB, ACM SSL certificates, EC2 autoscaling, CloudWatch alarms, CAA records, and spot instance support. ","archived":false,"fork":false,"pushed_at":"2026-01-23T14:13:32.000Z","size":1093,"stargazers_count":1,"open_issues_count":4,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-01-23T17:38:39.577Z","etag":null,"topics":["alb","autoscaling","aws","infrahouse","terraform","terraform-module","website"],"latest_commit_sha":null,"homepage":"https://infrahouse.github.io/terraform-aws-website-pod","language":"HCL","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/infrahouse.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2023-08-13T19:44:15.000Z","updated_at":"2026-01-23T14:13:33.000Z","dependencies_parsed_at":"2023-08-13T20:42:10.974Z","dependency_job_id":"50e81955-55b5-4945-9079-e8b1e63ba316","html_url":"https://github.com/infrahouse/terraform-aws-website-pod","commit_stats":null,"previous_names":["infrahouse/terraform-aws-website-pod"],"tags_count":89,"template":false,"template_full_name":null,"purl":"pkg:github/infrahouse/terraform-aws-website-pod","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/infrahouse%2Fterraform-aws-website-pod","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/infrahouse%2Fterraform-aws-website-pod/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/infrahouse%2Fterraform-aws-website-pod/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/infrahouse%2Fterraform-aws-website-pod/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/infrahouse","download_url":"https://codeload.github.com/infrahouse/terraform-aws-website-pod/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/infrahouse%2Fterraform-aws-website-pod/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28840132,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-28T02:10:51.810Z","status":"ssl_error","status_checked_at":"2026-01-28T02:10:50.806Z","response_time":57,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["alb","autoscaling","aws","infrahouse","terraform","terraform-module","website"],"created_at":"2026-01-18T05:40:06.477Z","updated_at":"2026-01-28T05:37:04.095Z","avatar_url":"https://github.com/infrahouse.png","language":"HCL","funding_links":[],"categories":[],"sub_categories":[],"readme":"# terraform-aws-website-pod\n\n[![Need Help?](https://img.shields.io/badge/Need%20Help%3F-Contact%20Us-0066CC)](https://infrahouse.com/contact)\n[![Docs](https://img.shields.io/badge/docs-github.io-blue)](https://infrahouse.github.io/terraform-aws-website-pod/)\n[![Registry](https://img.shields.io/badge/Terraform-Registry-purple?logo=terraform)](https://registry.terraform.io/modules/infrahouse/website-pod/aws/latest)\n[![Release](https://img.shields.io/github/release/infrahouse/terraform-aws-website-pod.svg)](https://github.com/infrahouse/terraform-aws-website-pod/releases/latest)\n[![AWS ALB](https://img.shields.io/badge/AWS-ALB-orange?logo=amazonwebservices)](https://aws.amazon.com/elasticloadbalancing/)\n[![Security](https://img.shields.io/github/actions/workflow/status/infrahouse/terraform-aws-website-pod/vuln-scanner-pr.yml?label=Security)](https://github.com/infrahouse/terraform-aws-website-pod/actions/workflows/vuln-scanner-pr.yml)\n[![License](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](LICENSE)\n\nThe module creates resources to run an HTTP service in an autoscaling group.\nIt creates a load balancer that terminates SSL on the TCP port 443.\nIt also issues the SSL certificate in ACM.\n\n## Why This Module?\n\nSetting up a production-ready web application on AWS requires configuring multiple interconnected services:\nALB, ASG, ACM certificates, Route53 DNS, security groups, CloudWatch alarms, and more.\nThis module handles all of that complexity in a single, well-tested package.\n\n**Key benefits:**\n- **Production-ready**: Battle-tested in production environments with comprehensive monitoring and alerting\n- **Security-focused**: Automatic SSL/TLS certificates, CAA records, configurable access controls, and compliance support (SOC2, HIPAA, PCI-DSS)\n- **Cost-optimized**: Support for spot instances to reduce costs by up to 90%\n- **Fully automated**: Zero-downtime deployments with instance refresh and lifecycle hooks\n\n## Features\n\n- **Application Load Balancer (ALB)** with automatic HTTP to HTTPS redirect\n- **Auto Scaling Group (ASG)** with target tracking scaling policies\n- **ACM SSL Certificate** with automatic DNS validation\n- **Route53 DNS Records** including CAA records for certificate security\n- **CloudWatch Alarms** for CPU, latency, error rates, and unhealthy hosts\n- **Security Groups** with configurable ingress rules for ALB and backend instances\n- **Spot Instance Support** with configurable on-demand base capacity\n- **ALB Access Logging** to S3 for security and compliance\n- **Session Stickiness** for stateful applications\n- **Lifecycle Hooks** for graceful instance launch and termination\n\n## Documentation\n\nFor detailed documentation, visit the [GitHub Pages documentation site](https://infrahouse.github.io/terraform-aws-website-pod/).\n\n- [Getting Started](https://infrahouse.github.io/terraform-aws-website-pod/getting-started/)\n- [Architecture](https://infrahouse.github.io/terraform-aws-website-pod/architecture/)\n- [Configuration Reference](https://infrahouse.github.io/terraform-aws-website-pod/configuration/)\n- [Examples](https://infrahouse.github.io/terraform-aws-website-pod/examples/)\n- [Troubleshooting](https://infrahouse.github.io/terraform-aws-website-pod/troubleshooting/)\n\n## Quick Start\n\n\u003e **Note**: Starting from version 2.0 the module separates the main aws provider and a provider for\n\u003e Route53 resources. If you don't need to separate them, just pass the same provider for `aws` and `aws.dns`\n\u003e ```hcl\n\u003e providers = {\n\u003e   aws     = aws\n\u003e   aws.dns = aws\n\u003e }\n\u003e ```\n\n## Usage\n\n```hcl\nmodule \"website\" {\n  providers = {\n    aws     = aws.aws-uw1\n    aws.dns = aws.aws-uw1\n  }\n  source  = \"infrahouse/website-pod/aws\"\n  version = \"5.17.0\"\n\n  environment           = var.environment\n  ami                   = data.aws_ami.ubuntu_22.image_id\n  backend_subnets       = module.website-vpc.subnet_private_ids\n  zone_id               = \"Z07662251LH3YRF2ERM3G\"\n  dns_a_records         = [\"\", \"www\"]\n  internet_gateway_id   = module.website-vpc.internet_gateway_id\n  key_pair_name         = data.aws_key_pair.aleks.key_name\n  subnets               = module.website-vpc.subnet_public_ids\n  userdata              = module.webserver_userdata.userdata\n  stickiness_enabled    = true\n}\n```\n\n### Security groups\n\nThe module used default security groups up until version 2.5.0.\n\nStarting from the version 2.6.0 the behavior changes, however in a backward-compatible manner.\nThe module creates two security groups. One for the load balancer, another - for the backend instances.\n\nThe load balancer security group allows traffic to TCP ports 443 and `var.alb_listener_port` (80 by default).\nBy default, traffic is allowed from any source (0.0.0.0/0), but this can be restricted using `var.alb_ingress_cidr_blocks`.\n\nThe backend security group allows user traffic and health checks coming from the load balancer.\nAlso, the security group allows SSH from the VPC where the backend instances reside and from `var.ssh_cidr_block`.\nIt is 0.0.0.0/0 by default, but the goal is allow user restrict access let's say to anyone but the management VPC.\n\nBoth security groups allow incoming ICMP traffic.\n\nAdditionally, the user can specify additional security groups via `var.extra_security_groups_backend`.\nThey will be added to the backend instance alongside with the created backend security group.\n\n### Using spot instances\n\nBy default, the module launches on-demand instances only. However, if you specify `var.on_demand_base_capacity`,\nthe ASG will fulfill its capacity by as many on-demand instances as `var.on_demand_base_capacity` and the rest will\nbe spot instances.\n\n### Certificate Authority Authorization (CAA) Records\n\nThe module automatically creates CAA records for each DNS A record to control which certificate authorities can issue certificates for your domain. By default, only Amazon (ACM) is allowed to issue certificates.\n\nTo allow additional certificate authorities, use the `certificate_issuers` variable:\n\n```hcl\nmodule \"website\" {\n  # ... other configuration ...\n\n  # Allow both Amazon and Let's Encrypt to issue certificates\n  certificate_issuers = [\"amazon.com\", \"letsencrypt.org\"]\n}\n```\n\nThe module automatically formats these domains into proper CAA records and adds a wildcard certificate blocking record (`0 issuewild \";\"`) for security.\n\n### Restricting ALB Access\n\nBy default, the load balancer accepts traffic from any source (0.0.0.0/0).\nYou can restrict access to specific CIDR ranges using the `alb_ingress_cidr_blocks` variable:\n\n```hcl\nmodule \"website\" {\n  # ... other configuration ...\n\n  # Allow access only from specific networks\n  alb_ingress_cidr_blocks = [\n    \"10.0.0.0/8\",      # Internal corporate network\n    \"203.0.113.0/24\"   # Specific external IP range\n  ]\n}\n```\n\nThis creates separate security group rules for each CIDR block, allowing fine-grained control over\nwho can access your load balancer on both HTTP (port 80/`var.alb_listener_port`) and HTTPS (port 443).\n\n### ALB Access Logging (Security Best Practice)\n\n**Recommended:** Enable ALB access logging for security investigations, incident response, debugging, and compliance requirements.\n\n```hcl\nmodule \"website\" {\n  # ... other configuration ...\n\n  # Enable access logging (recommended for production)\n  alb_access_log_enabled = true\n}\n```\n\nWhen enabled, the module creates an encrypted, versioned S3 bucket that stores detailed ALB access logs. These logs are essential for:\n- **Security:** Track unauthorized access attempts and identify suspicious traffic patterns\n- **Compliance:** Meet SOC2, HIPAA, PCI-DSS, and ISO 27001 requirements\n- **Operations:** Debug production issues and analyze traffic patterns\n- **AWS Best Practices:** Aligns with AWS Well-Architected Framework security pillar\n\n**Cost Impact:** Minimal (~$4/year for moderate traffic). Storage costs are negligible compared to security and compliance benefits.\n\n**Note:** Starting in v6.0.0, access logging will be enabled by default. See `variables.tf` for details.\n\n## Deprecated Variables\n\nThe following variables contain typos and are deprecated. They will be removed in **v6.0.0**.\n\n| Deprecated Variable (v5.x)          | Correct Variable (Use This)          | Status                    |\n|-------------------------------------|--------------------------------------|---------------------------|\n| `alb_healthcheck_uhealthy_threshold`| `alb_healthcheck_unhealthy_threshold`| ⚠️  Deprecated in v5.11.0 |\n| `attach_tagret_group_to_asg`        | `attach_target_group_to_asg`         | ⚠️  Deprecated in v5.11.0 |\n\n### Migration Instructions\n\nIf you're using the deprecated variables, update your code before upgrading to v6.0.0:\n\n**Before:**\n```hcl\nmodule \"website\" {\n  source  = \"infrahouse/website-pod/aws\"\n  version = \"~\u003e 5.0\"\n\n  alb_healthcheck_uhealthy_threshold = 3  # Typo: \"uhealthy\"\n  attach_tagret_group_to_asg         = true  # Typo: \"tagret\"\n}\n```\n\n**After:**\n```hcl\nmodule \"website\" {\n  source  = \"infrahouse/website-pod/aws\"\n  version = \"~\u003e 5.11\"  # or \"~\u003e 6.0\" when available\n\n  alb_healthcheck_unhealthy_threshold = 3  # Correct spelling\n  attach_target_group_to_asg          = true  # Correct spelling\n}\n```\n\nFor detailed migration guidance, see [UPGRADE-6.0.md](UPGRADE-6.0.md).\n\n## Development and Testing\n\n### Running Tests\n\nThe module includes a comprehensive test suite. Tests use pytest and deploy real infrastructure to AWS.\n\n#### Run all tests:\n```bash\nmake test\n```\n\n#### Run tests with AWS credentials:\n```bash\nmake test-keep  # Keeps infrastructure for debugging\nmake test-clean # Destroys infrastructure after test\n```\n\n### Troubleshooting Test Failures\n\nIf a test fails in CI, you can run the specific failed test locally:\n\n#### Run a specific test:\n```bash\n# Run a specific test file and function\nmake test-keep TEST_PATH=tests/test_asg_name.py::test_lb TEST_FILTER=\n\n# Run all tests in a file\nmake test-keep TEST_PATH=tests/test_asg_name.py TEST_FILTER=\n```\n\n#### Available test variables:\n- `TEST_PATH` - Path to test file or specific test (default: `tests/test_create_lb.py`)\n- `TEST_FILTER` - pytest `-k` filter expression (default: `\"internet-facing and aws-6\"`)\n- `TEST_REGION` - AWS region for testing (default: `\"us-west-2\"`)\n- `TEST_ROLE` - IAM role ARN for testing (default: module-specific test role)\n\n#### Examples:\n\n```bash\n# Run with default filter\nmake test-keep\n\n# Run specific test without filter\nmake test-keep TEST_PATH=tests/test_spot.py::test_lb TEST_FILTER=\n\n# Run with custom filter\nmake test-keep TEST_FILTER=\"aws-5\"\n\n# Run specific test and keep resources for debugging\nmake test-keep TEST_PATH=tests/test_asg_name.py::test_lb TEST_FILTER=\n\n# Run and clean up afterward\nmake test-clean TEST_PATH=tests/test_asg_name.py::test_lb TEST_FILTER=\n```\n\n### Other Development Commands\n\n```bash\nmake bootstrap  # Install dependencies and git hooks\nmake fmt        # Format Terraform and Python code\nmake lint       # Check code style\nmake validate   # Validate Terraform configuration\n```\n\n\u003c!-- BEGIN_TF_DOCS --\u003e\n\n## Requirements\n\n| Name | Version |\n|------|---------|\n| \u003ca name=\"requirement_terraform\"\u003e\u003c/a\u003e [terraform](#requirement\\_terraform) | ~\u003e 1.5 |\n| \u003ca name=\"requirement_aws\"\u003e\u003c/a\u003e [aws](#requirement\\_aws) | \u003e= 5.11, \u003c 7.0 |\n| \u003ca name=\"requirement_random\"\u003e\u003c/a\u003e [random](#requirement\\_random) | ~\u003e 3.6 |\n\n## Providers\n\n| Name | Version |\n|------|---------|\n| \u003ca name=\"provider_aws\"\u003e\u003c/a\u003e [aws](#provider\\_aws) | \u003e= 5.11, \u003c 7.0 |\n| \u003ca name=\"provider_aws.dns\"\u003e\u003c/a\u003e [aws.dns](#provider\\_aws.dns) | \u003e= 5.11, \u003c 7.0 |\n| \u003ca name=\"provider_random\"\u003e\u003c/a\u003e [random](#provider\\_random) | ~\u003e 3.6 |\n\n## Modules\n\n| Name | Source | Version |\n|------|--------|---------|\n| \u003ca name=\"module_instance_profile\"\u003e\u003c/a\u003e [instance\\_profile](#module\\_instance\\_profile) | registry.infrahouse.com/infrahouse/instance-profile/aws | 1.9.0 |\n\n## Resources\n\n| Name | Type |\n|------|------|\n| [aws_acm_certificate.website](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/acm_certificate) | resource |\n| [aws_acm_certificate_validation.website](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/acm_certificate_validation) | resource |\n| [aws_alb.website](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/alb) | resource |\n| [aws_alb_listener.redirect_to_ssl](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/alb_listener) | resource |\n| [aws_alb_listener_rule.website](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/alb_listener_rule) | resource |\n| [aws_alb_target_group.website](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/alb_target_group) | resource |\n| [aws_autoscaling_group.website](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/autoscaling_group) | resource |\n| [aws_autoscaling_lifecycle_hook.launching](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/autoscaling_lifecycle_hook) | resource |\n| [aws_autoscaling_lifecycle_hook.terminating](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/autoscaling_lifecycle_hook) | resource |\n| [aws_autoscaling_policy.cpu_load](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/autoscaling_policy) | resource |\n| [aws_cloudwatch_metric_alarm.cpu_utilization](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_metric_alarm) | resource |\n| [aws_cloudwatch_metric_alarm.low_success_rate](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_metric_alarm) | resource |\n| [aws_cloudwatch_metric_alarm.target_response_time](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_metric_alarm) | resource |\n| [aws_cloudwatch_metric_alarm.unhealthy_host_count](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_metric_alarm) | resource |\n| [aws_launch_template.website](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/launch_template) | resource |\n| [aws_lb_listener.ssl](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lb_listener) | resource |\n| [aws_route53_record.cert_validation](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route53_record) | resource |\n| [aws_route53_record.extra](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route53_record) | resource |\n| [aws_route53_record.extra_caa_amazon](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route53_record) | resource |\n| [aws_s3_bucket.access_log](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket) | resource |\n| [aws_s3_bucket_policy.access_logs](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_policy) | resource |\n| [aws_s3_bucket_public_access_block.public_access](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_public_access_block) | resource |\n| [aws_s3_bucket_server_side_encryption_configuration.access_log](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_server_side_encryption_configuration) | resource |\n| [aws_s3_bucket_versioning.access_log](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_versioning) | resource |\n| [aws_security_group.alb](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group) | resource |\n| [aws_security_group.backend](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group) | resource |\n| [aws_sns_topic.alarms](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/sns_topic) | resource |\n| [aws_sns_topic_subscription.alarm_emails](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/sns_topic_subscription) | resource |\n| [aws_vpc_security_group_egress_rule.alb_outgoing](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/vpc_security_group_egress_rule) | resource |\n| [aws_vpc_security_group_egress_rule.backend_outgoing](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/vpc_security_group_egress_rule) | resource |\n| [aws_vpc_security_group_ingress_rule.alb_icmp](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/vpc_security_group_ingress_rule) | resource |\n| [aws_vpc_security_group_ingress_rule.alb_listener_port](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/vpc_security_group_ingress_rule) | resource |\n| [aws_vpc_security_group_ingress_rule.backend_healthcheck](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/vpc_security_group_ingress_rule) | resource |\n| [aws_vpc_security_group_ingress_rule.backend_icmp](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/vpc_security_group_ingress_rule) | resource |\n| [aws_vpc_security_group_ingress_rule.backend_ssh_input](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/vpc_security_group_ingress_rule) | resource |\n| [aws_vpc_security_group_ingress_rule.backend_ssh_local](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/vpc_security_group_ingress_rule) | resource |\n| [aws_vpc_security_group_ingress_rule.backend_user_traffic](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/vpc_security_group_ingress_rule) | resource |\n| [aws_vpc_security_group_ingress_rule.https](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/vpc_security_group_ingress_rule) | resource |\n| [random_string.profile_suffix](https://registry.terraform.io/providers/hashicorp/random/latest/docs/resources/string) | resource |\n| [aws_ami.selected](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/ami) | data source |\n| [aws_caller_identity.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/caller_identity) | data source |\n| [aws_default_tags.provider](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/default_tags) | data source |\n| [aws_ec2_instance_type.selected](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/ec2_instance_type) | data source |\n| [aws_iam_policy_document.access_logs](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |\n| [aws_iam_policy_document.default_permissions](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |\n| [aws_region.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/region) | data source |\n| [aws_route53_zone.webserver_zone](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/route53_zone) | data source |\n| [aws_subnet.selected](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/subnet) | data source |\n| [aws_vpc.service](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/vpc) | data source |\n\n## Inputs\n\n| Name | Description | Type | Default | Required |\n|------|-------------|------|---------|:--------:|\n| \u003ca name=\"input_alarm_cpu_utilization_threshold\"\u003e\u003c/a\u003e [alarm\\_cpu\\_utilization\\_threshold](#input\\_alarm\\_cpu\\_utilization\\_threshold) | CPU utilization percentage that triggers an alarm.\u003cbr/\u003e\u003cbr/\u003eThis alarm detects when autoscaling FAILS to keep up with demand, which may indicate:\u003cbr/\u003e- ASG reached max\\_size (cannot scale further)\u003cbr/\u003e- New instances failing to provision\u003cbr/\u003e- New instances not becoming healthy\u003cbr/\u003e- Infrastructure capacity/quota issues\u003cbr/\u003e\u003cbr/\u003e**Automatic calculation:**\u003cbr/\u003eIf not specified, defaults to autoscaling\\_target\\_cpu\\_load + 30%.\u003cbr/\u003eThis provides a buffer for autoscaling to respond before alarming.\u003cbr/\u003e\u003cbr/\u003e**Example automatic thresholds:**\u003cbr/\u003e- autoscaling\\_target\\_cpu\\_load = 60%: alarm at 90%\u003cbr/\u003e- autoscaling\\_target\\_cpu\\_load = 70%: alarm at 99% (capped)\u003cbr/\u003e\u003cbr/\u003e**How it works:**\u003cbr/\u003eWhen CPU exceeds target (60% default), ASG launches new instances (~5-10 min).\u003cbr/\u003eIf CPU stays high for 10 minutes (period × evaluation\\_periods), autoscaling has failed - time to alert!\u003cbr/\u003e\u003cbr/\u003e**Override for custom thresholds:**\u003cpre\u003ealarm_cpu_utilization_threshold = 85  # Explicit threshold\u003c/pre\u003e**Note:** This is a Vanta compliance requirement (Server CPU monitored). | `number` | `null` | no |\n| \u003ca name=\"input_alarm_emails\"\u003e\u003c/a\u003e [alarm\\_emails](#input\\_alarm\\_emails) | List of email addresses to receive CloudWatch alarm notifications for ALB monitoring.\u003cbr/\u003e\u003cbr/\u003e⚠️  **IMPORTANT - EMAIL CONFIRMATION REQUIRED:**\u003cbr/\u003eAfter deployment, AWS SNS will send a confirmation email to each address.\u003cbr/\u003e**You MUST click the confirmation link** in each email to activate notifications.\u003cbr/\u003e\u003cbr/\u003eUntil confirmed:\u003cbr/\u003e- Subscription status: PendingConfirmation\u003cbr/\u003e- Alarms will fire but notifications will NOT be delivered\u003cbr/\u003e- No alerts will reach your team during incidents\u003cbr/\u003e\u003cbr/\u003e**Action Required:** Check spam folders and confirm all subscription emails immediately after deployment.\u003cbr/\u003e\u003cbr/\u003e**Vanta Compliance Requirements:**\u003cbr/\u003eWhen configured, creates CloudWatch alarms for:\u003cbr/\u003e- Load balancer unhealthy host count monitoring\u003cbr/\u003e- Load balancer latency monitoring\u003cbr/\u003e- Load balancer server errors (5xx) monitoring\u003cbr/\u003e- Server CPU utilization monitoring\u003cbr/\u003e\u003cbr/\u003e**Example:**\u003cpre\u003ealarm_emails = [\"ops-team@example.com\", \"on-call@example.com\"]\u003c/pre\u003e⚠️  **FUTURE REQUIREMENT:** In v6.0.0, at least one email address will be required.\u003cbr/\u003eSee UPGRADE-6.0.md for migration details. | `list(string)` | `[]` | no |\n| \u003ca name=\"input_alarm_evaluation_periods\"\u003e\u003c/a\u003e [alarm\\_evaluation\\_periods](#input\\_alarm\\_evaluation\\_periods) | Number of periods over which to compare the metric to the threshold.\u003cbr/\u003e\u003cbr/\u003eWith 1-minute periods, setting this to 2 means the alarm must breach\u003cbr/\u003efor 2 consecutive minutes before triggering. | `number` | `2` | no |\n| \u003ca name=\"input_alarm_success_rate_period\"\u003e\u003c/a\u003e [alarm\\_success\\_rate\\_period](#input\\_alarm\\_success\\_rate\\_period) | Time period (in seconds) over which to calculate the success rate.\u003cbr/\u003e\u003cbr/\u003eLonger periods provide more statistical stability, especially important\u003cbr/\u003efor low-traffic sites where individual errors can skew short-term rates.\u003cbr/\u003e\u003cbr/\u003e**Default:** 300 seconds (5 minutes)\u003cbr/\u003e\u003cbr/\u003e**Recommendations by traffic volume:**\u003cbr/\u003e- Very low traffic (\u003c 1 req/min):   3600s (1 hour) for statistical significance\u003cbr/\u003e- Low traffic (1-10 req/min):       900s (15 min)\u003cbr/\u003e- Medium traffic (10-100 req/min):  300s (5 min) - default\u003cbr/\u003e- High traffic (\u003e 100 req/min):     60s (1 min) for faster detection\u003cbr/\u003e\u003cbr/\u003e**Detection time:** With evaluation\\_periods=2:\u003cbr/\u003e- 3600s (1 hour) = 2 hour detection time\u003cbr/\u003e- 900s (15 min) = 30 minute detection time\u003cbr/\u003e- 300s (5 min) = 10 minute detection time\u003cbr/\u003e- 60s (1 min) = 2 minute detection time\u003cbr/\u003e\u003cbr/\u003e**Example for low-traffic site:**\u003cpre\u003ealarm_success_rate_period = 3600  # 1 hour window\u003cbr/\u003ealarm_success_rate_threshold = 99.0\u003c/pre\u003eWith 10 requests/hour, allows 1 error before alarming. | `number` | `300` | no |\n| \u003ca name=\"input_alarm_success_rate_threshold\"\u003e\u003c/a\u003e [alarm\\_success\\_rate\\_threshold](#input\\_alarm\\_success\\_rate\\_threshold) | Minimum success rate (percentage) before triggering an alarm.\u003cbr/\u003e\u003cbr/\u003eSuccess rate = (non-5xx responses) / (total responses) * 100\u003cbr/\u003e\u003cbr/\u003eThis is smarter than a raw error count because it scales with traffic volume.\u003cbr/\u003eA 1% error rate means the same thing whether you have 100 or 100,000 requests.\u003cbr/\u003e\u003cbr/\u003e**Default:** 99.0 (alerts when error rate exceeds 1%)\u003cbr/\u003e\u003cbr/\u003e**Examples:**\u003cbr/\u003e- 99.9 = Alert when more than 0.1% of requests fail (very strict SLO)\u003cbr/\u003e- 99.0 = Alert when more than 1% of requests fail (recommended)\u003cbr/\u003e- 95.0 = Alert when more than 5% of requests fail (lenient)\u003cbr/\u003e\u003cbr/\u003e**Note:** Alarms won't trigger during periods with zero traffic. | `number` | `99` | no |\n| \u003ca name=\"input_alarm_target_response_time_threshold\"\u003e\u003c/a\u003e [alarm\\_target\\_response\\_time\\_threshold](#input\\_alarm\\_target\\_response\\_time\\_threshold) | Target response time threshold in seconds that triggers a latency alarm.\u003cbr/\u003e\u003cbr/\u003eIf not specified, defaults to 80% of alb\\_idle\\_timeout to alert before\u003cbr/\u003econnections start timing out.\u003cbr/\u003e\u003cbr/\u003eExample: With default alb\\_idle\\_timeout=60s, this will default to 48s.\u003cbr/\u003e\u003cbr/\u003eYou can override this for more aggressive monitoring:\u003cbr/\u003e- API services: 0.5 - 1.0 seconds\u003cbr/\u003e- Web applications: 1.0 - 2.0 seconds\u003cbr/\u003e- Backend services: 2.0 - 5.0 seconds | `number` | `null` | no |\n| \u003ca name=\"input_alarm_topic_arns\"\u003e\u003c/a\u003e [alarm\\_topic\\_arns](#input\\_alarm\\_topic\\_arns) | List of existing SNS topic ARNs to send ALB alarms to.\u003cbr/\u003eUse this for advanced integrations like PagerDuty, Slack, OpsGenie, etc.\u003cbr/\u003e\u003cbr/\u003eThese topics will receive notifications in addition to any configured alarm\\_emails.\u003cbr/\u003e\u003cbr/\u003e**Example:**\u003cpre\u003ealarm_topic_arns = [\u003cbr/\u003e  \"arn:aws:sns:us-east-1:123456789012:pagerduty-critical\",\u003cbr/\u003e  \"arn:aws:sns:us-east-1:123456789012:slack-alerts\"\u003cbr/\u003e]\u003c/pre\u003e | `list(string)` | `[]` | no |\n| \u003ca name=\"input_alarm_unhealthy_host_threshold\"\u003e\u003c/a\u003e [alarm\\_unhealthy\\_host\\_threshold](#input\\_alarm\\_unhealthy\\_host\\_threshold) | Number of unhealthy hosts that triggers an alarm.\u003cbr/\u003e\u003cbr/\u003eUses GreaterThanThreshold comparison, so:\u003cbr/\u003e- 0 = Alert when ANY host becomes unhealthy (count \u003e 0)\u003cbr/\u003e- 1 = Alert when 2+ hosts are unhealthy (count \u003e 1) - default\u003cbr/\u003e- 2 = Alert when 3+ hosts are unhealthy (count \u003e 2)\u003cbr/\u003e\u003cbr/\u003e**Recommended:** Set to 0 for immediate alerting in production, or 1 to allow\u003cbr/\u003efor graceful deployments where one host may briefly be unhealthy during updates. | `number` | `1` | no |\n| \u003ca name=\"input_alb_access_log_enabled\"\u003e\u003c/a\u003e [alb\\_access\\_log\\_enabled](#input\\_alb\\_access\\_log\\_enabled) | Whether to enable ALB access logging to S3.\u003cbr/\u003e\u003cbr/\u003e**Security Best Practice:** Enabling access logs is recommended for:\u003cbr/\u003e- Security investigations and incident response\u003cbr/\u003e- Debugging production issues\u003cbr/\u003e- Compliance requirements (SOC2, HIPAA, PCI-DSS)\u003cbr/\u003e- AWS Well-Architected Framework best practices\u003cbr/\u003e\u003cbr/\u003eWhen enabled, creates an encrypted, versioned S3 bucket for access logs.\u003cbr/\u003eStorage costs are minimal compared to security and operational benefits.\u003cbr/\u003e\u003cbr/\u003e**Note:** In v6.0.0, this will default to `true` (enabled by default).\u003cbr/\u003eSee UPGRADE-6.0.md for details. | `bool` | `false` | no |\n| \u003ca name=\"input_alb_access_log_force_destroy\"\u003e\u003c/a\u003e [alb\\_access\\_log\\_force\\_destroy](#input\\_alb\\_access\\_log\\_force\\_destroy) | Destroy S3 bucket with access logs even if non-empty | `bool` | `false` | no |\n| \u003ca name=\"input_alb_healthcheck_enabled\"\u003e\u003c/a\u003e [alb\\_healthcheck\\_enabled](#input\\_alb\\_healthcheck\\_enabled) | Whether health checks are enabled. | `bool` | `true` | no |\n| \u003ca name=\"input_alb_healthcheck_healthy_threshold\"\u003e\u003c/a\u003e [alb\\_healthcheck\\_healthy\\_threshold](#input\\_alb\\_healthcheck\\_healthy\\_threshold) | Number of times the host have to pass the test to be considered healthy | `number` | `2` | no |\n| \u003ca name=\"input_alb_healthcheck_interval\"\u003e\u003c/a\u003e [alb\\_healthcheck\\_interval](#input\\_alb\\_healthcheck\\_interval) | Number of seconds between checks | `number` | `5` | no |\n| \u003ca name=\"input_alb_healthcheck_path\"\u003e\u003c/a\u003e [alb\\_healthcheck\\_path](#input\\_alb\\_healthcheck\\_path) | Path on the webserver that the elb will check to determine whether the instance is healthy or not | `string` | `\"/index.html\"` | no |\n| \u003ca name=\"input_alb_healthcheck_port\"\u003e\u003c/a\u003e [alb\\_healthcheck\\_port](#input\\_alb\\_healthcheck\\_port) | Port of the webserver that the elb will check to determine whether the instance is healthy or not | `any` | `80` | no |\n| \u003ca name=\"input_alb_healthcheck_protocol\"\u003e\u003c/a\u003e [alb\\_healthcheck\\_protocol](#input\\_alb\\_healthcheck\\_protocol) | Protocol to use with the webserver that the elb will check to determine whether the instance is healthy or not | `string` | `\"HTTP\"` | no |\n| \u003ca name=\"input_alb_healthcheck_response_code_matcher\"\u003e\u003c/a\u003e [alb\\_healthcheck\\_response\\_code\\_matcher](#input\\_alb\\_healthcheck\\_response\\_code\\_matcher) | Range of http return codes that can match | `string` | `\"200-299\"` | no |\n| \u003ca name=\"input_alb_healthcheck_timeout\"\u003e\u003c/a\u003e [alb\\_healthcheck\\_timeout](#input\\_alb\\_healthcheck\\_timeout) | Number of seconds to timeout a check | `number` | `4` | no |\n| \u003ca name=\"input_alb_healthcheck_uhealthy_threshold\"\u003e\u003c/a\u003e [alb\\_healthcheck\\_uhealthy\\_threshold](#input\\_alb\\_healthcheck\\_uhealthy\\_threshold) | ⚠️  DEPRECATED - Contains typo, use 'alb\\_healthcheck\\_unhealthy\\_threshold' instead.\u003cbr/\u003eThis variable will be removed in v6.0.0. See deprecations.tf for details.\u003cbr/\u003eNumber of times the host must fail the test to be considered unhealthy. | `number` | `null` | no |\n| \u003ca name=\"input_alb_healthcheck_unhealthy_threshold\"\u003e\u003c/a\u003e [alb\\_healthcheck\\_unhealthy\\_threshold](#input\\_alb\\_healthcheck\\_unhealthy\\_threshold) | Number of consecutive health check failures required before considering the target unhealthy | `number` | `2` | no |\n| \u003ca name=\"input_alb_idle_timeout\"\u003e\u003c/a\u003e [alb\\_idle\\_timeout](#input\\_alb\\_idle\\_timeout) | The time in seconds that the connection is allowed to be idle. | `number` | `60` | no |\n| \u003ca name=\"input_alb_ingress_cidr_blocks\"\u003e\u003c/a\u003e [alb\\_ingress\\_cidr\\_blocks](#input\\_alb\\_ingress\\_cidr\\_blocks) | List of CIDR blocks allowed to access the ALB. Defaults to allow all (0.0.0.0/0). | `list(string)` | \u003cpre\u003e[\u003cbr/\u003e  \"0.0.0.0/0\"\u003cbr/\u003e]\u003c/pre\u003e | no |\n| \u003ca name=\"input_alb_listener_port\"\u003e\u003c/a\u003e [alb\\_listener\\_port](#input\\_alb\\_listener\\_port) | TCP port that a load balancer listens to to serve client HTTP requests. The load balancer redirects this port to 443 and HTTPS. | `number` | `80` | no |\n| \u003ca name=\"input_alb_name_prefix\"\u003e\u003c/a\u003e [alb\\_name\\_prefix](#input\\_alb\\_name\\_prefix) | Name prefix for the load balancer | `string` | `\"web\"` | no |\n| \u003ca name=\"input_allow_wildcard_certificates\"\u003e\u003c/a\u003e [allow\\_wildcard\\_certificates](#input\\_allow\\_wildcard\\_certificates) | If true, CAA records will allow wildcard certificates from the configured certificate\\_issuers.\u003cbr/\u003eIf false, wildcard certificates are blocked. | `bool` | `false` | no |\n| \u003ca name=\"input_ami\"\u003e\u003c/a\u003e [ami](#input\\_ami) | Image for EC2 instances | `string` | n/a | yes |\n| \u003ca name=\"input_asg_default_cooldown\"\u003e\u003c/a\u003e [asg\\_default\\_cooldown](#input\\_asg\\_default\\_cooldown) | Amount of time, in seconds, after a scaling activity completes before another\u003cbr/\u003escaling activity can start. This prevents rapid scale-in/scale-out cycles. | `number` | `300` | no |\n| \u003ca name=\"input_asg_enabled_metrics\"\u003e\u003c/a\u003e [asg\\_enabled\\_metrics](#input\\_asg\\_enabled\\_metrics) | List of ASG metrics to enable for CloudWatch monitoring.\u003cbr/\u003eSet to empty list to disable metrics collection.\u003cbr/\u003e\u003cbr/\u003eAvailable metrics:\u003cbr/\u003e- GroupDesiredCapacity\u003cbr/\u003e- GroupInServiceInstances\u003cbr/\u003e- GroupPendingInstances\u003cbr/\u003e- GroupTerminatingInstances\u003cbr/\u003e- GroupTotalInstances\u003cbr/\u003e- GroupMinSize\u003cbr/\u003e- GroupMaxSize\u003cbr/\u003e- GroupInServiceCapacity\u003cbr/\u003e- GroupPendingCapacity\u003cbr/\u003e- GroupStandbyCapacity\u003cbr/\u003e- GroupStandbyInstances\u003cbr/\u003e- GroupTerminatingCapacity\u003cbr/\u003e- GroupTotalCapacity\u003cbr/\u003e- WarmPoolDesiredCapacity\u003cbr/\u003e- WarmPoolWarmedCapacity\u003cbr/\u003e- WarmPoolPendingCapacity\u003cbr/\u003e- WarmPoolTerminatingCapacity\u003cbr/\u003e- WarmPoolTotalCapacity\u003cbr/\u003e- WarmPoolMinSize\u003cbr/\u003e- GroupAndWarmPoolDesiredCapacity\u003cbr/\u003e- GroupAndWarmPoolTotalCapacity | `list(string)` | \u003cpre\u003e[\u003cbr/\u003e  \"GroupDesiredCapacity\",\u003cbr/\u003e  \"GroupInServiceInstances\",\u003cbr/\u003e  \"GroupPendingInstances\",\u003cbr/\u003e  \"GroupTerminatingInstances\",\u003cbr/\u003e  \"GroupTotalInstances\"\u003cbr/\u003e]\u003c/pre\u003e | no |\n| \u003ca name=\"input_asg_lifecycle_hook_heartbeat_timeout\"\u003e\u003c/a\u003e [asg\\_lifecycle\\_hook\\_heartbeat\\_timeout](#input\\_asg\\_lifecycle\\_hook\\_heartbeat\\_timeout) | How much time in seconds to wait until the hook is completed before proceeding with the default action. | `number` | `3600` | no |\n| \u003ca name=\"input_asg_lifecycle_hook_initial\"\u003e\u003c/a\u003e [asg\\_lifecycle\\_hook\\_initial](#input\\_asg\\_lifecycle\\_hook\\_initial) | Name for an initial LAUNCHING lifecycle hook configured via the initial\\_lifecycle\\_hook\u003cbr/\u003eblock in the ASG. This hook is evaluated during ASG creation.\u003cbr/\u003eOnly one initial hook is allowed per ASG.\u003cbr/\u003e\u003cbr/\u003eUse this for simple lifecycle hooks that don't require additional configuration. | `string` | `null` | no |\n| \u003ca name=\"input_asg_lifecycle_hook_launching\"\u003e\u003c/a\u003e [asg\\_lifecycle\\_hook\\_launching](#input\\_asg\\_lifecycle\\_hook\\_launching) | Name for a LAUNCHING lifecycle hook configured via a separate\u003cbr/\u003eaws\\_autoscaling\\_lifecycle\\_hook resource. This allows for more complex configurations\u003cbr/\u003eand can be created after the ASG exists.\u003cbr/\u003e\u003cbr/\u003eUse this if you need to attach SNS notifications or additional settings to the lifecycle hook. | `string` | `null` | no |\n| \u003ca name=\"input_asg_lifecycle_hook_launching_default_result\"\u003e\u003c/a\u003e [asg\\_lifecycle\\_hook\\_launching\\_default\\_result](#input\\_asg\\_lifecycle\\_hook\\_launching\\_default\\_result) | Default result for launching lifecycle hook. | `string` | `\"ABANDON\"` | no |\n| \u003ca name=\"input_asg_lifecycle_hook_terminating\"\u003e\u003c/a\u003e [asg\\_lifecycle\\_hook\\_terminating](#input\\_asg\\_lifecycle\\_hook\\_terminating) | Create a TERMINATING lifecycle hook with this name. | `string` | `null` | no |\n| \u003ca name=\"input_asg_lifecycle_hook_terminating_default_result\"\u003e\u003c/a\u003e [asg\\_lifecycle\\_hook\\_terminating\\_default\\_result](#input\\_asg\\_lifecycle\\_hook\\_terminating\\_default\\_result) | Default result for terminating lifecycle hook. | `string` | `\"ABANDON\"` | no |\n| \u003ca name=\"input_asg_max_healthy_percentage\"\u003e\u003c/a\u003e [asg\\_max\\_healthy\\_percentage](#input\\_asg\\_max\\_healthy\\_percentage) | Specifies the upper limit on the number of instances that are in the InService or Pending state with a healthy status during an instance replacement activity. | `number` | `200` | no |\n| \u003ca name=\"input_asg_max_size\"\u003e\u003c/a\u003e [asg\\_max\\_size](#input\\_asg\\_max\\_size) | Maximum number of instances in ASG | `number` | `10` | no |\n| \u003ca name=\"input_asg_min_elb_capacity\"\u003e\u003c/a\u003e [asg\\_min\\_elb\\_capacity](#input\\_asg\\_min\\_elb\\_capacity) | Terraform will wait until this many EC2 instances in the autoscaling group become healthy. By default, it's equal to var.asg\\_min\\_size. | `number` | `null` | no |\n| \u003ca name=\"input_asg_min_healthy_percentage\"\u003e\u003c/a\u003e [asg\\_min\\_healthy\\_percentage](#input\\_asg\\_min\\_healthy\\_percentage) | Specifies the lower limit on the number of instances that must be in the InService state with a healthy status during an instance replacement activity. | `number` | `100` | no |\n| \u003ca name=\"input_asg_min_size\"\u003e\u003c/a\u003e [asg\\_min\\_size](#input\\_asg\\_min\\_size) | Minimum number of instances in ASG | `number` | `2` | no |\n| \u003ca name=\"input_asg_name\"\u003e\u003c/a\u003e [asg\\_name](#input\\_asg\\_name) | Autoscaling group name, if provided. | `string` | `null` | no |\n| \u003ca name=\"input_asg_scale_in_protected_instances\"\u003e\u003c/a\u003e [asg\\_scale\\_in\\_protected\\_instances](#input\\_asg\\_scale\\_in\\_protected\\_instances) | Behavior when encountering instances protected from scale in are found. Available behaviors are Refresh, Ignore, and Wait. | `string` | `\"Ignore\"` | no |\n| \u003ca name=\"input_assume_dns\"\u003e\u003c/a\u003e [assume\\_dns](#input\\_assume\\_dns) | If True, create DNS records provided by var.dns\\_a\\_records. | `bool` | `true` | no |\n| \u003ca name=\"input_attach_tagret_group_to_asg\"\u003e\u003c/a\u003e [attach\\_tagret\\_group\\_to\\_asg](#input\\_attach\\_tagret\\_group\\_to\\_asg) | ⚠️  DEPRECATED - Contains typo, use 'attach\\_target\\_group\\_to\\_asg' instead.\u003cbr/\u003eThis variable will be removed in v6.0.0. See deprecations.tf for details.\u003cbr/\u003eWhether to register ASG instances in the target group. Disable if using ECS which registers targets itself. | `bool` | `null` | no |\n| \u003ca name=\"input_attach_target_group_to_asg\"\u003e\u003c/a\u003e [attach\\_target\\_group\\_to\\_asg](#input\\_attach\\_target\\_group\\_to\\_asg) | Whether to register ASG instances in the target group. Disable if using ECS which registers targets itself. | `bool` | `true` | no |\n| \u003ca name=\"input_autoscaling_target_cpu_load\"\u003e\u003c/a\u003e [autoscaling\\_target\\_cpu\\_load](#input\\_autoscaling\\_target\\_cpu\\_load) | Target CPU load for autoscaling | `number` | `60` | no |\n| \u003ca name=\"input_backend_subnets\"\u003e\u003c/a\u003e [backend\\_subnets](#input\\_backend\\_subnets) | Subnet ids where EC2 instances should be present | `list(string)` | n/a | yes |\n| \u003ca name=\"input_certificate_issuers\"\u003e\u003c/a\u003e [certificate\\_issuers](#input\\_certificate\\_issuers) | List of certificate authority domains allowed to issue certificates for this domain (e.g., [\"amazon.com\", \"letsencrypt.org\"]). The module will format these as CAA records. | `list(string)` | \u003cpre\u003e[\u003cbr/\u003e  \"amazon.com\"\u003cbr/\u003e]\u003c/pre\u003e | no |\n| \u003ca name=\"input_dns_a_records\"\u003e\u003c/a\u003e [dns\\_a\\_records](#input\\_dns\\_a\\_records) | List of A records in the zone\\_id that will resolve to the ALB dns name. | `list(string)` | \u003cpre\u003e[\u003cbr/\u003e  \"\"\u003cbr/\u003e]\u003c/pre\u003e | no |\n| \u003ca name=\"input_dns_routing_policy\"\u003e\u003c/a\u003e [dns\\_routing\\_policy](#input\\_dns\\_routing\\_policy) | DNS routing policy for Route53 A records.\u003cbr/\u003e\u003cbr/\u003e**Available policies:**\u003cbr/\u003e- `simple` (default): Standard DNS routing. Each A record resolves directly to the ALB.\u003cbr/\u003e  Best for: Single deployments, standard configurations.\u003cbr/\u003e\u003cbr/\u003e- `weighted`: Enables Route53 weighted routing policy for zero-downtime migrations.\u003cbr/\u003e  Requires: dns\\_set\\_identifier must be set.\u003cbr/\u003e  Best for: Blue/green deployments, gradual traffic migration, A/B testing.\u003cbr/\u003e\u003cbr/\u003e**Migration workflow example:**\u003cbr/\u003e1. Deploy new service with `dns_routing_policy = \"weighted\"`, `dns_weight = 0`\u003cbr/\u003e2. Convert existing service to weighted with `dns_weight = 100`\u003cbr/\u003e3. Gradually shift: 90/10 → 50/50 → 10/90 → 0/100\u003cbr/\u003e4. Remove old service\u003cbr/\u003e\u003cbr/\u003e**Note:** When using weighted routing, you can have multiple modules create\u003cbr/\u003erecords for the same DNS name, each with a unique dns\\_set\\_identifier.\u003cbr/\u003e\u003cbr/\u003e**Note:** This routing policy applies to ALL DNS records created via dns\\_a\\_records.\u003cbr/\u003eIf you need different routing policies per record, deploy separate module instances. | `string` | `\"simple\"` | no |\n| \u003ca name=\"input_dns_set_identifier\"\u003e\u003c/a\u003e [dns\\_set\\_identifier](#input\\_dns\\_set\\_identifier) | Unique identifier for weighted routing records.\u003cbr/\u003eRequired when dns\\_routing\\_policy is not \"simple\".\u003cbr/\u003e\u003cbr/\u003eThis identifier distinguishes between multiple weighted records with the same name.\u003cbr/\u003eMust be unique across all weighted records for the same DNS name.\u003cbr/\u003e\u003cbr/\u003e**Recommended naming conventions:**\u003cbr/\u003e- Environment-based: \"production-blue\", \"production-green\"\u003cbr/\u003e- Version-based: \"v1\", \"v2\", \"v3\"\u003cbr/\u003e- Region-based: \"us-west-2-primary\", \"us-east-1-secondary\"\u003cbr/\u003e- Module-based: \"website-pod-main\", \"ecs-service-new\"\u003cbr/\u003e\u003cbr/\u003e**Example:**\u003cpre\u003ehcl\u003cbr/\u003e# Old service (being deprecated)\u003cbr/\u003edns_routing_policy = \"weighted\"\u003cbr/\u003edns_set_identifier = \"legacy-service\"\u003cbr/\u003edns_weight         = 10\u003cbr/\u003e\u003cbr/\u003e# New service (receiving traffic)\u003cbr/\u003edns_routing_policy = \"weighted\"\u003cbr/\u003edns_set_identifier = \"new-service\"\u003cbr/\u003edns_weight         = 90\u003c/pre\u003e | `string` | `null` | no |\n| \u003ca name=\"input_dns_weight\"\u003e\u003c/a\u003e [dns\\_weight](#input\\_dns\\_weight) | Weight for Route53 weighted routing policy (0-255).\u003cbr/\u003eOnly used when dns\\_routing\\_policy = \"weighted\".\u003cbr/\u003e\u003cbr/\u003e**Weight behavior:**\u003cbr/\u003e- 0: No traffic routed to this endpoint (useful during initial deployment)\u003cbr/\u003e- 255: Maximum weight priority\u003cbr/\u003e- Traffic distribution = (this\\_weight / sum\\_of\\_all\\_weights) * 100%\u003cbr/\u003e\u003cbr/\u003e**Examples:**\u003cbr/\u003e- Two endpoints with weights 100 and 100: 50% each\u003cbr/\u003e- Two endpoints with weights 100 and 0: 100% to first, 0% to second\u003cbr/\u003e- Three endpoints with weights 70, 20, 10: 70%, 20%, 10%\u003cbr/\u003e\u003cbr/\u003e**Migration tip:** Start new deployments with weight=0, then gradually increase. | `number` | `100` | no |\n| \u003ca name=\"input_enable_deletion_protection\"\u003e\u003c/a\u003e [enable\\_deletion\\_protection](#input\\_enable\\_deletion\\_protection) | Prevent load balancer from destroying | `bool` | `false` | no |\n| \u003ca name=\"input_environment\"\u003e\u003c/a\u003e [environment](#input\\_environment) | Name of environment | `string` | `\"development\"` | no |\n| \u003ca name=\"input_extra_security_groups_backend\"\u003e\u003c/a\u003e [extra\\_security\\_groups\\_backend](#input\\_extra\\_security\\_groups\\_backend) | A list of security group ids to assign to backend instances | `list(string)` | `[]` | no |\n| \u003ca name=\"input_health_check_grace_period\"\u003e\u003c/a\u003e [health\\_check\\_grace\\_period](#input\\_health\\_check\\_grace\\_period) | ASG will wait up to this number of seconds for instance to become healthy | `number` | `600` | no |\n| \u003ca name=\"input_health_check_type\"\u003e\u003c/a\u003e [health\\_check\\_type](#input\\_health\\_check\\_type) | Type of healthcheck the ASG uses. Can be EC2 or ELB. | `string` | `\"ELB\"` | no |\n| \u003ca name=\"input_instance_profile_permissions\"\u003e\u003c/a\u003e [instance\\_profile\\_permissions](#input\\_instance\\_profile\\_permissions) | A JSON policy document to attach to the instance profile.\u003cbr/\u003eThis should be the output of an aws\\_iam\\_policy\\_document data source.\u003cbr/\u003e\u003cbr/\u003eExample:\u003cbr/\u003e  instance\\_profile\\_permissions = data.aws\\_iam\\_policy\\_document.my\\_policy.json\u003cbr/\u003e\u003cbr/\u003eIf not specified, defaults to a minimal policy allowing sts:GetCallerIdentity. | `string` | `null` | no |\n| \u003ca name=\"input_instance_role_name\"\u003e\u003c/a\u003e [instance\\_role\\_name](#input\\_instance\\_role\\_name) | If specified, the instance profile role will have this name. Otherwise, the role name will be generated. | `string` | `null` | no |\n| \u003ca name=\"input_instance_type\"\u003e\u003c/a\u003e [instance\\_type](#input\\_instance\\_type) | EC2 instances type | `string` | `\"t3.micro\"` | no |\n| \u003ca name=\"input_internet_gateway_id\"\u003e\u003c/a\u003e [internet\\_gateway\\_id](#input\\_internet\\_gateway\\_id) | Not used, but AWS Internet Gateway must be present. Ensure by passing its id. | `string` | `null` | no |\n| \u003ca name=\"input_key_pair_name\"\u003e\u003c/a\u003e [key\\_pair\\_name](#input\\_key\\_pair\\_name) | SSH keypair name to be deployed in EC2 instances | `string` | n/a | yes |\n| \u003ca name=\"input_load_balancing_algorithm_type\"\u003e\u003c/a\u003e [load\\_balancing\\_algorithm\\_type](#input\\_load\\_balancing\\_algorithm\\_type) | Load balancing algorithm for the target group.\u003cbr/\u003e\u003cbr/\u003e**Available algorithms:**\u003cbr/\u003e- `round_robin` (default): Distributes requests evenly across healthy targets.\u003cbr/\u003e  Best for: General-purpose workloads with similar request processing times.\u003cbr/\u003e\u003cbr/\u003e- `least_outstanding_requests`: Routes to the target with fewest in-flight requests.\u003cbr/\u003e  Best for: Workloads with varying request processing times, long-running requests,\u003cbr/\u003e  or when backend instances have different capacities.\u003cbr/\u003e\u003cbr/\u003e**Note:** When stickiness is enabled, the algorithm applies only to initial\u003cbr/\u003esession assignment. Subsequent requests from the same client go to the same target. | `string` | `\"round_robin\"` | no |\n| \u003ca name=\"input_max_instance_lifetime_days\"\u003e\u003c/a\u003e [max\\_instance\\_lifetime\\_days](#input\\_max\\_instance\\_lifetime\\_days) | The maximum amount of time, in \\_days\\_, that an instance can be in service, values must be either equal to 0 or between 7 and 365 days. | `number` | `30` | no |\n| \u003ca name=\"input_min_healthy_percentage\"\u003e\u003c/a\u003e [min\\_healthy\\_percentage](#input\\_min\\_healthy\\_percentage) | Amount of capacity in the Auto Scaling group that must remain healthy during an instance refresh to allow the operation to continue, as a percentage of the desired capacity of the Auto Scaling group. | `number` | `100` | no |\n| \u003ca name=\"input_on_demand_base_capacity\"\u003e\u003c/a\u003e [on\\_demand\\_base\\_capacity](#input\\_on\\_demand\\_base\\_capacity) | If specified, the ASG will request spot instances and this will be the minimal number of on-demand instances. | `number` | `null` | no |\n| \u003ca name=\"input_protect_from_scale_in\"\u003e\u003c/a\u003e [protect\\_from\\_scale\\_in](#input\\_protect\\_from\\_scale\\_in) | Whether newly launched instances are automatically protected from termination by Amazon EC2 Auto Scaling when scaling in. | `bool` | `false` | no |\n| \u003ca name=\"input_root_volume_size\"\u003e\u003c/a\u003e [root\\_volume\\_size](#input\\_root\\_volume\\_size) | Root volume size in EC2 instance in Gigabytes | `number` | `30` | no |\n| \u003ca name=\"input_service_name\"\u003e\u003c/a\u003e [service\\_name](#input\\_service\\_name) | Descriptive name of a service that will use this VPC | `string` | `\"website\"` | no |\n| \u003ca name=\"input_sns_topic_alarm_arn\"\u003e\u003c/a\u003e [sns\\_topic\\_alarm\\_arn](#input\\_sns\\_topic\\_alarm\\_arn) | ARN of SNS topic for Cloudwatch alarms on base EC2 instance. | `string` | `null` | no |\n| \u003ca name=\"input_ssh_cidr_block\"\u003e\u003c/a\u003e [ssh\\_cidr\\_block](#input\\_ssh\\_cidr\\_block) | CIDR range that is allowed to SSH into the backend instances.  Format is a.b.c.d/\u003cprefix\u003e. | `string` | `null` | no |\n| \u003ca name=\"input_stickiness_enabled\"\u003e\u003c/a\u003e [stickiness\\_enabled](#input\\_stickiness\\_enabled) | If true, enable stickiness on the target group ensuring a clients is forwarded to the same target. | `bool` | `true` | no |\n| \u003ca name=\"input_subnets\"\u003e\u003c/a\u003e [subnets](#input\\_subnets) | Subnet ids where load balancer should be present | `list(string)` | n/a | yes |\n| \u003ca name=\"input_tags\"\u003e\u003c/a\u003e [tags](#input\\_tags) | Tags to apply to resources creatded by the module. | `map(string)` | `{}` | no |\n| \u003ca name=\"input_target_group_deregistration_delay\"\u003e\u003c/a\u003e [target\\_group\\_deregistration\\_delay](#input\\_target\\_group\\_deregistration\\_delay) | Time in seconds for ALB to wait before deregistering a target.\u003cbr/\u003eDuring this time, the target continues to receive existing connections\u003cbr/\u003ebut no new connections. This allows in-flight requests to complete.\u003cbr/\u003e\u003cbr/\u003eCommon use cases:\u003cbr/\u003e- Reduce for faster deployments (e.g., 30s for stateless apps)\u003cbr/\u003e- Increase for long-running requests (e.g., 600s for file uploads)\u003cbr/\u003e\u003cbr/\u003eValid range: 0-3600 seconds. AWS default is 300 seconds. | `number` | `300` | no |\n| \u003ca name=\"input_target_group_port\"\u003e\u003c/a\u003e [target\\_group\\_port](#input\\_target\\_group\\_port) | TCP port that a target listens to to serve requests from the load balancer. | `number` | `80` | no |\n| \u003ca name=\"input_target_group_protocol\"\u003e\u003c/a\u003e [target\\_group\\_protocol](#input\\_target\\_group\\_protocol) | Protocol for the target group.\u003cbr/\u003eUse HTTP for standard backend communication (ALB terminates SSL).\u003cbr/\u003eUse HTTPS for end-to-end encryption to backend instances. | `string` | `\"HTTP\"` | no |\n| \u003ca name=\"input_target_group_type\"\u003e\u003c/a\u003e [target\\_group\\_type](#input\\_target\\_group\\_type) | Target group type: instance, ip, alb. Default is instance. | `string` | `\"instance\"` | no |\n| \u003ca name=\"input_upstream_module\"\u003e\u003c/a\u003e [upstream\\_module](#input\\_upstream\\_module) | Module that called this module. | `string` | `null` | no |\n| \u003ca name=\"input_userdata\"\u003e\u003c/a\u003e [userdata](#input\\_userdata) | userdata for cloud-init to provision EC2 instances | `string` | n/a | yes |\n| \u003ca name=\"input_vanta_contains_ephi\"\u003e\u003c/a\u003e [vanta\\_contains\\_ephi](#input\\_vanta\\_contains\\_ephi) | This tag allows administrators to define whether or not a resource contains electronically Protected Health Information (ePHI). It can be set to either (true) or if they do not have ephi data (false). | `bool` | `false` | no |\n| \u003ca name=\"input_vanta_contains_user_data\"\u003e\u003c/a\u003e [vanta\\_contains\\_user\\_data](#input\\_vanta\\_contains\\_user\\_data) | his tag allows administrators to define whether or not a resource contains user data (true) or if they do not contain user data (false). | `bool` | `false` | no |\n| \u003ca name=\"input_vanta_description\"\u003e\u003c/a\u003e [vanta\\_description](#input\\_vanta\\_description) | This tag allows administrators to set a description, for instance, or add any other descriptive information. | `string` | `null` | no |\n| \u003ca name=\"input_vanta_no_alert\"\u003e\u003c/a\u003e [vanta\\_no\\_alert](#input\\_vanta\\_no\\_alert) | Administrators can add this tag to mark a resource as out of scope for their audit. If this tag is added, the administrator will need to set a reason for why it's not relevant to their audit. | `string` | `null` | no |\n| \u003ca name=\"input_vanta_owner\"\u003e\u003c/a\u003e [vanta\\_owner](#input\\_vanta\\_owner) | The email address of the instance's owner, and it should be set to the email address of a user in Vanta. An owner will not be assigned if there is no user in Vanta with the email specified. | `string` | `null` | no |\n| \u003ca name=\"input_vanta_production_environments\"\u003e\u003c/a\u003e [vanta\\_production\\_environments](#input\\_vanta\\_production\\_environments) | Environment names to consider production grade in Vanta. | `list(string)` | \u003cpre\u003e[\u003cbr/\u003e  \"production\",\u003cbr/\u003e  \"prod\"\u003cbr/\u003e]\u003c/pre\u003e | no |\n| \u003ca name=\"input_vanta_user_data_stored\"\u003e\u003c/a\u003e [vanta\\_user\\_data\\_stored](#input\\_vanta\\_user\\_data\\_stored) | This tag allows administrators to describe the type of user data the instance contains. | `string` | `null` | no |\n| \u003ca name=\"input_wait_for_capacity_timeout\"\u003e\u003c/a\u003e [wait\\_for\\_capacity\\_timeout](#input\\_wait\\_for\\_capacity\\_timeout) | How much time to wait until all instances are healthy | `string` | `\"20m\"` | no |\n| \u003ca name=\"input_zone_id\"\u003e\u003c/a\u003e [zone\\_id](#input\\_zone\\_id) | Domain name zone ID where the website will be available | `string` | n/a | yes |\n\n## Outputs\n\n| Name | Description |\n|------|-------------|\n| \u003ca name=\"output_acm_certificate_arn\"\u003e\u003c/a\u003e [acm\\_certificate\\_arn](#output\\_acm\\_certificate\\_arn) | ARN of the ACM certificate used by the load balancer |\n| \u003ca name=\"output_alarm_sns_topic_arn\"\u003e\u003c/a\u003e [alarm\\_sns\\_topic\\_arn](#output\\_alarm\\_sns\\_topic\\_arn) | ARN of the SNS topic for ALB CloudWatch alarms (if created). IMPORTANT: Email subscribers must confirm their subscription via the AWS confirmation email to receive notifications. |\n| \u003ca name=\"output_alarm_sns_topic_name\"\u003e\u003c/a\u003e [alarm\\_sns\\_topic\\_name](#output\\_alarm\\_sns\\_topic\\_name) | Name of the SNS topic for ALB CloudWatch alarms (if created) |\n| \u003ca name=\"output_alb_security_group_id\"\u003e\u003c/a\u003e [alb\\_security\\_group\\_id](#output\\_alb\\_security\\_group\\_id) | ID of the ALB security group |\n| \u003ca name=\"output_asg_arn\"\u003e\u003c/a\u003e [asg\\_arn](#output\\_asg\\_arn) | ARN of the created autoscaling group |\n| \u003ca name=\"output_asg_name\"\u003e\u003c/a\u003e [asg\\_name](#output\\_asg\\_name) | Name of the created autoscaling group |\n| \u003ca name=\"output_backend_security_group\"\u003e\u003c/a\u003e [backend\\_security\\_group](#output\\_backend\\_security\\_group) | Map with security group id and rules |\n| \u003ca name=\"output_backend_security_group_id\"\u003e\u003c/a\u003e [backend\\_security\\_group\\_id](#output\\_backend\\_security\\_group\\_id) | ID of the backend instances security group |\n| \u003ca name=\"output_cloudwatch_alarm_arns\"\u003e\u003c/a\u003e [cloudwatch\\_alarm\\_arns](#output\\_cloudwatch\\_alarm\\_arns) | ARNs of CloudWatch alarms created for ALB and ASG monitoring |\n| \u003ca name=\"output_dns_name\"\u003e\u003c/a\u003e [dns\\_name](#output\\_dns\\_name) | DNS name of the load balancer. |\n| \u003ca name=\"output_instance_profile_name\"\u003e\u003c/a\u003e [instance\\_profile\\_name](#output\\_instance\\_profile\\_name) | EC2 instance profile name. |\n| \u003ca name=\"output_instance_role_arn\"\u003e\u003c/a\u003e [instance\\_role\\_arn](#output\\_instance\\_role\\_arn) | ARN of the instance role. |\n| \u003ca name=\"output_instance_role_name\"\u003e\u003c/a\u003e [instance\\_role\\_name](#output\\_instance\\_role\\_name) | Name of the instance role. |\n| \u003ca name=\"output_instance_role_policy_arn\"\u003e\u003c/a\u003e [instance\\_role\\_policy\\_arn](#output\\_instance\\_role\\_policy\\_arn) | Policy ARN attached to EC2 instance profile. |\n| \u003ca name=\"output_instance_role_policy_attachment\"\u003e\u003c/a\u003e [instance\\_role\\_policy\\_attachment](#output\\_instance\\_role\\_policy\\_attachment) | Policy attachment id. |\n| \u003ca name=\"output_instance_role_policy_name\"\u003e\u003c/a\u003e [instance\\_role\\_policy\\_name](#output\\_instance\\_role\\_policy\\_name) | Policy name attached to EC2 instance profile. |\n| \u003ca name=\"output_load_balancer_arn\"\u003e\u003c/a\u003e [load\\_balancer\\_arn](#output\\_load\\_balancer\\_arn) | Load Balancer ARN |\n| \u003ca name=\"output_load_balancer_arn_suffix\"\u003e\u003c/a\u003e [load\\_balancer\\_arn\\_suffix](#output\\_load\\_balancer\\_arn\\_suffix) | Load Balancer ARN suffix for use in CloudWatch metrics dimensions. |\n| \u003ca name=\"output_load_balancer_dns_name\"\u003e\u003c/a\u003e [load\\_balancer\\_dns\\_name](#output\\_load\\_balancer\\_dns\\_name) | Load balancer DNS name. |\n| \u003ca name=\"output_load_balancer_security_groups\"\u003e\u003c/a\u003e [load\\_balancer\\_security\\_groups](#output\\_load\\_balancer\\_security\\_groups) | Security groups associated with the load balancer |\n| \u003ca name=\"output_load_balancing_algorithm_type\"\u003e\u003c/a\u003e [load\\_balancing\\_algorithm\\_type](#output\\_load\\_balancing\\_algorithm\\_type) | Load balancing algorithm used by the target group (round\\_robin or least\\_outstanding\\_requests). |\n| \u003ca name=\"output_ssl_listener_arn\"\u003e\u003c/a\u003e [ssl\\_listener\\_arn](#output\\_ssl\\_listener\\_arn) | SSL listener ARN |\n| \u003ca name=\"output_target_group_arn\"\u003e\u003c/a\u003e [target\\_group\\_arn](#output\\_target\\_group\\_arn) | Target group ARN that listens to the service port. |\n| \u003ca name=\"output_target_group_arn_suffix\"\u003e\u003c/a\u003e [target\\_group\\_arn\\_suffix](#output\\_target\\_group\\_arn\\_suffix) | Target group ARN suffix for use in CloudWatch metrics dimensions. |\n| \u003ca name=\"output_zone_id\"\u003e\u003c/a\u003e [zone\\_id](#output\\_zone\\_id) | Zone id where A records are created for the service. |\n\u003c!-- END_TF_DOCS --\u003e\n\n## Contributing\n\nContributions are welcome! Please see [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.\n\n## License\n\nThis project is licensed under the Apache License 2.0 - see the [LICENSE](LICENSE) file for details.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Finfrahouse%2Fterraform-aws-website-pod","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Finfrahouse%2Fterraform-aws-website-pod","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Finfrahouse%2Fterraform-aws-website-pod/lists"}