Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: support optional tag binding #885

Merged
merged 8 commits into from
Mar 1, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,7 @@ determining that location is as follows:
| sa\_role | A role to give the default Service Account for the project (defaults to none) | `string` | `""` | no |
| shared\_vpc\_subnets | List of subnets fully qualified subnet IDs (ie. projects/$project\_id/regions/$region/subnetworks/$subnet\_id) | `list(string)` | `[]` | no |
| svpc\_host\_project\_id | The ID of the host project which hosts the shared VPC | `string` | `""` | no |
| tag\_binding\_values | Tag values to bind the project to. | `list(string)` | `[]` | no |
| usage\_bucket\_name | Name of a GCS bucket to store GCE usage reports in (optional) | `string` | `""` | no |
| usage\_bucket\_prefix | Prefix in the GCS bucket to store GCE usage reports in (optional) | `string` | `""` | no |
| vpc\_service\_control\_attach\_enabled | Whether the project will be attached to a VPC Service Control Perimeter | `bool` | `false` | no |
Expand Down Expand Up @@ -185,6 +186,7 @@ determining that location is as follows:
| service\_account\_id | The id of the default service account |
| service\_account\_name | The fully-qualified name of the default service account |
| service\_account\_unique\_id | The unique id of the default service account |
| tag\_bindings | Tag bindings |

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

Expand Down
20 changes: 20 additions & 0 deletions build/int.cloudbuild.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,26 @@ steps:
- verify-quota-project-example
name: 'gcr.io/cloud-foundation-cicd/$_DOCKER_IMAGE_DEVELOPER_TOOLS:$_DOCKER_TAG_VERSION_DEVELOPER_TOOLS'
args: ['/bin/bash', '-c', 'cft test run TestQuotaProject --stage destroy --verbose']
- id: init-tags-project-example
waitFor:
- create-all
name: 'gcr.io/cloud-foundation-cicd/$_DOCKER_IMAGE_DEVELOPER_TOOLS:$_DOCKER_TAG_VERSION_DEVELOPER_TOOLS'
args: ['/bin/bash', '-c', 'cft test run TestTagsProject --stage init --verbose']
bharathkkb marked this conversation as resolved.
Show resolved Hide resolved
- id: apply-tags-project-example
waitFor:
- init-tags-project-example
name: 'gcr.io/cloud-foundation-cicd/$_DOCKER_IMAGE_DEVELOPER_TOOLS:$_DOCKER_TAG_VERSION_DEVELOPER_TOOLS'
args: ['/bin/bash', '-c', 'cft test run TestTagsProject --stage apply --verbose']
- id: verify-tags-project-example
waitFor:
- apply-tags-project-example
name: 'gcr.io/cloud-foundation-cicd/$_DOCKER_IMAGE_DEVELOPER_TOOLS:$_DOCKER_TAG_VERSION_DEVELOPER_TOOLS'
args: ['/bin/bash', '-c', 'cft test run TestTagsProject --stage verify --verbose']
- id: destroy-tags-project-example
waitFor:
- verify-tags-project-example
name: 'gcr.io/cloud-foundation-cicd/$_DOCKER_IMAGE_DEVELOPER_TOOLS:$_DOCKER_TAG_VERSION_DEVELOPER_TOOLS'
args: ['/bin/bash', '-c', 'cft test run TestTagsProject --stage destroy --verbose']

tags:
- 'ci'
Expand Down
22 changes: 22 additions & 0 deletions examples/tags_project/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Project with tags

This example illustrates how to create a project with a tag binding.

<!-- BEGINNING OF PRE-COMMIT-TERRAFORM DOCS HOOK -->
## Inputs

| Name | Description | Type | Default | Required |
|------|-------------|------|---------|:--------:|
| billing\_account | The ID of the billing account to associate this project with | `any` | n/a | yes |
| folder\_id | The ID of a folder to host this project. | `string` | `""` | no |
| organization\_id | The organization id for the associated services | `string` | `"684124036889"` | no |
| tag\_value | value | `string` | n/a | yes |

## Outputs

| Name | Description |
|------|-------------|
| project\_id | The ID of the created project |
| project\_num | The number of the created project |

<!-- END OF PRE-COMMIT-TERRAFORM DOCS HOOK -->
28 changes: 28 additions & 0 deletions examples/tags_project/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/**
* Copyright 2024 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.
*/

module "project-factory" {
source = "terraform-google-modules/project-factory/google"
version = "~> 14.0"

random_project_id = true
name = "simple-tag-project"
org_id = var.organization_id
folder_id = var.folder_id
billing_account = var.billing_account
default_service_account = "deprivilege"
tag_binding_values = [var.tag_value]
}
26 changes: 26 additions & 0 deletions examples/tags_project/outputs.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/**
* Copyright 2024 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.
*/

output "project_id" {
value = module.project-factory.project_id
description = "The ID of the created project"
}

output "project_num" {
value = module.project-factory.project_number
description = "The number of the created project"
}

35 changes: 35 additions & 0 deletions examples/tags_project/variables.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/**
* Copyright 2024 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.
*/

variable "organization_id" {
description = "The organization id for the associated services"
default = "684124036889"
}

variable "folder_id" {
description = "The ID of a folder to host this project."
type = string
default = ""
bharathkkb marked this conversation as resolved.
Show resolved Hide resolved
}

variable "billing_account" {
description = "The ID of the billing account to associate this project with"
}

