Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 0 additions & 4 deletions aws/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,3 @@ resource "aws_iam_role_policy_attachment" "sudo" {
role = aws_iam_role.stacks_role.name
policy_arn = "arn:aws:iam::aws:policy/PowerUserAccess"
}

output "role_arn" {
value = aws_iam_role.stacks_role.arn
}
6 changes: 6 additions & 0 deletions aws/outputs.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Copyright (c) HashiCorp, Inc.
# SPDX-License-Identifier: MPL-2.0

output "role_arn" {
value = aws_iam_role.stacks_role.arn
}
59 changes: 11 additions & 48 deletions azure/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -8,69 +8,32 @@ provider "azurerm" {
provider "azuread" {
}

# Data source used to get the current subscription's ID.
#
# https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/data-sources/subscription
data "azurerm_subscription" "current" {
}

# Creates an application registration within Azure Active Directory.
#
# https://registry.terraform.io/providers/hashicorp/azuread/latest/docs/resources/application
data "azuread_client_config" "current" {
}

resource "azuread_application" "tfc_application" {
display_name = "tfc-application"
}

# Creates a service principal associated with the previously created
# application registration.
#
# https://registry.terraform.io/providers/hashicorp/azuread/latest/docs/resources/service_principal
resource "azuread_service_principal" "tfc_service_principal" {
application_id = azuread_application.tfc_application.application_id
client_id = azuread_application.tfc_application.client_id
app_role_assignment_required = false
owners = [data.azuread_client_config.current.object_id]
}

# Creates a role assignment which controls the permissions the service
# principal has within the Azure subscription.
#
# https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/role_assignment
resource "azurerm_role_assignment" "tfc_role_assignment" {
scope = data.azurerm_subscription.current.id
principal_id = azuread_service_principal.tfc_service_principal.object_id
role_definition_name = "Contributor"
}

# Creates a federated identity credential which ensures that the given
# workspace will be able to authenticate to Azure for the "plan" run phase.
#
# https://registry.terraform.io/providers/hashicorp/azuread/latest/docs/resources/application_federated_identity_credential
resource "azuread_application_federated_identity_credential" "tfc_federated_credential_plan" {
application_object_id = azuread_application.tfc_application.object_id
display_name = "my-tfc-federated-credential-plan"
audiences = [var.tfc_azure_audience]
issuer = "https://${var.tfc_hostname}"
subject = "organization:${var.tfc_organization}:project:${var.tfc_project}:stack:${var.tfc_stack}:deployment:${var.tfc_deployment}:operation:plan"
}

# Creates a federated identity credential which ensures that the given
# workspace will be able to authenticate to Azure for the "apply" run phase.
#
# https://registry.terraform.io/providers/hashicorp/azuread/latest/docs/resources/application_federated_identity_credential
resource "azuread_application_federated_identity_credential" "tfc_federated_credential_apply" {
application_object_id = azuread_application.tfc_application.object_id
display_name = "my-tfc-federated-credential-apply"
audiences = [var.tfc_azure_audience]
issuer = "https://${var.tfc_hostname}"
subject = "organization:${var.tfc_organization}:project:${var.tfc_project}:stack:${var.tfc_stack}:deployment:${var.tfc_deployment}:operation:apply"
}

output "subscription_id" {
value = data.azurerm_subscription.current.subscription_id
}

output "client_id" {
value = azuread_application.tfc_application.client_id
}

output "tenant_id" {
value = azuread_service_principal.tfc_service_principal.application_tenant_id
application_id = azuread_application.tfc_application.id
display_name = "my-tfc-federated-credential"
audiences = [var.tfc_azure_audience]
issuer = "https://${var.tfc_hostname}"
subject = "organization:${var.tfc_organization}:project:${var.tfc_project}:stack:*:*"
}
14 changes: 14 additions & 0 deletions azure/outputs.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Copyright (c) HashiCorp, Inc.
# SPDX-License-Identifier: MPL-2.0

output "subscription_id" {
value = data.azurerm_subscription.current.subscription_id
}

output "client_id" {
value = azuread_application.tfc_application.client_id
}

output "tenant_id" {
value = azuread_service_principal.tfc_service_principal.application_tenant_id
}
14 changes: 14 additions & 0 deletions azure/terraform.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
terraform {
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = "~> 4.34"
}
azuread = {
source = "hashicorp/azuread"
version = "~> 3.4"
}
}

