{"id":39493370,"url":"https://github.com/infrahouse/terraform-aws-github-backup","last_synced_at":"2026-02-12T02:47:30.014Z","repository":{"id":262858001,"uuid":"869733707","full_name":"infrahouse/terraform-aws-github-backup","owner":"infrahouse","description":"Terraform module for Lambda-based GitHub organization backup to S3 with scheduled execution, repository cloning, and CloudWatch monitoring. ","archived":false,"fork":false,"pushed_at":"2026-01-28T03:24:00.000Z","size":161,"stargazers_count":0,"open_issues_count":4,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-01-28T19:13:53.167Z","etag":null,"topics":["aws","backup","github","infrahouse","lambda","terraform","terraform-module"],"latest_commit_sha":null,"homepage":"https://infrahouse.github.io/terraform-aws-github-backup","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":null,"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":"2024-10-08T19:45:26.000Z","updated_at":"2026-01-28T03:24:04.000Z","dependencies_parsed_at":"2025-05-31T17:47:35.198Z","dependency_job_id":"12320b6f-cb9d-46fa-ba5f-ef8d72cd969a","html_url":"https://github.com/infrahouse/terraform-aws-github-backup","commit_stats":null,"previous_names":["infrahouse/terraform-aws-github-backup"],"tags_count":17,"template":false,"template_full_name":null,"purl":"pkg:github/infrahouse/terraform-aws-github-backup","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/infrahouse%2Fterraform-aws-github-backup","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/infrahouse%2Fterraform-aws-github-backup/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/infrahouse%2Fterraform-aws-github-backup/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/infrahouse%2Fterraform-aws-github-backup/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/infrahouse","download_url":"https://codeload.github.com/infrahouse/terraform-aws-github-backup/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/infrahouse%2Fterraform-aws-github-backup/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29352843,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-12T01:03:07.613Z","status":"ssl_error","status_checked_at":"2026-02-12T01:00:51.346Z","response_time":97,"last_error":"SSL_read: 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":["aws","backup","github","infrahouse","lambda","terraform","terraform-module"],"created_at":"2026-01-18T05:38:35.515Z","updated_at":"2026-02-12T02:47:30.008Z","avatar_url":"https://github.com/infrahouse.png","language":"HCL","funding_links":[],"categories":[],"sub_categories":[],"readme":"# terraform-aws-github-backup\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-github-backup/)\n[![Registry](https://img.shields.io/badge/Terraform-Registry-purple?logo=terraform)](https://registry.terraform.io/modules/infrahouse/github-backup/aws/latest)\n[![Release](https://img.shields.io/github/release/infrahouse/terraform-aws-github-backup.svg)](https://github.com/infrahouse/terraform-aws-github-backup/releases/latest)\n[![Security](https://img.shields.io/github/actions/workflow/status/infrahouse/terraform-aws-github-backup/vuln-scanner-pr.yml?label=Security)](https://github.com/infrahouse/terraform-aws-github-backup/actions/workflows/vuln-scanner-pr.yml)\n[![License](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](LICENSE)\n\n[![AWS ECS](https://img.shields.io/badge/AWS-ECS-orange?logo=amazonecs)](https://aws.amazon.com/ecs/)\n[![AWS S3](https://img.shields.io/badge/AWS-S3-green?logo=amazons3)](https://aws.amazon.com/s3/)\n\nA Terraform module that backs up all repositories in a GitHub organization to S3\nusing an ECS Fargate scheduled task. Designed to be deployed by the customer in\ntheir own AWS account with zero operational dependency on InfraHouse.\n\n## Why This Module?\n\n- **No always-on compute** -- Fargate runs on a schedule, you only pay for backup time\n- **No Lambda timeout limits** -- large organizations with many repos back up without issues\n- **Customer-owned GitHub App** -- no shared credentials, short-lived tokens only\n- **Cross-region replication** -- S3 replication for disaster recovery\n- **Full git history** -- uses `git bundle` for complete, portable backups\n\n## Features\n\n- ECS Fargate scheduled task (EventBridge) for daily/custom-schedule backups\n- S3 bucket with versioning and configurable retention lifecycle\n- Cross-region S3 replication (AWS provider v6, no aliased providers)\n- CloudWatch Logs, metrics, and alarm on backup failure\n- Least-privilege IAM roles for task execution and task runtime\n- Customer creates and owns their own GitHub App (read-only access)\n\n## Quick Start\n\n1. **Create a GitHub App** in your organization (see [Getting Started](https://infrahouse.github.io/terraform-aws-github-backup/#getting-started))\n2. **Deploy the module** -- the module creates a Secrets Manager secret for the App private key:\n\n```hcl\nmodule \"github_backup\" {\n  source  = \"registry.infrahouse.com/infrahouse/github-backup/aws\"\n  version = \"2.0.1\"\n\n  github_app_id              = \"123456\"\n  github_app_installation_id = \"78901234\"\n\n  alarm_emails                  = [\"devops@example.com\"]\n  github_app_key_secret_writers = [aws_iam_role.deployer.arn]\n  replica_region                = \"us-east-1\"\n  subnets                       = [\"subnet-abc123\", \"subnet-def456\"]\n\n  # Optional\n  schedule_expression  = \"rate(1 day)\"\n  backup_retention_days = 365\n}\n```\n\n3. **Store the App private key** in the secret created by the module (output: `github_app_key_secret_arn`)\n\n## Documentation\n\n- [Getting Started](https://infrahouse.github.io/terraform-aws-github-backup/#getting-started)\n- [Configuration](https://infrahouse.github.io/terraform-aws-github-backup/#configuration)\n- [Architecture](https://infrahouse.github.io/terraform-aws-github-backup/#architecture)\n- [Restoring from a Backup](https://infrahouse.github.io/terraform-aws-github-backup/#restoring-from-a-backup)\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 6.0 |\n\n## Providers\n\n| Name | Version |\n|------|---------|\n| \u003ca name=\"provider_aws\"\u003e\u003c/a\u003e [aws](#provider\\_aws) | ~\u003e 6.0 |\n\n## Modules\n\n| Name | Source | Version |\n|------|--------|---------|\n| \u003ca name=\"module_backup_bucket\"\u003e\u003c/a\u003e [backup\\_bucket](#module\\_backup\\_bucket) | registry.infrahouse.com/infrahouse/s3-bucket/aws | 0.3.1 |\n| \u003ca name=\"module_github_app_key\"\u003e\u003c/a\u003e [github\\_app\\_key](#module\\_github\\_app\\_key) | registry.infrahouse.com/infrahouse/secret/aws | ~\u003e 1.1 |\n\n## Resources\n\n| Name | Type |\n|------|------|\n| [aws_cloudwatch_event_rule.backup](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_event_rule) | resource |\n| [aws_cloudwatch_event_target.backup](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_event_target) | resource |\n| [aws_cloudwatch_log_group.backup](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_log_group) | resource |\n| [aws_cloudwatch_metric_alarm.backup_failure](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_metric_alarm) | resource |\n| [aws_cloudwatch_metric_alarm.task_not_running](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_metric_alarm) | resource |\n| [aws_ecs_cluster.backup](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ecs_cluster) | resource |\n| [aws_ecs_task_definition.backup](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ecs_task_definition) | resource |\n| [aws_iam_role.eventbridge](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource |\n| [aws_iam_role.execution](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource |\n| [aws_iam_role.replication](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource |\n| [aws_iam_role.task](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource |\n| [aws_iam_role_policy.eventbridge](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy) | resource |\n| [aws_iam_role_policy.replication](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy) | resource |\n| [aws_iam_role_policy.task](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy) | resource |\n| [aws_iam_role_policy_attachment.execution](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource |\n| [aws_s3_bucket.replica](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket) | resource |\n| [aws_s3_bucket_lifecycle_configuration.backup](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_lifecycle_configuration) | resource |\n| [aws_s3_bucket_lifecycle_configuration.replica](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_lifecycle_configuration) | resource |\n| [aws_s3_bucket_public_access_block.replica](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_public_access_block) | resource |\n| [aws_s3_bucket_replication_configuration.backup](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_replication_configuration) | resource |\n| [aws_s3_bucket_server_side_encryption_configuration.replica](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_server_side_encryption_configuration) | resource |\n| [aws_s3_bucket_versioning.replica](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_versioning) | resource |\n| [aws_security_group.backup](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.all_outbound](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/vpc_security_group_egress_rule) | resource |\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_iam_policy.ecs_task_execution](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy) | data source |\n| [aws_iam_policy_document.eventbridge_assume_role](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |\n| [aws_iam_policy_document.eventbridge_permissions](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |\n| [aws_iam_policy_document.execution_assume_role](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |\n| [aws_iam_policy_document.replication_assume_role](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |\n| [aws_iam_policy_document.replication_permissions](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |\n| [aws_iam_policy_document.task_assume_role](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |\n| [aws_iam_policy_document.task_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_subnet.selected](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/subnet) | data source |\n\n## Inputs\n\n| Name | Description | Type | Default | Required |\n|------|-------------|------|---------|:--------:|\n| \u003ca name=\"input_alarm_emails\"\u003e\u003c/a\u003e [alarm\\_emails](#input\\_alarm\\_emails) | List of email addresses to receive CloudWatch alarm\u003cbr/\u003enotifications. AWS will send confirmation emails that\u003cbr/\u003emust be accepted. | `list(string)` | n/a | yes |\n| \u003ca name=\"input_backup_retention_days\"\u003e\u003c/a\u003e [backup\\_retention\\_days](#input\\_backup\\_retention\\_days) | Number of days to retain backups in S3 before\u003cbr/\u003eexpiration. Set to 0 to disable expiration. | `number` | `365` | no |\n| \u003ca name=\"input_environment\"\u003e\u003c/a\u003e [environment](#input\\_environment) | Name of environment. | `string` | `\"development\"` | no |\n| \u003ca name=\"input_force_destroy\"\u003e\u003c/a\u003e [force\\_destroy](#input\\_force\\_destroy) | Allow destroying S3 buckets even when they contain\u003cbr/\u003eobjects. Set to true only for testing. | `bool` | `false` | no |\n| \u003ca name=\"input_github_app_id\"\u003e\u003c/a\u003e [github\\_app\\_id](#input\\_github\\_app\\_id) | The GitHub App ID. Found in the App's settings page. | `string` | n/a | yes |\n| \u003ca name=\"input_github_app_installation_id\"\u003e\u003c/a\u003e [github\\_app\\_installation\\_id](#input\\_github\\_app\\_installation\\_id) | The installation ID of the GitHub App on\u003cbr/\u003ethe target organization. | `string` | n/a | yes |\n| \u003ca name=\"input_github_app_key_secret_writers\"\u003e\u003c/a\u003e [github\\_app\\_key\\_secret\\_writers](#input\\_github\\_app\\_key\\_secret\\_writers) | List of IAM role ARNs that are allowed to write\u003cbr/\u003ethe GitHub App private key (PEM) into the secret\u003cbr/\u003ecreated by this module. | `list(string)` | n/a | yes |\n| \u003ca name=\"input_image_uri\"\u003e\u003c/a\u003e [image\\_uri](#input\\_image\\_uri) | Docker image URI for the backup runner.\u003cbr/\u003eDefaults to the InfraHouse public ECR image tagged \"latest\".\u003cbr/\u003eFor production use, consider pinning to a specific commit SHA tag\u003cbr/\u003e(e.g., \"public.ecr.aws/infrahouse/github-backup:abc1234\")\u003cbr/\u003eto avoid unexpected changes. | `string` | `\"public.ecr.aws/infrahouse/github-backup:latest\"` | no |\n| \u003ca name=\"input_log_group_kms_key_arn\"\u003e\u003c/a\u003e [log\\_group\\_kms\\_key\\_arn](#input\\_log\\_group\\_kms\\_key\\_arn) | ARN of a KMS key to encrypt the CloudWatch Log Group.\u003cbr/\u003eIf null, logs are encrypted with the default\u003cbr/\u003eAWS-managed key. | `string` | `null` | no |\n| \u003ca name=\"input_log_retention_days\"\u003e\u003c/a\u003e [log\\_retention\\_days](#input\\_log\\_retention\\_days) | Number of days to retain CloudWatch logs. | `number` | `365` | no |\n| \u003ca name=\"input_replica_region\"\u003e\u003c/a\u003e [replica\\_region](#input\\_replica\\_region) | AWS region for cross-region backup replication. | `string` | n/a | yes |\n| \u003ca name=\"input_s3_bucket_name\"\u003e\u003c/a\u003e [s3\\_bucket\\_name](#input\\_s3\\_bucket\\_name) | Name for the S3 backup bucket.\u003cbr/\u003eIf null, a name is auto-generated. | `string` | `null` | no |\n| \u003ca name=\"input_schedule_expression\"\u003e\u003c/a\u003e [schedule\\_expression](#input\\_schedule\\_expression) | EventBridge schedule expression for backup frequency.\u003cbr/\u003eExamples: \"rate(1 day)\", \"cron(0 2 * * ? *)\" | `string` | `\"rate(1 day)\"` | no |\n| \u003ca name=\"input_service_name\"\u003e\u003c/a\u003e [service\\_name](#input\\_service\\_name) | Descriptive name of the service.\u003cbr/\u003eUsed for naming resources. | `string` | `\"github-backup\"` | no |\n| \u003ca name=\"input_subnets\"\u003e\u003c/a\u003e [subnets](#input\\_subnets) | List of subnet IDs for the Fargate task.\u003cbr/\u003eThe subnets must have outbound internet access\u003cbr/\u003e(GitHub API, S3, etc.) — either private subnets\u003cbr/\u003ewith a NAT gateway or public subnets.\u003cbr/\u003ePublic IP assignment is detected automatically\u003cbr/\u003efrom the subnet configuration. | `list(string)` | n/a | yes |\n| \u003ca name=\"input_tags\"\u003e\u003c/a\u003e [tags](#input\\_tags) | Tags to apply to all resources. | `map(string)` | `{}` | no |\n| \u003ca name=\"input_task_cpu\"\u003e\u003c/a\u003e [task\\_cpu](#input\\_task\\_cpu) | CPU units for the Fargate task (1024 = 1 vCPU). | `number` | `1024` | no |\n| \u003ca name=\"input_task_ephemeral_storage_gb\"\u003e\u003c/a\u003e [task\\_ephemeral\\_storage\\_gb](#input\\_task\\_ephemeral\\_storage\\_gb) | Ephemeral storage (GiB) for the Fargate task.\u003cbr/\u003eMust be large enough to hold the biggest single\u003cbr/\u003erepository mirror and its git bundle simultaneously. | `number` | `50` | no |\n| \u003ca name=\"input_task_memory\"\u003e\u003c/a\u003e [task\\_memory](#input\\_task\\_memory) | Memory (MiB) for the Fargate task. | `number` | `2048` | no |\n\n## Outputs\n\n| Name | Description |\n|------|-------------|\n| \u003ca name=\"output_ecs_cluster_arn\"\u003e\u003c/a\u003e [ecs\\_cluster\\_arn](#output\\_ecs\\_cluster\\_arn) | ARN of the ECS cluster. |\n| \u003ca name=\"output_ecs_cluster_name\"\u003e\u003c/a\u003e [ecs\\_cluster\\_name](#output\\_ecs\\_cluster\\_name) | Name of the ECS cluster. |\n| \u003ca name=\"output_github_app_key_secret_arn\"\u003e\u003c/a\u003e [github\\_app\\_key\\_secret\\_arn](#output\\_github\\_app\\_key\\_secret\\_arn) | ARN of the Secrets Manager secret for the GitHub App private key. |\n| \u003ca name=\"output_log_group_name\"\u003e\u003c/a\u003e [log\\_group\\_name](#output\\_log\\_group\\_name) | Name of the CloudWatch log group. |\n| \u003ca name=\"output_replica_bucket_arn\"\u003e\u003c/a\u003e [replica\\_bucket\\_arn](#output\\_replica\\_bucket\\_arn) | ARN of the replica S3 bucket (cross-region). |\n| \u003ca name=\"output_replica_bucket_name\"\u003e\u003c/a\u003e [replica\\_bucket\\_name](#output\\_replica\\_bucket\\_name) | Name of the replica S3 bucket (cross-region). |\n| \u003ca name=\"output_s3_bucket_arn\"\u003e\u003c/a\u003e [s3\\_bucket\\_arn](#output\\_s3\\_bucket\\_arn) | ARN of the S3 bucket where backups are stored. |\n| \u003ca name=\"output_s3_bucket_name\"\u003e\u003c/a\u003e [s3\\_bucket\\_name](#output\\_s3\\_bucket\\_name) | Name of the S3 bucket where backups are stored. |\n| \u003ca name=\"output_schedule_rule_arn\"\u003e\u003c/a\u003e [schedule\\_rule\\_arn](#output\\_schedule\\_rule\\_arn) | ARN of the EventBridge schedule rule. |\n| \u003ca name=\"output_security_group_id\"\u003e\u003c/a\u003e [security\\_group\\_id](#output\\_security\\_group\\_id) | ID of the security group for the Fargate task. |\n| \u003ca name=\"output_task_definition_arn\"\u003e\u003c/a\u003e [task\\_definition\\_arn](#output\\_task\\_definition\\_arn) | ARN of the ECS task definition. |\n| \u003ca name=\"output_task_role_arn\"\u003e\u003c/a\u003e [task\\_role\\_arn](#output\\_task\\_role\\_arn) | ARN of the IAM role used by the backup task. |\n\u003c!-- END_TF_DOCS --\u003e\n\n## Contributing\n\nSee [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.\n\n## License\n\n[Apache 2.0](LICENSE)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Finfrahouse%2Fterraform-aws-github-backup","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Finfrahouse%2Fterraform-aws-github-backup","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Finfrahouse%2Fterraform-aws-github-backup/lists"}