This guide will walk you through integrating Common Fate with Google Cloud (GCP). At the end of this guide you’ll have a functioning integration with Common Fate reading your account inventory and provisioning access to entitlements.

GCP Setup

Common Fate is hosted in your AWS account, and uses Workload Identity Federation to authenticate to GCP Service Accounts. By default, we use two Service Accounts: one for reading available entitlements and a separate one for assigning access. If you want to keep things simple you can combine these into a single role, but we recommend using two separate roles to give you easy visibility as to when the provisioning role is being used.

Before you deploy any resources you’ll need to created a dedicated project in Google Cloud for our integration. You can call this project “Common Fate”.

You’ll also need access to a role with the ability to create role bindings in your GCP organization.

The permissions that the read role uses are:

  • iam.roles.get
  • iam.roles.list
  • resourcemanager.folders.get
  • resourcemanager.folders.list
  • resourcemanager.organizations.get
  • resourcemanager.projects.get
  • resourcemanager.projects.list
  • resourcemanager.tagKeys.list
  • resourcemanager.tagValues.list
  • cloudasset.assets.listResource

The permissions that the provision role uses are:

  • resourcemanager.projects.getIamPolicy
  • resourcemanager.projects.setIamPolicy
  • resourcemanager.folders.getIamPolicy
  • resourcemanager.folders.setIamPolicy

We have developed a reference integration Terraform module which deploys the following resources:

  • A Workload Identity Pool
  • A Workload Identity Pool Provider linked to the Common Fate AWS account
  • Custom IAM Roles for the reader and provisioner, deployed to the organization
  • Service Accounts for the reader and provisioner, deployed to the dedicated Common Fate GCP project you created above
  • Role bindings with attribute bindings allowing the Common Fate Control Plane and Provisioner roles to obtain Service Account credentials.

This Terraform module requires sensitive organizational-level GCP permissions. Because of this, we recommend deploying this module in a separate Terraform root module to your Common Fate deployment. This avoids having a single Terraform deployment with privileged access to both your Common Fate deployment, and to your GCP organization.

  1. To deploy the reference integration first, create a dedicated project in Google Cloud called “Common Fate”

  2. Follow this link to enable the required APIs on your new project: https://console.cloud.google.com/flows/enableapi?apiid=cloudasset.googleapis.com,iam.googleapis.com,cloudresourcemanager.googleapis.com,iamcredentials.googleapis.com,sts.googleapis.com&redirect=https://console.cloud.google.com

  3. Create a new Terraform root module with the following module:

module "common-fate-gcp-roles" {
  source                                = "common-fate/common-fate-deployment/aws//modules/gcp-integration/workload-identity-roles"
  version                               = "1.38.0"
  gcp_project                           = "<ID of the GCP project you created above, excluding the '/project' prefix>"
  common_fate_aws_account_id            = "<Common Fate AWS Account ID>"
  gcp_organization_id                   = "<Your GCP organization ID>"
  common_fate_aws_reader_role_name      = "common-fate-prod-control-plane-ecs-tr"
  common_fate_aws_provisioner_role_name = "common-fate-prod-provisioner-ecs-tr"
}

If you’ve customised the names of your reader and provisioner roles from our defaults, you’ll need to update the common_fate_aws_reader_role_name and common_fate_aws_provisioner_role_name above.

Configuring Common Fate

In this section, you will register the GCP integration with your Common Fate deployment. At the end of this section you should have Common Fate reading GCP resources and see them inside of the web dashboard. You’ll need to have set up the Common Fate Application Configuration repository using our Terraform provider.

Obtain your GCP organization ID (it should look like organizations/12312324167). Obtain your Google Workspace Customer ID (it should look like C02abcde123).

