# Tenant bootstrap The primary purpose of this stage is to decouple a single tenant from centrally managed resources in the organization, so that subsequent management of the tenant's own hierarchy and resources can be implemented with a high degree of autonomy. It is logically equivalent to organization-level bootstrap as it's concerned with setting up IAM bindings on a root node and creating supporting projects attached to it, but it depends on the organization-level resource management stage and uses the same service account and permissions since it operates at the hierarchy level (folders, tags, organization policies). The resources and policies managed here are: - the tag value in the `tenant` key used in IAM conditions - the billing IAM bindings for the tenant-specific automation service accounts - the organization-level IAM binding that allows conditional managing of org policies on the tenant folder - the top-level tenant folder which acts as the root of the tenant's hierarchy - any organization policy that needs to be set for the tenant on its root folder - the tenant automation and logging projects - service accounts for all tenant stages - GCS buckets for bootstrap and resource management state - optional CI/CD setup for this and the resource management tenant stages - tenant-specific Workload Identity Federation pool and providers (planned) One notable difference compared to organization-level bootstrap is the creation of service accounts for all tenant stages: this is done here so that Billing and Organization Policy Admin bindings can be set, leveraging permissions of the org-level resman service account which is used to run this stage. Doing this here avoids the need to grant broad scoped permissions on the organization to tenant-level service accounts, and effectively decouples the tenant from the organization. The following diagram is a high level reference of what this stage manages, showing one hypothetical tenant (additional tenants require additional instances of this stage being deployed): ```mermaid %%{init: {'theme':'base'}}%% classDiagram Organization~🏢~ -- Tenant 0~📁~ Tenant 0~📁~ -- tn0_automation Tenant 0~📁~ -- tn0_logging class Organization~🏢~ { - tag value - IAM bindings() - org policies() } class Tenant 0~📁~ { - log sinks - IAM bindings() - tag bindings() } class tn0_automation { - GCS buckets - service accounts - optional CI/CD - IAM bindings() } class tn0_logging { - log sink destinations } ``` As most of the features of this stage follow the same design and configurations of the [organization-level bootstrap stage](../../stages/0-bootstrap/), we will only focus on the tenant-specific configuration in this document. ## Naming This stage sets the prefix used to name tenant resources, and passes it downstream to the other tenant stages together with the other globals needed by the tenant. The default is to append the tenant short name (a 3 or 4 letter acronym or abbreviation) to the organization-level prefix, if that is not desired this can be changed by editing local definitions in the `main.tf` file. Just be aware that some resources have name length constraints. ## How to run this stage The tenant bootstrap stage is the effective boundary between organization and tenant-level resources: it uses the same inputs as the organization-level resource management stage, and produces outputs which provide the needed context to all other tenant stages. ### Output files and cross-stage variables As mentioned above, the organization-level set of output files are used here with one exception: the provider file is different since state is specific to this stage. The `stage-links.sh` script can be used to get the commands needed for the provider and output files, just pass a single argument with your FAST output files folder path, or GCS bucket URI: ```bash ../../stage-links.sh ~/fast-config ``` The script output can be copy/pasted to a terminal: ```bash # copy and paste the following commands for '0-bootstrap-tenant' cp ~/fast-config/providers/0-bootstrap-tenant-providers.tf ./ ln -s ~/fast-config/tfvars/globals.auto.tfvars.json ./ ln -s ~/fast-config/tfvars/0-bootstrap.auto.tfvars.json ./ ln -s ~/fast-config/tfvars/1-resman.auto.tfvars.json ./ # ---> remember to set the prefix in the provider file <--- ``` As shown in the script output above, the provider file is a template used as a source for potentially multiple tenant installations, so it needs to be specifically configured for this tenant by setting the backend `prefix` to a unique string so that the Terraform state file will not overlap with other tenants. Open it in an editor and perform the change before proceeding. ### Global overrides The globals variable file linekd above contains definition which were set for the organization, for example the locations used for log sink destinations. These might not be correct for each tenant, so this stage allows overriding them via the tenant configuration variable described in the next section. ### Tenant-level configuration The tenant configuration resides in the `tenant_config` variable, this is an example configuration for a tenant with comments explaining the different choices that need to be made: ```hcl tenant_config = { # used for the top-level folder name descriptive_name = "My First Tenant" # tenant-specific groups, only the admin group is required # the organization domain is automatically added after the group name groups = { gcp-admins = "tn01-admins" # gcp-devops = "tn01-devops" # gcp-network-admins = "tn01-networking" # gcp-security-admins = "tn01-security" } # the 3 or 4 letter acronym or abbreviation used in resource names short_name = "tn01" # optional CI/CD configuration, refer to the org-level stages for information # cicd = { # branch = null # identity_provider = "foo-provider" # name = "myorg/tn01-bootstrap" # type = "github" # } # optional group-level IAM bindings to add to the top-level folder # group_iam = { # tn01-support = ["roles/viewer"] # } # optional IAM bindings to add to the top-level folder # iam = { # "roles/logging.admin" = [ # "serviceAccount:foo@myprj.iam.gserviceaccount.com" # ] # } # optional location overrides to global locations # locations = { # bq = null # gcs = null # logging = null # pubsub = null # } # optional folder ids for automation and logging project folders, typically # added in later stages and entered here once created # project_parent_ids = { # automation = "folders/012345678" # logging = "folders/0123456789" # } } # tftest skip ``` Configure the tenant variable in a tfvars file for this stage. A few minor points worth noting: - the administrator group is the only one required here, specifying other groups only has the effect of populating the output file with group names for reuse in later stages - the `iam` variable is merged with the IAM bindings for service accounts in the `main.tf` file, which take precedence; if a role specified in the variable is ignored, that's probably the case - locations can be overridden at the attribute level, there's no need to specify those that are equal to the ones in the organization globals file ### Running the stage Once the configuration is done just go through the usual `init/apply` cycle. On successful apply, a tfvars file specific for this tenant and a set of provider files will be created. ### TODO - [ ] tenant-level Workload Identity Federation pool and providers configuration - [ ] tenant-level logging project and sinks ## Files | name | description | modules | resources | |---|---|---|---| | [automation-sas.tf](./automation-sas.tf) | Tenant automation stage 2 and 3 service accounts. | iam-service-account | google_organization_iam_member | | [automation.tf](./automation.tf) | Tenant automation project and resources. | gcs · iam-service-account · project | | | [billing.tf](./billing.tf) | Billing roles for standalone billing accounts. | | google_billing_account_iam_member | | [cicd.tf](./cicd.tf) | Workload Identity Federation configurations for CI/CD. | iam-service-account · source-repository | | | [identity-providers.tf](./identity-providers.tf) | Workload Identity Federation provider definitions. | | google_iam_workload_identity_pool · google_iam_workload_identity_pool_provider | | [log-export.tf](./log-export.tf) | Audit log project and sink. | bigquery-dataset · gcs · logging-bucket · project · pubsub | | | [main.tf](./main.tf) | Module-level locals and resources. | folder | | | [organization.tf](./organization.tf) | Organization tag and conditional IAM grant. | organization | google_organization_iam_member · google_tags_tag_value_iam_member | | [outputs-files.tf](./outputs-files.tf) | Output files persistence to local filesystem. | | local_file | | [outputs-gcs.tf](./outputs-gcs.tf) | Output files persistence to automation GCS bucket. | | google_storage_bucket_object | | [outputs.tf](./outputs.tf) | Module outputs. | | | | [variables.tf](./variables.tf) | Module variables. | | | ## Variables | name | description | type | required | default | producer | |---|---|:---:|:---:|:---:|:---:| | [automation](variables.tf#L20) | Automation resources created by the organization-level bootstrap stage. | object({…}) | ✓ | | 0-bootstrap | | [billing_account](variables.tf#L38) | Billing account id. If billing account is not part of the same org set `is_org_level` to false. | object({…}) | ✓ | | 0-bootstrap | | [organization](variables.tf#L193) | Organization details. | object({…}) | ✓ | | 0-bootstrap | | [prefix](variables.tf#L209) | Prefix used for resources that need unique names. Use 9 characters or less. | string | ✓ | | 0-bootstrap | | [tag_keys](variables.tf#L232) | Organization tag keys. | object({…}) | ✓ | | 1-resman | | [tag_names](variables.tf#L243) | Customized names for resource management tags. | object({…}) | ✓ | | 1-resman | | [tag_values](variables.tf#L254) | Organization resource management tag values. | map(string) | ✓ | | 1-resman | | [tenant_config](variables.tf#L261) | Tenant configuration. Short name must be 4 characters or less. | object({…}) | ✓ | | | | [cicd_repositories](variables.tf#L51) | CI/CD repository configuration. Identity providers reference keys in the `federated_identity_providers` variable. Set to null to disable, or set individual repositories to null if not needed. | object({…}) | | null | | | [custom_roles](variables.tf#L97) | Custom roles defined at the organization level, in key => id format. | object({…}) | | null | 0-bootstrap | | [fast_features](variables.tf#L106) | Selective control for top-level FAST features. | object({…}) | | {} | 0-bootstrap | | [federated_identity_providers](variables.tf#L120) | Workload Identity Federation pools. The `cicd_repositories` variable references keys here. | map(object({…})) | | {} | | | [group_iam](variables.tf#L134) | Tenant-level custom group IAM settings in group => [roles] format. | map(list(string)) | | {} | | | [iam](variables.tf#L140) | Tenant-level custom IAM settings in role => [principal] format. | map(list(string)) | | {} | | | [iam_additive](variables.tf#L146) | Tenant-level custom IAM settings in role => [principal] format for non-authoritative bindings. | map(list(string)) | | {} | | | [locations](variables.tf#L152) | Optional locations for GCS, BigQuery, and logging buckets created here. These are the defaults set at the organization level, and can be overridden via the tenant config variable. | object({…}) | | {…} | 0-bootstrap | | [log_sinks](variables.tf#L172) | Tenant-level log sinks, in name => {type, filter} format. | map(object({…})) | | {…} | | | [outputs_location](variables.tf#L203) | Enable writing provider, tfvars and CI/CD workflow files to local filesystem. Leave null to disable. | string | | null | | | [project_parent_ids](variables.tf#L219) | Optional parents for projects created here in folders/nnnnnnn format. Null values will use the tenant folder as parent. | object({…}) | | {…} | | | [test_principal](variables.tf#L301) | Used when testing to bypass the data source returning the current identity. | string | | null | | ## Outputs | name | description | sensitive | consumers | |---|---|:---:|---| | [cicd_workflows](outputs.tf#L102) | CI/CD workflows for tenant bootstrap and resource management stages. | ✓ | | | [federated_identity](outputs.tf#L108) | Workload Identity Federation pool and providers. | | | | [provider](outputs.tf#L118) | Terraform provider file for tenant resource management stage. | ✓ | stage-01 | | [tenant_resources](outputs.tf#L125) | Tenant-level resources. | | | | [tfvars](outputs.tf#L136) | Terraform variable files for the following tenant stages. | ✓ | |