Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 48 additions & 0 deletions modules/gh-repo-secrets-section/.terraform-docs.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
formatter: "markdown"

version: ""

header-from: docs/header.md
footer-from: docs/footer.md

recursive:
enabled: false
path: modules
include-main: true

sections:
hide: []
show: []

content: ""

output:
file: "README.md"
mode: inject
template: |-
<!-- BEGIN_TF_DOCS -->
{{ .Content }}
<!-- END_TF_DOCS -->

output-values:
enabled: false
from: ""

sort:
enabled: true
by: name

settings:
anchor: true
color: true
default: true
description: false
escape: true
hide-empty: false
html: true
indent: 2
lockfile: true
read-comments: true
required: true
sensitive: true
type: true
5 changes: 5 additions & 0 deletions modules/gh-repo-secrets-section/_examples/basic/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module "repo_secrets" {
source = "git::https://github.com/prefapp/tfm.git//modules/github-repository-secrets"

config = var.config # Terraform automatically loads terraform.tfvars.json
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"config": {
"actions": {
"SECRET_A": {
"secretName": "SECRET_A",
"repository": "component_a",
"encryptedValue": "r+RFBGIn8U7z2Opm5RN7PXKdgFzefXiV91IpG3O2DrClZl9dkTJBfhRZbi2uV2nu4ijn5yUfZ9O1eqjaXL2dWByFV+T2swZCQVQdDGmDlF24MPvEFh2ZbQ=="
},
"SECRET_B": {
"secretName": "SECRET_B",
"repository": "component_a",
"encryptedValue": "MkLxMAQDhWu0HotjWvNDzyquGJyCxf+n/Qh+eLtDi+Ci0U+M5CCYYLabH6Y0/+sx5aksP+PJPM9PxIYQImx82lOcy1MJ08Fi+JCtT2l2CixOP19McNu7XQ=="
}
},
"codespaces": {
"SECRET_C": {
"secretName": "SECRET_C",
"repository": "component_a",
"encryptedValue": "rJ0H0YfnvedPtMOD0Ete+Pl802OQXa8e7Ia6lbfeTEnIiFRnqjc3XMPGxk7VC8yMeM2znStVAP2eeI97XrfMzvM/N/289g8ZElsCXlLesn4JXY2+chmcCQ=="
}
},
"dependabot": {}
}
}
20 changes: 20 additions & 0 deletions modules/gh-repo-secrets-section/docs/footer.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@

### 3. `docs/footer.md`
```markdown
## Examples

For detailed examples, refer to the [module examples](https://github.com/prefapp/tfm/tree/main/modules/github-repository-secrets/_examples):

- [basic](https://github.com/prefapp/tfm/tree/main/modules/github-repository-secrets/_examples/basic) - Full example with Actions, Codespaces, and Dependabot secrets

## Resources

- **github_actions_secret**: [Official Documentation](https://registry.terraform.io/providers/integrations/github/latest/docs/resources/actions_secret)
- **github_codespaces_secret**: [Official Documentation](https://registry.terraform.io/providers/integrations/github/latest/docs/resources/codespaces_secret)
- **github_dependabot_secret**: [Official Documentation](https://registry.terraform.io/providers/integrations/github/latest/docs/resources/dependabot_secret)
- **GitHub Terraform Provider**: [Official Documentation](https://registry.terraform.io/providers/integrations/github/latest/docs)

## Support

For issues, questions, or contributions related to this module, please visit the [repository's issue tracker](https://github.com/prefapp/tfm/issues).

50 changes: 50 additions & 0 deletions modules/gh-repo-secrets-section/docs/header.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# **GitHub Repository Secrets Terraform Module**

## Overview

This module manages GitHub repository secrets for **Actions**, **Codespaces**, and **Dependabot** using a single strongly-typed `config` object.

**Important**:
The `encryptedValue` values passed in the `config` **must already be encrypted** using **libsodium** against the target repository’s public key (GitHub’s recommended method).
**Terraform does not perform any encryption**. It simply forwards the pre-encrypted value to the GitHub API.

This approach is the most secure for automated pipelines (Prefapp IDP, GitHub Actions, etc.).

## Key Features

- **Single complex object**: All secrets are defined in one `config` variable.
- **Pre-encrypted values**: `encryptedValue` must be provided already encrypted with libsodium.
- **Three secret types**: Actions, Codespaces, and Dependabot supported in the same module.
- **Lifecycle protection**: `ignore_changes` on `encrypted_value` to prevent unnecessary drift.
- **Full validation**: Enforces required fields and non-empty values.
- **JSON-native**: Works seamlessly with `terraform.tfvars.json` generated by external programs.

## Basic Usage

### Using `terraform.tfvars.json` (recommended)

```hcl
module "repo_secrets" {
source = "git::https://github.com/prefapp/tfm.git//modules/github-repository-secrets"

config = var.config # Terraform automatically loads terraform.tfvars.json
}

### Inline example