variable "tag_value" {
description = "value"
type = string
}
1 change: 1 addition & 0 deletions main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ module "project-factory" {
vpc_service_control_perimeter_name = var.vpc_service_control_perimeter_name
vpc_service_control_sleep_duration = var.vpc_service_control_sleep_duration
default_network_tier = var.default_network_tier
tag_binding_values = var.tag_binding_values
}

/******************************************
Expand Down
6 changes: 6 additions & 0 deletions modules/core_project_factory/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -371,3 +371,9 @@ resource "google_compute_project_default_network_tier" "default" {
project = google_project.main.number
network_tier = var.default_network_tier
}

resource "google_tags_tag_binding" "bindings" {
for_each = toset(var.tag_binding_values)
parent = "//cloudresourcemanager.googleapis.com/projects/${google_project.main.number}"
tag_value = "tagValues/${each.value}"
}
5 changes: 5 additions & 0 deletions modules/core_project_factory/outputs.tf
Original file line number Diff line number Diff line change
Expand Up @@ -95,3 +95,8 @@ output "enabled_api_identities" {
description = "Enabled API identities in the project"
value = module.project_services.enabled_api_identities
}

output "tag_bindings" {
description = "Tag bindings"
value = google_tags_tag_binding.bindings
}
6 changes: 6 additions & 0 deletions modules/core_project_factory/variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -258,3 +258,9 @@ variable "grant_network_role" {
type = bool
default = true
}

variable "tag_binding_values" {
description = "Tag values to bind the project to."
type = list(string)
bharathkkb marked this conversation as resolved.
Show resolved Hide resolved
default = []
}
4 changes: 2 additions & 2 deletions modules/core_project_factory/versions.tf
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,11 @@ terraform {
required_providers {
google = {
source = "hashicorp/google"
version = ">= 3.50, < 6"
version = ">= 3.64, < 6"
g-awmalik marked this conversation as resolved.
Show resolved Hide resolved
}
google-beta = {
source = "hashicorp/google-beta"
version = ">= 3.50, < 6"
version = ">= 3.64, < 6"
}
null = {
source = "hashicorp/null"
Expand Down
5 changes: 5 additions & 0 deletions outputs.tf
Original file line number Diff line number Diff line change
Expand Up @@ -98,3 +98,8 @@ output "budget_name" {
value = module.budget.name
description = "The name of the budget if created"
}

output "tag_bindings" {
description = "Tag bindings"
value = module.project-factory.tag_bindings
}
43 changes: 43 additions & 0 deletions test/integration/tags_project/tags_project_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// Copyright 2024 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.

package tags_project

import (
"fmt"
"testing"

"github.com/GoogleCloudPlatform/cloud-foundation-toolkit/infra/blueprint-test/pkg/gcloud"
"github.com/GoogleCloudPlatform/cloud-foundation-toolkit/infra/blueprint-test/pkg/tft"
"github.com/GoogleCloudPlatform/cloud-foundation-toolkit/infra/blueprint-test/pkg/utils"
"github.com/stretchr/testify/assert"
)

func TestTagsProject(t *testing.T) {
tagsProjectT := tft.NewTFBlueprintTest(t)
tagsProjectT.DefineVerify(func(assert *assert.Assertions) {
tagsProjectT.DefaultVerify(assert)

projectNum := tagsProjectT.GetStringOutput("project_num")
tagValue := tagsProjectT.GetTFSetupStringOutput("tag_value")

parent := fmt.Sprintf("//cloudresourcemanager.googleapis.com/projects/%s", projectNum)
projBindings := gcloud.Runf(t, "resource-manager tags bindings list --parent=%s", parent).Array()
assert.Len(projBindings, 1, "expected one binding")

binding := utils.GetFirstMatchResult(t, projBindings, "parent", parent)
assert.Equalf(fmt.Sprintf("tagValues/%s", tagValue), binding.Get("tagValue").String(), "expected binding to %s", tagValue)
})
tagsProjectT.Test()
}
4 changes: 4 additions & 0 deletions test/setup/iam.tf
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ locals {
int_required_org_roles = [
"roles/accesscontextmanager.policyAdmin",
"roles/resourcemanager.organizationViewer",
# CRUD tags.
"roles/resourcemanager.tagAdmin",
# Binding tags to resources.
"roles/resourcemanager.tagUser"
]
}

Expand Down
4 changes: 4 additions & 0 deletions test/setup/outputs.tf
Original file line number Diff line number Diff line change
Expand Up @@ -58,3 +58,7 @@ output "group_name" {
output "service_account_email" {
value = google_service_account.int_test.email
}

output "tag_value" {
value = google_tags_tag_value.value.name
}
33 changes: 33 additions & 0 deletions test/setup/tags.tf
bharathkkb marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/**
* Copyright 2024 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.
*/

resource "random_string" "key_suffix" {
length = 7
special = false
upper = false
}

resource "google_tags_tag_key" "key" {
parent = "organizations/${var.org_id}"
short_name = "pf-key-${random_string.key_suffix.result}"
description = "Sample tag key"
}

resource "google_tags_tag_value" "value" {
parent = "tagKeys/${google_tags_tag_key.key.name}"
short_name = "sample-val"
description = "Sample val"
}
6 changes: 6 additions & 0 deletions variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -347,3 +347,9 @@ variable "language_tag" {
type = string
default = "en-US"
}

variable "tag_binding_values" {
description = "Tag values to bind the project to."
type = list(string)
default = []
}
Loading