You can find your Google Workspace Customer ID in the Google Admin console (https://admin.google.com). Log in using the same email as used in GCP. You’ll find the customer ID under >Account >Account settings.

Inside your Application Configuration repository, add the following module:

resource "commonfate_gcp_integration" "main" {
  name                         = "GCP"
  organization_id              = "<Your GCP organization ID, including the 'organizations/' prefix>"
  google_workspace_customer_id = "<Your Google Workspace Customer ID>"
  reader_workload_identity_config = jsonencode(<your reader Workload Identity Config>)
}

Apply the changes. If the apply succeeds, you should see the integration appear on the settings page in the web dashboard, and GCP resources should populate within 10 minutes.

If after 10 minutes you do not see resources appear, check the logs of the common-fate-prod-control-plane service in ECS. You can search for gcp to filter for structured logs containing data about the GCP integration.

Provisioning access to GCP

To grant and revoke access to GCP, Common Fate uses a service called the Provisioner. The Provisioner is an internal service and is not user-facing. In default deployments, there is a provisioner builtin to the main stack which can be configured using blocks.

To configure the builtin Provisioner for GCP, add the provisioner_gcp_config config block as per the example below:

module "common-fate-deployment" {
  source = "common-fate/common-fate-deployment/aws"
  version = "1.22.0"

  provisioner_gcp_config = {
    workload_identity_config_json = jsonencode(<Your Provisioner Workload Identity Config>)
  }
}

You’ll additionally need to add the following Provisioner registration inside your Application Configuration repository:

resource "commonfate_webhook_provisioner" "prod" {
 url = "http://common-fate-prod-builtin-provisioner.common-fate-prod-builtin.internal:9999"
  capabilities = [
    {
      target_type = "GCP::Project"
      role_type   = "GCP::Role"
      belonging_to = {
        type = "GCP::Organization"
        id   = "organizations/123456789123"
      }
    },
    {
      target_type = "GCP::Folder"
      role_type   = "GCP::Role"
      belonging_to = {
        type = "GCP::Organization"
        id   = "organizations/123456789123"
      }
    },
  ]
}

GCP project selectors

To make GCP projects available for Just-In-Time (JIT) access you can add a commonfate_gcp_project_selector Selector resource to your Common Fate application Terraform code. As shown below, the when clause in the resource is a Cedar expression. You can use any Cedar operator in the when clause, such as && and || to combine conditions.

You’ll need to use the commonfate_gcp_project_selector in conjunction with a commonfate_gcp_project_availabilities and commonfate_access_workflow resources.

We’ve included some examples below.

Select a project by ID

resource "commonfate_gcp_project_selector" "example" {
  id                  = "example"
  name                = "Example"
  gcp_organization_id = = "<Your GCP Organization ID>"
  when                = <<EOT
  resource == GCP::Project::"replace-this-with-your-project-id"
  EOT
}

Select multiple projects by ID

resource "commonfate_gcp_project_selector" "example" {
  id                  = "example"
  name                = "Example"
  gcp_organization_id = = "<Your GCP Organization ID>"
  when                = <<EOT
  resource == GCP::Project::"replace-this-with-your-project-id" || resource == GCP::Project::"some-other-project-id"
  EOT
}

Select projects based on a naming pattern

Select projects with a name ending in -prod:

resource "commonfate_gcp_project_selector" "example" {
  id                  = "example"
  name                = "Example"
  gcp_organization_id = = "<Your GCP Organization ID>"
  when                = <<EOT
  resource.name like "*-prod"
  EOT
}

Select projects with a name beginning with develop:

resource "commonfate_gcp_project_selector" "example" {
  id                  = "example"
  name                = "Example"
  gcp_organization_id = = "<Your GCP Organization ID>"
  when                = <<EOT
  resource.name like "develop*"
  EOT
}

Select projects belonging to a particular folder

resource "commonfate_gcp_project_selector" "example" {
  id                  = "example"
  name                = "Example"
  gcp_organization_id = = "<Your GCP Organization ID>"
  when                = <<EOT
  resource in GCP::Folder::"folders/1234567890"
  EOT
}

Select projects belonging to a particular tag key

resource "commonfate_gcp_project_selector" "example" {
  id                  = "example"
  name                = "Example"
  gcp_organization_id = = "<Your GCP Organization ID>"
  when                = <<EOT
  resource in GCP::TagKey::"tagKeys/1234567890123"
  EOT
}

Select projects belonging to a particular tag value

resource "commonfate_gcp_project_selector" "example" {
  id                  = "example"
  name                = "Example"
  gcp_organization_id = = "<Your GCP Organization ID>"
  when                = <<EOT
  resource in GCP::TagValue::"tagValues/1234567890123"
  EOT
}

GCP folder selectors

To make GCP folders available for Just-In-Time (JIT) access you can add a commonfate_gcp_folder_selector Selector resource to your Common Fate application Terraform code. As shown below, the when clause in the resource is a Cedar expression. You can use any Cedar operator in the when clause, such as && and || to combine conditions.

You’ll need to use the commonfate_gcp_folder_selector in conjunction with a commonfate_gcp_folder_availabilities and commonfate_access_workflow resources:

// the selector resource chooses which targets
resource "commonfate_gcp_folder_selector" "example" {
  id                  = "example"
  name                = "Example"
  gcp_organization_id = = "<Your GCP Organization ID>"
  when                = <<EOT
  resource == GCP::Folder::"replace-this-with-your-folder-id"
  EOT
}

// the availability resource binds the targets to an Access Workflow
// with a particular role.
resource "commonfate_gcp_folder_availabilities" "demo" {
  workflow_id                  = commonfate_access_workflow.demo.id
  gcp_folder_selector_id       = commonfate_gcp_folder_selector.example.id
  gcp_role                     = "roles/resourcemanager.folderAdmin"
  google_workspace_customer_id = "<Your Google Customer ID>"
}

We’ve included some examples below.

Select a folder by ID

resource "commonfate_gcp_folder_selector" "example" {
  id                  = "example"
  name                = "Example"
  gcp_organization_id = = "<Your GCP Organization ID>"
  when                = <<EOT
  resource == GCP::Folder::"replace-this-with-your-folder-id"
  EOT
}

Select multiple folders by ID

resource "commonfate_gcp_folder_selector" "example" {
  id                  = "example"
  name                = "Example"
  gcp_organization_id = = "<Your GCP Organization ID>"
  when                = <<EOT
  resource == GCP::Folder::"replace-this-with-your-folder-id" || resource == GCP::Folder::"some-other-folder-id"
  EOT
}

Select folders based on a naming pattern

Select folders with a name ending in -prod:

resource "commonfate_gcp_folder_selector" "example" {
  id                  = "example"
  name                = "Example"
  gcp_organization_id = = "<Your GCP Organization ID>"
  when                = <<EOT
  resource.name like "*-prod"
  EOT
}

Select folders with a name beginning with develop:

resource "commonfate_gcp_folder_selector" "example" {
  id                  = "example"
  name                = "Example"
  gcp_organization_id = = "<Your GCP Organization ID>"
  when                = <<EOT
  resource.name like "develop*"
  EOT
}

Select folders belonging to a particular folder

resource "commonfate_gcp_folder_selector" "example" {
  id                  = "example"
  name                = "Example"
  gcp_organization_id = = "<Your GCP Organization ID>"
  when                = <<EOT
  resource in GCP::Folder::"folders/1234567890"
  EOT
}

Organization-level access

Common Fate optionally supports provisioning just-in-time organization-level access to GCP. To provision access, the Common Fate provisioner role needs the following additional permissions:

  • resourcemanager.organizations.getIamPolicy
  • resourcemanager.organizations.setIamPolicy

If you used our reference integration Terraform module to deploy the GCP roles, you can add these permissions by providing the permit_organization_provisioning variable:

module "common-fate-gcp-roles" {
  source                                = "common-fate/common-fate-deployment/aws//modules/gcp-integration/workload-identity-roles"
  version                               = "1.38.0"
  gcp_project                           = "<ID of the GCP project you created above, excluding the '/project' prefix>"
  common_fate_aws_account_id            = "<Common Fate AWS Account ID>"
  gcp_organization_id                   = "<Your GCP organization ID>"
  common_fate_aws_reader_role_name      = "common-fate-prod-control-plane-ecs-tr"
  common_fate_aws_provisioner_role_name = "common-fate-prod-provisioner-ecs-tr"
+ permit_organization_provisioning      = true
}

To make organization roles available for Just-In-Time (JIT) access you can add a commonfate_gcp_organization_selector Selector resource to your Common Fate application Terraform code.

You’ll need to use the commonfate_gcp_folder_selector in conjunction with a commonfate_gcp_folder_availabilities and commonfate_access_workflow resources:

// the selector resource chooses which targets
resource "commonfate_gcp_organization_selector" "example" {
  id                  = "example"
  name                = "Example"
  gcp_organization_id = = "<Your GCP Organization ID>"
}

// the availability resource binds the targets to an Access Workflow
// with a particular role.
resource "commonfate_gcp_organization_availabilities" "demo" {
  workflow_id                  = commonfate_access_workflow.demo.id
  gcp_organization_selector_id = commonfate_gcp_folder_selector.example.id
  gcp_role                     = "roles/editor"
  google_workspace_customer_id = "<Your Google Customer ID>"
}