diff --git a/.changes/v4.0.0/1345-features.md b/.changes/v4.0.0/1345-features.md index 6cb9cec99..dd01512d3 100644 --- a/.changes/v4.0.0/1345-features.md +++ b/.changes/v4.0.0/1345-features.md @@ -1,2 +1,2 @@ -* **New Resource:** `vcd_tm_org` to manage TM Organizations [GH-1345, GH-1351] +* **New Resource:** `vcd_tm_org` to manage TM Organizations [GH-1345, GH-1351, GH-1369] * **New Data Source:** `vcd_tm_org` to read TM Organizations [GH-1345] diff --git a/.github/workflows/check-security.yml b/.github/workflows/check-security.yml index 21c5f6217..b5cf3a7de 100644 --- a/.github/workflows/check-security.yml +++ b/.github/workflows/check-security.yml @@ -1,5 +1,13 @@ name: Run Gosec -on: [push, pull_request] +on: + push: + paths-ignore: + - '**.md' + - 'website/**' + pull_request: + paths-ignore: + - '**.md' + - 'website/**' jobs: gosec: runs-on: ubuntu-latest diff --git a/vcd/resource_vcd_tm_org.go b/vcd/resource_vcd_tm_org.go index f6548e670..d30a7c197 100644 --- a/vcd/resource_vcd_tm_org.go +++ b/vcd/resource_vcd_tm_org.go @@ -50,6 +50,12 @@ func resourceVcdTmOrg() *schema.Resource { Optional: true, Description: fmt.Sprintf("Enables this organization to manage other %ss", labelTmOrg), }, + "is_classic_tenant": { + Type: schema.TypeBool, + Optional: true, + ForceNew: true, // Cannot be changed once created + Description: fmt.Sprintf("Defines whether the %s is a classic VRA-style tenant", labelTmOrg), + }, "managed_by_id": { Type: schema.TypeString, Computed: true, @@ -100,11 +106,6 @@ func resourceVcdTmOrg() *schema.Resource { Computed: true, Description: fmt.Sprintf("Number of directly managed %ss", labelTmOrg), }, - "is_classic_tenant": { - Type: schema.TypeBool, - Computed: true, - Description: fmt.Sprintf("Defines whether the %s is a classic VRA-style tenant", labelTmOrg), - }, }, } } @@ -160,7 +161,7 @@ func resourceVcdTmOrgDelete(ctx context.Context, d *schema.ResourceData, meta in return deleteResource(ctx, d, meta, c) } -// disableTmOrg disables Org which is usefull before deletion as a non-disabled Org cannot be +// disableTmOrg disables Org which is useful before deletion as a non-disabled Org cannot be // removed func disableTmOrg(t *govcd.TmOrg) error { if t.TmOrg.IsEnabled { @@ -204,11 +205,12 @@ func resourceVcdTmOrgImport(ctx context.Context, d *schema.ResourceData, meta in func getTmOrgType(_ *VCDClient, d *schema.ResourceData) (*types.TmOrg, error) { t := &types.TmOrg{ - Name: d.Get("name").(string), - DisplayName: d.Get("display_name").(string), - Description: d.Get("description").(string), - IsEnabled: d.Get("is_enabled").(bool), - CanManageOrgs: d.Get("is_subprovider").(bool), + Name: d.Get("name").(string), + DisplayName: d.Get("display_name").(string), + Description: d.Get("description").(string), + IsEnabled: d.Get("is_enabled").(bool), + CanManageOrgs: d.Get("is_subprovider").(bool), + IsClassicTenant: d.Get("is_classic_tenant").(bool), } return t, nil diff --git a/vcd/resource_vcd_tm_org_test.go b/vcd/resource_vcd_tm_org_test.go index 9e968ca94..787c1d116 100644 --- a/vcd/resource_vcd_tm_org_test.go +++ b/vcd/resource_vcd_tm_org_test.go @@ -48,6 +48,7 @@ func TestAccVcdTmOrg(t *testing.T) { resource.TestCheckResourceAttr("vcd_tm_org.test", "is_subprovider", "false"), resource.TestMatchResourceAttr("vcd_tm_org.test", "managed_by_id", regexp.MustCompile("^urn:vcloud:org:")), resource.TestCheckResourceAttr("vcd_tm_org.test", "managed_by_name", "System"), + resource.TestCheckResourceAttr("vcd_tm_org.test", "is_classic_tenant", "false"), ), }, { @@ -60,6 +61,7 @@ func TestAccVcdTmOrg(t *testing.T) { resource.TestCheckResourceAttr("vcd_tm_org.test", "is_subprovider", "false"), resource.TestMatchResourceAttr("vcd_tm_org.test", "managed_by_id", regexp.MustCompile("^urn:vcloud:org:")), resource.TestCheckResourceAttr("vcd_tm_org.test", "managed_by_name", "System"), + resource.TestCheckResourceAttr("vcd_tm_org.test", "is_classic_tenant", "false"), ), }, { @@ -141,6 +143,7 @@ func TestAccVcdTmOrgSubProvider(t *testing.T) { resource.TestCheckResourceAttr("vcd_tm_org.test", "is_subprovider", "true"), resource.TestMatchResourceAttr("vcd_tm_org.test", "managed_by_id", regexp.MustCompile("^urn:vcloud:org:")), resource.TestCheckResourceAttr("vcd_tm_org.test", "managed_by_name", "System"), + resource.TestCheckResourceAttr("vcd_tm_org.test", "is_classic_tenant", "false"), ), }, { @@ -176,3 +179,85 @@ data "vcd_tm_org" "test" { name = vcd_tm_org.test.name } ` + +// TestAccVcdTmOrgClassicTenant tests a Tenant Manager Organization configured as "Classic Tenant" +func TestAccVcdTmOrgClassicTenant(t *testing.T) { + preTestChecks(t) + + skipIfNotSysAdmin(t) + skipIfNotTm(t) + + var params = StringMap{ + "Testname": t.Name(), + "Tags": "tm", + } + testParamsNotEmpty(t, params) + + configText1 := templateFill(testAccVcdTmOrgClassicStep1, params) + params["FuncName"] = t.Name() + "-step2" + configText2 := templateFill(testAccVcdTmOrgClassicStep2, params) + + debugPrintf("#[DEBUG] CONFIGURATION step1: %s\n", configText1) + debugPrintf("#[DEBUG] CONFIGURATION step2: %s\n", configText2) + if vcdShortTest { + t.Skip(acceptanceTestsSkipped) + return + } + + resource.Test(t, resource.TestCase{ + ProviderFactories: testAccProviders, + Steps: []resource.TestStep{ + { + Config: configText1, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("vcd_tm_org.test", "name", t.Name()), + resource.TestCheckResourceAttr("vcd_tm_org.test", "display_name", "terraform-test"), + resource.TestCheckResourceAttr("vcd_tm_org.test", "description", "terraform test"), + resource.TestCheckResourceAttr("vcd_tm_org.test", "is_enabled", "true"), + resource.TestCheckResourceAttr("vcd_tm_org.test", "is_subprovider", "false"), + resource.TestMatchResourceAttr("vcd_tm_org.test", "managed_by_id", regexp.MustCompile("^urn:vcloud:org:")), + resource.TestCheckResourceAttr("vcd_tm_org.test", "managed_by_name", "System"), + resource.TestCheckResourceAttr("vcd_tm_org.test", "is_classic_tenant", "true"), + ), + }, + { + Config: configText2, + Check: resource.ComposeTestCheckFunc( + resourceFieldsEqual("vcd_tm_org.test", "data.vcd_tm_org.test", nil), + ), + }, + { + ResourceName: "vcd_tm_org.test", + ImportState: true, + ImportStateVerify: true, + ImportStateId: params["Testname"].(string), + }, + }, + }) + + postTestChecks(t) +} + +const testAccVcdTmOrgClassicStep1 = ` +resource "vcd_tm_org" "test" { + name = "{{.Testname}}" + display_name = "terraform-test" + description = "terraform test" + is_enabled = true + is_classic_tenant = true +} + +resource "vcd_tm_org" "test2" { + name = "{{.Testname}}2" + display_name = "terraform-test" + description = "terraform test" + is_enabled = true + is_classic_tenant = true +} +` + +const testAccVcdTmOrgClassicStep2 = testAccVcdTmOrgClassicStep1 + ` +data "vcd_tm_org" "test" { + name = vcd_tm_org.test.name +} +` diff --git a/website/docs/r/tm_org.html.markdown b/website/docs/r/tm_org.html.markdown index 96c6fc5e2..b7587cd2e 100644 --- a/website/docs/r/tm_org.html.markdown +++ b/website/docs/r/tm_org.html.markdown @@ -40,21 +40,23 @@ The following arguments are supported: * `name` - (Required) A name for Organization with which users log in to it as it will be used in the URL. The Org must be disabled to or transition from previous disabled state (`is_enabled=false`) to change a name because it changes tenant login URL -* `display_name` - (Required) A human readable name for Organization +* `display_name` - (Required) A human-readable name for Organization * `description` - (Optional) An optional description for Organization * `is_enabled` - (Optional) Defines if Organization is enabled. Default `true`. **Note:** Organization has to be disabled before removal and this resource will automatically disable it if the resource is destroyed. * `is_subprovider` - (Optional) Enables this Organization to manage other Organizations. **Note**: - This value cannot updated as there may be any number of Rights Bundles granting sub-provider rights + This value cannot be updated as there may be any number of Rights Bundles granting sub-provider rights to this Org. Instead, unpublish any rights bundles that have the `Org Traverse` right from this Org. This can be toggled to true to automatically perform the following steps: - * Publish the Default Sub-Provider Entitlement Rights Bundle to the Organization - * Publish the Sub-Provider Administrator global role (if it exists) to the Organization - * Create a Default Rights Bundle in the Organization containing all publishable rights that are - currently published to the Organization and mark that Rights Bundle as publish all. - * Clone all default roles currently published to the Organization into Global Roles in the - Organization and marks them all publish all. + * Publish the Default Sub-Provider Entitlement Rights Bundle to the Organization + * Publish the Sub-Provider Administrator global role (if it exists) to the Organization + * Create a Default Rights Bundle in the Organization containing all publishable rights that are + currently published to the Organization and mark that Rights Bundle as publish all. + * Clone all default roles currently published to the Organization into Global Roles in the + Organization and marks them all publish all. +* `is_classic_tenant` - (Optional) Defines if this Organization is a classic VRA style tenant. Defaults to `false`. Cannot be + changed after creation (changing it will force the re-creation of the Organization) ## Attribute Reference @@ -70,7 +72,6 @@ The following attributes are exported on this resource: * `disk_count` - Number of disks belonging to this Organization * `can_publish` - Defines if this Organization can publish catalogs externally * `directly_managed_org_count` - Number of directly managed Organizations -* `is_classic_tenant` - Defines if this Organization is a classic VRA style tenant ## Importing