Skip to content

Commit

Permalink
Merge pull request #11 from mineiros-io/waxb/update-module
Browse files Browse the repository at this point in the history
feat: add support for computed members and condition
  • Loading branch information
waxb authored Nov 14, 2022
2 parents 4e25a8b + 68ab973 commit 05d1bd8
Show file tree
Hide file tree
Showing 6 changed files with 199 additions and 17 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Added
- Add support for `computed_members_map`
- Add support for `iam.condition`

### Removed

- BREAKING CHANGE: remove output `module_enabled`
Expand Down
28 changes: 26 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ See [variables.tf] and [examples/] for details and use-cases.
Example:

```hcl
secondary_ip_range {
secondary_ip_range = {
range_name = "tf-test-secondary-range-update1"
ip_cidr_range = "192.168.10.0/24"
}
Expand All @@ -135,7 +135,7 @@ See [variables.tf] and [examples/] for details and use-cases.
Example:

```hcl
log_config {
log_config = {
aggregation_interval = "INTERVAL_10_MIN"
flow_sampling = 0.5
metadata = "INCLUDE_ALL_METADATA"
Expand Down Expand Up @@ -196,19 +196,43 @@ See [variables.tf] and [examples/] for details and use-cases.
- `projectOwner:projectid`: Owners of the given project. For example, `projectOwner:my-example-project`
- `projectEditor:projectid`: Editors of the given project. For example, `projectEditor:my-example-project`
- `projectViewer:projectid`: Viewers of the given project. For example, `projectViewer:my-example-project`
- `computed:{identifier}`: An existing key from `var.computed_members_map`.

Default is `[]`.

- [**`role`**](#attr-iam-role): *(Optional `string`)*<a name="attr-iam-role"></a>

The role that should be applied. Note that custom roles must be of the format `[projects|organizations]/{parent-name}/roles/{role-name}`.

- [**`roles`**](#attr-iam-roles): *(Optional `list(string)`)*<a name="attr-iam-roles"></a>

The set of roles that should be applied. Note that custom roles must be of the format `[projects|organizations]/{parent-name}/roles/{role-name}`.

- [**`authoritative`**](#attr-iam-authoritative): *(Optional `bool`)*<a name="attr-iam-authoritative"></a>

Whether to exclusively set (authoritative mode) or add (non-authoritative/additive mode) members to the role.

Default is `true`.

- [**`condition`**](#attr-iam-condition): *(Optional `object(condition)`)*<a name="attr-iam-condition"></a>

An IAM Condition for a given binding.

Example:

```hcl
condition = {
expression = "request.time < timestamp(\"2022-01-01T00:00:00Z\")"
title = "expires_after_2021_12_31"
}
```
- [**`computed_members_map`**](#var-computed_members_map): *(Optional `map(string)`)*<a name="var-computed_members_map"></a>
A map of members to replace in `members` of various IAM settings to handle terraform computed values.
Default is `{}`.
- [**`policy_bindings`**](#var-policy_bindings): *(Optional `list(policy_binding)`)*<a name="var-policy_bindings"></a>
A list of IAM policy bindings.
Expand Down
33 changes: 31 additions & 2 deletions README.tfdoc.hcl
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ section {
An array of configurations for secondary IP ranges for VM instances contained in this subnetwork. The primary IP of such VM must belong to the primary ipCidrRange of the subnetwork. The alias IPs may belong to either primary or secondary ranges.
END
readme_example = <<-END
secondary_ip_range {
secondary_ip_range = {
range_name = "tf-test-secondary-range-update1"
ip_cidr_range = "192.168.10.0/24"
}
Expand Down Expand Up @@ -185,7 +185,7 @@ section {
END

readme_example = <<-END
log_config {
log_config = {
aggregation_interval = "INTERVAL_10_MIN"
flow_sampling = 0.5
metadata = "INCLUDE_ALL_METADATA"
Expand Down Expand Up @@ -261,6 +261,7 @@ section {
- `projectOwner:projectid`: Owners of the given project. For example, `projectOwner:my-example-project`
- `projectEditor:projectid`: Editors of the given project. For example, `projectEditor:my-example-project`
- `projectViewer:projectid`: Viewers of the given project. For example, `projectViewer:my-example-project`
- `computed:{identifier}`: An existing key from `var.computed_members_map`.
END
}

Expand All @@ -271,13 +272,41 @@ section {
END
}

attribute "roles" {
type = list(string)
description = <<-END
The set of roles that should be applied. Note that custom roles must be of the format `[projects|organizations]/{parent-name}/roles/{role-name}`.
END
}

attribute "authoritative" {
type = bool
default = true
description = <<-END
Whether to exclusively set (authoritative mode) or add (non-authoritative/additive mode) members to the role.
END
}

attribute "condition" {
type = object(condition)
description = <<-END
An IAM Condition for a given binding.
END
readme_example = <<-END
condition = {
expression = "request.time < timestamp(\"2022-01-01T00:00:00Z\")"
title = "expires_after_2021_12_31"
}
END
}
}

variable "computed_members_map" {
type = map(string)
description = <<-END
A map of members to replace in `members` of various IAM settings to handle terraform computed values.
END
default = {}
}

variable "policy_bindings" {
Expand Down
36 changes: 27 additions & 9 deletions iam.tf
Original file line number Diff line number Diff line change
@@ -1,29 +1,47 @@
locals {
iam_map = { for iam in var.iam : iam.role => iam }
# filter all objects that define a single role
iam_role = [for iam in var.iam : iam if can(iam.role)]

# filter all objects that define multiple roles and expand them to single roles
iam_roles = flatten([for iam in var.iam :
[for role in iam.roles : merge(iam, { role = role })] if can(iam.roles)
])

iam = concat(local.iam_role, local.iam_roles)

iam_map = { for idx, iam in local.iam :
try(iam._key, "${iam.role}/${iam.condition._key}", "${iam.role}/${md5(jsonencode(iam.condition))}", iam.role) => idx
}
}

module "iam" {
source = "github.com/mineiros-io/terraform-google-subnetwork-iam.git?ref=v0.0.1"
source = "github.com/mineiros-io/terraform-google-subnetwork-iam.git?ref=v0.1.0"

for_each = var.policy_bindings == null ? local.iam_map : {}

module_enabled = var.module_enabled
module_depends_on = var.module_depends_on

subnetwork = try(google_compute_subnetwork.subnetwork[0].name, null)
role = each.value.role
members = each.value.members
authoritative = try(each.value.authoritative, true)
subnetwork = try(google_compute_subnetwork.subnetwork[0].name, null)

role = local.iam[each.value].role

members = try(local.iam[each.value].members, [])
computed_members_map = var.computed_members_map

condition = try(local.iam[each.value].condition, null)
authoritative = try(local.iam[each.value].authoritative, true)
}

module "policy_bindings" {
source = "github.com/mineiros-io/terraform-google-subnetwork-iam.git?ref=v0.0.1"
source = "github.com/mineiros-io/terraform-google-subnetwork-iam.git?ref=v0.1.0"

count = var.policy_bindings != null ? 1 : 0

module_enabled = var.module_enabled
module_depends_on = var.module_depends_on

subnetwork = try(google_compute_subnetwork.subnetwork[0].name, null)
policy_bindings = var.policy_bindings
subnetwork = try(google_compute_subnetwork.subnetwork[0].name, null)
policy_bindings = var.policy_bindings
computed_members_map = var.computed_members_map
}
96 changes: 96 additions & 0 deletions test/unit-complete/main.tf
Original file line number Diff line number Diff line change
@@ -1,11 +1,84 @@
module "test-sa" {
source = "github.com/mineiros-io/terraform-google-service-account?ref=v0.0.12"

account_id = "service-account-id-${local.random_suffix}"
}

module "test" {
source = "../.."

module_enabled = true
project = local.project_id

# add all required arguments
network = "projects/test-project/global/networks/test-network"
name = "test-subnetwork"
description = "unit-complete"
ip_cidr_range = "10.2.0.0/16"
region = "us-central1"
secondary_ip_ranges = [
{
range_name = "kubernetes-pods"
ip_cidr_range = "10.10.0.0/20"
}
]

private_ip_google_access = false

# add all optional arguments that create additional resources

# add most/all other optional arguments

# module_tags = {
# Environment = "unknown"
# }

log_config = {
aggregation_interval = "INTERVAL_10_MIN"
flow_sampling = 0.5
metadata = "CUSTOM_METADATA"
metadata_fields = ["field0"]
filter_expr = true
}

iam = [
{
role = "roles/browser"
members = ["domain:example-domain"]
condition = {
title = "expires_after_2021_12_31"
description = "Expiring at midnight of 2021-12-31"
expression = "request.time < timestamp(\"2022-01-01T00:00:00Z\")"
}
},
{
role = "roles/viewer"
members = ["domain:example-domain"]
authoritative = false
},
{
roles = ["roles/editor", "roles/browser"]
members = ["computed:computed_sa"]
}
]

computed_members_map = {
myserviceaccount = "serviceAccount:${module.test-sa.service_account.email}"
}

module_depends_on = ["nothing"]
}

module "test2" {
source = "../.."

module_enabled = true
project = local.project_id

# add all required arguments
network = "projects/test-project/global/networks/test-network"
name = "test-subnetwork"
description = "unit-complete"
ip_cidr_range = "10.2.0.0/16"
region = "us-central1"
secondary_ip_ranges = [
Expand All @@ -15,6 +88,8 @@ module "test" {
}
]

private_ip_google_access = false

# add all optional arguments that create additional resources

# add most/all other optional arguments
Expand All @@ -23,5 +98,26 @@ module "test" {
# Environment = "unknown"
# }

log_config = {
aggregation_interval = "INTERVAL_10_MIN"
flow_sampling = 0.5
metadata = "INCLUDE_ALL_METADATA"
filter_expr = true
}

policy_bindings = [{
role = "roles/storage.admin"
members = ["user:[email protected]"]
condition = {
title = "expires_after_2021_12_31"
description = "Expiring at midnight of 2021-12-31"
expression = "request.time < timestamp(\"2022-01-01T00:00:00Z\")"
}
}]

computed_members_map = {
myserviceaccount = "serviceAccount:${module.test-sa.service_account.email}"
}

module_depends_on = ["nothing"]
}
19 changes: 15 additions & 4 deletions variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -67,14 +67,14 @@ variable "iam" {

# validate required keys in each object
validation {
condition = alltrue([for x in var.iam : length(setintersection(keys(x), ["role", "members"])) == 2])
error_message = "Each object in var.iam must specify a role and a set of members."
condition = alltrue([for x in var.iam : length(setintersection(keys(x), ["role", "roles", "members"])) == 2])
error_message = "Each object in var.iam must specify a role or roles and a set of members."
}

# validate no invalid keys are in each object
validation {
condition = alltrue([for x in var.iam : length(setsubtract(keys(x), ["role", "members", "authoritative"])) == 0])
error_message = "Each object in var.iam does only support role, members and authoritative attributes."
condition = alltrue([for x in var.iam : length(setsubtract(keys(x), ["role", "roles", "members", "condition", "authoritative"])) == 0])
error_message = "Each object in var.iam does only support role, roles, members, condition and authoritative attributes."
}
}

Expand All @@ -96,6 +96,17 @@ variable "policy_bindings" {
}
}

variable "computed_members_map" {
type = map(string)
description = "(Optional) A map of members to replace in 'members' to handle terraform computed values. Will be ignored when policy bindings are used."
default = {}

validation {
condition = alltrue([for k, v in var.computed_members_map : can(regex("^(allUsers|allAuthenticatedUsers|(user|serviceAccount|group|domain|projectOwner|projectEditor|projectViewer):)", v))])
error_message = "The value must be a non-empty list of strings where each entry is a valid principal type identified with `user:`, `serviceAccount:`, `group:`, `domain:`, `projectOwner:`, `projectEditor:` or `projectViewer:`."
}
}

# ------------------------------------------------------------------------------
# MODULE CONFIGURATION PARAMETERS
# These variables are used to configure the module.
Expand Down

0 comments on commit 05d1bd8

Please sign in to comment.