required_version = ">= 1.2"
}
9 changes: 0 additions & 9 deletions azure/variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,3 @@ variable "tfc_organization" {
variable "tfc_project" {
type = string
}

variable "tfc_stack" {
type = string
}

variable "tfc_deployment" {
type = string
}

97 changes: 50 additions & 47 deletions gcp/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -2,76 +2,79 @@
# SPDX-License-Identifier: MPL-2.0

provider "google" {
region = var.google_region
project = var.google_project
region = "global"
}

resource "google_iam_workload_identity_pool" "stacks_identity_pool" {
workload_identity_pool_id = "stacks-${var.tfc_organization}-${var.tfc_project}-${var.tfc_stack}"
resource "google_service_account" "terraform_stacks_sa" {
account_id = "terraform-stacks-sa"
display_name = "Terraform Stacks Service Account"
description = "Service account used by Terraform Stacks for GCP resources"
}

locals {
# This value decides exactly which HCP Terraform organisations, projects
# and stacks will have access to the chosen GCP project.
#
# You can widen access here to an entire organization or project by
# tweaking the value below. You can also restrict access to specific
# deployments or operations. See the User Guide for more info.
sub_starts_with = "organization:${var.tfc_organization}:project:${var.tfc_project}:stack:${var.tfc_stack}"
gcp_service_list = [
"sts.googleapis.com",
"iam.googleapis.com",
"iamcredentials.googleapis.com"
]
}

resource "google_project_service" "services" {
for_each = toset(local.gcp_service_list)
project = var.project_id
service = each.key
disable_dependent_services = false
disable_on_destroy = false
}

resource "google_iam_workload_identity_pool" "terraform_stacks_pool" {
depends_on = [google_project_service.services]
workload_identity_pool_id = "terraform-stacks-pool"
display_name = "Terraform Stacks Pool"
description = "Identity pool for Terraform Stacks authentication"
}

resource "google_iam_workload_identity_pool_provider" "stacks_identity_pool_provider" {
workload_identity_pool_id = google_iam_workload_identity_pool.stacks_identity_pool.workload_identity_pool_id
workload_identity_pool_provider_id = "stacks-${var.tfc_organization}-${var.tfc_project}-${var.tfc_stack}"
resource "google_iam_workload_identity_pool_provider" "terraform_stacks_provider" {
workload_identity_pool_id = google_iam_workload_identity_pool.terraform_stacks_pool.workload_identity_pool_id
workload_identity_pool_provider_id = "terraform-stacks-provider"
display_name = "Terraform Stacks Provider"
description = "OIDC identity pool provider for Terraform Stacks"

attribute_mapping = {
"google.subject" = "assertion.sub",
"attribute.aud" = "type(assertion.aud) == list ? assertion.aud[0] : assertion.aud",
"google.subject" = "assertion.sub", # WARNING - this value is has to be <=127 bytes, and is "organization:<ORG NAME>:project:<PROJ NAME>:stack:<STACK NAME>:deployment:development:operation:plan
"attribute.aud" = "assertion.aud",
"attribute.terraform_operation" = "assertion.terraform_operation",
"attribute.terraform_stack_deployment_name" = "assertion.terraform_stack_deployment_name",
"attribute.terraform_stack_id" = "assertion.terraform_stack_id",
"attribute.terraform_stack_name" = "assertion.terraform_stack_name",
"attribute.terraform_project_id" = "assertion.terraform_project_id",
"attribute.terraform_project_name" = "assertion.terraform_project_name",
"attribute.terraform_stack_id" = "assertion.terraform_stack_id",
"attribute.terraform_stack_name" = "assertion.terraform_stack_name",
"attribute.terraform_stack_deployment_name" = "assertion.terraform_stack_deployment_name",
"attribute.terraform_organization_id" = "assertion.terraform_organization_id",
"attribute.terraform_organization_name" = "assertion.terraform_organization_name",
"attribute.terraform_plan_id" = "assertion.terraform_plan_id"
"attribute.terraform_run_id" = "assertion.terraform_run_id",
}
oidc {
issuer_uri = "https://app.terraform.io"
allowed_audiences = ["hcp.workload.identity"]
}

// only my organisation can access, and only from the stacks project
attribute_condition = "assertion.sub.startsWith(\"${local.sub_starts_with}\")"
attribute_condition = "assertion.sub.startsWith(\"organization:${var.tfc_organization}:project:${var.tfc_project}:stack\")"
}

resource "google_service_account" "stacks_service_account" {
account_id = "stacks-${var.tfc_organization}-${var.tfc_project}-${var.tfc_stack}"
display_name = "Terraform Stacks Service Account"
}

resource "google_service_account_iam_member" "stacks_service_account_membership" {
service_account_id = google_service_account.stacks_service_account.name
resource "google_service_account_iam_member" "workload_identity_user" {
service_account_id = google_service_account.terraform_stacks_sa.name
role = "roles/iam.workloadIdentityUser"
member = "principalSet://iam.googleapis.com/${google_iam_workload_identity_pool.tfc.name}/*"
member = "principalSet://iam.googleapis.com/${google_iam_workload_identity_pool.terraform_stacks_pool.name}/*"
}


# Now, we're going to give the new service account access to things we're going to be managing in our stack.
#
# The policies being attached here are way to broad for a regular use case, but they make sure
# my stacks can do anything during development and testing. In practice you should give the
# stack access only to what it needs to manage.

resource "google_project_iam_member" "stacks_service_account_membership" {
project = var.google_project
role = "roles/editor"
member = "serviceAccount:${google_service_account.stacks_service_account.email}"
resource "google_project_iam_member" "sa_more_permissions" {
project = var.project_id
role = "roles/iam.serviceAccountTokenCreator"
member = "serviceAccount:${google_service_account.terraform_stacks_sa.email}"
}

output "workload_identity_pool_provider" {
value = google_iam_workload_identity_pool_provider.stacks_identity_pool_provider.id
resource "google_project_iam_member" "sa_editor" {
project = var.project_id
role = "roles/editor"
member = "serviceAccount:${google_service_account.terraform_stacks_sa.email}"
}

output "service_account_email" {
value = google_service_account.stacks_service_account.email
}
14 changes: 14 additions & 0 deletions gcp/outputs.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
output "service_account_email" {
value = google_service_account.terraform_stacks_sa.email
description = "Email of the service account to be used by Terraform Stacks"
}

output "jwt_audience" {
value = "//iam.googleapis.com/${google_iam_workload_identity_pool_provider.terraform_stacks_provider.name}"
description = "The audience value to use when generating OIDC tokens"
}

output "gcp_project_id" {
value = var.project_id
description = "GCP Project ID"
}
15 changes: 4 additions & 11 deletions gcp/variables.tf
Original file line number Diff line number Diff line change
@@ -1,14 +1,6 @@
# Copyright (c) HashiCorp, Inc.
# SPDX-License-Identifier: MPL-2.0

variable "google_project" {
type = string
}

variable "google_region" {
type = string
}

variable "tfc_organization" {
type = string
}
Expand All @@ -17,6 +9,7 @@ variable "tfc_project" {
type = string
}

variable "tfc_stack" {
type = string
}
variable "project_id" {
type = string
description = "GCP Project ID"
}