From 8d9c73c76645e0335d998711a4254e43d9450161 Mon Sep 17 00:00:00 2001 From: Tyler Mizuyabu Date: Wed, 28 Feb 2024 13:19:39 -0500 Subject: [PATCH 1/6] coppied modules --- .../foundations-github-organization/README.md | 55 ++++ .../action-variables.tf | 71 +++++ .../organization.tf | 11 + .../outputs.tf | 3 + .../repositories.tf | 116 ++++++++ .../foundations-github-organization/teams.tf | 7 + .../variables.tf | 55 ++++ .../versions.tf | 10 + modules/github-gcloud-oidc/README.md | 90 ++++++ modules/github-gcloud-oidc/folder.tf | 18 ++ modules/github-gcloud-oidc/locals.tf | 3 + modules/github-gcloud-oidc/oidc.tf | 70 +++++ modules/github-gcloud-oidc/outputs.tf | 54 ++++ modules/github-gcloud-oidc/project.tf | 69 +++++ modules/github-gcloud-oidc/storage.tf | 97 ++++++ modules/github-gcloud-oidc/variables.tf | 280 ++++++++++++++++++ modules/github-gcloud-oidc/versions.tf | 13 + modules/organization/README.md | 63 ++++ modules/organization/block.tf | 5 + modules/organization/members.tf | 6 + modules/organization/outputs.tf | 26 ++ modules/organization/roles.tf | 57 ++++ modules/organization/settings.tf | 48 +++ modules/organization/variables.tf | 134 +++++++++ modules/organization/versions.tf | 9 + modules/private_repository/README.md | 40 +++ modules/private_repository/repository.tf | 27 ++ modules/private_repository/variables.tf | 63 ++++ modules/private_repository/versions.tf | 9 + modules/public_repository/README.md | 40 +++ modules/public_repository/repository.tf | 27 ++ modules/public_repository/variables.tf | 63 ++++ modules/public_repository/versions.tf | 9 + modules/repository_base/README.md | 55 ++++ modules/repository_base/repository.tf | 80 +++++ modules/repository_base/teams.tf | 11 + modules/repository_base/variables.tf | 117 ++++++++ modules/repository_base/versions.tf | 9 + modules/repository_set/README.md | 33 +++ modules/repository_set/repositories.tf | 35 +++ modules/repository_set/variables.tf | 38 +++ modules/repository_set/versions.tf | 9 + modules/team/README.md | 42 +++ modules/team/doc.md | 38 +++ modules/team/members.tf | 17 ++ modules/team/outputs.tf | 9 + modules/team/team.tf | 10 + modules/team/variables.tf | 37 +++ modules/team/versions.tf | 9 + modules/team_set/README.md | 38 +++ modules/team_set/data.tf | 26 ++ modules/team_set/outputs.tf | 6 + modules/team_set/teams.tf | 22 ++ modules/team_set/variables.tf | 21 ++ modules/team_set/versions.tf | 9 + 55 files changed, 2319 insertions(+) create mode 100644 modules/foundations-github-organization/README.md create mode 100644 modules/foundations-github-organization/action-variables.tf create mode 100644 modules/foundations-github-organization/organization.tf create mode 100644 modules/foundations-github-organization/outputs.tf create mode 100644 modules/foundations-github-organization/repositories.tf create mode 100644 modules/foundations-github-organization/teams.tf create mode 100644 modules/foundations-github-organization/variables.tf create mode 100644 modules/foundations-github-organization/versions.tf create mode 100644 modules/github-gcloud-oidc/README.md create mode 100644 modules/github-gcloud-oidc/folder.tf create mode 100644 modules/github-gcloud-oidc/locals.tf create mode 100644 modules/github-gcloud-oidc/oidc.tf create mode 100644 modules/github-gcloud-oidc/outputs.tf create mode 100644 modules/github-gcloud-oidc/project.tf create mode 100644 modules/github-gcloud-oidc/storage.tf create mode 100644 modules/github-gcloud-oidc/variables.tf create mode 100644 modules/github-gcloud-oidc/versions.tf create mode 100644 modules/organization/README.md create mode 100644 modules/organization/block.tf create mode 100644 modules/organization/members.tf create mode 100644 modules/organization/outputs.tf create mode 100644 modules/organization/roles.tf create mode 100644 modules/organization/settings.tf create mode 100644 modules/organization/variables.tf create mode 100644 modules/organization/versions.tf create mode 100644 modules/private_repository/README.md create mode 100644 modules/private_repository/repository.tf create mode 100644 modules/private_repository/variables.tf create mode 100644 modules/private_repository/versions.tf create mode 100644 modules/public_repository/README.md create mode 100644 modules/public_repository/repository.tf create mode 100644 modules/public_repository/variables.tf create mode 100644 modules/public_repository/versions.tf create mode 100644 modules/repository_base/README.md create mode 100644 modules/repository_base/repository.tf create mode 100644 modules/repository_base/teams.tf create mode 100644 modules/repository_base/variables.tf create mode 100644 modules/repository_base/versions.tf create mode 100644 modules/repository_set/README.md create mode 100644 modules/repository_set/repositories.tf create mode 100644 modules/repository_set/variables.tf create mode 100644 modules/repository_set/versions.tf create mode 100644 modules/team/README.md create mode 100644 modules/team/doc.md create mode 100644 modules/team/members.tf create mode 100644 modules/team/outputs.tf create mode 100644 modules/team/team.tf create mode 100644 modules/team/variables.tf create mode 100644 modules/team/versions.tf create mode 100644 modules/team_set/README.md create mode 100644 modules/team_set/data.tf create mode 100644 modules/team_set/outputs.tf create mode 100644 modules/team_set/teams.tf create mode 100644 modules/team_set/variables.tf create mode 100644 modules/team_set/versions.tf diff --git a/modules/foundations-github-organization/README.md b/modules/foundations-github-organization/README.md new file mode 100644 index 0000000..5a88c81 --- /dev/null +++ b/modules/foundations-github-organization/README.md @@ -0,0 +1,55 @@ +## Requirements + +| Name | Version | +|------|---------| +| [terraform](#requirement\_terraform) | >= 1.3 | +| [github](#requirement\_github) | 5.44.0 | + +## Providers + +| Name | Version | +|------|---------| +| [github.enterprise\_scoped](#provider\_github.enterprise\_scoped) | 5.44.0 | +| [github.foundation\_org\_scoped](#provider\_github.foundation\_org\_scoped) | 5.44.0 | + +## Modules + +No modules. + +## Resources + +| Name | Type | +|------|------| +| [github_actions_organization_variable.tf_state_bucket_location](https://registry.terraform.io/providers/hashicorp/github/5.44.0/docs/resources/actions_organization_variable) | resource | +| [github_actions_organization_variable.tf_state_bucket_name](https://registry.terraform.io/providers/hashicorp/github/5.44.0/docs/resources/actions_organization_variable) | resource | +| [github_actions_organization_variable.tf_state_bucket_project_id](https://registry.terraform.io/providers/hashicorp/github/5.44.0/docs/resources/actions_organization_variable) | resource | +| [github_actions_organization_variable.workload_identity_provider](https://registry.terraform.io/providers/hashicorp/github/5.44.0/docs/resources/actions_organization_variable) | resource | +| [github_actions_variable.bootstrap_workload_identity_sa](https://registry.terraform.io/providers/hashicorp/github/5.44.0/docs/resources/actions_variable) | resource | +| [github_actions_variable.gcp_secret_manager_project_id](https://registry.terraform.io/providers/hashicorp/github/5.44.0/docs/resources/actions_variable) | resource | +| [github_actions_variable.organization_workload_identity_sa](https://registry.terraform.io/providers/hashicorp/github/5.44.0/docs/resources/actions_variable) | resource | +| [github_branch_protection.protect_bootstrap_main](https://registry.terraform.io/providers/hashicorp/github/5.44.0/docs/resources/branch_protection) | resource | +| [github_branch_protection.protect_organization_main](https://registry.terraform.io/providers/hashicorp/github/5.44.0/docs/resources/branch_protection) | resource | +| [github_enterprise_organization.github-foundations](https://registry.terraform.io/providers/hashicorp/github/5.44.0/docs/resources/enterprise_organization) | resource | +| [github_issue_labels.drift_labels](https://registry.terraform.io/providers/hashicorp/github/5.44.0/docs/resources/issue_labels) | resource | +| [github_repository.bootstrap_repo](https://registry.terraform.io/providers/hashicorp/github/5.44.0/docs/resources/repository) | resource | +| [github_repository.organizations_repo](https://registry.terraform.io/providers/hashicorp/github/5.44.0/docs/resources/repository) | resource | + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| [admin\_logins](#input\_admin\_logins) | List of organization owner usernames. | `list(string)` | n/a | yes | +| [billing\_email](#input\_billing\_email) | The email to use for the organizations billing. | `string` | n/a | yes | +| [bootstrap\_workload\_identity\_sa](#input\_bootstrap\_workload\_identity\_sa) | The service account to use for the bootstrap repository oidc. | `string` | n/a | yes | +| [bucket\_location](#input\_bucket\_location) | The location of the tf state bucket. | `string` | n/a | yes | +| [bucket\_name](#input\_bucket\_name) | The name of the tf state bucket. | `string` | n/a | yes | +| [enterprise\_id](#input\_enterprise\_id) | The id of the enterprise account to create the organization under. | `string` | n/a | yes | +| [gcp\_project\_id](#input\_gcp\_project\_id) | The id of the gcp project where secret manager was setup. | `string` | n/a | yes | +| [gcp\_tf\_state\_bucket\_project\_id](#input\_gcp\_tf\_state\_bucket\_project\_id) | The id of the gcp project where the tf state bucket was setup. | `string` | n/a | yes | +| [github\_foundations\_organization\_name](#input\_github\_foundations\_organization\_name) | The name of the organization to create. | `string` | n/a | yes | +| [organization\_workload\_identity\_sa](#input\_organization\_workload\_identity\_sa) | The service account to use for the organization repository oidc. | `string` | n/a | yes | +| [workload\_identity\_provider\_name](#input\_workload\_identity\_provider\_name) | The name of the workload identity provider to use for the oidc of the github foundation repositories. | `string` | n/a | yes | + +## Outputs + +No outputs. diff --git a/modules/foundations-github-organization/action-variables.tf b/modules/foundations-github-organization/action-variables.tf new file mode 100644 index 0000000..6ad3262 --- /dev/null +++ b/modules/foundations-github-organization/action-variables.tf @@ -0,0 +1,71 @@ +resource "github_actions_variable" "organization_workload_identity_sa" { + provider = github.foundation_org_scoped + + repository = github_repository.organizations_repo.name + variable_name = "GCP_SERVICE_ACCOUNT" + value = var.organization_workload_identity_sa +} + +resource "github_actions_variable" "bootstrap_workload_identity_sa" { + provider = github.foundation_org_scoped + + repository = github_repository.bootstrap_repo.name + variable_name = "GCP_SERVICE_ACCOUNT" + value = var.bootstrap_workload_identity_sa +} + +resource "github_actions_variable" "gcp_secret_manager_project_id" { + provider = github.foundation_org_scoped + + repository = github_repository.organizations_repo.name + variable_name = "GCP_SECRET_MANAGER_PROJECT" + value = var.gcp_project_id +} + +resource "github_actions_organization_variable" "workload_identity_provider" { + provider = github.foundation_org_scoped + + variable_name = "WORKLOAD_IDENTITY_PROVIDER" + value = var.workload_identity_provider_name + visibility = "selected" + selected_repository_ids = [ + github_repository.bootstrap_repo.repo_id, + github_repository.organizations_repo.repo_id + ] +} + +resource "github_actions_organization_variable" "tf_state_bucket_project_id" { + provider = github.foundation_org_scoped + + variable_name = "TF_STATE_BUCKET_PROJECT_ID" + value = var.gcp_tf_state_bucket_project_id + visibility = "selected" + selected_repository_ids = [ + github_repository.bootstrap_repo.repo_id, + github_repository.organizations_repo.repo_id + ] +} + +resource "github_actions_organization_variable" "tf_state_bucket_name" { + provider = github.foundation_org_scoped + + variable_name = "TF_STATE_BUCKET_NAME" + value = var.bucket_name + visibility = "selected" + selected_repository_ids = [ + github_repository.bootstrap_repo.repo_id, + github_repository.organizations_repo.repo_id + ] +} + +resource "github_actions_organization_variable" "tf_state_bucket_location" { + provider = github.foundation_org_scoped + + variable_name = "TF_STATE_BUCKET_LOCATION" + value = var.bucket_location + visibility = "selected" + selected_repository_ids = [ + github_repository.bootstrap_repo.repo_id, + github_repository.organizations_repo.repo_id + ] +} \ No newline at end of file diff --git a/modules/foundations-github-organization/organization.tf b/modules/foundations-github-organization/organization.tf new file mode 100644 index 0000000..c5a710e --- /dev/null +++ b/modules/foundations-github-organization/organization.tf @@ -0,0 +1,11 @@ +resource "github_enterprise_organization" "github-foundations" { + provider = github.enterprise_scoped + + enterprise_id = var.enterprise_id + name = var.github_foundations_organization_name + display_name = "Github Foundations" + description = "Organization created to host github foundation toolkit repositories" + billing_email = var.billing_email + admin_logins = var.admin_logins +} + diff --git a/modules/foundations-github-organization/outputs.tf b/modules/foundations-github-organization/outputs.tf new file mode 100644 index 0000000..bec8676 --- /dev/null +++ b/modules/foundations-github-organization/outputs.tf @@ -0,0 +1,3 @@ +output "foundation_dev_team_id" { + value = github_team.foundation_devs.id +} \ No newline at end of file diff --git a/modules/foundations-github-organization/repositories.tf b/modules/foundations-github-organization/repositories.tf new file mode 100644 index 0000000..3d5d2bf --- /dev/null +++ b/modules/foundations-github-organization/repositories.tf @@ -0,0 +1,116 @@ +locals { + repos_with_drift_detection = [github_repository.organizations_repo] +} + +#Creates the repository for the bootstrap layer +resource "github_repository" "bootstrap_repo" { + provider = github.foundation_org_scoped + #TODO: figure out what seems to be a race condition between repository creation and organization creation + depends_on = [github_enterprise_organization.github-foundations] + + name = "bootstrap" + description = "The repository for the bootstrap layer of the foundations. This repository contains the Terraform code to setup the github organization for the foundation repositories, create the GCP project, the GCP service account, the GCP secret manager secrets, and the GCP storage bucket for the state files." + + visibility = "private" + + auto_init = true + delete_branch_on_merge = true + vulnerability_alerts = true +} + +resource "github_repository_collaborators" "bootstrap_repo_collaborators" { + provider = github.foundation_org_scoped + repository = github_repository.bootstrap_repo.name + + team { + permission = "push" + team_id = github_team.foundation_devs.id + } +} + +resource "github_branch_protection" "protect_bootstrap_main" { + provider = github.foundation_org_scoped + + repository_id = github_repository.bootstrap_repo.id + + pattern = "main" + enforce_admins = true + allows_deletions = false + + # TODO: Add a required check for the terrafom apply workflow + required_status_checks { + strict = true + } + + required_pull_request_reviews { + dismiss_stale_reviews = true + restrict_dismissals = true + required_approving_review_count = 1 + require_last_push_approval = true + } +} + +#Creates the repository for the organizations layer +resource "github_repository" "organizations_repo" { + provider = github.foundation_org_scoped + depends_on = [github_enterprise_organization.github-foundations] + + name = "organizations" + description = "The repository for the organizations layer of the foundations. This repository contains the Terraform code to manage github organizations under the enterprise account and their repositories, teams, and members." + + visibility = "private" + + auto_init = true + delete_branch_on_merge = true + vulnerability_alerts = true + has_issues = true +} + +resource "github_repository_collaborators" "organization_repo_collaborators" { + provider = github.foundation_org_scoped + repository = github_repository.organizations_repo.name + + team { + permission = "push" + team_id = github_team.foundation_devs.id + } +} + + +resource "github_branch_protection" "protect_organization_main" { + provider = github.foundation_org_scoped + + repository_id = github_repository.organizations_repo.id + + pattern = "main" + enforce_admins = true + allows_deletions = false + + required_status_checks { + strict = true + } + + required_pull_request_reviews { + dismiss_stale_reviews = true + restrict_dismissals = true + required_approving_review_count = 1 + require_last_push_approval = true + } +} + +resource "github_issue_labels" "drift_labels" { + for_each = { for idx, val in local.repos_with_drift_detection : idx => val } + provider = github.foundation_org_scoped + + repository = each.value.name + + label { + name = "Action Required" + color = "FF0000" + } + + label { + name = "Re-Apply" + color = "0800FF" + } +} diff --git a/modules/foundations-github-organization/teams.tf b/modules/foundations-github-organization/teams.tf new file mode 100644 index 0000000..9511724 --- /dev/null +++ b/modules/foundations-github-organization/teams.tf @@ -0,0 +1,7 @@ +resource "github_team" "foundation_devs" { + provider = github.foundation_org_scoped + + name = "foundation-devs" + description = "Team members with write access to the foundation repositories" + privacy = "closed" +} diff --git a/modules/foundations-github-organization/variables.tf b/modules/foundations-github-organization/variables.tf new file mode 100644 index 0000000..f3f60e1 --- /dev/null +++ b/modules/foundations-github-organization/variables.tf @@ -0,0 +1,55 @@ +variable "enterprise_id" { + type = string + description = "The id of the enterprise account to create the organization under." +} + +variable "github_foundations_organization_name" { + type = string + description = "The name of the organization to create." +} + +variable "billing_email" { + type = string + description = "The email to use for the organizations billing." +} + +variable "admin_logins" { + type = list(string) + description = "List of organization owner usernames." +} + +variable "workload_identity_provider_name" { + type = string + description = "The name of the workload identity provider to use for the oidc of the github foundation repositories." +} + +variable "bootstrap_workload_identity_sa" { + type = string + description = "The service account to use for the bootstrap repository oidc." +} + +variable "organization_workload_identity_sa" { + type = string + description = "The service account to use for the organization repository oidc." +} + +variable "gcp_project_id" { + type = string + description = "The id of the gcp project where secret manager was setup." + +} + +variable "gcp_tf_state_bucket_project_id" { + type = string + description = "The id of the gcp project where the tf state bucket was setup." +} + +variable "bucket_name" { + type = string + description = "The name of the tf state bucket." +} + +variable "bucket_location" { + type = string + description = "The location of the tf state bucket." +} \ No newline at end of file diff --git a/modules/foundations-github-organization/versions.tf b/modules/foundations-github-organization/versions.tf new file mode 100644 index 0000000..ac486db --- /dev/null +++ b/modules/foundations-github-organization/versions.tf @@ -0,0 +1,10 @@ +terraform { + required_version = ">= 1.3" + required_providers { + github = { + source = "hashicorp/github" + version = "5.44.0" + configuration_aliases = [github.enterprise_scoped, github.foundation_org_scoped] + } + } +} diff --git a/modules/github-gcloud-oidc/README.md b/modules/github-gcloud-oidc/README.md new file mode 100644 index 0000000..3e251e4 --- /dev/null +++ b/modules/github-gcloud-oidc/README.md @@ -0,0 +1,90 @@ +## Requirements + +| Name | Version | +|------|---------| +| [terraform](#requirement\_terraform) | >= 1.3 | +| [google](#requirement\_google) | >= 3.77 | +| [google-beta](#requirement\_google-beta) | >= 3.77 | + +## Providers + +| Name | Version | +|------|---------| +| [google](#provider\_google) | >= 3.77 | +| [random](#provider\_random) | n/a | + +## Modules + +| Name | Source | Version | +|------|--------|---------| +| [oidc](#module\_oidc) | terraform-google-modules/github-actions-runners/google//modules/gh-oidc | n/a | + +## Resources + +| Name | Type | +|------|------| +| [google_folder.folder](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/folder) | resource | +| [google_project.project](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/project) | resource | +| [google_project_iam_member.bootstrap_project_member](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/project_iam_member) | resource | +| [google_project_iam_member.organizations_member](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/project_iam_member) | resource | +| [google_project_service.project_services](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/project_service) | resource | +| [google_service_account.bootstrap_sa](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/service_account) | resource | +| [google_service_account.organizations_sa](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/service_account) | resource | +| [google_storage_bucket.bucket](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/storage_bucket) | resource | +| [random_id.unique_project_suffix](https://registry.terraform.io/providers/hashicorp/random/latest/docs/resources/id) | resource | +| [google_folder.folder](https://registry.terraform.io/providers/hashicorp/google/latest/docs/data-sources/folder) | data source | +| [google_project.project](https://registry.terraform.io/providers/hashicorp/google/latest/docs/data-sources/project) | data source | + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| [auto\_create\_network](#input\_auto\_create\_network) | Whether to create the default network for the project. | `bool` | `false` | no | +| [autoclass](#input\_autoclass) | Enable autoclass to automatically transition objects to appropriate storage classes based on their access pattern. If set to true, storage\_class must be set to STANDARD. Defaults to false. | `bool` | `false` | no | +| [billing\_account](#input\_billing\_account) | Billing account id. | `string` | `null` | no | +| [bucket\_name](#input\_bucket\_name) | Bucket name | `string` | n/a | yes | +| [cors](#input\_cors) | CORS configuration for the bucket. Defaults to null. |
object({
origin = optional(list(string))
method = optional(list(string))
response_header = optional(list(string))
max_age_seconds = optional(number)
})
| `null` | no | +| [custom\_placement\_config](#input\_custom\_placement\_config) | The bucket's custom location configuration, which specifies the individual regions that comprise a dual-region bucket. If the bucket is designated as REGIONAL or MULTI\_REGIONAL, the parameters are empty. | `list(string)` | `null` | no | +| [default\_event\_based\_hold](#input\_default\_event\_based\_hold) | Enable event based hold to new objects added to specific bucket, defaults to false. | `bool` | `null` | no | +| [descriptive\_name](#input\_descriptive\_name) | Name of the project name. Used for project name instead of `project_name` variable. | `string` | `null` | no | +| [encryption\_key](#input\_encryption\_key) | KMS key that will be used for encryption. | `string` | `null` | no | +| [folder\_create](#input\_folder\_create) | Create folder. When set to false, uses id to reference an existing folder. | `bool` | `true` | no | +| [folder\_name](#input\_folder\_name) | Folder name. | `string` | `null` | no | +| [force\_destroy](#input\_force\_destroy) | Optional map to set force destroy keyed by name, defaults to false. | `bool` | `false` | no | +| [github\_foundations\_organization\_name](#input\_github\_foundations\_organization\_name) | The name of the organization that the github foundation repos will be under. | `string` | n/a | yes | +| [id](#input\_id) | Folder ID in case you use folder\_create=false. | `string` | `null` | no | +| [labels](#input\_labels) | Resource labels. | `map(string)` | `{}` | no | +| [lifecycle\_rules](#input\_lifecycle\_rules) | Bucket lifecycle rule. |
map(object({
action = object({
type = string
storage_class = optional(string)
})
condition = object({
age = optional(number)
created_before = optional(string)
custom_time_before = optional(string)
days_since_custom_time = optional(number)
days_since_noncurrent_time = optional(number)
matches_prefix = optional(list(string))
matches_storage_class = optional(list(string)) # STANDARD, MULTI_REGIONAL, REGIONAL, NEARLINE, COLDLINE, ARCHIVE, DURABLE_REDUCED_AVAILABILITY
matches_suffix = optional(list(string))
noncurrent_time_before = optional(string)
num_newer_versions = optional(number)
with_state = optional(string) # "LIVE", "ARCHIVED", "ANY"
})
}))
| `{}` | no | +| [location](#input\_location) | Bucket location. | `string` | n/a | yes | +| [logging\_config](#input\_logging\_config) | Bucket logging configuration. |
object({
log_bucket = string
log_object_prefix = optional(string)
})
| `null` | no | +| [organization\_id](#input\_organization\_id) | The organization id. | `string` | n/a | yes | +| [parent](#input\_parent) | Parent in folders/folder\_id or organizations/org\_id format. | `string` | `null` | no | +| [prefix](#input\_prefix) | Optional prefix used to generate project id and name. | `string` | `null` | no | +| [project\_create](#input\_project\_create) | Create project. When set to false, uses a data source to reference existing project. | `bool` | `true` | no | +| [project\_name](#input\_project\_name) | Project name and id suffix. | `string` | n/a | yes | +| [project\_parent](#input\_project\_parent) | Parent folder or organization in 'folders/folder\_id' or 'organizations/org\_id' format. | `string` | `null` | no | +| [requester\_pays](#input\_requester\_pays) | Enables Requester Pays on a storage bucket. | `bool` | `null` | no | +| [retention\_policy](#input\_retention\_policy) | Bucket retention policy. |
object({
retention_period = number
is_locked = optional(bool)
})
| `null` | no | +| [service\_config](#input\_service\_config) | Configure service API activation. |
object({
disable_on_destroy = bool
disable_dependent_services = bool
})
|
{
"disable_dependent_services": false,
"disable_on_destroy": false
}
| no | +| [services](#input\_services) | Service APIs to enable. | `list(string)` | `[]` | no | +| [skip\_delete](#input\_skip\_delete) | Allows the underlying resources to be destroyed without destroying the project itself. | `bool` | `false` | no | +| [storage\_class](#input\_storage\_class) | Bucket storage class. | `string` | `"STANDARD"` | no | +| [uniform\_bucket\_level\_access](#input\_uniform\_bucket\_level\_access) | Allow using object ACLs (false) or not (true, this is the recommended behavior) , defaults to true (which is the recommended practice, but not the behavior of storage API). | `bool` | `true` | no | +| [versioning](#input\_versioning) | Enable versioning, defaults to false. | `bool` | `false` | no | +| [website](#input\_website) | Bucket website. |
object({
main_page_suffix = optional(string)
not_found_page = optional(string)
})
| `null` | no | + +## Outputs + +| Name | Description | +|------|-------------| +| [bootstrap\_sa](#output\_bootstrap\_sa) | Bootstrap repository service account email. | +| [bootstrap\_sa\_name](#output\_bootstrap\_sa\_name) | Bootstrap repository service account name. | +| [bucket\_location](#output\_bucket\_location) | Terraform state bucket location. | +| [bucket\_name](#output\_bucket\_name) | Terraform state bucket name. | +| [folder](#output\_folder) | Folder resource. | +| [id](#output\_id) | Fully qualified folder id. | +| [name](#output\_name) | Folder name. | +| [organizations\_sa](#output\_organizations\_sa) | Organizations repository service account email. | +| [organizations\_sa\_name](#output\_organizations\_sa\_name) | Organizations repository service account name. | +| [project\_id](#output\_project\_id) | Project id. | +| [provider\_name](#output\_provider\_name) | Workload identity provider name. | diff --git a/modules/github-gcloud-oidc/folder.tf b/modules/github-gcloud-oidc/folder.tf new file mode 100644 index 0000000..9f23744 --- /dev/null +++ b/modules/github-gcloud-oidc/folder.tf @@ -0,0 +1,18 @@ +locals { + folder = ( + var.folder_create + ? try(google_folder.folder.0, null) + : try(data.google_folder.folder.0, null) + ) +} + +data "google_folder" "folder" { + count = var.folder_create ? 0 : 1 + folder = var.id +} + +resource "google_folder" "folder" { + count = var.folder_create ? 1 : 0 + display_name = var.folder_name + parent = var.parent +} \ No newline at end of file diff --git a/modules/github-gcloud-oidc/locals.tf b/modules/github-gcloud-oidc/locals.tf new file mode 100644 index 0000000..4ecb8ef --- /dev/null +++ b/modules/github-gcloud-oidc/locals.tf @@ -0,0 +1,3 @@ +locals { + project_parent = var.project_parent == null ? google_folder.folder[0].id : var.project_parent +} diff --git a/modules/github-gcloud-oidc/oidc.tf b/modules/github-gcloud-oidc/oidc.tf new file mode 100644 index 0000000..ae8fdc9 --- /dev/null +++ b/modules/github-gcloud-oidc/oidc.tf @@ -0,0 +1,70 @@ +locals { + pool_id = "pool-oidc-github-foundation" + provider_id = "provider-oidc-github-foundation" + + bootstrap_repo_name = "bootstrap" + organizations_repo_name = "organizations" + projects_repo_name = "projects" + + state_file_access_roles = tolist(["roles/storage.objectAdmin", "roles/storage.admin"]) + + bootstrap_project_roles = local.state_file_access_roles + + organziations_project_roles = concat( + local.state_file_access_roles, + tolist([ + "roles/secretmanager.viewer", + "roles/secretmanager.secretAccessor", + "roles/iam.workloadIdentityUser" + ]) + ) +} + +/** +* Service account and roles for github state bucket and oidc module setup +*/ + +resource "google_service_account" "bootstrap_sa" { + project = google_project.project[0].project_id + account_id = "${local.bootstrap_repo_name}-sa" +} + +resource "google_project_iam_member" "bootstrap_project_member" { + for_each = toset(local.bootstrap_project_roles) + project = google_project.project[0].project_id + role = each.value + member = "serviceAccount:${google_service_account.bootstrap_sa.email}" +} + +resource "google_service_account" "organizations_sa" { + project = google_project.project[0].project_id + account_id = "${local.organizations_repo_name}-sa" +} + +resource "google_project_iam_member" "organizations_member" { + for_each = toset(local.organziations_project_roles) + project = google_service_account.organizations_sa.project + role = each.value + member = "serviceAccount:${google_service_account.organizations_sa.email}" +} + +/* +* oidc setup +*/ +module "oidc" { + source = "terraform-google-modules/github-actions-runners/google//modules/gh-oidc" + depends_on = [google_project_service.project_services, google_service_account.bootstrap_sa, google_service_account.organizations_sa] + project_id = google_project.project[0].project_id + pool_id = local.pool_id + provider_id = local.provider_id + sa_mapping = { + (google_service_account.bootstrap_sa.account_id) = { + sa_name = google_service_account.bootstrap_sa.name + attribute = "attribute.repository/${var.github_foundations_organization_name}/${local.bootstrap_repo_name}" + }, + (google_service_account.organizations_sa.account_id) = { + sa_name = google_service_account.organizations_sa.name + attribute = "attribute.repository/${var.github_foundations_organization_name}/${local.organizations_repo_name}" + } + } +} diff --git a/modules/github-gcloud-oidc/outputs.tf b/modules/github-gcloud-oidc/outputs.tf new file mode 100644 index 0000000..e688d63 --- /dev/null +++ b/modules/github-gcloud-oidc/outputs.tf @@ -0,0 +1,54 @@ +output "folder" { + description = "Folder resource." + value = local.folder +} + +output "id" { + description = "Fully qualified folder id." + value = local.folder.name +} + +output "project_id" { + description = "Project id." + value = google_project.project[0].project_id +} + +output "name" { + description = "Folder name." + value = local.folder.display_name +} + +output "provider_name" { + description = "Workload identity provider name." + value = module.oidc.provider_name +} + +output "bootstrap_sa" { + description = "Bootstrap repository service account email." + value = google_service_account.bootstrap_sa.email +} + +output "bootstrap_sa_name" { + description = "Bootstrap repository service account name." + value = google_service_account.bootstrap_sa.name +} + +output "organizations_sa" { + description = "Organizations repository service account email." + value = google_service_account.organizations_sa.email +} + +output "organizations_sa_name" { + description = "Organizations repository service account name." + value = google_service_account.organizations_sa.name +} + +output "bucket_name" { + description = "Terraform state bucket name." + value = google_storage_bucket.bucket.name +} + +output "bucket_location" { + description = "Terraform state bucket location." + value = google_storage_bucket.bucket.location +} \ No newline at end of file diff --git a/modules/github-gcloud-oidc/project.tf b/modules/github-gcloud-oidc/project.tf new file mode 100644 index 0000000..81382da --- /dev/null +++ b/modules/github-gcloud-oidc/project.tf @@ -0,0 +1,69 @@ +/** + * Copyright 2022 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +locals { + project_id = "${local.prefix}${var.project_name}${random_id.unique_project_suffix.dec}" #TODO project id's need to be a min of 4 and a max of 30 characters. So we need to make sure input isn't more than 23 characters since the 2 byte random_id is 7 digits + descriptive_name = ( + var.descriptive_name != null ? var.descriptive_name : local.project_id + ) + parent_type = local.project_parent == null ? null : split("/", local.project_parent)[0] + parent_id = local.project_parent == null ? null : split("/", local.project_parent)[1] + prefix = var.prefix == null ? "" : "${var.prefix}-" + project = ( + var.project_create ? + { + project_id = try(google_project.project.0.project_id, null) + number = try(google_project.project.0.number, null) + name = try(google_project.project.0.name, null) + } + : { + project_id = local.project_id + number = try(data.google_project.project.0.number, null) + name = try(data.google_project.project.0.name, null) + } + ) +} + +data "google_project" "project" { + count = var.project_create ? 0 : 1 + project_id = "${local.prefix}${var.project_name}" #TODO - clean this up. It doesn't make any sense that we add a prefix if we are asking them for an existing project name +} + +resource "random_id" "unique_project_suffix" { + byte_length = 2 +} + +resource "google_project" "project" { + count = var.project_create ? 1 : 0 + org_id = local.parent_type == "organizations" ? local.parent_id : null + folder_id = local.parent_type == "folders" ? local.parent_id : null + project_id = local.project_id + name = local.descriptive_name + billing_account = var.billing_account + auto_create_network = var.auto_create_network + labels = var.labels + skip_delete = var.skip_delete + depends_on = [google_folder.folder] +} + +resource "google_project_service" "project_services" { + for_each = toset(var.services) + project = local.project.project_id + service = each.value + disable_on_destroy = var.service_config.disable_on_destroy + disable_dependent_services = var.service_config.disable_dependent_services +} + diff --git a/modules/github-gcloud-oidc/storage.tf b/modules/github-gcloud-oidc/storage.tf new file mode 100644 index 0000000..e39de1f --- /dev/null +++ b/modules/github-gcloud-oidc/storage.tf @@ -0,0 +1,97 @@ +resource "google_storage_bucket" "bucket" { + name = lower(var.bucket_name) + depends_on = [google_project_service.project_services] + project = google_project.project[0].project_id + location = var.location + storage_class = var.storage_class + force_destroy = var.force_destroy + uniform_bucket_level_access = var.uniform_bucket_level_access + labels = var.labels + default_event_based_hold = var.default_event_based_hold + requester_pays = var.requester_pays + versioning { + enabled = var.versioning + } + + dynamic "autoclass" { + for_each = var.autoclass == null ? [] : [""] + content { + enabled = var.autoclass + } + } + + dynamic "website" { + for_each = var.website == null ? [] : [""] + + content { + main_page_suffix = var.website.main_page_suffix + not_found_page = var.website.not_found_page + } + } + + dynamic "encryption" { + for_each = var.encryption_key == null ? [] : [""] + + content { + default_kms_key_name = var.encryption_key + } + } + + dynamic "retention_policy" { + for_each = var.retention_policy == null ? [] : [""] + content { + retention_period = var.retention_policy.retention_period + is_locked = var.retention_policy.is_locked + } + } + + dynamic "logging" { + for_each = var.logging_config == null ? [] : [""] + content { + log_bucket = var.logging_config.log_bucket + log_object_prefix = var.logging_config.log_object_prefix + } + } + + dynamic "cors" { + for_each = var.cors == null ? [] : [""] + content { + origin = var.cors.origin + method = var.cors.method + response_header = var.cors.response_header + max_age_seconds = max(3600, var.cors.max_age_seconds) + } + } + + dynamic "lifecycle_rule" { + for_each = var.lifecycle_rules + iterator = rule + content { + action { + type = rule.value.action.type + storage_class = rule.value.action.storage_class + } + condition { + age = rule.value.condition.age + created_before = rule.value.condition.created_before + custom_time_before = rule.value.condition.custom_time_before + days_since_custom_time = rule.value.condition.days_since_custom_time + days_since_noncurrent_time = rule.value.condition.days_since_noncurrent_time + matches_prefix = rule.value.condition.matches_prefix + matches_storage_class = rule.value.condition.matches_storage_class + matches_suffix = rule.value.condition.matches_suffix + noncurrent_time_before = rule.value.condition.noncurrent_time_before + num_newer_versions = rule.value.condition.num_newer_versions + with_state = rule.value.condition.with_state + } + } + } + + dynamic "custom_placement_config" { + for_each = var.custom_placement_config == null ? [] : [""] + + content { + data_locations = var.custom_placement_config + } + } +} \ No newline at end of file diff --git a/modules/github-gcloud-oidc/variables.tf b/modules/github-gcloud-oidc/variables.tf new file mode 100644 index 0000000..8c70aab --- /dev/null +++ b/modules/github-gcloud-oidc/variables.tf @@ -0,0 +1,280 @@ +#Organization Variables +variable "organization_id" { + description = "The organization id." + type = string +} + +#Folder Variables +variable "folder_create" { + description = "Create folder. When set to false, uses id to reference an existing folder." + type = bool + default = true +} + + +variable "id" { + description = "Folder ID in case you use folder_create=false." + type = string + default = null +} + +variable "folder_name" { + description = "Folder name." + type = string + default = null +} + +variable "parent" { + description = "Parent in folders/folder_id or organizations/org_id format." + type = string + default = null + validation { + condition = var.parent == null || can(regex("(organizations|folders)/[0-9]+", var.parent)) + error_message = "Parent must be of the form folders/folder_id or organizations/organization_id." + } +} + +#Project Variables + +variable "project_name" { + description = "Project name and id suffix." + type = string +} + +variable "descriptive_name" { + description = "Name of the project name. Used for project name instead of `project_name` variable." + type = string + default = null +} + + +variable "prefix" { + description = "Optional prefix used to generate project id and name." + type = string + default = null + validation { + condition = var.prefix != "" + error_message = "Prefix cannot be empty, please use null instead." + } +} + +variable "project_create" { + description = "Create project. When set to false, uses a data source to reference existing project." + type = bool + default = true +} + +variable "billing_account" { + description = "Billing account id." + type = string + default = null +} + +variable "auto_create_network" { + description = "Whether to create the default network for the project." + type = bool + default = false +} + +variable "labels" { + description = "Resource labels." + type = map(string) + default = {} +} + +variable "skip_delete" { + description = "Allows the underlying resources to be destroyed without destroying the project itself." + type = bool + default = false +} + +variable "services" { + description = "Service APIs to enable." + type = list(string) + default = [] +} + +variable "project_parent" { + description = "Parent folder or organization in 'folders/folder_id' or 'organizations/org_id' format." + type = string + default = null + validation { + condition = var.project_parent == null || can(regex("(organizations|folders)/[0-9]+", var.project_parent)) + error_message = "Parent must be of the form folders/folder_id or organizations/organization_id." + } +} + +variable "service_config" { + description = "Configure service API activation." + type = object({ + disable_on_destroy = bool + disable_dependent_services = bool + }) + default = { + disable_on_destroy = false + disable_dependent_services = false + } +} + +#Storage Bucket Variables + +variable "bucket_name" { + description = "Bucket name " + type = string +} + +variable "location" { + description = "Bucket location." + type = string +} + +variable "storage_class" { + description = "Bucket storage class." + type = string + default = "STANDARD" + validation { + condition = contains(["STANDARD", "MULTI_REGIONAL", "REGIONAL", "NEARLINE", "COLDLINE", "ARCHIVE"], var.storage_class) + error_message = "Storage class must be one of STANDARD, MULTI_REGIONAL, REGIONAL, NEARLINE, COLDLINE, ARCHIVE." + } +} + +variable "force_destroy" { + description = "Optional map to set force destroy keyed by name, defaults to false." + type = bool + default = false +} + +variable "uniform_bucket_level_access" { + description = "Allow using object ACLs (false) or not (true, this is the recommended behavior) , defaults to true (which is the recommended practice, but not the behavior of storage API)." + type = bool + default = true +} + +variable "default_event_based_hold" { + description = "Enable event based hold to new objects added to specific bucket, defaults to false." + type = bool + default = null +} + +variable "requester_pays" { + description = "Enables Requester Pays on a storage bucket." + type = bool + default = null +} + +variable "versioning" { + description = "Enable versioning, defaults to false." + type = bool + default = false +} + +variable "autoclass" { + description = "Enable autoclass to automatically transition objects to appropriate storage classes based on their access pattern. If set to true, storage_class must be set to STANDARD. Defaults to false." + type = bool + default = false +} + +variable "website" { + description = "Bucket website." + type = object({ + main_page_suffix = optional(string) + not_found_page = optional(string) + }) + default = null +} + +variable "encryption_key" { + description = "KMS key that will be used for encryption." + type = string + default = null +} + +variable "retention_policy" { + description = "Bucket retention policy." + type = object({ + retention_period = number + is_locked = optional(bool) + }) + default = null +} + +variable "logging_config" { + description = "Bucket logging configuration." + type = object({ + log_bucket = string + log_object_prefix = optional(string) + }) + default = null +} + +variable "cors" { + description = "CORS configuration for the bucket. Defaults to null." + type = object({ + origin = optional(list(string)) + method = optional(list(string)) + response_header = optional(list(string)) + max_age_seconds = optional(number) + }) + default = null +} + +variable "lifecycle_rules" { + description = "Bucket lifecycle rule." + type = map(object({ + action = object({ + type = string + storage_class = optional(string) + }) + condition = object({ + age = optional(number) + created_before = optional(string) + custom_time_before = optional(string) + days_since_custom_time = optional(number) + days_since_noncurrent_time = optional(number) + matches_prefix = optional(list(string)) + matches_storage_class = optional(list(string)) # STANDARD, MULTI_REGIONAL, REGIONAL, NEARLINE, COLDLINE, ARCHIVE, DURABLE_REDUCED_AVAILABILITY + matches_suffix = optional(list(string)) + noncurrent_time_before = optional(string) + num_newer_versions = optional(number) + with_state = optional(string) # "LIVE", "ARCHIVED", "ANY" + }) + })) + default = {} + validation { + condition = alltrue([ + for k, v in var.lifecycle_rules : v.action != null && v.condition != null + ]) + error_message = "Lifecycle rules action and condition cannot be null." + } + validation { + condition = alltrue([ + for k, v in var.lifecycle_rules : contains( + ["Delete", "SetStorageClass", "AbortIncompleteMultipartUpload"], + v.action.type + ) + ]) + error_message = "Lifecycle rules action type has unsupported value." + } + validation { + condition = alltrue([ + for k, v in var.lifecycle_rules : + v.action.type != "SetStorageClass" + || + v.action.storage_class != null + ]) + error_message = "Lifecycle rules with action type SetStorageClass require a storage class." + } +} + +variable "custom_placement_config" { + type = list(string) + default = null + description = "The bucket's custom location configuration, which specifies the individual regions that comprise a dual-region bucket. If the bucket is designated as REGIONAL or MULTI_REGIONAL, the parameters are empty." +} + +#OIDC variables + +variable "github_foundations_organization_name" { + type = string + description = "The name of the organization that the github foundation repos will be under." +} \ No newline at end of file diff --git a/modules/github-gcloud-oidc/versions.tf b/modules/github-gcloud-oidc/versions.tf new file mode 100644 index 0000000..6381a79 --- /dev/null +++ b/modules/github-gcloud-oidc/versions.tf @@ -0,0 +1,13 @@ +terraform { + required_version = ">= 1.3" + required_providers { + google = { + source = "hashicorp/google" + version = ">= 3.77" # tftest + } + google-beta = { + source = "hashicorp/google-beta" + version = ">= 3.77" # tftest + } + } +} \ No newline at end of file diff --git a/modules/organization/README.md b/modules/organization/README.md new file mode 100644 index 0000000..ce6081d --- /dev/null +++ b/modules/organization/README.md @@ -0,0 +1,63 @@ +## Requirements + +| Name | Version | +|------|---------| +| [terraform](#requirement\_terraform) | >= 1.3 | +| [github](#requirement\_github) | 5.42.0 | + +## Providers + +| Name | Version | +|------|---------| +| [github](#provider\_github) | 5.42.0 | + +## Modules + +No modules. + +## Resources + +| Name | Type | +|------|------| +| [github_membership.membership_for_user](https://registry.terraform.io/providers/integrations/github/5.42.0/docs/resources/membership) | resource | +| [github_organization_block.blocked_user](https://registry.terraform.io/providers/integrations/github/5.42.0/docs/resources/organization_block) | resource | +| [github_organization_custom_role.community_manager_role](https://registry.terraform.io/providers/integrations/github/5.42.0/docs/resources/organization_custom_role) | resource | +| [github_organization_custom_role.contractor_role](https://registry.terraform.io/providers/integrations/github/5.42.0/docs/resources/organization_custom_role) | resource | +| [github_organization_custom_role.custom_repository_role](https://registry.terraform.io/providers/integrations/github/5.42.0/docs/resources/organization_custom_role) | resource | +| [github_organization_custom_role.security_engineer_role](https://registry.terraform.io/providers/integrations/github/5.42.0/docs/resources/organization_custom_role) | resource | +| [github_organization_settings.organization_settings](https://registry.terraform.io/providers/integrations/github/5.42.0/docs/resources/organization_settings) | resource | + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| [custom\_repository\_roles](#input\_custom\_repository\_roles) | A map of custom repository roles to create. The key is the name of the role and the value is the role configurations. |
map(object({
description = string
base_role = string
permissions = list(string)
}))
| n/a | yes | +| [enable\_community\_manager\_role](#input\_enable\_community\_manager\_role) | If `true` will create a custom repository role for community managers. Defaults to `false`. If `true` the maximum number of `custom_repository_roles` that can be defined will be reduced by one. | `bool` | `false` | no | +| [enable\_contractor\_role](#input\_enable\_contractor\_role) | If `true` will create a custom repository role for contractors. Defaults to `false`. If `true` the maximum number of `custom_repository_roles` that can be defined will be reduced by one. | `bool` | `false` | no | +| [enable\_security\_engineer\_role](#input\_enable\_security\_engineer\_role) | If `true` will create a custom repository role for security engineers. Defaults to `false`. If `true` the maximum number of `custom_repository_roles` that can be defined will be reduced by one. | `bool` | `false` | no | +| [github\_organization\_billing\_email](#input\_github\_organization\_billing\_email) | The billing email to set for the organization. | `string` | n/a | yes | +| [github\_organization\_blocked\_users](#input\_github\_organization\_blocked\_users) | A list of usernames to block from the organization. Defaults to `[]`. | `list(string)` | `[]` | no | +| [github\_organization\_blog](#input\_github\_organization\_blog) | Url to organization blog. Defaults to `''`. | `string` | `""` | no | +| [github\_organization\_email](#input\_github\_organization\_email) | Organization email. Defaults to `''`. | `string` | `""` | no | +| [github\_organization\_enable\_dependabot\_alerts](#input\_github\_organization\_enable\_dependabot\_alerts) | If set dependabot alerts will be enabled for new repositories in the organization. Defaults to `true`. | `bool` | `true` | no | +| [github\_organization\_enable\_dependabot\_updates](#input\_github\_organization\_enable\_dependabot\_updates) | If set dependabot security updates will be enabled for new repositories in the organization. Defaults to `true`. | `bool` | `true` | no | +| [github\_organization\_enable\_dependancy\_graph](#input\_github\_organization\_enable\_dependancy\_graph) | If set dependancy graph will be enabled for new repositories in the organization. Defaults to `true`. | `bool` | `true` | no | +| [github\_organization\_enable\_ghas](#input\_github\_organization\_enable\_ghas) | If set github advance security will be enabled for new repositories in the organization. Defaults to `true`. | `bool` | `true` | no | +| [github\_organization\_enable\_secret\_scanning](#input\_github\_organization\_enable\_secret\_scanning) | If set secret scanning will be enabled for new repositories in the organization. Defaults to `true`. | `bool` | `true` | no | +| [github\_organization\_enable\_secret\_scanning\_push\_protection](#input\_github\_organization\_enable\_secret\_scanning\_push\_protection) | If set secret scanning push protection will be enabled for new repositories in the organization. Defaults to `true`. | `bool` | `true` | no | +| [github\_organization\_id](#input\_github\_organization\_id) | The ID of the organization to manage. | `string` | n/a | yes | +| [github\_organization\_location](#input\_github\_organization\_location) | Organization location. Defaults to `''`. | `string` | `""` | no | +| [github\_organization\_members](#input\_github\_organization\_members) | A list of usernames to invite to the organization. Defaults to `[]`. | `list(string)` | `[]` | no | +| [github\_organization\_pages\_settings](#input\_github\_organization\_pages\_settings) | Settings for organization page creation. The default setting does not allow members to create public and private pages. |
object({
members_can_create_public = bool,
members_can_create_private = bool
})
|
{
"members_can_create_private": false,
"members_can_create_public": false
}
| no | +| [github\_organization\_repository\_settings](#input\_github\_organization\_repository\_settings) | Settings for organization repository creation. The default setting allows members to create internal and private repositories but not public. |
object({
members_can_create_public = bool,
members_can_create_internal = bool,
members_can_create_private = bool
})
|
{
"members_can_create_internal": true,
"members_can_create_private": true,
"members_can_create_public": false
}
| no | +| [github\_organization\_requires\_web\_commit\_signing](#input\_github\_organization\_requires\_web\_commit\_signing) | If set commit signatures are required for commits to the organization. Defaults to `false`. | `bool` | `false` | no | + +## Outputs + +| Name | Description | +|------|-------------| +| [community\_manager\_role\_id](#output\_community\_manager\_role\_id) | The id of the community manager custom role. | +| [contractor\_role\_id](#output\_contractor\_role\_id) | The id of the contractor custom role. | +| [custom\_role\_ids](#output\_custom\_role\_ids) | A map of custom role names to custom role ids. | +| [ghas\_enabled](#output\_ghas\_enabled) | A boolean value indicating if GitHub Advanced Security is enabled for new repositories in the organization. | +| [security\_engineer\_role\_id](#output\_security\_engineer\_role\_id) | The id of the security engineer custom role. | diff --git a/modules/organization/block.tf b/modules/organization/block.tf new file mode 100644 index 0000000..6d548e7 --- /dev/null +++ b/modules/organization/block.tf @@ -0,0 +1,5 @@ +resource "github_organization_block" "blocked_user" { + for_each = toset(var.github_organization_blocked_users) + + username = each.value +} \ No newline at end of file diff --git a/modules/organization/members.tf b/modules/organization/members.tf new file mode 100644 index 0000000..343ecdb --- /dev/null +++ b/modules/organization/members.tf @@ -0,0 +1,6 @@ +resource "github_membership" "membership_for_user" { + for_each = toset(var.github_organization_members) + + username = each.value + role = "member" +} \ No newline at end of file diff --git a/modules/organization/outputs.tf b/modules/organization/outputs.tf new file mode 100644 index 0000000..793876e --- /dev/null +++ b/modules/organization/outputs.tf @@ -0,0 +1,26 @@ +output "ghas_enabled" { + value = github_organization_settings.organization_settings.advanced_security_enabled_for_new_repositories + description = "A boolean value indicating if GitHub Advanced Security is enabled for new repositories in the organization." +} + +output "custom_role_ids" { + value = { + for role in github_organization_custom_role.custom_repository_role : role.name => role.id + } + description = "A map of custom role names to custom role ids." +} + +output "security_engineer_role_id" { + value = length(github_organization_custom_role.security_engineer_role) > 0 ? github_organization_custom_role.security_engineer_role[0].id : null + description = "The id of the security engineer custom role." +} + +output "contractor_role_id" { + value = length(github_organization_custom_role.contractor_role) > 0 ? github_organization_custom_role.contractor_role[0].id : null + description = "The id of the contractor custom role." +} + +output "community_manager_role_id" { + value = length(github_organization_custom_role.community_manager_role) > 0 ? github_organization_custom_role.community_manager_role[0].id : null + description = "The id of the community manager custom role." +} \ No newline at end of file diff --git a/modules/organization/roles.tf b/modules/organization/roles.tf new file mode 100644 index 0000000..f4427ac --- /dev/null +++ b/modules/organization/roles.tf @@ -0,0 +1,57 @@ +resource "github_organization_custom_role" "custom_repository_role" { + for_each = var.custom_repository_roles + name = each.key + description = each.value.description + base_role = each.value.base_role + permissions = each.value.permissions + + lifecycle { + precondition { + condition = length(var.custom_repository_roles) <= 5 - (var.enable_security_engineer_role ? 1 : 0) - (var.enable_contractor_role ? 1 : 0) - (var.enable_community_manager_role ? 1 : 0) + error_message = "To many custom repository roles defined, an orrganization's maximum is 5. This limit is reduced by one for each of the following variables that are set to true: `enable_security_engineer_role`, `enable_contractor_role`, `enable_community_manager_role`." + } + } +} + +resource "github_organization_custom_role" "security_engineer_role" { + count = var.enable_security_engineer_role ? 1 : 0 + name = "Security Engineer" + description = "Security Engineers have maintainer permissions and are able to contribute code and maintain the security pipeline." + base_role = "maintain" + permissions = [ + "delete_alerts_code_scanning", + "write_code_scanning" + ] +} + +resource "github_organization_custom_role" "contractor_role" { + count = var.enable_contractor_role ? 1 : 0 + name = "Contractor" + description = "Contractors have write permissions and are able to develop webhooks integrations." + base_role = "write" + permissions = [ + "manage_webhooks" + ] +} + +resource "github_organization_custom_role" "community_manager_role" { + count = var.enable_community_manager_role ? 1 : 0 + name = "Community Manager" + description = "Community Managers have read permissions and are able to handle all the community interactions without being able to contribute code." + base_role = "read" + permissions = [ + "mark_as_duplicate", + "manage_settings_pages", + "manage_settings_wiki", + "set_social_preview", + "edit_repo_metadata", + "edit_discussion_category", + "create_discussion_category", + "edit_category_on_discussion", + "toggle_discussion_answer", + "convert_issues_to_discussions", + "close_discussion", + "reopen_discussion", + "delete_discussion_comment" + ] +} diff --git a/modules/organization/settings.tf b/modules/organization/settings.tf new file mode 100644 index 0000000..d1c56fa --- /dev/null +++ b/modules/organization/settings.tf @@ -0,0 +1,48 @@ +locals { + members_can_create_pages = var.github_organization_pages_settings.members_can_create_public || var.github_organization_pages_settings.members_can_create_private + members_can_create_repositories = var.github_organization_repository_settings.members_can_create_public || var.github_organization_repository_settings.members_can_create_internal || var.github_organization_repository_settings.members_can_create_private +} + +import { + to = github_organization_settings.organization_settings + id = var.github_organization_id +} + +resource "github_organization_settings" "organization_settings" { + billing_email = var.github_organization_billing_email + email = var.github_organization_email + blog = var.github_organization_blog + location = var.github_organization_location + web_commit_signoff_required = var.github_organization_requires_web_commit_signing + has_organization_projects = true + has_repository_projects = true + + # Github advance security, dependabot, and secret scanning + advanced_security_enabled_for_new_repositories = var.github_organization_enable_ghas + dependabot_alerts_enabled_for_new_repositories = var.github_organization_enable_dependabot_alerts + dependabot_security_updates_enabled_for_new_repositories = var.github_organization_enable_dependabot_updates + dependency_graph_enabled_for_new_repositories = var.github_organization_enable_dependancy_graph + secret_scanning_enabled_for_new_repositories = var.github_organization_enable_secret_scanning + secret_scanning_push_protection_enabled_for_new_repositories = var.github_organization_enable_secret_scanning_push_protection + + #Organization pages + members_can_create_pages = local.members_can_create_pages + members_can_create_public_pages = var.github_organization_pages_settings.members_can_create_public + members_can_create_private_pages = var.github_organization_pages_settings.members_can_create_private + + #Oranization Repository settings + members_can_create_repositories = local.members_can_create_repositories + members_can_create_public_repositories = var.github_organization_repository_settings.members_can_create_public + members_can_create_internal_repositories = var.github_organization_repository_settings.members_can_create_internal + members_can_create_private_repositories = var.github_organization_repository_settings.members_can_create_private + default_repository_permission = "none" + members_can_fork_private_repositories = false + + lifecycle { + ignore_changes = [ + name, + description, + billing_email + ] + } +} diff --git a/modules/organization/variables.tf b/modules/organization/variables.tf new file mode 100644 index 0000000..4df88ef --- /dev/null +++ b/modules/organization/variables.tf @@ -0,0 +1,134 @@ +variable "github_organization_id" { + type = string + description = "The ID of the organization to manage." +} + +variable "github_organization_billing_email" { + type = string + description = "The billing email to set for the organization." +} + +variable "github_organization_members" { + type = list(string) + default = [] + description = "A list of usernames to invite to the organization. Defaults to `[]`." +} + +variable "github_organization_blocked_users" { + type = list(string) + default = [] + description = "A list of usernames to block from the organization. Defaults to `[]`." +} + +variable "github_organization_enable_ghas" { + type = bool + default = true + description = "If set github advance security will be enabled for new repositories in the organization. Defaults to `true`." +} + +variable "github_organization_enable_dependabot_alerts" { + type = bool + default = true + description = "If set dependabot alerts will be enabled for new repositories in the organization. Defaults to `true`." +} + +variable "github_organization_enable_dependabot_updates" { + type = bool + default = true + description = "If set dependabot security updates will be enabled for new repositories in the organization. Defaults to `true`." +} + +variable "github_organization_enable_dependancy_graph" { + type = bool + default = true + description = "If set dependancy graph will be enabled for new repositories in the organization. Defaults to `true`." +} + +variable "github_organization_enable_secret_scanning" { + type = bool + default = true + description = "If set secret scanning will be enabled for new repositories in the organization. Defaults to `true`." +} + +variable "github_organization_enable_secret_scanning_push_protection" { + type = bool + default = true + description = "If set secret scanning push protection will be enabled for new repositories in the organization. Defaults to `true`." +} + +variable "github_organization_pages_settings" { + type = object({ + members_can_create_public = bool, + members_can_create_private = bool + }) + default = { + members_can_create_private = false, + members_can_create_public = false + } + description = "Settings for organization page creation. The default setting does not allow members to create public and private pages." +} + +variable "github_organization_repository_settings" { + type = object({ + members_can_create_public = bool, + members_can_create_internal = bool, + members_can_create_private = bool + }) + default = { + members_can_create_public = false, + members_can_create_internal = true, + members_can_create_private = true + } + description = "Settings for organization repository creation. The default setting allows members to create internal and private repositories but not public." +} + +variable "github_organization_requires_web_commit_signing" { + type = bool + default = false + description = "If set commit signatures are required for commits to the organization. Defaults to `false`." +} + +variable "github_organization_blog" { + type = string + default = "" + description = "Url to organization blog. Defaults to `''`." +} + +variable "github_organization_email" { + type = string + default = "" + description = "Organization email. Defaults to `''`." +} + +variable "github_organization_location" { + type = string + default = "" + description = "Organization location. Defaults to `''`." +} + +variable "enable_security_engineer_role" { + type = bool + default = false + description = "If `true` will create a custom repository role for security engineers. Defaults to `false`. If `true` the maximum number of `custom_repository_roles` that can be defined will be reduced by one." +} + +variable "enable_contractor_role" { + type = bool + default = false + description = "If `true` will create a custom repository role for contractors. Defaults to `false`. If `true` the maximum number of `custom_repository_roles` that can be defined will be reduced by one." +} + +variable "enable_community_manager_role" { + type = bool + default = false + description = "If `true` will create a custom repository role for community managers. Defaults to `false`. If `true` the maximum number of `custom_repository_roles` that can be defined will be reduced by one." +} + +variable "custom_repository_roles" { + type = map(object({ + description = string + base_role = string + permissions = list(string) + })) + description = "A map of custom repository roles to create. The key is the name of the role and the value is the role configurations." +} diff --git a/modules/organization/versions.tf b/modules/organization/versions.tf new file mode 100644 index 0000000..99d757e --- /dev/null +++ b/modules/organization/versions.tf @@ -0,0 +1,9 @@ +terraform { + required_version = ">= 1.3" + required_providers { + github = { + source = "integrations/github" + version = "5.42.0" + } + } +} \ No newline at end of file diff --git a/modules/private_repository/README.md b/modules/private_repository/README.md new file mode 100644 index 0000000..785ff82 --- /dev/null +++ b/modules/private_repository/README.md @@ -0,0 +1,40 @@ +## Requirements + +| Name | Version | +|------|---------| +| [terraform](#requirement\_terraform) | >= 1.7.1 | +| [github](#requirement\_github) | 5.42.0 | + +## Providers + +No providers. + +## Modules + +| Name | Source | Version | +|------|--------|---------| +| [repository\_base](#module\_repository\_base) | ../repository_base | n/a | + +## Resources + +No resources. + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| [advance\_security](#input\_advance\_security) | Enables advance security for the repository. If repository is public `advance_security` is enabled by default and cannot be changed. | `bool` | `true` | no | +| [allow\_auto\_merge](#input\_allow\_auto\_merge) | Allow auto-merging pull requests on the repository | `bool` | `true` | no | +| [default\_branch](#input\_default\_branch) | The branch to set as the default branch for this repository. Defaults to "main" | `string` | `"main"` | no | +| [delete\_head\_on\_merge](#input\_delete\_head\_on\_merge) | Sets the delete head on merge option for the repository. If true it will delete pull request branches automatically on merge. Defaults to true | `bool` | `true` | no | +| [dependabot\_security\_updates](#input\_dependabot\_security\_updates) | Enables dependabot security updates. Only works when `has_vulnerability_alerts` is set because that is required to enable dependabot for the repository. | `bool` | `true` | no | +| [description](#input\_description) | The description to give to the repository. Defaults to `""` | `string` | `""` | no | +| [homepage](#input\_homepage) | The homepage for the repository | `string` | `""` | no | +| [name](#input\_name) | The name of the repository to create/import. | `string` | n/a | yes | +| [protected\_branches](#input\_protected\_branches) | A list of ref names or patterns that should be protected. Defaults `["main"]` | `list(string)` |
[
"main"
]
| no | +| [repository\_team\_permissions](#input\_repository\_team\_permissions) | A map where the keys are github team ids and the value is the permissions the team should have in the repository | `map(string)` | n/a | yes | +| [topics](#input\_topics) | The topics to apply to the repository | `list(string)` | `[]` | no | + +## Outputs + +No outputs. diff --git a/modules/private_repository/repository.tf b/modules/private_repository/repository.tf new file mode 100644 index 0000000..0b06190 --- /dev/null +++ b/modules/private_repository/repository.tf @@ -0,0 +1,27 @@ +module "repository_base" { + source = "../repository_base" + + name = var.name + description = var.description + homepage = var.homepage + topics = var.topics + visibility = "private" + has_downloads = false + has_issues = true + has_projects = true + has_wiki = true + has_discussions = false + + repository_team_permissions = var.repository_team_permissions + + default_branch = var.default_branch + protected_branches = var.protected_branches + delete_head_on_merge = var.delete_head_on_merge + allow_auto_merge = var.allow_auto_merge + + secret_scanning = true + secret_scanning_on_push = true + has_vulnerability_alerts = true + advance_security = var.advance_security + dependabot_security_updates = var.dependabot_security_updates +} \ No newline at end of file diff --git a/modules/private_repository/variables.tf b/modules/private_repository/variables.tf new file mode 100644 index 0000000..1ef5389 --- /dev/null +++ b/modules/private_repository/variables.tf @@ -0,0 +1,63 @@ +variable "name" { + type = string + description = "The name of the repository to create/import." +} + +variable "description" { + type = string + description = "The description to give to the repository. Defaults to `\"\"`" + default = "" +} + +variable "default_branch" { + type = string + description = "The branch to set as the default branch for this repository. Defaults to \"main\"" + default = "main" +} + +variable "repository_team_permissions" { + type = map(string) + description = "A map where the keys are github team ids and the value is the permissions the team should have in the repository" +} + +variable "protected_branches" { + type = list(string) + description = "A list of ref names or patterns that should be protected. Defaults `[\"main\"]`" + default = ["main"] +} + +variable "topics" { + description = "The topics to apply to the repository" + type = list(string) + default = [] +} + +variable "homepage" { + description = "The homepage for the repository" + type = string + default = "" +} + +variable "delete_head_on_merge" { + description = "Sets the delete head on merge option for the repository. If true it will delete pull request branches automatically on merge. Defaults to true" + type = bool + default = true +} + +variable "allow_auto_merge" { + description = "Allow auto-merging pull requests on the repository" + type = bool + default = true +} + +variable "dependabot_security_updates" { + description = "Enables dependabot security updates. Only works when `has_vulnerability_alerts` is set because that is required to enable dependabot for the repository." + type = bool + default = true +} + +variable "advance_security" { + description = "Enables advance security for the repository. If repository is public `advance_security` is enabled by default and cannot be changed." + type = bool + default = true +} \ No newline at end of file diff --git a/modules/private_repository/versions.tf b/modules/private_repository/versions.tf new file mode 100644 index 0000000..d6e8a27 --- /dev/null +++ b/modules/private_repository/versions.tf @@ -0,0 +1,9 @@ +terraform { + required_version = ">= 1.7.1" + required_providers { + github = { + source = "integrations/github" + version = "5.42.0" + } + } +} \ No newline at end of file diff --git a/modules/public_repository/README.md b/modules/public_repository/README.md new file mode 100644 index 0000000..785ff82 --- /dev/null +++ b/modules/public_repository/README.md @@ -0,0 +1,40 @@ +## Requirements + +| Name | Version | +|------|---------| +| [terraform](#requirement\_terraform) | >= 1.7.1 | +| [github](#requirement\_github) | 5.42.0 | + +## Providers + +No providers. + +## Modules + +| Name | Source | Version | +|------|--------|---------| +| [repository\_base](#module\_repository\_base) | ../repository_base | n/a | + +## Resources + +No resources. + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| [advance\_security](#input\_advance\_security) | Enables advance security for the repository. If repository is public `advance_security` is enabled by default and cannot be changed. | `bool` | `true` | no | +| [allow\_auto\_merge](#input\_allow\_auto\_merge) | Allow auto-merging pull requests on the repository | `bool` | `true` | no | +| [default\_branch](#input\_default\_branch) | The branch to set as the default branch for this repository. Defaults to "main" | `string` | `"main"` | no | +| [delete\_head\_on\_merge](#input\_delete\_head\_on\_merge) | Sets the delete head on merge option for the repository. If true it will delete pull request branches automatically on merge. Defaults to true | `bool` | `true` | no | +| [dependabot\_security\_updates](#input\_dependabot\_security\_updates) | Enables dependabot security updates. Only works when `has_vulnerability_alerts` is set because that is required to enable dependabot for the repository. | `bool` | `true` | no | +| [description](#input\_description) | The description to give to the repository. Defaults to `""` | `string` | `""` | no | +| [homepage](#input\_homepage) | The homepage for the repository | `string` | `""` | no | +| [name](#input\_name) | The name of the repository to create/import. | `string` | n/a | yes | +| [protected\_branches](#input\_protected\_branches) | A list of ref names or patterns that should be protected. Defaults `["main"]` | `list(string)` |
[
"main"
]
| no | +| [repository\_team\_permissions](#input\_repository\_team\_permissions) | A map where the keys are github team ids and the value is the permissions the team should have in the repository | `map(string)` | n/a | yes | +| [topics](#input\_topics) | The topics to apply to the repository | `list(string)` | `[]` | no | + +## Outputs + +No outputs. diff --git a/modules/public_repository/repository.tf b/modules/public_repository/repository.tf new file mode 100644 index 0000000..04a9b6d --- /dev/null +++ b/modules/public_repository/repository.tf @@ -0,0 +1,27 @@ +module "repository_base" { + source = "../repository_base" + + name = var.name + description = var.description + homepage = var.homepage + topics = var.topics + visibility = "public" + has_downloads = false + has_issues = true + has_projects = true + has_wiki = true + has_discussions = true + + repository_team_permissions = var.repository_team_permissions + + default_branch = var.default_branch + protected_branches = var.protected_branches + delete_head_on_merge = var.delete_head_on_merge + allow_auto_merge = var.allow_auto_merge + + secret_scanning = true + secret_scanning_on_push = true + has_vulnerability_alerts = true + advance_security = var.advance_security + dependabot_security_updates = var.dependabot_security_updates +} \ No newline at end of file diff --git a/modules/public_repository/variables.tf b/modules/public_repository/variables.tf new file mode 100644 index 0000000..1ef5389 --- /dev/null +++ b/modules/public_repository/variables.tf @@ -0,0 +1,63 @@ +variable "name" { + type = string + description = "The name of the repository to create/import." +} + +variable "description" { + type = string + description = "The description to give to the repository. Defaults to `\"\"`" + default = "" +} + +variable "default_branch" { + type = string + description = "The branch to set as the default branch for this repository. Defaults to \"main\"" + default = "main" +} + +variable "repository_team_permissions" { + type = map(string) + description = "A map where the keys are github team ids and the value is the permissions the team should have in the repository" +} + +variable "protected_branches" { + type = list(string) + description = "A list of ref names or patterns that should be protected. Defaults `[\"main\"]`" + default = ["main"] +} + +variable "topics" { + description = "The topics to apply to the repository" + type = list(string) + default = [] +} + +variable "homepage" { + description = "The homepage for the repository" + type = string + default = "" +} + +variable "delete_head_on_merge" { + description = "Sets the delete head on merge option for the repository. If true it will delete pull request branches automatically on merge. Defaults to true" + type = bool + default = true +} + +variable "allow_auto_merge" { + description = "Allow auto-merging pull requests on the repository" + type = bool + default = true +} + +variable "dependabot_security_updates" { + description = "Enables dependabot security updates. Only works when `has_vulnerability_alerts` is set because that is required to enable dependabot for the repository." + type = bool + default = true +} + +variable "advance_security" { + description = "Enables advance security for the repository. If repository is public `advance_security` is enabled by default and cannot be changed." + type = bool + default = true +} \ No newline at end of file diff --git a/modules/public_repository/versions.tf b/modules/public_repository/versions.tf new file mode 100644 index 0000000..d6e8a27 --- /dev/null +++ b/modules/public_repository/versions.tf @@ -0,0 +1,9 @@ +terraform { + required_version = ">= 1.7.1" + required_providers { + github = { + source = "integrations/github" + version = "5.42.0" + } + } +} \ No newline at end of file diff --git a/modules/repository_base/README.md b/modules/repository_base/README.md new file mode 100644 index 0000000..15ad2d1 --- /dev/null +++ b/modules/repository_base/README.md @@ -0,0 +1,55 @@ +## Requirements + +| Name | Version | +|------|---------| +| [terraform](#requirement\_terraform) | >= 1.7.1 | +| [github](#requirement\_github) | 5.42.0 | + +## Providers + +| Name | Version | +|------|---------| +| [github](#provider\_github) | 5.42.0 | + +## Modules + +No modules. + +## Resources + +| Name | Type | +|------|------| +| [github_branch_default.default_branch](https://registry.terraform.io/providers/integrations/github/5.42.0/docs/resources/branch_default) | resource | +| [github_repository.repository](https://registry.terraform.io/providers/integrations/github/5.42.0/docs/resources/repository) | resource | +| [github_repository_collaborators.collaborators](https://registry.terraform.io/providers/integrations/github/5.42.0/docs/resources/repository_collaborators) | resource | +| [github_repository_dependabot_security_updates.automated_security_fixes](https://registry.terraform.io/providers/integrations/github/5.42.0/docs/resources/repository_dependabot_security_updates) | resource | +| [github_repository_ruleset.protected_branch_base_rules](https://registry.terraform.io/providers/integrations/github/5.42.0/docs/resources/repository_ruleset) | resource | + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| [advance\_security](#input\_advance\_security) | Enables advance security for the repository. If repository is public `advance_security` is enabled by default and cannot be changed. | `bool` | `true` | no | +| [allow\_auto\_merge](#input\_allow\_auto\_merge) | Allow auto-merging pull requests on the repository | `bool` | `true` | no | +| [default\_branch](#input\_default\_branch) | The branch to set as the default branch for this repository. Defaults to "main" | `string` | `"main"` | no | +| [delete\_head\_on\_merge](#input\_delete\_head\_on\_merge) | Sets the delete head on merge option for the repository. If true it will delete pull request branches automatically on merge. Defaults to true | `bool` | `true` | no | +| [dependabot\_security\_updates](#input\_dependabot\_security\_updates) | Enables dependabot security updates. Only works when `has_vulnerability_alerts` is set because that is required to enable dependabot for the repository. | `bool` | `true` | no | +| [description](#input\_description) | The description to give to the repository. Defaults to `""` | `string` | `""` | no | +| [has\_discussions](#input\_has\_discussions) | Enables Github Discussions. | `bool` | `true` | no | +| [has\_downloads](#input\_has\_downloads) | Enables downloads for the repository | `bool` | `false` | no | +| [has\_issues](#input\_has\_issues) | Enables Github Issues for the repository | `bool` | `true` | no | +| [has\_projects](#input\_has\_projects) | Enables Github Projects for the repository | `bool` | `true` | no | +| [has\_vulnerability\_alerts](#input\_has\_vulnerability\_alerts) | Enables security alerts for vulnerable dependencies for the repository | `bool` | `true` | no | +| [has\_wiki](#input\_has\_wiki) | Enables Github Wiki for the repository | `bool` | `true` | no | +| [homepage](#input\_homepage) | The homepage for the repository | `string` | `""` | no | +| [name](#input\_name) | The name of the repository to create/import. | `string` | n/a | yes | +| [protected\_branches](#input\_protected\_branches) | A list of ref names or patterns that should be protected. Defaults `["main"]` | `list(string)` |
[
"main"
]
| no | +| [repository\_team\_permissions](#input\_repository\_team\_permissions) | A map where the keys are github team ids and the value is the permissions the team should have in the repository | `map(string)` | n/a | yes | +| [secret\_scanning](#input\_secret\_scanning) | Enables secret scanning for the repository. If repository is private `advance_security` must also be enabled. | `bool` | `true` | no | +| [secret\_scanning\_on\_push](#input\_secret\_scanning\_on\_push) | Enables secret scanning push protection for the repository. If repository is private `advance_security` must also be enabled. | `bool` | `true` | no | +| [topics](#input\_topics) | The topics to apply to the repository | `list(string)` | `[]` | no | +| [visibility](#input\_visibility) | Sets the visibility property of a repository. Defaults to "private" | `string` | `"private"` | no | + +## Outputs + +No outputs. diff --git a/modules/repository_base/repository.tf b/modules/repository_base/repository.tf new file mode 100644 index 0000000..08c7df1 --- /dev/null +++ b/modules/repository_base/repository.tf @@ -0,0 +1,80 @@ +locals { + enable_dependabot_automated_security_fixes = var.has_vulnerability_alerts && var.dependabot_security_updates ? 1 : 0 + is_public = var.visibility == "public" + can_configure_security_and_analysis = !local.is_public && var.advance_security + + protected_branches_refs = [ + for branch in var.protected_branches : "refs/heads/${branch}" + ] +} + +resource "github_repository" "repository" { + name = var.name + description = var.description + visibility = var.visibility + + auto_init = true + archive_on_destroy = false + has_downloads = var.has_downloads + has_issues = var.has_issues + has_projects = var.has_projects + has_wiki = var.has_wiki + has_discussions = var.has_discussions + vulnerability_alerts = var.has_vulnerability_alerts + topics = var.topics + homepage_url = var.homepage + delete_branch_on_merge = var.delete_head_on_merge + allow_auto_merge = var.allow_auto_merge + + # A hacky way of getting around the 422 errors received from github api + dynamic "security_and_analysis" { + for_each = local.can_configure_security_and_analysis ? [1] : [] + content { + advanced_security { + status = var.advance_security ? "enabled" : "disabled" + } + secret_scanning { + status = var.secret_scanning ? "enabled" : "disabled" + } + secret_scanning_push_protection { + status = var.secret_scanning_on_push ? "enabled" : "disabled" + } + } + } +} + +resource "github_repository_dependabot_security_updates" "automated_security_fixes" { + count = local.enable_dependabot_automated_security_fixes + repository = github_repository.repository.name + enabled = true +} + +resource "github_branch_default" "default_branch" { + repository = github_repository.repository.name + branch = var.default_branch +} + +resource "github_repository_ruleset" "protected_branch_base_rules" { + name = "protected_branch_base_ruleset" + repository = github_repository.repository.name + target = "branch" + enforcement = "active" + rules { + deletion = true + creation = false + update = false + pull_request { + dismiss_stale_reviews_on_push = true + require_last_push_approval = true + required_approving_review_count = 1 + } + non_fast_forward = true + } + + conditions { + ref_name { + exclude = [] + include = toset(concat(["~DEFAULT_BRANCH"], local.protected_branches_refs)) + } + } +} diff --git a/modules/repository_base/teams.tf b/modules/repository_base/teams.tf new file mode 100644 index 0000000..8532470 --- /dev/null +++ b/modules/repository_base/teams.tf @@ -0,0 +1,11 @@ +resource "github_repository_collaborators" "collaborators" { + repository = github_repository.repository.name + + dynamic "team" { + for_each = var.repository_team_permissions + content { + permission = team.value + team_id = team.key + } + } +} diff --git a/modules/repository_base/variables.tf b/modules/repository_base/variables.tf new file mode 100644 index 0000000..0907e73 --- /dev/null +++ b/modules/repository_base/variables.tf @@ -0,0 +1,117 @@ +variable "name" { + type = string + description = "The name of the repository to create/import." +} + +variable "description" { + type = string + description = "The description to give to the repository. Defaults to `\"\"`" + default = "" +} + +variable "default_branch" { + type = string + description = "The branch to set as the default branch for this repository. Defaults to \"main\"" + default = "main" +} + +variable "repository_team_permissions" { + type = map(string) + description = "A map where the keys are github team ids and the value is the permissions the team should have in the repository" +} + +variable "protected_branches" { + type = list(string) + description = "A list of ref names or patterns that should be protected. Defaults `[\"main\"]`" + default = ["main"] +} + +variable "has_downloads" { + description = "Enables downloads for the repository" + type = bool + default = false +} + +variable "has_discussions" { + description = "Enables Github Discussions." + type = bool + default = true +} + +variable "has_issues" { + description = "Enables Github Issues for the repository" + type = bool + default = true +} + +variable "has_projects" { + description = "Enables Github Projects for the repository" + type = bool + default = true +} + +variable "has_wiki" { + description = "Enables Github Wiki for the repository" + type = bool + default = true +} + +variable "has_vulnerability_alerts" { + description = "Enables security alerts for vulnerable dependencies for the repository" + type = bool + default = true +} + +variable "topics" { + description = "The topics to apply to the repository" + type = list(string) + default = [] +} + +variable "homepage" { + description = "The homepage for the repository" + type = string + default = "" +} + +variable "delete_head_on_merge" { + description = "Sets the delete head on merge option for the repository. If true it will delete pull request branches automatically on merge. Defaults to true" + type = bool + default = true +} + +variable "allow_auto_merge" { + description = "Allow auto-merging pull requests on the repository" + type = bool + default = true +} + +variable "visibility" { + description = "Sets the visibility property of a repository. Defaults to \"private\"" + type = string + default = "private" +} + +variable "secret_scanning" { + description = "Enables secret scanning for the repository. If repository is private `advance_security` must also be enabled." + type = bool + default = true +} + +variable "secret_scanning_on_push" { + description = "Enables secret scanning push protection for the repository. If repository is private `advance_security` must also be enabled." + type = bool + default = true +} + +variable "advance_security" { + description = "Enables advance security for the repository. If repository is public `advance_security` is enabled by default and cannot be changed." + type = bool + default = true +} + +variable "dependabot_security_updates" { + description = "Enables dependabot security updates. Only works when `has_vulnerability_alerts` is set because that is required to enable dependabot for the repository." + type = bool + default = true +} \ No newline at end of file diff --git a/modules/repository_base/versions.tf b/modules/repository_base/versions.tf new file mode 100644 index 0000000..d6e8a27 --- /dev/null +++ b/modules/repository_base/versions.tf @@ -0,0 +1,9 @@ +terraform { + required_version = ">= 1.7.1" + required_providers { + github = { + source = "integrations/github" + version = "5.42.0" + } + } +} \ No newline at end of file diff --git a/modules/repository_set/README.md b/modules/repository_set/README.md new file mode 100644 index 0000000..0a6af0b --- /dev/null +++ b/modules/repository_set/README.md @@ -0,0 +1,33 @@ +## Requirements + +| Name | Version | +|------|---------| +| [terraform](#requirement\_terraform) | >= 1.7.1 | +| [github](#requirement\_github) | 5.42.0 | + +## Providers + +No providers. + +## Modules + +| Name | Source | Version | +|------|--------|---------| +| [private\_repositories](#module\_private\_repositories) | ../private_repository | n/a | +| [public\_repositories](#module\_public\_repositories) | ../public_repository | n/a | + +## Resources + +No resources. + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| [default\_repository\_team\_permissions](#input\_default\_repository\_team\_permissions) | A map where the keys are github team slugs and the value is the permissions the team should have by default for every repository. If an entry exists in `repository_team_permissions_override` for a repository then that will take precedence over this default. | `map(string)` | n/a | yes | +| [private\_repositories](#input\_private\_repositories) | A map of private repositories where the key is the repository name and the value is the configuration |
map(object({
description = string
default_branch = string
repository_team_permissions_override = map(string)
protected_branches = list(string)
advance_security = bool
has_vulnerability_alerts = bool
topics = list(string)
homepage = string
delete_head_on_merge = bool
allow_auto_merge = bool
dependabot_security_updates = bool
}))
| n/a | yes | +| [public\_repositories](#input\_public\_repositories) | A map of public repositories where the key is the repository name and the value is the configuration |
map(object({
description = string
default_branch = string
repository_team_permissions_override = map(string)
protected_branches = list(string)
advance_security = bool
topics = list(string)
homepage = string
delete_head_on_merge = bool
allow_auto_merge = bool
dependabot_security_updates = bool
}))
| n/a | yes | + +## Outputs + +No outputs. diff --git a/modules/repository_set/repositories.tf b/modules/repository_set/repositories.tf new file mode 100644 index 0000000..b32b84c --- /dev/null +++ b/modules/repository_set/repositories.tf @@ -0,0 +1,35 @@ +module "public_repositories" { + source = "../public_repository" + + for_each = var.public_repositories + + name = each.key + repository_team_permissions = merge(var.default_repository_team_permissions, each.value.repository_team_permissions_override) + description = each.value.description + default_branch = each.value.default_branch + protected_branches = each.value.protected_branches + advance_security = each.value.advance_security + topics = each.value.topics + homepage = each.value.homepage + delete_head_on_merge = each.value.delete_head_on_merge + allow_auto_merge = each.value.allow_auto_merge + dependabot_security_updates = each.value.dependabot_security_updates +} + +module "private_repositories" { + source = "../private_repository" + + for_each = var.private_repositories + + name = each.key + repository_team_permissions = merge(var.default_repository_team_permissions, each.value.repository_team_permissions_override) + description = each.value.description + default_branch = each.value.default_branch + protected_branches = each.value.protected_branches + advance_security = each.value.advance_security + topics = each.value.topics + homepage = each.value.homepage + delete_head_on_merge = each.value.delete_head_on_merge + allow_auto_merge = each.value.allow_auto_merge + dependabot_security_updates = each.value.dependabot_security_updates +} diff --git a/modules/repository_set/variables.tf b/modules/repository_set/variables.tf new file mode 100644 index 0000000..c3cf6d1 --- /dev/null +++ b/modules/repository_set/variables.tf @@ -0,0 +1,38 @@ +variable "private_repositories" { + type = map(object({ + description = string + default_branch = string + repository_team_permissions_override = map(string) + protected_branches = list(string) + advance_security = bool + has_vulnerability_alerts = bool + topics = list(string) + homepage = string + delete_head_on_merge = bool + allow_auto_merge = bool + dependabot_security_updates = bool + })) + description = "A map of private repositories where the key is the repository name and the value is the configuration" +} + +variable "public_repositories" { + type = map(object({ + description = string + default_branch = string + repository_team_permissions_override = map(string) + protected_branches = list(string) + advance_security = bool + topics = list(string) + homepage = string + delete_head_on_merge = bool + allow_auto_merge = bool + dependabot_security_updates = bool + })) + description = "A map of public repositories where the key is the repository name and the value is the configuration" +} + +variable "default_repository_team_permissions" { + type = map(string) + description = "A map where the keys are github team slugs and the value is the permissions the team should have by default for every repository. If an entry exists in `repository_team_permissions_override` for a repository then that will take precedence over this default." + +} diff --git a/modules/repository_set/versions.tf b/modules/repository_set/versions.tf new file mode 100644 index 0000000..d6e8a27 --- /dev/null +++ b/modules/repository_set/versions.tf @@ -0,0 +1,9 @@ +terraform { + required_version = ">= 1.7.1" + required_providers { + github = { + source = "integrations/github" + version = "5.42.0" + } + } +} \ No newline at end of file diff --git a/modules/team/README.md b/modules/team/README.md new file mode 100644 index 0000000..78f4064 --- /dev/null +++ b/modules/team/README.md @@ -0,0 +1,42 @@ +## Requirements + +| Name | Version | +|------|---------| +| [terraform](#requirement\_terraform) | >= 1.3 | +| [github](#requirement\_github) | 5.42.0 | + +## Providers + +| Name | Version | +|------|---------| +| [github](#provider\_github) | 5.42.0 | + +## Modules + +No modules. + +## Resources + +| Name | Type | +|------|------| +| [github_team.team](https://registry.terraform.io/providers/integrations/github/5.42.0/docs/resources/team) | resource | +| [github_team_membership.maintainers](https://registry.terraform.io/providers/integrations/github/5.42.0/docs/resources/team_membership) | resource | +| [github_team_membership.members](https://registry.terraform.io/providers/integrations/github/5.42.0/docs/resources/team_membership) | resource | + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| [privacy](#input\_privacy) | The privacy setting for the github team. Must be one of `closed` or `secret`. | `string` | `"closed"` | no | +| [team\_description](#input\_team\_description) | Description of the github team to be created. | `string` | `""` | no | +| [team\_id](#input\_team\_id) | The ID of the team if it exists (optional). | `string` | `""` | no | +| [team\_maintainers](#input\_team\_maintainers) | A list of team maintainers for the github team. These user's will have permissions to manage the team. | `list(string)` | n/a | yes | +| [team\_members](#input\_team\_members) | A list of team members for the github team. These user's will not have permissions to manage the team. | `list(string)` | `[]` | no | +| [team\_name](#input\_team\_name) | The name to give to the github team that will be created. | `string` | n/a | yes | + +## Outputs + +| Name | Description | +|------|-------------| +| [name](#output\_name) | Name of the created team. | +| [slug](#output\_slug) | The slug of the created team. | diff --git a/modules/team/doc.md b/modules/team/doc.md new file mode 100644 index 0000000..eb01c66 --- /dev/null +++ b/modules/team/doc.md @@ -0,0 +1,38 @@ +## Requirements + +| Name | Version | +|------|---------| +| [terraform](#requirement\_terraform) | >= 1.3 | +| [github](#requirement\_github) | 5.44.0 | + +## Providers + +| Name | Version | +|------|---------| +| [github](#provider\_github) | 5.44.0 | + +## Modules + +No modules. + +## Resources + +| Name | Type | +|------|------| +| [github_team.team](https://registry.terraform.io/providers/integrations/github/5.44.0/docs/resources/team) | resource | +| [github_team_membership.maintainers](https://registry.terraform.io/providers/integrations/github/5.44.0/docs/resources/team_membership) | resource | +| [github_team_membership.members](https://registry.terraform.io/providers/integrations/github/5.44.0/docs/resources/team_membership) | resource | + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| [organization](#input\_organization) | The organization that the team should be created under. | `string` | n/a | yes | +| [team\_description](#input\_team\_description) | Description of the github team to be created. | `string` | `""` | no | +| [team\_maintainers](#input\_team\_maintainers) | A list of team maintainers for the github team. These user's will have permissions to manage the team. | `list(string)` | n/a | yes | +| [team\_members](#input\_team\_members) | A list of team members for the github team. These user's will not have permissions to manage the team. | `list(string)` | `[]` | no | +| [team\_name](#input\_team\_name) | The name to give to the github team that will be created. | `string` | n/a | yes | + +## Outputs + +No outputs. diff --git a/modules/team/members.tf b/modules/team/members.tf new file mode 100644 index 0000000..1c7ce1d --- /dev/null +++ b/modules/team/members.tf @@ -0,0 +1,17 @@ +locals { + team_id = local.create_team ? github_team.team[0].id : var.team_id +} + +resource "github_team_membership" "maintainers" { + for_each = toset(var.team_maintainers) + team_id = local.team_id + username = each.value + role = "maintainer" +} + +resource "github_team_membership" "members" { + for_each = toset(var.team_members) + team_id = local.team_id + username = each.value + role = "member" +} \ No newline at end of file diff --git a/modules/team/outputs.tf b/modules/team/outputs.tf new file mode 100644 index 0000000..0176c7f --- /dev/null +++ b/modules/team/outputs.tf @@ -0,0 +1,9 @@ +output "slug" { + value = local.create_team ? github_team.team[0].slug : null + description = "The slug of the created team." +} + +output "name" { + value = local.create_team ? github_team.team[0].name : null + description = "Name of the created team." +} \ No newline at end of file diff --git a/modules/team/team.tf b/modules/team/team.tf new file mode 100644 index 0000000..8803f4a --- /dev/null +++ b/modules/team/team.tf @@ -0,0 +1,10 @@ +locals { + create_team = length(var.team_id) > 0 ? false : true +} + +resource "github_team" "team" { + count = local.create_team ? 1 : 0 + name = var.team_name + description = var.team_description + privacy = var.privacy +} \ No newline at end of file diff --git a/modules/team/variables.tf b/modules/team/variables.tf new file mode 100644 index 0000000..d8a9264 --- /dev/null +++ b/modules/team/variables.tf @@ -0,0 +1,37 @@ +variable "team_name" { + type = string + description = "The name to give to the github team that will be created." +} + +variable "privacy" { + type = string + description = "The privacy setting for the github team. Must be one of `closed` or `secret`." + default = "closed" +} + +variable "team_description" { + type = string + description = "Description of the github team to be created." + default = "" +} + +variable "team_maintainers" { + type = list(string) + description = "A list of team maintainers for the github team. These user's will have permissions to manage the team." + validation { + condition = length(var.team_maintainers) > 0 + error_message = "The team_maintainers value must be a list of atleast length 1." + } +} + +variable "team_members" { + type = list(string) + description = "A list of team members for the github team. These user's will not have permissions to manage the team." + default = [] +} + +variable "team_id" { + type = string + description = "The ID of the team if it exists (optional)." + default = "" +} \ No newline at end of file diff --git a/modules/team/versions.tf b/modules/team/versions.tf new file mode 100644 index 0000000..99d757e --- /dev/null +++ b/modules/team/versions.tf @@ -0,0 +1,9 @@ +terraform { + required_version = ">= 1.3" + required_providers { + github = { + source = "integrations/github" + version = "5.42.0" + } + } +} \ No newline at end of file diff --git a/modules/team_set/README.md b/modules/team_set/README.md new file mode 100644 index 0000000..9968721 --- /dev/null +++ b/modules/team_set/README.md @@ -0,0 +1,38 @@ +## Requirements + +| Name | Version | +|------|---------| +| [terraform](#requirement\_terraform) | >= 1.3 | +| [github](#requirement\_github) | 5.42.0 | + +## Providers + +| Name | Version | +|------|---------| +| [terraform](#provider\_terraform) | n/a | + +## Modules + +| Name | Source | Version | +|------|--------|---------| +| [prexisting\_team](#module\_prexisting\_team) | ../team | n/a | +| [team](#module\_team) | ../team | n/a | + +## Resources + +| Name | Type | +|------|------| +| [terraform_remote_state.state](https://registry.terraform.io/providers/hashicorp/terraform/latest/docs/data-sources/remote_state) | data source | + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| [preexisting\_teams](#input\_preexisting\_teams) | A map of existing teams where the key is the team name and the value is the configuration |
map(object({
bucket = string
prefix = string
output_name = string
maintainers = list(string)
members = list(string)
}))
| `{}` | no | +| [teams](#input\_teams) | A map of teams to create where the key is the team name and the value is the configuration |
map(object({
description = string
privacy = string
maintainers = list(string)
members = list(string)
}))
| n/a | yes | + +## Outputs + +| Name | Description | +|------|-------------| +| [team\_slugs](#output\_team\_slugs) | Map of team names to their respective slugs | diff --git a/modules/team_set/data.tf b/modules/team_set/data.tf new file mode 100644 index 0000000..c4879a2 --- /dev/null +++ b/modules/team_set/data.tf @@ -0,0 +1,26 @@ +locals { + distinct_states = distinct([ + for team in var.preexisting_teams : { + bucket = team.bucket + prefix = team.prefix + } + ]) + + team_to_state_index_map = { + for team_name, team_config in var.preexisting_teams : team_name => index(local.distinct_states, { + bucket = team_config.bucket + prefix = team_config.prefix + }) + } +} + +data "terraform_remote_state" "state" { + for_each = { + for i, state in local.distinct_states : "${i}" => state + } + backend = "gcs" + config = { + bucket = each.value.bucket + prefix = each.value.prefix + } +} \ No newline at end of file diff --git a/modules/team_set/outputs.tf b/modules/team_set/outputs.tf new file mode 100644 index 0000000..07c393d --- /dev/null +++ b/modules/team_set/outputs.tf @@ -0,0 +1,6 @@ +output "team_slugs" { + value = { + for _, team in module.team : team.name => team.slug + } + description = "Map of team names to their respective slugs" +} \ No newline at end of file diff --git a/modules/team_set/teams.tf b/modules/team_set/teams.tf new file mode 100644 index 0000000..27aaa7a --- /dev/null +++ b/modules/team_set/teams.tf @@ -0,0 +1,22 @@ +module "team" { + source = "../team" + + for_each = var.teams + + team_maintainers = each.value.maintainers + team_members = each.value.members + team_description = each.value.description + privacy = each.value.privacy + team_name = each.key +} + +module "prexisting_team" { + source = "../team" + for_each = var.preexisting_teams + + team_id = data.terraform_remote_state.state[local.team_to_state_index_map[each.key]].outputs["${each.value.output_name}"] + + team_maintainers = each.value.maintainers + team_members = each.value.members + team_name = each.key +} diff --git a/modules/team_set/variables.tf b/modules/team_set/variables.tf new file mode 100644 index 0000000..40abc57 --- /dev/null +++ b/modules/team_set/variables.tf @@ -0,0 +1,21 @@ +variable "teams" { + type = map(object({ + description = string + privacy = string + maintainers = list(string) + members = list(string) + })) + description = "A map of teams to create where the key is the team name and the value is the configuration" +} + +variable "preexisting_teams" { + type = map(object({ + bucket = string + prefix = string + output_name = string + maintainers = list(string) + members = list(string) + })) + description = "A map of existing teams where the key is the team name and the value is the configuration" + default = {} +} diff --git a/modules/team_set/versions.tf b/modules/team_set/versions.tf new file mode 100644 index 0000000..a369b06 --- /dev/null +++ b/modules/team_set/versions.tf @@ -0,0 +1,9 @@ +terraform { + required_version = ">= 1.3" + required_providers { + github = { + source = "integrations/github" + version = "5.42.0" + } + } +} From 66dc3067ffb61489a7478780d04650b0047d6b62 Mon Sep 17 00:00:00 2001 From: Tyler Mizuyabu Date: Wed, 28 Feb 2024 13:25:49 -0500 Subject: [PATCH 2/6] added workflow to autogenerate docs --- .github/workflows/on-pull-request.yaml | 47 ++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 .github/workflows/on-pull-request.yaml diff --git a/.github/workflows/on-pull-request.yaml b/.github/workflows/on-pull-request.yaml new file mode 100644 index 0000000..4460d62 --- /dev/null +++ b/.github/workflows/on-pull-request.yaml @@ -0,0 +1,47 @@ +name: "Pull Request Jobs" + +on: + pull_request: + + +env: + tf_version: 1.7.1 + working_dir: . + +jobs: + terraform-doc-generation: + permissions: + contents: 'write' + id-token: 'write' + pull-requests: 'write' + issues: 'write' + name: "Terraform Documentation Generation" + runs-on: ubuntu-latest + defaults: + run: + shell: bash + working-directory: ${{ env.working_dir }} + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + ref: ${{ github.head_ref }} + + - name: Setup Terraform + uses: hashicorp/setup-terraform@v3 + with: + terraform_version: ${{ env.tf_version }} + + - name: Check Terraform Format + id: fmt + run: terraform fmt -check + + - name: Generate TF docs + uses: terraform-docs/gh-actions@v1.0.0 + with: + working-dir: modules/ + recursive: true + recursive-path: . + git-push: true + output-method: replace + template: "{{ .Content }}" \ No newline at end of file From f9c9b92e46c0cb73938019a0e9212281778d121d Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 28 Feb 2024 18:31:12 +0000 Subject: [PATCH 3/6] terraform-docs: automated action --- modules/README.md | 23 +++++++++++++++++++ .../foundations-github-organization/README.md | 7 +++++- modules/github-gcloud-oidc/README.md | 2 +- modules/organization/README.md | 2 +- modules/private_repository/README.md | 2 +- modules/public_repository/README.md | 2 +- modules/repository_base/README.md | 2 +- modules/repository_set/README.md | 2 +- modules/team/README.md | 2 +- modules/team_set/README.md | 2 +- 10 files changed, 37 insertions(+), 9 deletions(-) create mode 100644 modules/README.md diff --git a/modules/README.md b/modules/README.md new file mode 100644 index 0000000..aa73e35 --- /dev/null +++ b/modules/README.md @@ -0,0 +1,23 @@ +## Requirements + +No requirements. + +## Providers + +No providers. + +## Modules + +No modules. + +## Resources + +No resources. + +## Inputs + +No inputs. + +## Outputs + +No outputs. \ No newline at end of file diff --git a/modules/foundations-github-organization/README.md b/modules/foundations-github-organization/README.md index 5a88c81..7f67b67 100644 --- a/modules/foundations-github-organization/README.md +++ b/modules/foundations-github-organization/README.md @@ -33,6 +33,9 @@ No modules. | [github_issue_labels.drift_labels](https://registry.terraform.io/providers/hashicorp/github/5.44.0/docs/resources/issue_labels) | resource | | [github_repository.bootstrap_repo](https://registry.terraform.io/providers/hashicorp/github/5.44.0/docs/resources/repository) | resource | | [github_repository.organizations_repo](https://registry.terraform.io/providers/hashicorp/github/5.44.0/docs/resources/repository) | resource | +| [github_repository_collaborators.bootstrap_repo_collaborators](https://registry.terraform.io/providers/hashicorp/github/5.44.0/docs/resources/repository_collaborators) | resource | +| [github_repository_collaborators.organization_repo_collaborators](https://registry.terraform.io/providers/hashicorp/github/5.44.0/docs/resources/repository_collaborators) | resource | +| [github_team.foundation_devs](https://registry.terraform.io/providers/hashicorp/github/5.44.0/docs/resources/team) | resource | ## Inputs @@ -52,4 +55,6 @@ No modules. ## Outputs -No outputs. +| Name | Description | +|------|-------------| +| [foundation\_dev\_team\_id](#output\_foundation\_dev\_team\_id) | n/a | \ No newline at end of file diff --git a/modules/github-gcloud-oidc/README.md b/modules/github-gcloud-oidc/README.md index 3e251e4..0609fcd 100644 --- a/modules/github-gcloud-oidc/README.md +++ b/modules/github-gcloud-oidc/README.md @@ -87,4 +87,4 @@ | [organizations\_sa](#output\_organizations\_sa) | Organizations repository service account email. | | [organizations\_sa\_name](#output\_organizations\_sa\_name) | Organizations repository service account name. | | [project\_id](#output\_project\_id) | Project id. | -| [provider\_name](#output\_provider\_name) | Workload identity provider name. | +| [provider\_name](#output\_provider\_name) | Workload identity provider name. | \ No newline at end of file diff --git a/modules/organization/README.md b/modules/organization/README.md index ce6081d..25ec89a 100644 --- a/modules/organization/README.md +++ b/modules/organization/README.md @@ -60,4 +60,4 @@ No modules. | [contractor\_role\_id](#output\_contractor\_role\_id) | The id of the contractor custom role. | | [custom\_role\_ids](#output\_custom\_role\_ids) | A map of custom role names to custom role ids. | | [ghas\_enabled](#output\_ghas\_enabled) | A boolean value indicating if GitHub Advanced Security is enabled for new repositories in the organization. | -| [security\_engineer\_role\_id](#output\_security\_engineer\_role\_id) | The id of the security engineer custom role. | +| [security\_engineer\_role\_id](#output\_security\_engineer\_role\_id) | The id of the security engineer custom role. | \ No newline at end of file diff --git a/modules/private_repository/README.md b/modules/private_repository/README.md index 785ff82..c706d92 100644 --- a/modules/private_repository/README.md +++ b/modules/private_repository/README.md @@ -37,4 +37,4 @@ No resources. ## Outputs -No outputs. +No outputs. \ No newline at end of file diff --git a/modules/public_repository/README.md b/modules/public_repository/README.md index 785ff82..c706d92 100644 --- a/modules/public_repository/README.md +++ b/modules/public_repository/README.md @@ -37,4 +37,4 @@ No resources. ## Outputs -No outputs. +No outputs. \ No newline at end of file diff --git a/modules/repository_base/README.md b/modules/repository_base/README.md index 15ad2d1..b7fd73e 100644 --- a/modules/repository_base/README.md +++ b/modules/repository_base/README.md @@ -52,4 +52,4 @@ No modules. ## Outputs -No outputs. +No outputs. \ No newline at end of file diff --git a/modules/repository_set/README.md b/modules/repository_set/README.md index 0a6af0b..7e7aacd 100644 --- a/modules/repository_set/README.md +++ b/modules/repository_set/README.md @@ -30,4 +30,4 @@ No resources. ## Outputs -No outputs. +No outputs. \ No newline at end of file diff --git a/modules/team/README.md b/modules/team/README.md index 78f4064..884bb1f 100644 --- a/modules/team/README.md +++ b/modules/team/README.md @@ -39,4 +39,4 @@ No modules. | Name | Description | |------|-------------| | [name](#output\_name) | Name of the created team. | -| [slug](#output\_slug) | The slug of the created team. | +| [slug](#output\_slug) | The slug of the created team. | \ No newline at end of file diff --git a/modules/team_set/README.md b/modules/team_set/README.md index 9968721..1a8243a 100644 --- a/modules/team_set/README.md +++ b/modules/team_set/README.md @@ -35,4 +35,4 @@ | Name | Description | |------|-------------| -| [team\_slugs](#output\_team\_slugs) | Map of team names to their respective slugs | +| [team\_slugs](#output\_team\_slugs) | Map of team names to their respective slugs | \ No newline at end of file From 89d03762fab4b870ce350fb0170d7e73dd6ac4e5 Mon Sep 17 00:00:00 2001 From: Tyler Mizuyabu Date: Wed, 28 Feb 2024 13:35:47 -0500 Subject: [PATCH 4/6] changed argument folder --- .github/workflows/on-pull-request.yaml | 15 ++++++++++++++- modules/README.md | 23 ----------------------- 2 files changed, 14 insertions(+), 24 deletions(-) delete mode 100644 modules/README.md diff --git a/.github/workflows/on-pull-request.yaml b/.github/workflows/on-pull-request.yaml index 4460d62..ecfbe79 100644 --- a/.github/workflows/on-pull-request.yaml +++ b/.github/workflows/on-pull-request.yaml @@ -27,11 +27,24 @@ jobs: with: ref: ${{ github.head_ref }} + - name: Set up Python 3.8 + uses: actions/setup-python@v4 + with: + python-version: 3.8 + + - name: Setup Terraform uses: hashicorp/setup-terraform@v3 with: terraform_version: ${{ env.tf_version }} + - name: Test with Checkov + id: checkov + uses: bridgecrewio/checkov-action@master + with: + directory: modules + framework: terraform + - name: Check Terraform Format id: fmt run: terraform fmt -check @@ -39,7 +52,7 @@ jobs: - name: Generate TF docs uses: terraform-docs/gh-actions@v1.0.0 with: - working-dir: modules/ + find-dir: modules/ recursive: true recursive-path: . git-push: true diff --git a/modules/README.md b/modules/README.md deleted file mode 100644 index aa73e35..0000000 --- a/modules/README.md +++ /dev/null @@ -1,23 +0,0 @@ -## Requirements - -No requirements. - -## Providers - -No providers. - -## Modules - -No modules. - -## Resources - -No resources. - -## Inputs - -No inputs. - -## Outputs - -No outputs. \ No newline at end of file From 0acc27db95b2e3e412cfff564b60e4a0e588bf38 Mon Sep 17 00:00:00 2001 From: Tyler Mizuyabu Date: Wed, 28 Feb 2024 13:37:02 -0500 Subject: [PATCH 5/6] updating workflow --- .github/workflows/on-pull-request.yaml | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/.github/workflows/on-pull-request.yaml b/.github/workflows/on-pull-request.yaml index ecfbe79..ce99f0c 100644 --- a/.github/workflows/on-pull-request.yaml +++ b/.github/workflows/on-pull-request.yaml @@ -27,10 +27,10 @@ jobs: with: ref: ${{ github.head_ref }} - - name: Set up Python 3.8 - uses: actions/setup-python@v4 - with: - python-version: 3.8 + # - name: Set up Python 3.8 + # uses: actions/setup-python@v4 + # with: + # python-version: 3.8 - name: Setup Terraform @@ -38,12 +38,12 @@ jobs: with: terraform_version: ${{ env.tf_version }} - - name: Test with Checkov - id: checkov - uses: bridgecrewio/checkov-action@master - with: - directory: modules - framework: terraform + # - name: Test with Checkov + # id: checkov + # uses: bridgecrewio/checkov-action@master + # with: + # directory: modules + # framework: terraform - name: Check Terraform Format id: fmt From 33406942bbcfb388eab4a47afc55564052b4a323 Mon Sep 17 00:00:00 2001 From: Tyler Mizuyabu Date: Wed, 28 Feb 2024 14:20:04 -0500 Subject: [PATCH 6/6] removed checkov --- .github/workflows/on-pull-request.yaml | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/.github/workflows/on-pull-request.yaml b/.github/workflows/on-pull-request.yaml index ce99f0c..573c779 100644 --- a/.github/workflows/on-pull-request.yaml +++ b/.github/workflows/on-pull-request.yaml @@ -26,25 +26,12 @@ jobs: uses: actions/checkout@v4 with: ref: ${{ github.head_ref }} - - # - name: Set up Python 3.8 - # uses: actions/setup-python@v4 - # with: - # python-version: 3.8 - - name: Setup Terraform uses: hashicorp/setup-terraform@v3 with: terraform_version: ${{ env.tf_version }} - # - name: Test with Checkov - # id: checkov - # uses: bridgecrewio/checkov-action@master - # with: - # directory: modules - # framework: terraform - - name: Check Terraform Format id: fmt run: terraform fmt -check