https://github.com/infrahouse/terraform-aws-github-backup
Terraform module for Lambda-based GitHub organization backup to S3 with scheduled execution, repository cloning, and CloudWatch monitoring.
https://github.com/infrahouse/terraform-aws-github-backup
aws backup github infrahouse lambda terraform terraform-module
Last synced: 5 months ago
JSON representation
Terraform module for Lambda-based GitHub organization backup to S3 with scheduled execution, repository cloning, and CloudWatch monitoring.
- Host: GitHub
- URL: https://github.com/infrahouse/terraform-aws-github-backup
- Owner: infrahouse
- License: apache-2.0
- Created: 2024-10-08T19:45:26.000Z (over 1 year ago)
- Default Branch: main
- Last Pushed: 2026-01-28T03:24:00.000Z (5 months ago)
- Last Synced: 2026-01-28T19:13:53.167Z (5 months ago)
- Topics: aws, backup, github, infrahouse, lambda, terraform, terraform-module
- Language: HCL
- Homepage: https://infrahouse.github.io/terraform-aws-github-backup
- Size: 157 KB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 4
-
Metadata Files:
- Readme: README.md
- Contributing: CONTRIBUTING.md
- License: LICENSE
- Security: SECURITY.md
Awesome Lists containing this project
README
# terraform-aws-github-backup
[](https://infrahouse.com/contact)
[](https://infrahouse.github.io/terraform-aws-github-backup/)
[](https://registry.terraform.io/modules/infrahouse/github-backup/aws/latest)
[](https://github.com/infrahouse/terraform-aws-github-backup/releases/latest)
[](https://github.com/infrahouse/terraform-aws-github-backup/actions/workflows/vuln-scanner-pr.yml)
[](LICENSE)
[](https://aws.amazon.com/ecs/)
[](https://aws.amazon.com/s3/)
A Terraform module that backs up all repositories in a GitHub organization to S3
using an ECS Fargate scheduled task. Designed to be deployed by the customer in
their own AWS account with zero operational dependency on InfraHouse.
## Why This Module?
- **No always-on compute** -- Fargate runs on a schedule, you only pay for backup time
- **No Lambda timeout limits** -- large organizations with many repos back up without issues
- **Customer-owned GitHub App** -- no shared credentials, short-lived tokens only
- **Cross-region replication** -- S3 replication for disaster recovery
- **Full git history** -- uses `git bundle` for complete, portable backups
## Features
- ECS Fargate scheduled task (EventBridge) for daily/custom-schedule backups
- S3 bucket with versioning and configurable retention lifecycle
- Cross-region S3 replication (AWS provider v6, no aliased providers)
- CloudWatch Logs, metrics, and alarm on backup failure
- Least-privilege IAM roles for task execution and task runtime
- Customer creates and owns their own GitHub App (read-only access)
## Quick Start
1. **Create a GitHub App** in your organization (see [Getting Started](https://infrahouse.github.io/terraform-aws-github-backup/#getting-started))
2. **Deploy the module** -- the module creates a Secrets Manager secret for the App private key:
```hcl
module "github_backup" {
source = "registry.infrahouse.com/infrahouse/github-backup/aws"
version = "2.0.1"
github_app_id = "123456"
github_app_installation_id = "78901234"
alarm_emails = ["devops@example.com"]
github_app_key_secret_writers = [aws_iam_role.deployer.arn]
replica_region = "us-east-1"
subnets = ["subnet-abc123", "subnet-def456"]
# Optional
schedule_expression = "rate(1 day)"
backup_retention_days = 365
}
```
3. **Store the App private key** in the secret created by the module (output: `github_app_key_secret_arn`)
## Documentation
- [Getting Started](https://infrahouse.github.io/terraform-aws-github-backup/#getting-started)
- [Configuration](https://infrahouse.github.io/terraform-aws-github-backup/#configuration)
- [Architecture](https://infrahouse.github.io/terraform-aws-github-backup/#architecture)
- [Restoring from a Backup](https://infrahouse.github.io/terraform-aws-github-backup/#restoring-from-a-backup)
## Requirements
| Name | Version |
|------|---------|
| [terraform](#requirement\_terraform) | ~> 1.5 |
| [aws](#requirement\_aws) | ~> 6.0 |
## Providers
| Name | Version |
|------|---------|
| [aws](#provider\_aws) | ~> 6.0 |
## Modules
| Name | Source | Version |
|------|--------|---------|
| [backup\_bucket](#module\_backup\_bucket) | registry.infrahouse.com/infrahouse/s3-bucket/aws | 0.3.1 |
| [github\_app\_key](#module\_github\_app\_key) | registry.infrahouse.com/infrahouse/secret/aws | ~> 1.1 |
## Resources
| Name | Type |
|------|------|
| [aws_cloudwatch_event_rule.backup](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_event_rule) | resource |
| [aws_cloudwatch_event_target.backup](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_event_target) | resource |
| [aws_cloudwatch_log_group.backup](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_log_group) | resource |
| [aws_cloudwatch_metric_alarm.backup_failure](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_metric_alarm) | resource |
| [aws_cloudwatch_metric_alarm.task_not_running](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_metric_alarm) | resource |
| [aws_ecs_cluster.backup](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ecs_cluster) | resource |
| [aws_ecs_task_definition.backup](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ecs_task_definition) | resource |
| [aws_iam_role.eventbridge](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource |
| [aws_iam_role.execution](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource |
| [aws_iam_role.replication](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource |
| [aws_iam_role.task](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource |
| [aws_iam_role_policy.eventbridge](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy) | resource |
| [aws_iam_role_policy.replication](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy) | resource |
| [aws_iam_role_policy.task](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy) | resource |
| [aws_iam_role_policy_attachment.execution](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource |
| [aws_s3_bucket.replica](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket) | resource |
| [aws_s3_bucket_lifecycle_configuration.backup](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_lifecycle_configuration) | resource |
| [aws_s3_bucket_lifecycle_configuration.replica](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_lifecycle_configuration) | resource |
| [aws_s3_bucket_public_access_block.replica](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_public_access_block) | resource |
| [aws_s3_bucket_replication_configuration.backup](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_replication_configuration) | resource |
| [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 |
| [aws_s3_bucket_versioning.replica](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_versioning) | resource |
| [aws_security_group.backup](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group) | resource |
| [aws_sns_topic.alarms](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/sns_topic) | resource |
| [aws_sns_topic_subscription.alarm_emails](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/sns_topic_subscription) | resource |
| [aws_vpc_security_group_egress_rule.all_outbound](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/vpc_security_group_egress_rule) | resource |
| [aws_caller_identity.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/caller_identity) | data source |
| [aws_default_tags.provider](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/default_tags) | data source |
| [aws_iam_policy.ecs_task_execution](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy) | data source |
| [aws_iam_policy_document.eventbridge_assume_role](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
| [aws_iam_policy_document.eventbridge_permissions](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
| [aws_iam_policy_document.execution_assume_role](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
| [aws_iam_policy_document.replication_assume_role](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
| [aws_iam_policy_document.replication_permissions](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
| [aws_iam_policy_document.task_assume_role](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
| [aws_iam_policy_document.task_permissions](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
| [aws_region.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/region) | data source |
| [aws_subnet.selected](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/subnet) | data source |
## Inputs
| Name | Description | Type | Default | Required |
|------|-------------|------|---------|:--------:|
| [alarm\_emails](#input\_alarm\_emails) | List of email addresses to receive CloudWatch alarm
notifications. AWS will send confirmation emails that
must be accepted. | `list(string)` | n/a | yes |
| [backup\_retention\_days](#input\_backup\_retention\_days) | Number of days to retain backups in S3 before
expiration. Set to 0 to disable expiration. | `number` | `365` | no |
| [environment](#input\_environment) | Name of environment. | `string` | `"development"` | no |
| [force\_destroy](#input\_force\_destroy) | Allow destroying S3 buckets even when they contain
objects. Set to true only for testing. | `bool` | `false` | no |
| [github\_app\_id](#input\_github\_app\_id) | The GitHub App ID. Found in the App's settings page. | `string` | n/a | yes |
| [github\_app\_installation\_id](#input\_github\_app\_installation\_id) | The installation ID of the GitHub App on
the target organization. | `string` | n/a | yes |
| [github\_app\_key\_secret\_writers](#input\_github\_app\_key\_secret\_writers) | List of IAM role ARNs that are allowed to write
the GitHub App private key (PEM) into the secret
created by this module. | `list(string)` | n/a | yes |
| [image\_uri](#input\_image\_uri) | Docker image URI for the backup runner.
Defaults to the InfraHouse public ECR image tagged "latest".
For production use, consider pinning to a specific commit SHA tag
(e.g., "public.ecr.aws/infrahouse/github-backup:abc1234")
to avoid unexpected changes. | `string` | `"public.ecr.aws/infrahouse/github-backup:latest"` | no |
| [log\_group\_kms\_key\_arn](#input\_log\_group\_kms\_key\_arn) | ARN of a KMS key to encrypt the CloudWatch Log Group.
If null, logs are encrypted with the default
AWS-managed key. | `string` | `null` | no |
| [log\_retention\_days](#input\_log\_retention\_days) | Number of days to retain CloudWatch logs. | `number` | `365` | no |
| [replica\_region](#input\_replica\_region) | AWS region for cross-region backup replication. | `string` | n/a | yes |
| [s3\_bucket\_name](#input\_s3\_bucket\_name) | Name for the S3 backup bucket.
If null, a name is auto-generated. | `string` | `null` | no |
| [schedule\_expression](#input\_schedule\_expression) | EventBridge schedule expression for backup frequency.
Examples: "rate(1 day)", "cron(0 2 * * ? *)" | `string` | `"rate(1 day)"` | no |
| [service\_name](#input\_service\_name) | Descriptive name of the service.
Used for naming resources. | `string` | `"github-backup"` | no |
| [subnets](#input\_subnets) | List of subnet IDs for the Fargate task.
The subnets must have outbound internet access
(GitHub API, S3, etc.) — either private subnets
with a NAT gateway or public subnets.
Public IP assignment is detected automatically
from the subnet configuration. | `list(string)` | n/a | yes |
| [tags](#input\_tags) | Tags to apply to all resources. | `map(string)` | `{}` | no |
| [task\_cpu](#input\_task\_cpu) | CPU units for the Fargate task (1024 = 1 vCPU). | `number` | `1024` | no |
| [task\_ephemeral\_storage\_gb](#input\_task\_ephemeral\_storage\_gb) | Ephemeral storage (GiB) for the Fargate task.
Must be large enough to hold the biggest single
repository mirror and its git bundle simultaneously. | `number` | `50` | no |
| [task\_memory](#input\_task\_memory) | Memory (MiB) for the Fargate task. | `number` | `2048` | no |
## Outputs
| Name | Description |
|------|-------------|
| [ecs\_cluster\_arn](#output\_ecs\_cluster\_arn) | ARN of the ECS cluster. |
| [ecs\_cluster\_name](#output\_ecs\_cluster\_name) | Name of the ECS cluster. |
| [github\_app\_key\_secret\_arn](#output\_github\_app\_key\_secret\_arn) | ARN of the Secrets Manager secret for the GitHub App private key. |
| [log\_group\_name](#output\_log\_group\_name) | Name of the CloudWatch log group. |
| [replica\_bucket\_arn](#output\_replica\_bucket\_arn) | ARN of the replica S3 bucket (cross-region). |
| [replica\_bucket\_name](#output\_replica\_bucket\_name) | Name of the replica S3 bucket (cross-region). |
| [s3\_bucket\_arn](#output\_s3\_bucket\_arn) | ARN of the S3 bucket where backups are stored. |
| [s3\_bucket\_name](#output\_s3\_bucket\_name) | Name of the S3 bucket where backups are stored. |
| [schedule\_rule\_arn](#output\_schedule\_rule\_arn) | ARN of the EventBridge schedule rule. |
| [security\_group\_id](#output\_security\_group\_id) | ID of the security group for the Fargate task. |
| [task\_definition\_arn](#output\_task\_definition\_arn) | ARN of the ECS task definition. |
| [task\_role\_arn](#output\_task\_role\_arn) | ARN of the IAM role used by the backup task. |
## Contributing
See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.
## License
[Apache 2.0](LICENSE)