github-repository
🔒 Managing GitHub repos in a compliant way just got way easier.
One of the most common requirements for companies operating in regulated spaces is having consistent, enforceable repository configurations. Some teams have hacked together homegrown solutions, while others know it's something they need to do but never quite got to.
Our aws-github-repository component changes that by providing comprehensive GitHub repository management entirely through infrastructure as code.
Manage repos and all settings declaratively:
- Repository configuration (visibility, topics, features, merge settings)
- Environments with deployment protection and reviewers
- Variables and secrets pulling from AWS Secrets Manager and Parameter Store
- Branch protection rules and rule sets
- Webhooks, deploy keys, labels, and custom properties
- Team and user permissions
Import existing repos and bring them under management:
- Import existing repositories and their configurations
- Bring legacy repos under consistent management
- Maintain existing settings while adding compliance controls
Define repository archetypes once:
- Create abstract components for common configurations
- Use Atmos inheritance to automatically apply baseline compliance
- Every new repo inherits your security and compliance standards
- DRY up infrastructure with reusable templates
All powered by Atmos + a GitHub App for secure, auditable rollout across your organization.
Usage​
Stack Level: Regional
This component provides comprehensive GitHub repository management with support for advanced features like environments, webhooks, deploy keys, custom properties, and more.
Basic Usage​
Here's a simple example for creating a basic repository:
components:
terraform:
my-basic-repo:
vars:
enabled: true
owner: "my-organization"
repository:
name: "my-basic-repo"
description: "A basic repository with standard settings"
homepage_url: "https://github.com/my-organization/my-basic-repo"
visibility: "private"
default_branch: "main"
topics:
- terraform
- github
- infrastructure
teams:
devops: admin
developers: push
variables:
ENVIRONMENT: "production"
REGION: "us-east-1"
secrets:
AWS_ACCESS_KEY_ID: "nacl:dGVzdC1hY2Nlc3Mta2V5LWlkCg=="
DATABASE_URL: "ssm:///my-basic-repo/database-url"
Advanced Usage with Atmos Inheritance and Imports​
The component supports Atmos inheritance and imports to create DRY (Don't Repeat Yourself) configurations. This allows you to define common settings once and reuse them across multiple repositories.
For complete working examples of inheritance and imports, see the /examples folder in this repository.
Creating Abstract Components​
First, create abstract components that define common configurations:
# catalog/github/repo/defaults.yaml
components:
terraform:
github/repo/defaults:
metadata:
component: github-repository
type: abstract
vars:
enabled: true
# Common team permissions
teams:
devops: admin
# Default repository settings
repository:
homepage_url: "https://github.com/{{ .vars.owner }}/{{ .vars.repository.name }}"
topics:
- terraform
- github
default_branch: "main"
visibility: "private"
# Common features
auto_init: true
gitignore_template: "TeX"
license_template: "GPL-3.0"
# Merge settings
allow_merge_commit: true
allow_squash_merge: true
allow_rebase_merge: true
allow_auto_merge: true
# Branch protection
delete_branch_on_merge: true
web_commit_signoff_required: true
Creating a Master Defaults Component​
Combine abstract components into a master defaults component:
# catalog/github/defaults.yaml
import:
- catalog/github/repo/*
- catalog/github/environment/*
- catalog/github/ruleset/*
components:
terraform:
github/defaults:
metadata:
type: abstract
inherits:
- github/repo/defaults
- github/ruleset/branch-protection
- github/environment/defaults
Using Inheritance in Your Stacks​
Now you can create repositories that inherit from these defaults:
# orgs/mycompany/myteam.yaml
import:
- ./_defaults
- catalog/github/defaults
components:
terraform:
# Simple repository inheriting all defaults
my-simple-app:
metadata:
component: github-repository
inherits:
- github/defaults
vars:
repository:
name: my-simple-app
description: "My simple application"
topics:
- application
- nodejs
# Repository with custom overrides
my-custom-app:
metadata:
component: github-repository
inherits:
- github/defaults
vars:
repository:
name: my-custom-app
description: "My custom application"
topics:
- application
- python
- api
# Override default team permissions
teams:
devops: admin
backend: push
frontend: push
# Add custom variables and secrets
variables:
LANGUAGE: "python"
FRAMEWORK: "fastapi"
secrets:
PYTHON_API_KEY: "asm://my-custom-app-api-key"
Secrets and Variables Management​
The component supports setting repository and environment secrets and variables. Secrets and variables can be set using the following methods:
- Raw values (unencrypted string) (example:
my-secret-value) - AWS Secrets Manager (SM) (example:
asm://secret-name) - AWS Systems Manager Parameter Store (SSM) (example:
ssm:///my/secret/path)
In addition to that secrets supports base64 encoded values encrypted
with repository key.
The value should be prefixed with nacl: (example: nacl:dGVzdC12YWx1ZS0yCg==).
Environment-Specific Secrets and Variables​
environments:
production:
variables:
ENVIRONMENT: "production"
CLUSTER_NAME: "prod-cluster"
LOG_LEVEL: "warn"
secrets:
PROD_DATABASE_URL: "ssm:///my-repo/prod/database-url"
PROD_API_KEY: "asm://my-repo-prod-api-key"
PROD_JWT_SECRET: "nacl:cHJvZC1qd3Qtc2VjcmV0Cg=="
staging:
variables:
ENVIRONMENT: "staging"
CLUSTER_NAME: "staging-cluster"
LOG_LEVEL: "info"
secrets:
STAGING_DATABASE_URL: "ssm:///my-repo/staging/database-url"
STAGING_API_KEY: "asm://my-repo-staging-api-key"
Import Mode​
The component supports importing existing repository and its configurations:
- collaborators
- variables
- environments
- environment variables
- labels
- custom properties values
- autolink references
- deploy keys
Import mode is enabled by setting import input variable to true.
components:
terraform:
my-imported-repo:
vars:
enabled: true
import: true
owner: "my-organization"
repository:
name: "existing-repo-to-import"
The following configurations are not supported for import:
- secrets
- environment secrets
- branch protection policies
- rulesets
Variables​
Required Variables​
owner(string) requiredOwner of the repository
region(string) requiredAWS Region
repositoryrequiredRepository configuration
Type:
object({
name = string
description = optional(string, null)
visibility = optional(string, "public")
homepage_url = optional(string, null)
archived = optional(bool, false)
has_issues = optional(bool, false)
has_projects = optional(bool, false)
has_discussions = optional(bool, false)
has_wiki = optional(bool, false)
has_downloads = optional(bool, false)
is_template = optional(bool, false)
allow_auto_merge = optional(bool, false)
allow_squash_merge = optional(bool, true)
squash_merge_commit_title = optional(string, "PR_TITLE")
squash_merge_commit_message = optional(string, "PR_BODY")
allow_merge_commit = optional(bool, true)
merge_commit_title = optional(string, "PR_TITLE")
merge_commit_message = optional(string, "PR_BODY")
allow_rebase_merge = optional(bool, true)
delete_branch_on_merge = optional(bool, false)
default_branch = optional(string, "main")
web_commit_signoff_required = optional(bool, false)
topics = optional(list(string), [])
license_template = optional(string, null)
gitignore_template = optional(string, null)
auto_init = optional(bool, false)
ignore_vulnerability_alerts_during_read = optional(bool, false)
enable_vulnerability_alerts = optional(bool, true)
allow_update_branch = optional(bool, false)
security_and_analysis = optional(object({
advanced_security = bool
secret_scanning = bool
secret_scanning_push_protection = bool
}), null)
archive_on_destroy = optional(bool, false)
})
Optional Variables​
autolink_referencesoptionalAutolink references
Type:
map(object({
key_prefix = string
target_url_template = string
is_alphanumeric = optional(bool, false)
}))Default value:
{ }custom_propertiesoptionalCustom properties for the repository
Type:
map(object({
string = optional(string, null)
boolean = optional(bool, null)
single_select = optional(string, null)
multi_select = optional(list(string), null)
}))Default value:
{ }deploy_keysoptionalDeploy keys for the repository
Type:
map(object({
title = string
key = string
read_only = optional(bool, false)
}))Default value:
{ }environmentsoptionalEnvironments for the repository. Enviroment secrets should be encrypted using the GitHub public key in Base64 format if prefixed with nacl:. Read more: https://docs.github.com/en/actions/security-for-github-actions/encrypted-secrets
Type:
map(object({
wait_timer = optional(number, 0)
can_admins_bypass = optional(bool, false)
prevent_self_review = optional(bool, false)
reviewers = optional(object({
teams = optional(list(string), [])
users = optional(list(string), [])
}), null)
deployment_branch_policy = optional(object({
protected_branches = optional(bool, false)
custom_branches = optional(object({
branches = optional(list(string), null)
tags = optional(list(string), null)
}), null)
}), null)
variables = optional(map(string), null)
secrets = optional(map(string), null)
}))Default value:
{ }import(bool) optionalImport repository
Default value:
falselabelsoptionalA map of labels to configure for the repository
Type:
map(object({
color = string
description = string
}))Default value:
{ }rulesetsoptionalA map of rulesets to configure for the repository
Type:
map(object({
name = string
# disabled, active
enforcement = string
# branch, tag
target = string
bypass_actors = optional(list(object({
# always, pull_request
bypass_mode = string
actor_id = optional(string, null)
# RepositoryRole, Team, Integration, OrganizationAdmin
actor_type = string
})), [])
conditions = object({
ref_name = object({
# Supports ~DEFAULT_BRANCH or ~ALL
include = optional(list(string), [])
exclude = optional(list(string), [])
})
})
rules = object({
branch_name_pattern = optional(object({
# starts_with, ends_with, contains, regex
operator = string
pattern = string
name = optional(string, null)
negate = optional(bool, false)
}), null),
commit_author_email_pattern = optional(object({
# starts_with, ends_with, contains, regex
operator = string
pattern = string
name = optional(string, null)
negate = optional(bool, false)
}), null),
creation = optional(bool, false),
deletion = optional(bool, false),
non_fast_forward = optional(bool, false),
required_pull_request_reviews = optional(object({
dismiss_stale_reviews = bool
required_approving_review_count = number
}), null),
commit_message_pattern = optional(object({
# starts_with, ends_with, contains, regex
operator = string
pattern = string
name = optional(string, null)
negate = optional(bool, false)
}), null),
committer_email_pattern = optional(object({
# starts_with, ends_with, contains, regex
operator = string
pattern = string
name = optional(string, null)
negate = optional(bool, false)
}), null),
merge_queue = optional(object({
check_response_timeout_minutes = optional(number, 60)
# ALLGREEN, HEADGREEN
grouping_strategy = string
max_entries_to_build = optional(number, 5)
max_entries_to_merge = optional(number, 5)
# MERGE, SQUASH, REBASE
merge_method = optional(string, "MERGE")
min_entries_to_merge = optional(number, 1)
min_entries_to_merge_wait_minutes = optional(number, 5)
}), null),
pull_request = optional(object({
dismiss_stale_reviews_on_push = optional(bool, false)
require_code_owner_review = optional(bool, false)
require_last_push_approval = optional(bool, false)
required_approving_review_count = optional(number, 0)
required_review_thread_resolution = optional(bool, false)
}), null),
required_deployments = optional(object({
required_deployment_environments = optional(list(string), [])
}), null),
required_status_checks = optional(object({
required_check = list(object({
context = string
integration_id = optional(number, null)
}))
strict_required_status_checks_policy = optional(bool, false)
do_not_enforce_on_create = optional(bool, false)
}), null),
tag_name_pattern = optional(object({
# starts_with, ends_with, contains, regex
operator = string
pattern = string
name = optional(string, null)
negate = optional(bool, false)
}), null),
# Unsupported due to drift.
# https://github.com/integrations/terraform-provider-github/pull/2701
# required_code_scanning = optional(object({
# required_code_scanning_tool = list(object({
# // none, errors, errors_and_warnings, all
# alerts_threshold = string
# // none, critical, high_or_higher, medium_or_higher, all
# security_alerts_threshold = string
# tool = string
# }))
# }), null),
}),
}))Default value:
{ }secrets(map(string)) optionalSecrets for the repository (if prefixed with nacl: it should be encrypted value using the GitHub public key in Base64 format. Read more: https://docs.github.com/en/actions/security-for-github-actions/encrypted-secrets)
Default value:
{ }teams(map(string)) optionalA map of teams and their permissions for the repository
Default value:
{ }templateoptionalTemplate repository
Type:
object({
owner = string
name = string
include_all_branches = optional(bool, false)
})Default value:
nullusers(map(string)) optionalA map of users and their permissions for the repository
Default value:
{ }variables(map(string)) optionalEnvironment variables for the repository
Default value:
{ }webhooksoptionalA map of webhooks to configure for the repository
Type:
map(object({
active = optional(bool, true)
events = list(string)
url = string
content_type = optional(string, "json")
insecure_ssl = optional(bool, false)
secret = optional(string, null)
}))Default value:
{ }
Context Variables​
The following variables are defined in the context.tf file of this module and part of the terraform-null-label pattern.
context.tf file of this module and part of the terraform-null-label pattern.additional_tag_map(map(string)) optionalAdditional key-value pairs to add to each map in
tags_as_list_of_maps. Not added totagsorid.
This is for some rare cases where resources want additional configuration of tags
and therefore take a list of maps with tag key, value, and additional configuration.Required: No
Default value:
{ }attributes(list(string)) optionalID element. Additional attributes (e.g.
workersorcluster) to add toid,
in the order they appear in the list. New attributes are appended to the
end of the list. The elements of the list are joined by thedelimiter
and treated as a single ID element.Required: No
Default value:
[ ]context(any) optionalSingle object for setting entire context at once.
See description of individual variables for details.
Leave string and numeric variables asnullto use default value.
Individual variable settings (non-null) override settings in context object,
except for attributes, tags, and additional_tag_map, which are merged.Required: No
Default value:
{
"additional_tag_map": {},
"attributes": [],
"delimiter": null,
"descriptor_formats": {},
"enabled": true,
"environment": null,
"id_length_limit": null,
"label_key_case": null,
"label_order": [],
"label_value_case": null,
"labels_as_tags": [
"unset"
],
"name": null,
"namespace": null,
"regex_replace_chars": null,
"stage": null,
"tags": {},
"tenant": null
}delimiter(string) optionalDelimiter to be used between ID elements.
Defaults to-(hyphen). Set to""to use no delimiter at all.Required: No
Default value:
nulldescriptor_formats(any) optionalDescribe additional descriptors to be output in the
descriptorsoutput map.
Map of maps. Keys are names of descriptors. Values are maps of the form
\{<br/> format = string<br/> labels = list(string)<br/> \}
(Type isanyso the map values can later be enhanced to provide additional options.)
formatis a Terraform format string to be passed to theformat()function.
labelsis a list of labels, in order, to pass toformat()function.
Label values will be normalized before being passed toformat()so they will be
identical to how they appear inid.
Default is{}(descriptorsoutput will be empty).Required: No
Default value:
{ }enabled(bool) optionalSet to false to prevent the module from creating any resources
Required: NoDefault value:
nullenvironment(string) optionalID element. Usually used for region e.g. 'uw2', 'us-west-2', OR role 'prod', 'staging', 'dev', 'UAT'
Required: NoDefault value:
nullid_length_limit(number) optionalLimit
idto this many characters (minimum 6).
Set to0for unlimited length.
Set tonullfor keep the existing setting, which defaults to0.
Does not affectid_full.Required: No
Default value:
nulllabel_key_case(string) optionalControls the letter case of the
tagskeys (label names) for tags generated by this module.
Does not affect keys of tags passed in via thetagsinput.
Possible values:lower,title,upper.
Default value:title.Required: No
Default value:
nulllabel_order(list(string)) optionalThe order in which the labels (ID elements) appear in the
id.
Defaults to ["namespace", "environment", "stage", "name", "attributes"].
You can omit any of the 6 labels ("tenant" is the 6th), but at least one must be present.Required: No
Default value:
nulllabel_value_case(string) optionalControls the letter case of ID elements (labels) as included in
id,
set as tag values, and output by this module individually.
Does not affect values of tags passed in via thetagsinput.
Possible values:lower,title,upperandnone(no transformation).
Set this totitleand setdelimiterto""to yield Pascal Case IDs.
Default value:lower.Required: No
Default value:
nulllabels_as_tags(set(string)) optionalSet of labels (ID elements) to include as tags in the
tagsoutput.
Default is to include all labels.
Tags with empty values will not be included in thetagsoutput.
Set to[]to suppress all generated tags.
Notes:
The value of thenametag, if included, will be theid, not thename.
Unlike othernull-labelinputs, the initial setting oflabels_as_tagscannot be
changed in later chained modules. Attempts to change it will be silently ignored.Required: No
Default value:
[
"default"
]name(string) optionalID element. Usually the component or solution name, e.g. 'app' or 'jenkins'.
This is the only ID element not also included as atag.
The "name" tag is set to the fullidstring. There is no tag with the value of thenameinput.Required: No
Default value:
nullnamespace(string) optionalID element. Usually an abbreviation of your organization name, e.g. 'eg' or 'cp', to help ensure generated IDs are globally unique
Required: NoDefault value:
nullregex_replace_chars(string) optionalTerraform regular expression (regex) string.
Characters matching the regex will be removed from the ID elements.
If not set,"/[^a-zA-Z0-9-]/"is used to remove all characters other than hyphens, letters and digits.Required: No
Default value:
nullstage(string) optionalID element. Usually used to indicate role, e.g. 'prod', 'staging', 'source', 'build', 'test', 'deploy', 'release'
Required: NoDefault value:
nulltags(map(string)) optionalAdditional tags (e.g.
{'BusinessUnit': 'XYZ'}).
Neither the tag keys nor the tag values will be modified by this module.Required: No
Default value:
{ }tenant(string) optionalID element (Rarely used, not included by default). A customer identifier, indicating who this instance of a resource is for
Required: NoDefault value:
null
Outputs​
collaborators_invitation_idsCollaborators invitation IDs
full_nameFull name of the created repository
git_clone_urlGit clone URL of the created repository
html_urlHTML URL of the created repository
http_clone_urlHTTP clone URL of the created repository
node_idNode ID of the created repository
primary_languagePrimary language of the created repository
repo_idRepository ID of the created repository
rulesets_etagsRulesets etags
rulesets_node_idsRulesets node IDs
rulesets_rules_idsRulesets rules IDs
ssh_clone_urlSSH clone URL of the created repository
svn_urlSVN URL of the created repository
webhooks_urlsWebhooks URLs
Dependencies​
Requirements​
terraform, version:>= 1.7.0aws, version:>= 5.0.0github, version:>= 6.6.0
Providers​
aws, version:>= 5.0.0github, version:>= 6.6.0
Modules​
| Name | Version | Source | Description |
|---|---|---|---|
iam_roles | latest | ../account-map/modules/iam-roles | n/a |
repository | 0.2.1 | cloudposse/repository/github | n/a |
this | 0.25.0 | cloudposse/label/null | n/a |
Resources​
The following resources are used by this module:
Data Sources​
The following data sources are used by this module:
aws_secretsmanager_secret.default(data source)aws_secretsmanager_secret_version.default(data source)aws_ssm_parameter.default(data source)github_actions_environment_variables.default(data source)github_actions_variables.default(data source)github_issue_labels.default(data source)github_repository.default(data source)github_repository_autolink_references.default(data source)github_repository_custom_properties.default(data source)github_repository_deploy_keys.default(data source)github_repository_environments.default(data source)