Add support for org policies to folder and project modules (#58)

* modules/folders: add support for org policies

* update README

* update cloud config modules READMEs

* modules/project: add org policies
This commit is contained in:
Ludovico Magnocavallo 2020-04-08 14:54:49 +02:00 committed by GitHub
parent 2e2d5f27c6
commit a280dd880d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 509 additions and 50 deletions

View File

@ -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

View File

@ -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

View File

@ -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> |

View File

@ -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" {
@ -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
@ -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

View File

@ -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" {
@ -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
}
}
}

View File

@ -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" {
@ -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" {
@ -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" {

View File

@ -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 = {}
}

View File

@ -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"]
}
}
}
```
@ -40,6 +71,8 @@ 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> |
@ -47,13 +80,12 @@ module "project" {
| 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 -->

View File

@ -88,6 +88,10 @@ resource "google_project_iam_binding" "authoritative" {
project = google_project.project.project_id
role = each.value
members = lookup(var.iam_members, each.value, [])
depends_on = [
google_project_service.project_services,
google_project_iam_custom_role.roles
]
}
resource "google_project_iam_member" "additive" {
@ -124,3 +128,76 @@ resource "google_project_iam_member" "oslogin_users" {
role = "roles/compute.osLogin"
member = each.value
}
resource "google_project_organization_policy" "boolean" {
for_each = var.policy_boolean
project = google_project.project.project_id
constraint = each.key
dynamic boolean_policy {
for_each = each.value == null ? [] : [each.value]
iterator = policy
content {
enforced = policy.value
}
}
dynamic restore_policy {
for_each = each.value == null ? [""] : []
content {
default = true
}
}
}
resource "google_project_organization_policy" "list" {
for_each = var.policy_list
project = google_project.project.project_id
constraint = each.key
dynamic list_policy {
for_each = each.value.status == null ? [] : [each.value]
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.status == null ? [true] : []
content {
default = true
}
}
}

View File

@ -15,55 +15,55 @@
*/
output "project_id" {
description = "Project id (depends on services)."
description = "Project id."
value = google_project.project.project_id
depends_on = [
google_project_organization_policy.boolean,
google_project_organization_policy.list,
google_project_service.project_services
]
}
output "iam_project_id" {
description = "Project id (depends on services and IAM bindings)."
value = google_project.project.project_id
output "name" {
description = "Project ame."
value = google_project.project.name
depends_on = [
google_project_service.project_services,
google_project_iam_binding.authoritative,
google_project_iam_member.non_authoritative
google_project_organization_policy.boolean,
google_project_organization_policy.list,
google_project_service.project_services
]
}
output "name" {
description = "Name (depends on services)."
value = google_project.project.name
depends_on = [google_project_service.project_services]
}
output "number" {
description = "Project number (depends on services)."
description = "Project number."
value = google_project.project.number
depends_on = [google_project_service.project_services]
depends_on = [
google_project_organization_policy.boolean,
google_project_organization_policy.list,
google_project_service.project_services
]
}
output "cloudsvc_service_account" {
description = "Cloud services service account (depends on services)."
description = "Cloud services service account."
value = "${local.cloudsvc_service_account}"
depends_on = [google_project_service.project_services]
}
output "gce_service_account" {
description = "Default GCE service account (depends on services)."
description = "Default GCE service account."
value = local.gce_service_account
depends_on = [google_project_service.project_services]
}
output "gcr_service_account" {
description = "Default GCR service account (depends on services)."
description = "Default GCR service account."
value = local.gcr_service_account
depends_on = [google_project_service.project_services]
}
output "gke_service_account" {
description = "Default GKE service account (depends on services)."
description = "Default GKE service account."
value = local.gke_service_account
depends_on = [google_project_service.project_services]
}

View File

@ -96,6 +96,23 @@ variable "parent" {
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 = {}
}
variable "prefix" {
description = "Prefix used to generate project id and name."
type = string

View File

@ -15,9 +15,11 @@
*/
module "test" {
source = "../../../../modules/folders"
parent = "organizations/12345678"
names = ["folder-a", "folder-b"]
iam_members = var.iam_members
iam_roles = var.iam_roles
source = "../../../../modules/folders"
parent = "organizations/12345678"
names = ["folder-a", "folder-b"]
iam_members = var.iam_members
iam_roles = var.iam_roles
policy_boolean = var.policy_boolean
policy_list = var.policy_list
}

View File

@ -23,3 +23,18 @@ variable "iam_roles" {
type = map(list(string))
default = null
}
variable "policy_boolean" {
type = map(bool)
default = {}
}
variable "policy_list" {
type = map(object({
inherit_from_parent = bool
suggested_value = string
status = bool
values = list(string)
}))
default = {}
}

View File

@ -0,0 +1,86 @@
# Copyright 2020 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.
import os
import pytest
FIXTURES_DIR = os.path.join(os.path.dirname(__file__), 'fixture')
def test_policy_boolean(plan_runner):
"Test boolean folder policy."
policy_boolean = '{policy-a = true, policy-b = false, policy-c = null}'
_, resources = plan_runner(FIXTURES_DIR, policy_boolean=policy_boolean)
assert len(resources) == 8
resources = [r for r in resources if r['type']
== 'google_folder_organization_policy']
assert sorted([r['index'] for r in resources]) == [
'folder-a-policy-a',
'folder-a-policy-b',
'folder-a-policy-c',
'folder-b-policy-a',
'folder-b-policy-b',
'folder-b-policy-c'
]
policy_values = []
for resource in resources:
for policy in ('boolean_policy', 'restore_policy'):
value = resource['values'][policy]
if value:
policy_values.append((resource['index'], policy,) + value[0].popitem())
assert sorted(policy_values) == [
('folder-a-policy-a', 'boolean_policy', 'enforced', True),
('folder-a-policy-b', 'boolean_policy', 'enforced', False),
('folder-a-policy-c', 'restore_policy', 'default', True),
('folder-b-policy-a', 'boolean_policy', 'enforced', True),
('folder-b-policy-b', 'boolean_policy', 'enforced', False),
('folder-b-policy-c', 'restore_policy', 'default', True)
]
def test_policy_list(plan_runner):
"Test list org policy."
policy_list = (
'{'
'policy-a = {inherit_from_parent = true, suggested_value = null, status = true, values = []}, '
'policy-b = {inherit_from_parent = null, suggested_value = "foo", status = false, values = ["bar"]}, '
'policy-c = {inherit_from_parent = null, suggested_value = true, status = null, values = null}'
'}'
)
_, resources = plan_runner(FIXTURES_DIR, policy_list=policy_list)
assert len(resources) == 8
resources = [r for r in resources if r['type']
== 'google_folder_organization_policy']
assert sorted([r['index'] for r in resources]) == [
'folder-a-policy-a',
'folder-a-policy-b',
'folder-a-policy-c',
'folder-b-policy-a',
'folder-b-policy-b',
'folder-b-policy-c'
]
values = [r['values'] for r in resources]
assert [r['constraint'] for r in values] == [
'policy-a', 'policy-b', 'policy-c', 'policy-a', 'policy-b', 'policy-c'
]
for i in (0, 3):
assert values[i]['list_policy'][0]['allow'] == [
{'all': True, 'values': None}]
for i in (1, 4):
assert values[i]['list_policy'][0]['deny'] == [
{'all': False, 'values': ["bar"]}]
for i in (2, 5):
assert values[i]['restore_policy'] == [{'default': True}]

View File

@ -30,6 +30,8 @@ module "test" {
oslogin_admins = var.oslogin_admins
oslogin_users = var.oslogin_users
parent = var.parent
policy_boolean = var.policy_boolean
policy_list = var.policy_list
prefix = var.prefix
services = var.services
}

View File

@ -74,6 +74,21 @@ variable "parent" {
default = "folders/12345678"
}
variable "policy_boolean" {
type = map(bool)
default = {}
}
variable "policy_list" {
type = map(object({
inherit_from_parent = bool
suggested_value = string
status = bool
values = list(string)
}))
default = {}
}
variable "prefix" {
type = string
default = null

View File

@ -0,0 +1,66 @@
# Copyright 2020 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.
import os
import pytest
FIXTURES_DIR = os.path.join(os.path.dirname(__file__), 'fixture')
def test_policy_boolean(plan_runner):
"Test boolean org policy."
policy_boolean = '{policy-a = true, policy-b = false, policy-c = null}'
_, resources = plan_runner(FIXTURES_DIR, policy_boolean=policy_boolean)
assert len(resources) == 4
resources = [r for r in resources if r['type']
== 'google_project_organization_policy']
assert sorted([r['index'] for r in resources]) == [
'policy-a', 'policy-b', 'policy-c'
]
policy_values = []
for resource in resources:
for policy in ('boolean_policy', 'restore_policy'):
value = resource['values'][policy]
if value:
policy_values.append((policy,) + value[0].popitem())
assert sorted(policy_values) == [
('boolean_policy', 'enforced', False),
('boolean_policy', 'enforced', True),
('restore_policy', 'default', True)
]
def test_policy_list(plan_runner):
"Test list org policy."
policy_list = (
'{'
'policy-a = {inherit_from_parent = true, suggested_value = null, status = true, values = []}, '
'policy-b = {inherit_from_parent = null, suggested_value = "foo", status = false, values = ["bar"]}, '
'policy-c = {inherit_from_parent = null, suggested_value = true, status = null, values = null}'
'}'
)
_, resources = plan_runner(FIXTURES_DIR, policy_list=policy_list)
assert len(resources) == 4
values = [r['values'] for r in resources if r['type']
== 'google_project_organization_policy']
assert [r['constraint'] for r in values] == [
'policy-a', 'policy-b', 'policy-c'
]
assert values[0]['list_policy'][0]['allow'] == [
{'all': True, 'values': None}]
assert values[1]['list_policy'][0]['deny'] == [
{'all': False, 'values': ["bar"]}]
assert values[2]['restore_policy'] == [{'default': True}]