Skip to content

Commit a00b159

Browse files
Add conflict between project parent kinds, allow migrating between them (#5323) (#10373)
Signed-off-by: Modular Magician <[email protected]>
1 parent bb60bf2 commit a00b159

File tree

4 files changed

+105
-11
lines changed

4 files changed

+105
-11
lines changed

.changelog/5323.txt

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
```release-note:breaking-change
2+
resourcemanager: added conflict between `org_id`, `folder_id` at plan time in `google_project`
3+
```
4+
```release-note:breaking-change
5+
resourcemanager: `org_id`, `folder_id` are unset when removed from config in `google_project`
6+
```

google/resource_google_project.go

+9-9
Original file line numberDiff line numberDiff line change
@@ -68,17 +68,17 @@ func resourceGoogleProject() *schema.Resource {
6868
Description: `The display name of the project.`,
6969
},
7070
"org_id": {
71-
Type: schema.TypeString,
72-
Optional: true,
73-
Computed: true,
74-
Description: `The numeric ID of the organization this project belongs to. Changing this forces a new project to be created. Only one of org_id or folder_id may be specified. If the org_id is specified then the project is created at the top level. Changing this forces the project to be migrated to the newly specified organization.`,
71+
Type: schema.TypeString,
72+
Optional: true,
73+
ConflictsWith: []string{"folder_id"},
74+
Description: `The numeric ID of the organization this project belongs to. Changing this forces a new project to be created. Only one of org_id or folder_id may be specified. If the org_id is specified then the project is created at the top level. Changing this forces the project to be migrated to the newly specified organization.`,
7575
},
7676
"folder_id": {
77-
Type: schema.TypeString,
78-
Optional: true,
79-
Computed: true,
80-
StateFunc: parseFolderId,
81-
Description: `The numeric ID of the folder this project should be created under. Only one of org_id or folder_id may be specified. If the folder_id is specified, then the project is created under the specified folder. Changing this forces the project to be migrated to the newly specified folder.`,
77+
Type: schema.TypeString,
78+
Optional: true,
79+
StateFunc: parseFolderId,
80+
ConflictsWith: []string{"org_id"},
81+
Description: `The numeric ID of the folder this project should be created under. Only one of org_id or folder_id may be specified. If the folder_id is specified, then the project is created under the specified folder. Changing this forces the project to be migrated to the newly specified folder.`,
8282
},
8383
"number": {
8484
Type: schema.TypeString,

google/resource_google_project_test.go

+73-2
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,47 @@ func TestAccProject_parentFolder(t *testing.T) {
240240
})
241241
}
242242