```hcl
module "repo_secrets" {
source = "git::https://github.com/prefapp/tfm.git//modules/github-repository-secrets"

config = {
actions = {
SECRET_A = {
secretName = "SECRET_A"
repository = "component_a"
encryptedValue = "r+RFBGIn8U7z2Opm5RN7PXKdgFzefXiV91IpG3O2DrClZl9dkTJBfhRZbi2uV2nu4ijn5yUfZ9O1eqjaXL2dWByFV+T2swZCQVQdDGmDlF24MPvEFh2ZbQ=="
}
}
codespaces = {}
dependabot = {}
}
}
50 changes: 50 additions & 0 deletions modules/gh-repo-secrets-section/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# ─────────────────────────────────────────────────────────────
# GitHub Actions Secrets
# encrypted_value is ALREADY libsodium-encrypted against the repo public key
# Terraform does NOT perform any encryption
# ─────────────────────────────────────────────────────────────
resource "github_actions_secret" "this" {
for_each = var.config.actions

repository = each.value.repository
secret_name = each.value.secretName
encrypted_value = each.value.encryptedValue

lifecycle {
ignore_changes = ["encrypted_value"]
}
}

# ─────────────────────────────────────────────────────────────
# GitHub Codespaces Secrets
# encrypted_value is ALREADY libsodium-encrypted against the repo public key
# Terraform does NOT perform any encryption
# ─────────────────────────────────────────────────────────────
resource "github_codespaces_secret" "this" {
for_each = var.config.codespaces

repository = each.value.repository
secret_name = each.value.secretName
encrypted_value = each.value.encryptedValue

lifecycle {
ignore_changes = ["encrypted_value"]
}
}

# ─────────────────────────────────────────────────────────────
# GitHub Dependabot Secrets
# encrypted_value is ALREADY libsodium-encrypted against the repo public key
# Terraform does NOT perform any encryption
# ─────────────────────────────────────────────────────────────
resource "github_dependabot_secret" "this" {
for_each = var.config.dependabot

repository = each.value.repository
secret_name = each.value.secretName
encrypted_value = each.value.encryptedValue

lifecycle {
ignore_changes = ["encrypted_value"]
}
}
23 changes: 23 additions & 0 deletions modules/gh-repo-secrets-section/outputs.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
output "actions_secrets" {
description = "List of Action secrets created"
value = [for k, v in var.config.actions : v.secretName]
}

output "codespaces_secrets" {
description = "List of Codespaces secrets created"
value = [for k, v in var.config.codespaces : v.secretName]
}

output "dependabot_secrets" {
description = "List of Dependabot secrets created"
value = [for k, v in var.config.dependabot : v.secretName]
}

output "all_secret_names" {
description = "Combined list of all secret names"
value = concat(
[for v in var.config.actions : v.secretName],
[for v in var.config.codespaces : v.secretName],
[for v in var.config.dependabot : v.secretName]
)
}
64 changes: 64 additions & 0 deletions modules/gh-repo-secrets-section/variables.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
terraform {
required_providers {
github = {
source = "integrations/github"
version = "~> 6.0"
}
}
}

variable "config" {
description = <<EOT
GitHub repository secrets configuration as a single complex object.

IMPORTANT:
- encryptedValue must be ALREADY encrypted with libsodium using the target repository's public key.
- Terraform does NOT encrypt anything — it only passes the pre-encrypted value.
EOT

type = object({
actions = optional(map(object({
secretName = string
repository = string
encryptedValue = string
})), {})

codespaces = optional(map(object({
secretName = string
repository = string
encryptedValue = string
})), {})

dependabot = optional(map(object({
secretName = string
repository = string
encryptedValue = string
})), {})
})

validation {
condition = length(var.config.actions) + length(var.config.codespaces) + length(var.config.dependabot) > 0
error_message = "At least one secret must be defined in actions, codespaces or dependabot."
}

validation {
condition = alltrue([
for k, v in var.config.actions : length(trimspace(v.secretName)) > 0 && length(trimspace(v.repository)) > 0
])
error_message = "Every action secret must have non-empty secretName and repository."
}

validation {
condition = alltrue([
for k, v in var.config.codespaces : length(trimspace(v.secretName)) > 0 && length(trimspace(v.repository)) > 0
])
error_message = "Every codespaces secret must have non-empty secretName and repository."
}

validation {
condition = alltrue([
for k, v in var.config.dependabot : length(trimspace(v.secretName)) > 0 && length(trimspace(v.repository)) > 0
])
error_message = "Every dependabot secret must have non-empty secretName and repository."
}
}
3 changes: 3 additions & 0 deletions release-please-config.json
Original file line number Diff line number Diff line change
Expand Up @@ -148,5 +148,8 @@
"modules/aws-secretsmanager-replication": {
"package-name": "aws-secretsmanager-replication"
}
"modules/gh-repo-secrets-section": {
"package-name": "gh-repo-secrets-section"
}
}
}