diff --git a/modules/folder/README.md b/modules/folder/README.md
index 219df4c1b3..a15d4b204b 100644
--- a/modules/folder/README.md
+++ b/modules/folder/README.md
@@ -5,6 +5,7 @@ This module allows the creation and management of folders, including support for
- [Basic example with IAM bindings](#basic-example-with-iam-bindings)
- [IAM](#iam)
+- [Assured Workload Folder](#assured-workload-folder)
- [Organization policies](#organization-policies)
- [Organization Policy Factory](#organization-policy-factory)
- [Hierarchical Firewall Policy Attachments](#hierarchical-firewall-policy-attachments)
@@ -55,6 +56,37 @@ The authoritative and additive approaches can be used together, provided differe
Refer to the [project module](../project/README.md#iam) for examples of the IAM interface.
+## Assured Workload Folder
+
+To create [Assured Workload](https://cloud.google.com/security/products/assured-workloads) folder instead of regular folder.
+Note that an existing folder can not be converted to an Assured Workload folder, hence `assured_workload_config` is mutually exclusive with `folder_create=false`.
+
+```hcl
+module "folder" {
+ source = "./fabric/modules/folder"
+ parent = var.folder_id
+ name = "Folder name"
+
+ assured_workload_config = {
+ compliance_regime = "EU_REGIONS_AND_SUPPORT"
+ display_name = "workload-name"
+ location = "europe-west1"
+ organization = var.organization_id
+ enable_sovereign_controls = true
+ }
+ iam = {
+ "roles/owner" = ["serviceAccount:${var.service_account.email}"]
+ }
+ iam_bindings_additive = {
+ am1-storage-admin = {
+ member = "serviceAccount:${var.service_account.email}"
+ role = "roles/storage.admin"
+ }
+ }
+}
+# tftest modules=1 resources=3 inventory=assured-workload.yaml
+```
+
## Organization policies
To manage organization policies, the `orgpolicy.googleapis.com` service should be enabled in the quota project.
@@ -347,7 +379,7 @@ module "folder" {
|---|---|---|
| [iam.tf](./iam.tf) | IAM bindings. | google_folder_iam_binding
· google_folder_iam_member
|
| [logging.tf](./logging.tf) | Log sinks and supporting resources. | google_bigquery_dataset_iam_member
· google_folder_iam_audit_config
· google_logging_folder_exclusion
· google_logging_folder_settings
· google_logging_folder_sink
· google_project_iam_member
· google_pubsub_topic_iam_member
· google_storage_bucket_iam_member
|
-| [main.tf](./main.tf) | Module-level locals and resources. | google_compute_firewall_policy_association
· google_essential_contacts_contact
· google_folder
|
+| [main.tf](./main.tf) | Module-level locals and resources. | google_assured_workloads_workload
· google_compute_firewall_policy_association
· google_essential_contacts_contact
· google_folder
|
| [organization-policies.tf](./organization-policies.tf) | Folder-level organization policies. | google_org_policy_policy
|
| [outputs.tf](./outputs.tf) | Module outputs. | |
| [tags.tf](./tags.tf) | None | google_tags_tag_binding
|
@@ -360,30 +392,32 @@ module "folder" {
| name | description | type | required | default |
|---|---|:---:|:---:|:---:|
-| [contacts](variables.tf#L17) | List of essential contacts for this resource. Must be in the form EMAIL -> [NOTIFICATION_TYPES]. Valid notification types are ALL, SUSPENSION, SECURITY, TECHNICAL, BILLING, LEGAL, PRODUCT_UPDATES. | map(list(string))
| | {}
|
-| [factories_config](variables.tf#L24) | Paths to data files and folders that enable factory functionality. | object({…})
| | {}
|
-| [firewall_policy](variables.tf#L33) | Hierarchical firewall policy to associate to this folder. | object({…})
| | null
|
-| [folder_create](variables.tf#L42) | Create folder. When set to false, uses id to reference an existing folder. | bool
| | true
|
+| [assured_workload_config](variables.tf#L17) | Create AssuredWorkloads folder instead of regular folder when value is provided. Incompatible with folder_create=false. | object({…})
| | null
|
+| [contacts](variables.tf#L70) | List of essential contacts for this resource. Must be in the form EMAIL -> [NOTIFICATION_TYPES]. Valid notification types are ALL, SUSPENSION, SECURITY, TECHNICAL, BILLING, LEGAL, PRODUCT_UPDATES. | map(list(string))
| | {}
|
+| [factories_config](variables.tf#L77) | Paths to data files and folders that enable factory functionality. | object({…})
| | {}
|
+| [firewall_policy](variables.tf#L86) | Hierarchical firewall policy to associate to this folder. | object({…})
| | null
|
+| [folder_create](variables.tf#L95) | Create folder. When set to false, uses id to reference an existing folder. | bool
| | true
|
| [iam](variables-iam.tf#L17) | IAM bindings in {ROLE => [MEMBERS]} format. | map(list(string))
| | {}
|
| [iam_bindings](variables-iam.tf#L24) | Authoritative IAM bindings in {KEY => {role = ROLE, members = [], condition = {}}}. Keys are arbitrary. | map(object({…}))
| | {}
|
| [iam_bindings_additive](variables-iam.tf#L39) | Individual additive IAM bindings. Keys are arbitrary. | map(object({…}))
| | {}
|
| [iam_by_principals](variables-iam.tf#L54) | Authoritative IAM binding in {PRINCIPAL => [ROLES]} format. Principals need to be statically defined to avoid cycle errors. Merged internally with the `iam` variable. | map(list(string))
| | {}
|
-| [id](variables.tf#L48) | Folder ID in case you use folder_create=false. | string
| | null
|
+| [id](variables.tf#L101) | Folder ID in case you use folder_create=false. | string
| | null
|
| [logging_data_access](variables-logging.tf#L17) | Control activation of data access logs. Format is service => { log type => [exempted members]}. The special 'allServices' key denotes configuration for all services. | map(map(list(string)))
| | {}
|
| [logging_exclusions](variables-logging.tf#L32) | Logging exclusions for this folder in the form {NAME -> FILTER}. | map(string)
| | {}
|
| [logging_settings](variables-logging.tf#L39) | Default settings for logging resources. | object({…})
| | null
|
| [logging_sinks](variables-logging.tf#L49) | Logging sinks to create for the folder. | map(object({…}))
| | {}
|
-| [name](variables.tf#L54) | Folder name. | string
| | null
|
-| [org_policies](variables.tf#L60) | Organization policies applied to this folder keyed by policy name. | map(object({…}))
| | {}
|
-| [parent](variables.tf#L87) | Parent in folders/folder_id or organizations/org_id format. | string
| | null
|
-| [tag_bindings](variables.tf#L97) | Tag bindings for this folder, in key => tag value id format. | map(string)
| | null
|
+| [name](variables.tf#L107) | Folder name. | string
| | null
|
+| [org_policies](variables.tf#L113) | Organization policies applied to this folder keyed by policy name. | map(object({…}))
| | {}
|
+| [parent](variables.tf#L140) | Parent in folders/folder_id or organizations/org_id format. | string
| | null
|
+| [tag_bindings](variables.tf#L150) | Tag bindings for this folder, in key => tag value id format. | map(string)
| | null
|
## Outputs
| name | description | sensitive |
|---|---|:---:|
-| [folder](outputs.tf#L17) | Folder resource. | |
-| [id](outputs.tf#L22) | Fully qualified folder id. | |
-| [name](outputs.tf#L33) | Folder name. | |
-| [sink_writer_identities](outputs.tf#L38) | Writer identities created for each sink. | |
+| [assured_workload](outputs.tf#L17) | Assured Workloads workload resource. | |
+| [folder](outputs.tf#L22) | Folder resource. | |
+| [id](outputs.tf#L27) | Fully qualified folder id. | |
+| [name](outputs.tf#L38) | Folder name. | |
+| [sink_writer_identities](outputs.tf#L47) | Writer identities created for each sink. | |
diff --git a/modules/folder/main.tf b/modules/folder/main.tf
index 8bb46a533c..4f838b0131 100644
--- a/modules/folder/main.tf
+++ b/modules/folder/main.tf
@@ -16,14 +16,28 @@
locals {
folder_id = (
- var.folder_create
- ? try(google_folder.folder[0].id, null)
- : var.id
+ var.assured_workload_config == null
+ ? (
+ var.folder_create
+ ? try(google_folder.folder[0].id, null)
+ : var.id
+ )
+ : format("folders/%s", try(google_assured_workloads_workload.folder[0].resources[0].resource_id, ""))
+ )
+ aw_parent = (
+ # Assured Workload only accepls folder as a parent and uses organization as a parent when no value provided.
+ var.parent == null
+ ? null
+ : (
+ try(startswith(var.parent, "folders/"))
+ ? var.parent
+ : null
+ )
)
}
resource "google_folder" "folder" {
- count = var.folder_create ? 1 : 0
+ count = var.folder_create && var.assured_workload_config == null ? 1 : 0
display_name = var.name
parent = var.parent
}
@@ -48,3 +62,30 @@ resource "google_compute_firewall_policy_association" "default" {
name = var.firewall_policy.name
firewall_policy = var.firewall_policy.policy
}
+
+resource "google_assured_workloads_workload" "folder" {
+ count = (var.assured_workload_config != null && var.folder_create) ? 1 : 0
+ compliance_regime = var.assured_workload_config.compliance_regime
+ display_name = var.assured_workload_config.display_name
+ location = var.assured_workload_config.location
+ organization = var.assured_workload_config.organization
+ enable_sovereign_controls = var.assured_workload_config.enable_sovereign_controls
+ labels = var.assured_workload_config.labels
+ partner = var.assured_workload_config.partner
+ dynamic "partner_permissions" {
+ for_each = try(var.assured_workload_config.partner_permissions, null) == null ? [] : [""]
+ content {
+ assured_workloads_monitoring = var.assured_workload_config.partner_permissions.assured_workloads_monitoring
+ data_logs_viewer = var.assured_workload_config.partner_permissions.data_logs_viewer
+ service_access_approver = var.assured_workload_config.partner_permissions.service_access_approver
+ }
+ }
+
+ provisioned_resources_parent = local.aw_parent
+
+ resource_settings {
+ display_name = var.name
+ resource_type = "CONSUMER_FOLDER"
+ }
+ violation_notifications_enabled = var.assured_workload_config.violation_notifications_enabled
+}
diff --git a/modules/folder/outputs.tf b/modules/folder/outputs.tf
index 79ff37cb7d..f9acdad405 100644
--- a/modules/folder/outputs.tf
+++ b/modules/folder/outputs.tf
@@ -14,6 +14,11 @@
* limitations under the License.
*/
+output "assured_workload" {
+ description = "Assured Workloads workload resource."
+ value = try(google_assured_workloads_workload.folder[0], null)
+}
+
output "folder" {
description = "Folder resource."
value = try(google_folder.folder[0], null)
@@ -32,7 +37,11 @@ output "id" {
output "name" {
description = "Folder name."
- value = try(google_folder.folder[0].display_name, null)
+ value = (
+ var.assured_workload_config == null
+ ? try(google_folder.folder[0].display_name, null)
+ : try(google_assured_workloads_workload.folder[0].resource_settings[0].display_name, null)
+ )
}
output "sink_writer_identities" {
diff --git a/modules/folder/variables.tf b/modules/folder/variables.tf
index e5f6d548be..1c899ab09a 100644
--- a/modules/folder/variables.tf
+++ b/modules/folder/variables.tf
@@ -14,6 +14,59 @@
* limitations under the License.
*/
+variable "assured_workload_config" {
+ description = "Create AssuredWorkloads folder instead of regular folder when value is provided. Incompatible with folder_create=false."
+ type = object({
+ compliance_regime = string
+ display_name = string
+ location = string
+ organization = string
+ enable_sovereign_controls = optional(bool)
+ labels = optional(map(string), {})
+ partner = optional(string)
+ partner_permissions = optional(object({
+ assured_workloads_monitoring = optional(bool)
+ data_logs_viewer = optional(bool)
+ service_access_approver = optional(bool)
+ }))
+ violation_notifications_enabled = optional(bool)
+
+ })
+ default = null
+ validation {
+ condition = try(contains([
+ "ASSURED_WORKLOADS_FOR_PARTNERS",
+ "AU_REGIONS_AND_US_SUPPORT",
+ "CA_PROTECTED_B, IL5",
+ "CA_REGIONS_AND_SUPPORT",
+ "CJIS",
+ "COMPLIANCE_REGIME_UNSPECIFIED",
+ "EU_REGIONS_AND_SUPPORT",
+ "FEDRAMP_HIGH",
+ "FEDRAMP_MODERATE",
+ "HIPAA, HITRUST",
+ "IL2",
+ "IL4",
+ "ISR_REGIONS_AND_SUPPORT",
+ "ISR_REGIONS",
+ "ITAR",
+ "JP_REGIONS_AND_SUPPORT",
+ "US_REGIONAL_ACCESS"
+ ], var.assured_workload_config.compliance_regime), true)
+ error_message = "Field assured_workload_config.compliance_regime must be one of the values listed in https://cloud.google.com/assured-workloads/docs/reference/rest/Shared.Types/ComplianceRegime"
+ }
+ validation {
+ condition = try(contains([
+ "LOCAL_CONTROLS_BY_S3NS",
+ "PARTNER_UNSPECIFIED",
+ "SOVEREIGN_CONTROLS_BY_PSN",
+ "SOVEREIGN_CONTROLS_BY_SIA_MINSAIT",
+ "SOVEREIGN_CONTROLS_BY_T_SYSTEMS"
+ ], var.assured_workload_config.partner), true)
+ error_message = "Field assured_workload_config.partner must be one of the values listed in https://cloud.google.com/assured-workloads/docs/reference/rest/Shared.Types/Partner"
+ }
+}
+
variable "contacts" {
description = "List of essential contacts for this resource. Must be in the form EMAIL -> [NOTIFICATION_TYPES]. Valid notification types are ALL, SUSPENSION, SECURITY, TECHNICAL, BILLING, LEGAL, PRODUCT_UPDATES."
type = map(list(string))
@@ -99,3 +152,4 @@ variable "tag_bindings" {
type = map(string)
default = null
}
+
diff --git a/tests/examples_e2e/setup_module/main.tf b/tests/examples_e2e/setup_module/main.tf
index d459013d25..f27b90e523 100644
--- a/tests/examples_e2e/setup_module/main.tf
+++ b/tests/examples_e2e/setup_module/main.tf
@@ -22,6 +22,7 @@ locals {
services = [
# trimmed down list of services, to be extended as needed
"alloydb.googleapis.com",
+ "assuredworkloads.googleapis.com",
"apigee.googleapis.com",
"bigquery.googleapis.com",
"cloudbuild.googleapis.com",
diff --git a/tests/modules/folder/examples/assured-workload.yaml b/tests/modules/folder/examples/assured-workload.yaml
new file mode 100644
index 0000000000..dfcba85ac8
--- /dev/null
+++ b/tests/modules/folder/examples/assured-workload.yaml
@@ -0,0 +1,38 @@
+# Copyright 2023 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.
+
+values:
+ module.folder.google_assured_workloads_workload.folder[0]:
+ compliance_regime: EU_REGIONS_AND_SUPPORT
+ display_name: workload-name
+ organization: organizations/1122334455
+ location: europe-west1
+ enable_sovereign_controls: true
+ module.folder.google_folder_iam_binding.authoritative["roles/owner"]:
+ condition: []
+ members:
+ - serviceAccount:service_account_email
+ role: roles/owner
+ module.folder.google_folder_iam_member.bindings["am1-storage-admin"]:
+ condition: []
+ member: serviceAccount:service_account_email
+ role: roles/storage.admin
+counts:
+ google_assured_workloads_workload: 1
+ google_folder_iam_binding: 1
+ google_folder_iam_member: 1
+ modules: 1
+ resources: 3
+
+outputs: {}