diff --git a/modules/gh-membership/.terraform-docs.yml b/modules/gh-membership/.terraform-docs.yml new file mode 100644 index 000000000..3a69365ff --- /dev/null +++ b/modules/gh-membership/.terraform-docs.yml @@ -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: |- + + {{ .Content }} + + +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 diff --git a/modules/gh-membership/README.md b/modules/gh-membership/README.md new file mode 100644 index 000000000..f2d33cea9 --- /dev/null +++ b/modules/gh-membership/README.md @@ -0,0 +1,106 @@ + +# **GitHub Membership Terraform Module** + +## Overview + +This module manages GitHub organization-level membership (admin/member) and team memberships using a single strongly-typed `config` object. + +It is designed for Prefapp’s Internal Developer Platform and automated user/team provisioning pipelines. The module accepts input directly from external programs via JSON. + +## Key Features + +- **Organization role**: Assign `member` or `admin` at organization level +- **Team relationships**: Add users to teams with `member` or `maintainer` roles +- **Single config object**: Everything in one `config` variable +- **Full validation**: Role enforcement and required fields +- **JSON-native**: Perfect for programmatic generation + +## Basic Usage + +### Using `terraform.tfvars.json` (recommended) + +```hcl +module "membership" { + source = "git::https://github.com/prefapp/tfm.git//modules/gh-membership" + + config = var.config +} + +#### Inline example +```hcl +module "membership" { + source = "git::https://github.com/prefapp/tfm.git//modules/gh-membership" + + config = { + user = { + username = "johndoe" + role = "admin" + } + relationships = [ + { + username = "johndoe" + teamId = "foo-all" + role = "member" + } + ] + } +} +``` +``` + +## Requirements + +| Name | Version | +|------|---------| +| [github](#requirement\_github) | ~> 6.0 | + +## Providers + +| Name | Version | +|------|---------| +| [github](#provider\_github) | ~> 6.0 | + +## Modules + +No modules. + +## Resources + +| Name | Type | +|------|------| +| [github_membership.this](https://registry.terraform.io/providers/integrations/github/latest/docs/resources/membership) | resource | +| [github_team_membership.relationships](https://registry.terraform.io/providers/integrations/github/latest/docs/resources/team_membership) | resource | + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| [config](#input\_config) | GitHub membership configuration (organization role + team relationships) |
object({
relationships = optional(list(object({
username = string
teamId = string # team slug (e.g. "jvazquez-prefapp-all")
role = optional(string, "member") # member | maintainer
})), [])
user = optional(object({
username = string
role = optional(string, "member") # member | admin
}))
}) | n/a | yes |
+
+## Outputs
+
+| Name | Description |
+|------|-------------|
+| [organization\_membership](#output\_organization\_membership) | Organization-level membership |
+| [team\_memberships](#output\_team\_memberships) | Team memberships created |
+| [user](#output\_user) | User details |
+
+### 3. `docs/footer.md`
+```markdown
+## Examples
+
+For detailed examples, refer to the [module examples](https://github.com/prefapp/tfm/tree/main/modules/gh-membership/_examples):
+
+- [basic](https://github.com/prefapp/tfm/tree/main/modules/gh-membership/_examples/basic) - Organization membership + team relationship
+
+## Resources
+
+- **github_membership**: [Official Documentation](https://registry.terraform.io/providers/integrations/github/latest/docs/resources/membership)
+- **github_team_membership**: [Official Documentation](https://registry.terraform.io/providers/integrations/github/latest/docs/resources/team_membership)
+- **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).
+```
+
\ No newline at end of file
diff --git a/modules/gh-membership/_examples/basic/config.json b/modules/gh-membership/_examples/basic/config.json
new file mode 100644
index 000000000..e9ac242bd
--- /dev/null
+++ b/modules/gh-membership/_examples/basic/config.json
@@ -0,0 +1,15 @@
+{
+ "config": {
+ "relationships": [
+ {
+ "username": "johndoe",
+ "teamId": "foo-all",
+ "role": "member"
+ }
+ ],
+ "user": {
+ "username": "johndoe",
+ "role": "admin"
+ }
+ }
+}
diff --git a/modules/gh-membership/_examples/basic/main.tf b/modules/gh-membership/_examples/basic/main.tf
new file mode 100644
index 000000000..6dfdb96e0
--- /dev/null
+++ b/modules/gh-membership/_examples/basic/main.tf
@@ -0,0 +1,22 @@
+terraform {
+ required_providers {
+ github = {
+ source = "integrations/github"
+ version = "~> 6.0"
+ }
+ }
+}
+
+module "membership" {
+ source = "../../"
+
+ config = jsondecode(file("${path.module}/config.json")).config
+}
+
+output "user_managed" {
+ value = module.membership.organization_membership
+}
+
+output "team_memberships" {
+ value = module.membership.team_memberships
+}
diff --git a/modules/gh-membership/docs/footer.md b/modules/gh-membership/docs/footer.md
new file mode 100644
index 000000000..0d7facfaa
--- /dev/null
+++ b/modules/gh-membership/docs/footer.md
@@ -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/gh-membership/_examples):
+
+- [basic](https://github.com/prefapp/tfm/tree/main/modules/gh-membership/_examples/basic) - Organization membership + team relationship
+
+## Resources
+
+- **github_membership**: [Official Documentation](https://registry.terraform.io/providers/integrations/github/latest/docs/resources/membership)
+- **github_team_membership**: [Official Documentation](https://registry.terraform.io/providers/integrations/github/latest/docs/resources/team_membership)
+- **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).
+```
+
diff --git a/modules/gh-membership/docs/header.md b/modules/gh-membership/docs/header.md
new file mode 100644
index 000000000..c830ce6a0
--- /dev/null
+++ b/modules/gh-membership/docs/header.md
@@ -0,0 +1,47 @@
+# **GitHub Membership Terraform Module**
+
+## Overview
+
+This module manages GitHub organization-level membership (admin/member) and team memberships using a single strongly-typed `config` object.
+
+It is designed for Prefapp’s Internal Developer Platform and automated user/team provisioning pipelines. The module accepts input directly from external programs via JSON.
+
+## Key Features
+
+- **Organization role**: Assign `member` or `admin` at organization level
+- **Team relationships**: Add users to teams with `member` or `maintainer` roles
+- **Single config object**: Everything in one `config` variable
+- **Full validation**: Role enforcement and required fields
+- **JSON-native**: Perfect for programmatic generation
+
+## Basic Usage
+
+### Using `terraform.tfvars.json` (recommended)
+
+```hcl
+module "membership" {
+ source = "git::https://github.com/prefapp/tfm.git//modules/gh-membership"
+
+ config = var.config
+}
+
+#### Inline example
+```hcl
+module "membership" {
+ source = "git::https://github.com/prefapp/tfm.git//modules/gh-membership"
+
+ config = {
+ user = {
+ username = "johndoe"
+ role = "admin"
+ }
+ relationships = [
+ {
+ username = "johndoe"
+ teamId = "foo-all"
+ role = "member"
+ }
+ ]
+ }
+}
+```
diff --git a/modules/gh-membership/main.tf b/modules/gh-membership/main.tf
new file mode 100644
index 000000000..2b5923daa
--- /dev/null
+++ b/modules/gh-membership/main.tf
@@ -0,0 +1,18 @@
+# Organization membership (admin / member)
+resource "github_membership" "this" {
+ count = var.config.user != null ? 1 : 0
+
+ username = var.config.user.username
+ role = var.config.user.role
+}
+
+# Team memberships
+resource "github_team_membership" "relationships" {
+ for_each = {
+ for r in var.config.relationships : "${r.username}-${r.teamId}" => r
+ }
+
+ team_id = each.value.teamId # accepts slug directly
+ username = each.value.username
+ role = each.value.role
+}
diff --git a/modules/gh-membership/outputs.tf b/modules/gh-membership/outputs.tf
new file mode 100644
index 000000000..213676058
--- /dev/null
+++ b/modules/gh-membership/outputs.tf
@@ -0,0 +1,23 @@
+output "organization_membership" {
+ description = "Organization-level membership"
+ value = var.config.user != null ? {
+ username = github_membership.this[0].username
+ role = github_membership.this[0].role
+ } : null
+}
+
+output "team_memberships" {
+ description = "Team memberships created"
+ value = [
+ for r in var.config.relationships : {
+ username = r.username
+ teamId = r.teamId
+ role = r.role
+ }
+ ]
+}
+
+output "user" {
+ description = "User details"
+ value = var.config.user
+}
diff --git a/modules/gh-membership/variables.tf b/modules/gh-membership/variables.tf
new file mode 100644
index 000000000..56e64b9d4
--- /dev/null
+++ b/modules/gh-membership/variables.tf
@@ -0,0 +1,27 @@
+variable "config" {
+ description = "GitHub membership configuration (organization role + team relationships)"
+ type = object({
+ relationships = optional(list(object({
+ username = string
+ teamId = string # team slug (e.g. "jvazquez-prefapp-all")
+ role = optional(string, "member") # member | maintainer
+ })), [])
+
+ user = optional(object({
+ username = string
+ role = optional(string, "member") # member | admin
+ }))
+ })
+
+ validation {
+ condition = alltrue([
+ for r in var.config.relationships : contains(["member", "maintainer"], r.role)
+ ])
+ error_message = "relationship.role must be 'member' or 'maintainer'."
+ }
+
+ validation {
+ condition = var.config.user == null || contains(["member", "admin"], var.config.user.role)
+ error_message = "user.role must be 'member' or 'admin'."
+ }
+}
diff --git a/modules/gh-membership/versions.tf b/modules/gh-membership/versions.tf
new file mode 100644
index 000000000..30369f8ed
--- /dev/null
+++ b/modules/gh-membership/versions.tf
@@ -0,0 +1,9 @@
+terraform {
+ required_providers {
+ github = {
+ source = "integrations/github"
+ version = "~> 6.0"
+ }
+ }
+}
+
diff --git a/release-please-config.json b/release-please-config.json
index b59f07b71..c751c4df4 100644
--- a/release-please-config.json
+++ b/release-please-config.json
@@ -151,5 +151,8 @@
"modules/aws-secretsmanager-replication": {
"package-name": "aws-secretsmanager-replication"
}
+ "modules/gh-membership": {
+ "package-name": "gh-membership"
+ }
}
}