243+
func TestAccProject_migrateParent(t *testing.T) {
244+
t.Parallel()
245+
246+
org := getTestOrgFromEnv(t)
247+
pid := fmt.Sprintf("%s-%d", testPrefix, randInt(t))
248+
folderDisplayName := testPrefix + randString(t, 10)
249+
vcrTest(t, resource.TestCase{
250+
PreCheck: func() { testAccPreCheck(t) },
251+
Providers: testAccProviders,
252+
Steps: []resource.TestStep{
253+
{
254+
Config: testAccProject_migrateParentFolder(pid, pname, folderDisplayName, org),
255+
},
256+
{
257+
ResourceName: "google_project.acceptance",
258+
ImportState: true,
259+
ImportStateVerify: true,
260+
ImportStateVerifyIgnore: []string{"skip_delete"},
261+
},
262+
{
263+
Config: testAccProject_migrateParentOrg(pid, pname, folderDisplayName, org),
264+
},
265+
{
266+
ResourceName: "google_project.acceptance",
267+
ImportState: true,
268+
ImportStateVerify: true,
269+
ImportStateVerifyIgnore: []string{"skip_delete"},
270+
},
271+
{
272+
Config: testAccProject_migrateParentFolder(pid, pname, folderDisplayName, org),
273+
},
274+
{
275+
ResourceName: "google_project.acceptance",
276+
ImportState: true,
277+
ImportStateVerify: true,
278+
ImportStateVerifyIgnore: []string{"skip_delete"},
279+
},
280+
},
281+
})
282+
}
283+
243284
func testAccCheckGoogleProjectExists(r, pid string) resource.TestCheckFunc {
244285
return func(s *terraform.State) error {
245286
rs, ok := s.RootModule().Resources[r]
@@ -412,8 +453,6 @@ resource "google_project" "acceptance" {
412453
project_id = "%s"
413454
name = "%s"
414455
415-
# ensures we can set both org_id and folder_id as long as only one is not empty.
416-
org_id = ""
417456
folder_id = google_folder.folder1.id
418457
}
419458
@@ -424,6 +463,38 @@ resource "google_folder" "folder1" {
424463
`, pid, projectName, folderName, org)
425464
}
426465

466+
func testAccProject_migrateParentFolder(pid, projectName, folderName, org string) string {
467+
return fmt.Sprintf(`
468+
resource "google_project" "acceptance" {
469+
project_id = "%s"
470+
name = "%s"
471+
472+
folder_id = google_folder.folder1.id
473+
}
474+
475+
resource "google_folder" "folder1" {
476+
display_name = "%s"
477+
parent = "organizations/%s"
478+
}
479+
`, pid, projectName, folderName, org)
480+
}
481+
482+
func testAccProject_migrateParentOrg(pid, projectName, folderName, org string) string {
483+
return fmt.Sprintf(`
484+
resource "google_project" "acceptance" {
485+
project_id = "%s"
486+
name = "%s"
487+
488+
org_id = "%s"
489+
}
490+
491+
resource "google_folder" "folder1" {
492+
display_name = "%s"
493+
parent = "organizations/%s"
494+
}
495+
`, pid, projectName, org, folderName, org)
496+
}
497+
427498
func skipIfEnvNotSet(t *testing.T, envs ...string) {
428499
if t == nil {
429500
log.Printf("[DEBUG] Not running inside of test - skip skipping")

website/docs/guides/version_4_upgrade.html.markdown

+17
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,9 @@ description: |-
4747
- [`node_config.workload_metadata_config.node_metadata` is now removed](#node_configworkload_metadata_confignode_metadata-is-now-removed)
4848
- [`workload_identity_config.0.identity_namespace` is now removed](#workload_identity_config0identity_namespace-is-now-removed)
4949
- [`pod_security_policy_config` is removed from the GA provider](#pod_security_policy_config-is-removed-from-the-ga-provider)
50+
- [Resource: `google_project`](#resource-google_project)
51+
- [`org_id`, `folder_id` now conflict at plan time](#org_id-folder_id-now-confict-at-plan-time)
52+
- [`org_id`, `folder_id` are unset when removed from config](#org_id-folder_id-are-unset-when-removed-from-config)
5053
- [Resource: `google_project_service`](#resource-google_project_service)
5154
- [`bigquery-json.googleapis.com` is no longer a valid service name](#bigquery-json.googleapis.com-is-no-longer-a-valid-service-name)
5255
- [Resource: `google_data_loss_prevention_trigger`](#resource-google_data_loss_prevention_trigger)
@@ -345,6 +348,20 @@ The provider will now enforce at plan time that one of these fields be set.
345348
### At least one of `patch_config.0.post_step.0.linux_exec_step_config` or `patch_config.0.post_step.0.windows_exec_step_config` is required
346349
The provider will now enforce at plan time that one of these fields be set.
347350

351+
## Resource: `google_project`
352+
353+
### `org_id`, `folder_id` now conflict at plan time
354+
355+
Previously, they were only checked for conflicts at apply time. Terraform will
356+
now report an error at plan time.
357+
358+
### `org_id`, `folder_id` are unset when removed from config
359+
360+
Previously, these fields kept their old value in state when they were removed
361+
from config, changing the value on next refresh. Going forward, removing one of
362+
the values or switching values will generate a correct plan that removes the
363+
value.
364+
348365
## Resource: `google_project_service`
349366

350367
### `bigquery-json.googleapis.com` is no longer a valid service name

0 commit comments

Comments
 (0)