Skip to content

Commit

Permalink
feat: Use for_each instead of count to create resource (#15)
Browse files Browse the repository at this point in the history
BREAKING CHANGE: Service accounts were changed to use for_each, causing a state migration. See the [upgrade guide](./docs/upgrading_to_v2.0.md) for details.
  • Loading branch information
adrian-gierakowski authored Jan 31, 2020
1 parent 191ef33 commit 19c8a02
Show file tree
Hide file tree
Showing 9 changed files with 75 additions and 61 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ and this project adheres to

## [Unreleased]

### Added

- Use `for_each` instead of `count` [#15]

## [2.0.2] - 2019-10-09

### Fixed
Expand Down Expand Up @@ -58,6 +62,7 @@ and this project adheres to
[0.1.1]: https://github.com/terraform-google-modules/terraform-google-service-accounts/compare/v0.1.0...v0.1.1
[0.1.0]: https://github.com/terraform-google-modules/terraform-google-service-accounts/releases/tag/v0.1.0

[#15]: https://github.com/terraform-google-modules/terraform-google-service-accounts/pull/15
[#14]: https://github.com/terraform-google-modules/terraform-google-service-accounts/pull/14
[#13]: https://github.com/terraform-google-modules/terraform-google-service-accounts/pull/13
[#9]: https://github.com/terraform-google-modules/terraform-google-service-accounts/pull/9
Expand Down
11 changes: 6 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,15 +57,16 @@ Functional examples are included in the
| Name | Description |
|------|-------------|
| email | Service account email (for single use). |
| emails | Service account emails. |
| emails\_list | Service account emails. |
| emails | Service account emails by name. |
| emails\_list | Service account emails as list. |
| iam\_email | IAM-format service account email (for single use). |
| iam\_emails | IAM-format service account emails. |
| iam\_emails\_list | IAM-format service account emails. |
| iam\_emails | IAM-format service account emails by name. |
| iam\_emails\_list | IAM-format service account emails as list. |
| key | Service account key (for single use). |
| keys | Map of service account keys. |
| service\_account | Service account resource (for single use). |
| service\_accounts | Service account resources. |
| service\_accounts | Service account resources as list. |
| service\_accounts\_map | Service account resources by name. |

<!-- END OF PRE-COMMIT-TERRAFORM DOCS HOOK -->

Expand Down
2 changes: 1 addition & 1 deletion examples/multiple_service_accounts/versions.tf
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,5 @@
*/

terraform {
required_version = ">= 0.12"
required_version = ">= 0.12.6"
}
2 changes: 1 addition & 1 deletion examples/single_service_account/versions.tf
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,5 @@
*/

terraform {
required_version = ">= 0.12"
required_version = ">= 0.12.6"
}
70 changes: 38 additions & 32 deletions main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -15,84 +15,90 @@
*/

locals {
account_billing = var.grant_billing_role && var.billing_account_id != ""
org_billing = var.grant_billing_role && var.billing_account_id == "" && var.org_id != ""
prefix = var.prefix != "" ? "${var.prefix}-" : ""
xpn = var.grant_xpn_roles && var.org_id != ""
emails = [for account in google_service_account.service_accounts : account.email]
iam_emails = [for email in local.emails : "serviceAccount:${email}"]
account_billing = var.grant_billing_role && var.billing_account_id != ""
org_billing = var.grant_billing_role && var.billing_account_id == "" && var.org_id != ""
prefix = var.prefix != "" ? "${var.prefix}-" : ""
xpn = var.grant_xpn_roles && var.org_id != ""
service_accounts_list = [for name in var.names : google_service_account.service_accounts[name]]
emails_list = [for account in local.service_accounts_list : account.email]
iam_emails_list = [for email in local.emails_list : "serviceAccount:${email}"]
names = toset(var.names)
name_role_pairs = setproduct(local.names, toset(var.project_roles))
project_roles_map_data = zipmap(
[for pair in local.name_role_pairs : "${pair[0]}-${pair[1]}"],
[for pair in local.name_role_pairs : {
name = pair[0]
role = pair[1]
}]
)
}

# create service accounts
resource "google_service_account" "service_accounts" {
count = length(var.names)
account_id = "${local.prefix}${lower(element(var.names, count.index))}"
for_each = local.names
account_id = "${local.prefix}${lower(each.value)}"
display_name = "Terraform-managed service account"
project = var.project_id
}

# common roles
resource "google_project_iam_member" "project-roles" {
count = length(var.project_roles) * length(var.names)
for_each = local.project_roles_map_data

project = element(
split(
"=>",
element(var.project_roles, count.index % length(var.project_roles)),
each.value.role
),
0,
)

role = element(
split(
"=>",
element(var.project_roles, count.index % length(var.project_roles)),
each.value.role
),
1,
)

member = "serviceAccount:${element(
google_service_account.service_accounts.*.email,
floor(count.index / length(var.project_roles)),
)}"
member = "serviceAccount:${google_service_account.service_accounts[each.value.name].email}"
}

# conditionally assign billing user role at the org level
resource "google_organization_iam_member" "billing_user" {
count = local.org_billing ? length(var.names) : 0
org_id = var.org_id
role = "roles/billing.user"
member = "serviceAccount:${google_service_account.service_accounts[count.index].email}"
for_each = local.org_billing ? local.names : toset([])
org_id = var.org_id
role = "roles/billing.user"
member = "serviceAccount:${google_service_account.service_accounts[each.value].email}"
}

# conditionally assign billing user role on a specific billing account
resource "google_billing_account_iam_member" "billing_user" {
count = local.account_billing ? length(var.names) : 0
for_each = local.account_billing ? local.names : toset([])
billing_account_id = var.billing_account_id
role = "roles/billing.user"
member = "serviceAccount:${google_service_account.service_accounts[count.index].email}"
member = "serviceAccount:${google_service_account.service_accounts[each.value].email}"
}

# conditionally assign roles for shared VPC
# ref: https://cloud.google.com/vpc/docs/shared-vpc

resource "google_organization_iam_member" "xpn_admin" {
count = local.xpn ? length(var.names) : 0
org_id = var.org_id
role = "roles/compute.xpnAdmin"
member = "serviceAccount:${google_service_account.service_accounts[count.index].email}"
for_each = local.xpn ? local.names : toset([])
org_id = var.org_id
role = "roles/compute.xpnAdmin"
member = "serviceAccount:${google_service_account.service_accounts[each.value].email}"
}

resource "google_organization_iam_member" "organization_viewer" {
count = local.xpn ? length(var.names) : 0
org_id = var.org_id
role = "roles/resourcemanager.organizationViewer"
member = "serviceAccount:${google_service_account.service_accounts[count.index].email}"
for_each = local.xpn ? local.names : toset([])
org_id = var.org_id
role = "roles/resourcemanager.organizationViewer"
member = "serviceAccount:${google_service_account.service_accounts[each.value].email}"
}

# keys
resource "google_service_account_key" "keys" {
count = var.generate_keys ? length(var.names) : 0
service_account_id = google_service_account.service_accounts[count.index].email
for_each = var.generate_keys ? local.names : toset([])
service_account_id = google_service_account.service_accounts[each.value].email
}

40 changes: 21 additions & 19 deletions outputs.tf
Original file line number Diff line number Diff line change
Expand Up @@ -16,63 +16,65 @@

output "service_account" {
description = "Service account resource (for single use)."
value = google_service_account.service_accounts[0]
value = local.service_accounts_list[0]
}

output "email" {
description = "Service account email (for single use)."
value = google_service_account.service_accounts[0].email
value = local.emails_list[0]
}

output "iam_email" {
description = "IAM-format service account email (for single use)."
value = "serviceAccount:${google_service_account.service_accounts[0].email}"
value = local.iam_emails_list[0]
}

output "key" {
description = "Service account key (for single use)."
value = data.template_file.keys[0].rendered
value = data.template_file.keys[var.names[0]]
}

output "service_accounts" {
description = "Service account resources."
description = "Service account resources as list."
value = local.service_accounts_list
}

output "service_accounts_map" {
description = "Service account resources by name."
value = google_service_account.service_accounts
}

output "emails" {
description = "Service account emails."
value = zipmap(var.names, slice(local.emails, 0, length(var.names)))
description = "Service account emails by name."
value = zipmap(var.names, local.emails_list)
}

output "iam_emails" {
description = "IAM-format service account emails."
value = zipmap(var.names, slice(local.iam_emails, 0, length(var.names)))
description = "IAM-format service account emails by name."
value = zipmap(var.names, local.iam_emails_list)
}

output "emails_list" {
description = "Service account emails."
value = local.emails
description = "Service account emails as list."
value = local.emails_list
}

output "iam_emails_list" {
description = "IAM-format service account emails."
value = local.iam_emails
description = "IAM-format service account emails as list."
value = local.iam_emails_list
}

data "template_file" "keys" {
count = length(var.names)
for_each = local.names
template = "$${key}"

vars = {
key = var.generate_keys ? base64decode(google_service_account_key.keys[count.index].private_key) : ""
key = var.generate_keys ? base64decode(google_service_account_key.keys[each.value].private_key) : ""
}
}

output "keys" {
description = "Map of service account keys."
sensitive = true
value = zipmap(
var.names,
slice(data.template_file.keys[*].rendered, 0, length(var.names))
)
value = { for k, v in data.template_file.keys : k => v.rendered }
}
2 changes: 1 addition & 1 deletion test/fixtures/multiple_service_accounts/versions.tf
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,5 @@
*/

terraform {
required_version = ">= 0.12"
required_version = ">= 0.12.6"
}
2 changes: 1 addition & 1 deletion test/fixtures/single_service_account/versions.tf
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,5 @@
*/

terraform {
required_version = ">= 0.12"
required_version = ">= 0.12.6"
}
2 changes: 1 addition & 1 deletion versions.tf
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,5 @@
*/

terraform {
required_version = ">= 0.12"
required_version = ">= 0.12.6"
}

0 comments on commit 19c8a02

Please sign in to comment.