Skip to content

Commit

Permalink
feat: define initial module
Browse files Browse the repository at this point in the history
  • Loading branch information
rcwbr committed Oct 8, 2024
1 parent d3b7d0f commit 0301f69
Show file tree
Hide file tree
Showing 7 changed files with 219 additions and 1 deletion.
21 changes: 21 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2024 Eric Weber

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
19 changes: 18 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,19 @@
# gha-gcp-opentofu
State bucket and access resources for managing OpenTofu IaC via GitHub Actions

State bucket and access resources for managing [OpenTofu](https://opentofu.org/) infrastructure-as-code via [GitHub Actions](https://docs.github.com/en/actions).

## Usage


### Module inputs

The module reads the following variables as input:

| Variable | Required | Default | Effect |
| --- | --- | --- | --- |
| `gcp_project` | ✓ | N/A | The GCP project name |
| `gcp_region` | ✓ | N/A | The GCP region for all resources managed within the project |
| `github_repo` | ✓ | N/A | The fully-qualified name of the GitHub repo to which the state access will be granted |
| `apply_action_project_roles` | ✗ | `[ "roles/iam.serviceAccountAdmin", "roles/storage.admin", "roles/iam.workloadIdentityPoolAdmin" ]` | The list of project-wide roles to grant apply actions |
| `github_default_branch_name` | ✗ | `"main"` | The default/mainline branch name for the GitHub repo, workflows for which have OpenTofu apply (vs. plan) access |
| `state_bucket_name` | ✗ | `"${var.gcp_project}-opentofu-state"` | The name of the bucket used for OpenTofu state |
32 changes: 32 additions & 0 deletions data.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
data "google_storage_bucket" "state_bucket" {
name = local.state_bucket_name
}

data "google_iam_policy" "github_actions_plan_sa_bindings" {
// Allow the plan identity to act as the service account
binding {
role = "roles/iam.workloadIdentityUser"

members = [local.github_actions_plan_identity]
}
}

data "google_iam_policy" "github_actions_apply_sa_bindings" {
// Allow the apply identity to act as the service account
binding {
role = "roles/iam.serviceAccountUser"

members = [local.github_actions_apply_identity]
}
}

data "google_iam_policy" "state_bucket" {
// Plan action service account state bucket binding
binding {
role = "roles/storage.objectUser"
members = [
google_service_account.github_actions_plan.member,
google_service_account.github_actions_apply.member
]
}
}
8 changes: 8 additions & 0 deletions locals.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
locals {
// Identity (for plan permissions) for GitHub Actions from any branch of the repo
github_actions_plan_identity = "principalSet://iam.googleapis.com/${google_iam_workload_identity_pool.github_actions.name}/*"
// Identity (for apply permissions) for GitHub Actions from only the default branch (https://docs.github.com/en/actions/security-for-github-actions/security-hardening-your-deployments/about-security-hardening-with-openid-connect)
github_actions_apply_identity = "principalSet://iam.googleapis.com/${google_iam_workload_identity_pool.github_actions.name}/attribute.ref/${var.github_default_branch_name}"

state_bucket_name = var.state_bucket_name != "" ? var.state_bucket_name : "${var.gcp_project}-opentofu-state"
}
95 changes: 95 additions & 0 deletions main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
terraform {
required_providers {
google = "~> 6.5.0"
}
}

// Enable APIs
resource "google_project_service" "iam" {
service = "iam.googleapis.com"
}
resource "google_project_service" "iam_creds" {
service = "iamcredentials.googleapis.com"
}
resource "google_project_service" "crm" {
service = "cloudresourcemanager.googleapis.com"
}

// WIF access and identity resources
resource "google_iam_workload_identity_pool" "github_actions" {
workload_identity_pool_id = "github-actions"
display_name = "GitHub Actions plan pool"
}
resource "google_iam_workload_identity_pool_provider" "github_actions" {
workload_identity_pool_id = google_iam_workload_identity_pool.github_actions.workload_identity_pool_id
workload_identity_pool_provider_id = "github-actions"
display_name = "GitHub Actions plan provider"

// GitHub Actions-GCP OIDC basic config (see https://github.com/terraform-google-modules/terraform-google-github-actions-runners/tree/master/modules/gh-oidc)
oidc {
issuer_uri = "https://token.actions.githubusercontent.com"
}
attribute_mapping = {
"google.subject" = "assertion.sub"
"attribute.actor" = "assertion.actor"
"attribute.repository" = "assertion.repository"
"attribute.ref" = "assertion.ref"
}

// Condition (https://cloud.google.com/iam/docs/reference/rest/v1/projects.locations.workloadIdentityPools.providers#WorkloadIdentityPoolProvider.FIELDS.attribute_condition)
// to grant access to workflows only from the target GitHub repository (https://docs.github.com/en/actions/security-for-github-actions/security-hardening-your-deployments/about-security-hardening-with-openid-connect)
attribute_condition = "assertion.repository == \"${var.github_repo}\""
}

// Authorization resources for plan actions (any workflow)
resource "google_service_account" "github_actions_plan" {
account_id = "github-actions-plan"
display_name = "GitHub Actions OpenTofu plan access account"
}
resource "google_service_account_iam_policy" "github_actions_plan" {
service_account_id = google_service_account.github_actions_plan.name
policy_data = data.google_iam_policy.github_actions_plan_sa_bindings.policy_data
}
resource "google_project_iam_custom_role" "plan_project_role" {
role_id = "planProjectRole"
title = "Plan project role"
description = "Role for project-level permissions for plan actions"
permissions = [
"storage.buckets.get",
"storage.buckets.getIamPolicy"
]
}
resource "google_project_iam_member" "github_actions_plan_sa_custom" {
project = var.gcp_project
role = google_project_iam_custom_role.plan_project_role.name
member = google_service_account.github_actions_plan.member
}
resource "google_project_iam_member" "github_actions_plan_sa_viewer" {
project = var.gcp_project
role = "roles/viewer"
member = google_service_account.github_actions_plan.member
}


// Authorization resources for apply actions (default branch workflows only)
resource "google_service_account" "github_actions_apply" {
account_id = "github-actions-apply"
display_name = "GitHub Actions OpenTofu apply access account"
}
resource "google_service_account_iam_policy" "github_actions_apply" {
service_account_id = google_service_account.github_actions_apply.name
policy_data = data.google_iam_policy.github_actions_apply_sa_bindings.policy_data
}
// Grant apply service account roles to administer project resources
resource "google_project_iam_member" "github_actions_apply_sa_admin" {
for_each = toset(var.apply_action_project_roles)
project = var.gcp_project
role = each.key
member = google_service_account.github_actions_apply.member
}

// OpenTofu state bucket access
resource "google_storage_bucket_iam_policy" "state_bucket_policy" {
bucket = data.google_storage_bucket.state_bucket.name
policy_data = data.google_iam_policy.state_bucket.policy_data
}
9 changes: 9 additions & 0 deletions outputs.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
output "github_actions_wif_provider_id" {
value = google_iam_workload_identity_pool_provider.github_actions.name
}
output "github_actions_plan_sa_email" {
value = google_service_account.github_actions_plan.email
}
output "github_actions_apply_sa_email" {
value = google_service_account.github_actions_apply.email
}
36 changes: 36 additions & 0 deletions variables.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
variable "apply_action_project_roles" {
type = list(string)
default = [
"roles/iam.serviceAccountAdmin",
"roles/storage.admin",
"roles/iam.workloadIdentityPoolAdmin"
]
description = "The list of project-wide roles to grant apply actions"
}

variable "gcp_project" {
type = string
description = "The GCP project name"
}

variable "gcp_region" {
type = string
description = "The GCP region for all resources managed within the project"
}

variable "github_default_branch_name" {
type = string
default = "main"
description = "The default/mainline branch name for the GitHub repo, workflows for which have OpenTofu apply (vs. plan) access"
}

variable "github_repo" {
type = string
description = "The fully-qualified name of the GitHub repo to which the state access will be granted"
}

variable "state_bucket_name" {
type = string
default = ""
description = "The name of the bucket to manage and use for OpenTofu state"
}

0 comments on commit 0301f69

Please sign in to comment.