Skip to content

Commit

Permalink
Add support for org policies to folder and project modules (#58)
Browse files Browse the repository at this point in the history
* modules/folders: add support for org policies

* update README

* update cloud config modules READMEs

* modules/project: add org policies
  • Loading branch information
ludoo authored Apr 8, 2020
1 parent 2e2d5f2 commit a280dd8
Show file tree
Hide file tree
Showing 17 changed files with 509 additions and 50 deletions.
4 changes: 2 additions & 2 deletions modules/cloud-config-container/coredns/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,12 +75,12 @@ module "cos-coredns" {
| name | description | type | required | default |
|---|---|:---: |:---:|:---:|
| *cloud_config* | Cloud config template path. If null default will be used. | <code title="">string</code> | | <code title="">null</code> |
| *config_variables* | Additional variables used to render the cloud-config template. | <code title="map&#40;any&#41;">map(any)</code> | | <code title="">{}</code> |
| *config_variables* | Additional variables used to render the cloud-config and CoreDNS templates. | <code title="map&#40;any&#41;">map(any)</code> | | <code title="">{}</code> |
| *coredns_config* | CoreDNS configuration path, if null default will be used. | <code title="">string</code> | | <code title="">null</code> |
| *file_defaults* | Default owner and permissions for files. | <code title="object&#40;&#123;&#10;owner &#61; string&#10;permissions &#61; string&#10;&#125;&#41;">object({...})</code> | | <code title="&#123;&#10;owner &#61; &#34;root&#34;&#10;permissions &#61; &#34;0644&#34;&#10;&#125;">...</code> |
| *files* | Map of extra files to create on the instance, path as key. Owner and permissions will use defaults if null. | <code title="map&#40;object&#40;&#123;&#10;content &#61; string&#10;owner &#61; string&#10;permissions &#61; string&#10;&#125;&#41;&#41;">map(object({...}))</code> | | <code title="">{}</code> |
| *test_instance* | Test/development instance attributes, leave null to skip creation. | <code title="object&#40;&#123;&#10;project_id &#61; string&#10;zone &#61; string&#10;name &#61; string&#10;type &#61; string&#10;network &#61; string&#10;subnetwork &#61; string&#10;&#125;&#41;">object({...})</code> | | <code title="">null</code> |
| *test_instance_defaults* | Test/development instance defaults used for optional configuration. | <code title="object&#40;&#123;&#10;disks &#61; map&#40;object&#40;&#123;&#10;read_only &#61; bool&#10;size &#61; number&#10;&#125;&#41;&#41;&#10;metadata &#61; map&#40;string&#41;&#10;service_account_roles &#61; list&#40;string&#41;&#10;tags &#61; list&#40;string&#41;&#10;&#125;&#41;">object({...})</code> | | <code title="&#123;&#10;disks &#61; &#123;&#125;&#10;metadata &#61; &#123;&#125;&#10;service_account_roles &#61; &#91;&#10;&#34;roles&#47;logging.logWriter&#34;,&#10;&#34;roles&#47;monitoring.metricWriter&#34;&#10;&#93;&#10;tags &#61; &#91;&#34;ssh&#34;&#93;&#10;&#125;">...</code> |
| *test_instance_defaults* | Test/development instance defaults used for optional configuration. If image is null, COS stable will be used. | <code title="object&#40;&#123;&#10;disks &#61; map&#40;object&#40;&#123;&#10;read_only &#61; bool&#10;size &#61; number&#10;&#125;&#41;&#41;&#10;image &#61; string&#10;metadata &#61; map&#40;string&#41;&#10;nat &#61; bool&#10;service_account_roles &#61; list&#40;string&#41;&#10;tags &#61; list&#40;string&#41;&#10;&#125;&#41;">object({...})</code> | | <code title="&#123;&#10;disks &#61; &#123;&#125;&#10;image &#61; null&#10;metadata &#61; &#123;&#125;&#10;nat &#61; false&#10;service_account_roles &#61; &#91;&#10;&#34;roles&#47;logging.logWriter&#34;,&#10;&#34;roles&#47;monitoring.metricWriter&#34;&#10;&#93;&#10;tags &#61; &#91;&#34;ssh&#34;&#93;&#10;&#125;">...</code> |

## Outputs

Expand Down
2 changes: 1 addition & 1 deletion modules/cloud-config-container/mysql/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ module "cos-mysql" {
| *mysql_config* | MySQL configuration file content, if null container default will be used. | <code title="">string</code> | | <code title="">null</code> |
| *mysql_data_disk* | MySQL data disk name in /dev/disk/by-id/ including the google- prefix. If null the boot disk will be used for data. | <code title="">string</code> | | <code title="">null</code> |
| *test_instance* | Test/development instance attributes, leave null to skip creation. | <code title="object&#40;&#123;&#10;project_id &#61; string&#10;zone &#61; string&#10;name &#61; string&#10;type &#61; string&#10;network &#61; string&#10;subnetwork &#61; string&#10;&#125;&#41;">object({...})</code> | | <code title="">null</code> |
| *test_instance_defaults* | Test/development instance defaults used for optional configuration. | <code title="object&#40;&#123;&#10;disks &#61; map&#40;object&#40;&#123;&#10;read_only &#61; bool&#10;size &#61; number&#10;&#125;&#41;&#41;&#10;metadata &#61; map&#40;string&#41;&#10;service_account_roles &#61; list&#40;string&#41;&#10;tags &#61; list&#40;string&#41;&#10;&#125;&#41;">object({...})</code> | | <code title="&#123;&#10;disks &#61; &#123;&#125;&#10;metadata &#61; &#123;&#125;&#10;service_account_roles &#61; &#91;&#10;&#34;roles&#47;logging.logWriter&#34;,&#10;&#34;roles&#47;monitoring.metricWriter&#34;&#10;&#93;&#10;tags &#61; &#91;&#34;ssh&#34;&#93;&#10;&#125;">...</code> |
| *test_instance_defaults* | Test/development instance defaults used for optional configuration. If image is null, COS stable will be used. | <code title="object&#40;&#123;&#10;disks &#61; map&#40;object&#40;&#123;&#10;read_only &#61; bool&#10;size &#61; number&#10;&#125;&#41;&#41;&#10;image &#61; string&#10;metadata &#61; map&#40;string&#41;&#10;nat &#61; bool&#10;service_account_roles &#61; list&#40;string&#41;&#10;tags &#61; list&#40;string&#41;&#10;&#125;&#41;">object({...})</code> | | <code title="&#123;&#10;disks &#61; &#123;&#125;&#10;image &#61; null&#10;metadata &#61; &#123;&#125;&#10;nat &#61; false&#10;service_account_roles &#61; &#91;&#10;&#34;roles&#47;logging.logWriter&#34;,&#10;&#34;roles&#47;monitoring.metricWriter&#34;&#10;&#93;&#10;tags &#61; &#91;&#34;ssh&#34;&#93;&#10;&#125;">...</code> |

## Outputs

Expand Down
3 changes: 2 additions & 1 deletion modules/cloud-config-container/onprem/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,10 +65,11 @@ module "on-prem" {
| name | description | type | required | default |
|---|---|:---: |:---:|:---:|
| vpn_config | VPN configuration, type must be one of 'dynamic' or 'static'. | <code title="object&#40;&#123;&#10;peer_ip &#61; string&#10;shared_secret &#61; string&#10;type &#61; string&#10;&#125;&#41;">object({...})</code> || |
| *config_variables* | Additional variables used to render the cloud-config and CoreDNS templates. | <code title="map&#40;any&#41;">map(any)</code> | | <code title="">{}</code> |
| *coredns_config* | CoreDNS configuration path, if null default will be used. | <code title="">string</code> | | <code title="">null</code> |
| *local_ip_cidr_range* | IP CIDR range used for the Docker onprem network. | <code title="">string</code> | | <code title="">192.168.192.0/24</code> |
| *test_instance* | Test/development instance attributes, leave null to skip creation. | <code title="object&#40;&#123;&#10;project_id &#61; string&#10;zone &#61; string&#10;name &#61; string&#10;type &#61; string&#10;network &#61; string&#10;subnetwork &#61; string&#10;&#125;&#41;">object({...})</code> | | <code title="">null</code> |
| *test_instance_defaults* | Test/development instance defaults used for optional configuration. | <code title="object&#40;&#123;&#10;disks &#61; map&#40;object&#40;&#123;&#10;read_only &#61; bool&#10;size &#61; number&#10;&#125;&#41;&#41;&#10;metadata &#61; map&#40;string&#41;&#10;service_account_roles &#61; list&#40;string&#41;&#10;tags &#61; list&#40;string&#41;&#10;&#125;&#41;">object({...})</code> | | <code title="&#123;&#10;disks &#61; &#123;&#125;&#10;metadata &#61; &#123;&#125;&#10;service_account_roles &#61; &#91;&#10;&#34;roles&#47;logging.logWriter&#34;,&#10;&#34;roles&#47;monitoring.metricWriter&#34;&#10;&#93;&#10;tags &#61; &#91;&#34;ssh&#34;&#93;&#10;&#125;">...</code> |
| *test_instance_defaults* | Test/development instance defaults used for optional configuration. If image is null, COS stable will be used. | <code title="object&#40;&#123;&#10;disks &#61; map&#40;object&#40;&#123;&#10;read_only &#61; bool&#10;size &#61; number&#10;&#125;&#41;&#41;&#10;image &#61; string&#10;metadata &#61; map&#40;string&#41;&#10;nat &#61; bool&#10;service_account_roles &#61; list&#40;string&#41;&#10;tags &#61; list&#40;string&#41;&#10;&#125;&#41;">object({...})</code> | | <code title="&#123;&#10;disks &#61; &#123;&#125;&#10;image &#61; null&#10;metadata &#61; &#123;&#125;&#10;nat &#61; false&#10;service_account_roles &#61; &#91;&#10;&#34;roles&#47;logging.logWriter&#34;,&#10;&#34;roles&#47;monitoring.metricWriter&#34;&#10;&#93;&#10;tags &#61; &#91;&#34;ssh&#34;&#93;&#10;&#125;">...</code> |
| *vpn_dynamic_config* | BGP configuration for dynamic VPN, ignored if VPN type is 'static'. | <code title="object&#40;&#123;&#10;local_bgp_asn &#61; number&#10;local_bgp_address &#61; string&#10;peer_bgp_asn &#61; number&#10;peer_bgp_address &#61; string&#10;&#125;&#41;">object({...})</code> | | <code title="&#123;&#10;local_bgp_asn &#61; 65002&#10;local_bgp_address &#61; &#34;169.254.0.2&#34;&#10;peer_bgp_asn &#61; 65001&#10;peer_bgp_address &#61; &#34;169.254.0.1&#34;&#10;&#125;">...</code> |
| *vpn_static_ranges* | Remote CIDR ranges for static VPN, ignored if VPN type is 'dynamic'. | <code title="list&#40;string&#41;">list(string)</code> | | <code title="">["10.0.0.0/8"]</code> |

Expand Down
30 changes: 28 additions & 2 deletions modules/folders/README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
# Google Cloud Folder Module

This module allow creation and management of sets of folders sharing a common parent, and their individual IAM bindings.
This module allow creation and management of sets of folders sharing a common parent, and their individual IAM bindings. It also allows setting a common set of organization policies on all folders.

## Example
## Examples

### IAM bindings

```hcl
module "folder" {
Expand All @@ -20,6 +22,28 @@ module "folder" {
}
```

### Organization policies

```hcl
module "folder" {
source = "./modules/folder"
parent = "organizations/1234567890"
names = ["Folder one", "Folder two]
policy_boolean = {
"constraints/compute.disableGuestAttributesAccess" = true
"constraints/compute.skipDefaultNetworkCreation" = true
}
policy_list = {
"constraints/compute.trustedImageProjects" = {
inherit_from_parent = null
suggested_value = null
status = true
values = ["projects/my-project"]
}
}
}
```

<!-- BEGIN TFDOC -->
## Variables

Expand All @@ -29,6 +53,8 @@ module "folder" {
| *iam_members* | List of IAM members keyed by folder name and role. | <code title="map&#40;map&#40;list&#40;string&#41;&#41;&#41;">map(map(list(string)))</code> | | <code title="">null</code> |
| *iam_roles* | List of IAM roles keyed by folder name. | <code title="map&#40;list&#40;string&#41;&#41;">map(list(string))</code> | | <code title="">null</code> |
| *names* | Folder names. | <code title="list&#40;string&#41;">list(string)</code> | | <code title="">[]</code> |
| *policy_boolean* | Map of boolean org policies and enforcement value, set value to null for policy restore. | <code title="map&#40;bool&#41;">map(bool)</code> | | <code title="">{}</code> |
| *policy_list* | Map of list org policies, status is true for allow, false for deny, null for restore. Values can only be used for allow or deny. | <code title="map&#40;object&#40;&#123;&#10;inherit_from_parent &#61; bool&#10;suggested_value &#61; string&#10;status &#61; bool&#10;values &#61; list&#40;string&#41;&#10;&#125;&#41;&#41;">map(object({...}))</code> | | <code title="">{}</code> |

## Outputs

Expand Down
88 changes: 88 additions & 0 deletions modules/folders/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,22 @@ locals {
"${pair.name}-${pair.role}" => pair
}
iam_members = var.iam_members == null ? {} : var.iam_members
policy_boolean_pairs = {
for pair in setproduct(var.names, keys(var.policy_boolean)) :
"${pair.0}-${pair.1}" => {
folder = pair.0,
policy = pair.1,
policy_data = var.policy_boolean[pair.1]
}
}
policy_list_pairs = {
for pair in setproduct(var.names, keys(var.policy_list)) :
"${pair.0}-${pair.1}" => {
folder = pair.0,
policy = pair.1,
policy_data = var.policy_list[pair.1]
}
}
}

resource "google_folder" "folders" {
Expand All @@ -48,3 +64,75 @@ resource "google_folder_iam_binding" "authoritative" {
)
}

resource "google_folder_organization_policy" "boolean" {
for_each = local.policy_boolean_pairs
folder = google_folder.folders[each.value.folder].id
constraint = each.value.policy

dynamic boolean_policy {
for_each = each.value.policy_data == null ? [] : [each.value.policy_data]
iterator = policy
content {
enforced = policy.value
}
}

dynamic restore_policy {
for_each = each.value.policy_data == null ? [""] : []
content {
default = true
}
}
}

resource "google_folder_organization_policy" "list" {
for_each = local.policy_list_pairs
folder = google_folder.folders[each.value.folder].id
constraint = each.value.policy

dynamic list_policy {
for_each = each.value.policy_data.status == null ? [] : [each.value.policy_data]
iterator = policy
content {
inherit_from_parent = policy.value.inherit_from_parent
suggested_value = policy.value.suggested_value
dynamic allow {
for_each = policy.value.status ? [""] : []
content {
values = (
try(length(policy.value.values) > 0, false)
? policy.value.values
: null
)
all = (
try(length(policy.value.values) > 0, false)
? null
: true
)
}
}
dynamic deny {
for_each = policy.value.status ? [] : [""]
content {
values = (
try(length(policy.value.values) > 0, false)
? policy.value.values
: null
)
all = (
try(length(policy.value.values) > 0, false)
? null
: true
)
}
}
}
}

dynamic restore_policy {
for_each = each.value.policy_data.status == null ? [true] : []
content {
default = true
}
}
}
15 changes: 15 additions & 0 deletions modules/folders/outputs.tf
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@ output "folder" {
output "id" {
description = "Folder id (for single use)."
value = local.has_folders ? local.folders[0].name : null
depends_on = [
google_folder_iam_binding.authoritative,
google_folder_organization_policy.boolean,
google_folder_organization_policy.list
]
}

output "name" {
Expand All @@ -41,6 +46,11 @@ output "ids" {
? zipmap(var.names, [for f in local.folders : f.name])
: {}
)
depends_on = [
google_folder_iam_binding.authoritative,
google_folder_organization_policy.boolean,
google_folder_organization_policy.list
]
}

output "names" {
Expand All @@ -55,6 +65,11 @@ output "names" {
output "ids_list" {
description = "List of folder ids."
value = [for f in local.folders : f.name]
depends_on = [
google_folder_iam_binding.authoritative,
google_folder_organization_policy.boolean,
google_folder_organization_policy.list
]
}

output "names_list" {
Expand Down
17 changes: 17 additions & 0 deletions modules/folders/variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,20 @@ variable "parent" {
description = "Parent in folders/folder_id or organizations/org_id format."
type = string
}

variable "policy_boolean" {
description = "Map of boolean org policies and enforcement value, set value to null for policy restore."
type = map(bool)
default = {}
}

variable "policy_list" {
description = "Map of list org policies, status is true for allow, false for deny, null for restore. Values can only be used for allow or deny."
type = map(object({
inherit_from_parent = bool
suggested_value = string
status = bool
values = list(string)
}))
default = {}
}
72 changes: 52 additions & 20 deletions modules/project/README.md
Original file line number Diff line number Diff line change
@@ -1,23 +1,54 @@
# Project Module

## Example
## Examples

### Minimal example with IAM

```hcl
module "project" {
source = "./modules/project"
parent = var.folder.id
billing_account = var.billing_account_id
prefix = "foo"
billing_account = "123456-123456-123456"
name = "project-example"
oslogin = true
oslogin_admins = var.admins
services = concat(var.project_services, [
"cloudkms.googleapis.com", "accesscontextmanager.googleapis.com"
])
parent = "folders/1234567890"
prefix = "foo"
services = [
"resourceviews.googleapis.com",
"stackdriver.googleapis.com"
]
iam_roles = ["roles/container.hostServiceAgentUser"]
iam_members = { "roles/container.hostServiceAgentUser" = [
"serviceAccount:${var.gke_service_account}"
] }
iam_members = {
"roles/container.hostServiceAgentUser" = [
"serviceAccount:${var.gke_service_account}"
]
}
}
```

### Organization policies

```hcl
module "project" {
source = "./modules/project"
billing_account = "123456-123456-123456"
name = "project-example"
parent = "folders/1234567890"
prefix = "foo"
services = [
"resourceviews.googleapis.com",
"stackdriver.googleapis.com"
]
policy_boolean = {
"constraints/compute.disableGuestAttributesAccess" = true
"constraints/compute.skipDefaultNetworkCreation" = true
}
policy_list = {
"constraints/compute.trustedImageProjects" = {
inherit_from_parent = null
suggested_value = null
status = true
values = ["projects/my-project"]
}
}
}
```

Expand All @@ -40,20 +71,21 @@ module "project" {
| *oslogin* | Enable OS Login. | <code title="">bool</code> | | <code title="">false</code> |
| *oslogin_admins* | List of IAM-style identities that will be granted roles necessary for OS Login administrators. | <code title="list&#40;string&#41;">list(string)</code> | | <code title="">[]</code> |
| *oslogin_users* | List of IAM-style identities that will be granted roles necessary for OS Login users. | <code title="list&#40;string&#41;">list(string)</code> | | <code title="">[]</code> |
| *policy_boolean* | Map of boolean org policies and enforcement value, set value to null for policy restore. | <code title="map&#40;bool&#41;">map(bool)</code> | | <code title="">{}</code> |
| *policy_list* | Map of list org policies, status is true for allow, false for deny, null for restore. Values can only be used for allow or deny. | <code title="map&#40;object&#40;&#123;&#10;inherit_from_parent &#61; bool&#10;suggested_value &#61; string&#10;status &#61; bool&#10;values &#61; list&#40;string&#41;&#10;&#125;&#41;&#41;">map(object({...}))</code> | | <code title="">{}</code> |
| *prefix* | Prefix used to generate project id and name. | <code title="">string</code> | | <code title="">null</code> |
| *services* | Service APIs to enable. | <code title="list&#40;string&#41;">list(string)</code> | | <code title="">[]</code> |

## Outputs

| name | description | sensitive |
|---|---|:---:|
| cloudsvc_service_account | Cloud services service account (depends on services). | |
| cloudsvc_service_account | Cloud services service account. | |
| custom_roles | Ids of the created custom roles. | |
| gce_service_account | Default GCE service account (depends on services). | |
| gcr_service_account | Default GCR service account (depends on services). | |
| gke_service_account | Default GKE service account (depends on services). | |
| iam_project_id | Project id (depends on services and IAM bindings). | |
| name | Name (depends on services). | |
| number | Project number (depends on services). | |
| project_id | Project id (depends on services). | |
| gce_service_account | Default GCE service account. | |
| gcr_service_account | Default GCR service account. | |
| gke_service_account | Default GKE service account. | |
| name | Project ame. | |
| number | Project number. | |
| project_id | Project id. | |
<!-- END TFDOC -->
Loading

0 comments on commit a280dd8

Please sign in to comment.