Skip to content

Commit

Permalink
data/resource: azurerm_management_group - now exports tenant_scoped_id (
Browse files Browse the repository at this point in the history
#25555)

* feat: add parser for mg id on system topic

Signed-off-by: Brendan Thompson <github@blt.is>

* feat: add mg id validator using parser

Signed-off-by: Brendan Thompson <github@blt.is>

* feat: use correct validator for mg id with system topic

Signed-off-by: Brendan Thompson <github@blt.is>

* feat: add generator for ID for system topic

Signed-off-by: Brendan Thompson <github@blt.is>

* feat: add `management_group_id` to data source

Signed-off-by: Brendan Thompson <github@blt.is>

* feat: add `management_group_id` to resource

Signed-off-by: Brendan Thompson <github@blt.is>

* chore: add `management_group_id` to read

Signed-off-by: Brendan Thompson <github@blt.is>

* fix: add function call to get ID

Signed-off-by: Brendan Thompson <github@blt.is>

* fix: order of input for system topic mg id

Signed-off-by: Brendan Thompson <github@blt.is>

* refactor: improve parsing for system topic mg id

Signed-off-by: Brendan Thompson <github@blt.is>

* tests: add test for mg id for system topic

Signed-off-by: Brendan Thompson <github@blt.is>

* docs: add `management_group_id` attribute

Signed-off-by: Brendan Thompson <github@blt.is>

* fix: made regex more generic

- Made regex for tenantID more generic (and non-adherent to standard) to allow for simpler test cases

Signed-off-by: Brendan Thompson <github@blt.is>

* refactor: reanmed attributes and functions

Signed-off-by: Brendan Thompson <github@blt.is>

---------

Signed-off-by: Brendan Thompson <github@blt.is>
  • Loading branch information
BrendanThompson committed Apr 18, 2024
1 parent 4ada1a4 commit 1f996cc
Show file tree
Hide file tree
Showing 8 changed files with 133 additions and 7 deletions.
Expand Up @@ -71,7 +71,7 @@ func resourceEventGridSystemTopic() *pluginsdk.Resource {
ForceNew: true,
ValidateFunc: validation.Any(
azure.ValidateResourceID,
mgmtGroupValidate.ManagementGroupID,
mgmtGroupValidate.TenantScopedManagementGroupID,
),
},

Expand Down
10 changes: 10 additions & 0 deletions internal/services/managementgroup/management_group_data_source.go
Expand Up @@ -41,6 +41,11 @@ func dataSourceManagementGroup() *pluginsdk.Resource {
ExactlyOneOf: []string{"name", "display_name"},
},

"tenant_scoped_id": {
Type: pluginsdk.TypeString,
Computed: true,
},

"parent_management_group_id": {
Type: pluginsdk.TypeString,
Computed: true,
Expand Down Expand Up @@ -75,6 +80,7 @@ func dataSourceManagementGroup() *pluginsdk.Resource {

func dataSourceManagementGroupRead(d *pluginsdk.ResourceData, meta interface{}) error {
client := meta.(*clients.Client).ManagementGroups.GroupsClient
accountClient := meta.(*clients.Client)
ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d)
defer cancel()

Expand Down Expand Up @@ -107,6 +113,10 @@ func dataSourceManagementGroupRead(d *pluginsdk.ResourceData, meta interface{})
d.SetId(id.ID())
d.Set("name", groupName)

tenantID := accountClient.Account.TenantId
tenantScopedID := parse.NewTenantScopedManagementGroupID(tenantID, id.Name)
d.Set("tenant_scoped_id", tenantScopedID.TenantScopedID())

if props := resp.Properties; props != nil {
d.Set("display_name", props.DisplayName)

Expand Down
21 changes: 18 additions & 3 deletions internal/services/managementgroup/management_group_resource.go
Expand Up @@ -59,7 +59,12 @@ func resourceManagementGroup() *pluginsdk.Resource {
Computed: true,
},

"parent_management_group_id": {
"tenant_scoped_id": {
Type: pluginsdk.TypeString,
Computed: true,
},

"parent_tenant_scoped_id": {
Type: pluginsdk.TypeString,
Optional: true,
Computed: true,
Expand All @@ -83,6 +88,7 @@ func resourceManagementGroup() *pluginsdk.Resource {
func resourceManagementGroupCreateUpdate(d *pluginsdk.ResourceData, meta interface{}) error {
client := meta.(*clients.Client).ManagementGroups.GroupsClient
subscriptionsClient := meta.(*clients.Client).ManagementGroups.SubscriptionClient
accountClient := meta.(*clients.Client)
ctx, cancel := timeouts.ForCreateUpdate(meta.(*clients.Client).StopContext, d)
defer cancel()
armTenantID := meta.(*clients.Client).Account.TenantId
Expand All @@ -98,7 +104,11 @@ func resourceManagementGroupCreateUpdate(d *pluginsdk.ResourceData, meta interfa

id := parse.NewManagementGroupId(groupName)

parentManagementGroupId := d.Get("parent_management_group_id").(string)
tenantID := accountClient.Account.TenantId
tenantScopedID := parse.NewTenantScopedManagementGroupID(tenantID, id.Name)
d.Set("tenant_scoped_id", tenantScopedID.TenantScopedID())

parentManagementGroupId := d.Get("parent_tenant_scoped_id").(string)
if parentManagementGroupId == "" {
parentManagementGroupId = fmt.Sprintf("/providers/Microsoft.Management/managementGroups/%s", armTenantID)
}
Expand Down Expand Up @@ -206,6 +216,7 @@ func resourceManagementGroupCreateUpdate(d *pluginsdk.ResourceData, meta interfa

func resourceManagementGroupRead(d *pluginsdk.ResourceData, meta interface{}) error {
client := meta.(*clients.Client).ManagementGroups.GroupsClient
accountClient := meta.(*clients.Client)
ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d)
defer cancel()

Expand All @@ -214,6 +225,10 @@ func resourceManagementGroupRead(d *pluginsdk.ResourceData, meta interface{}) er
return err
}

tenantID := accountClient.Account.TenantId
tenantScopedID := parse.NewTenantScopedManagementGroupID(tenantID, id.Name)
d.Set("tenant_scoped_id", tenantScopedID.TenantScopedID())

recurse := utils.Bool(true)
resp, err := client.Get(ctx, id.Name, "children", recurse, "", managementGroupCacheControl)
if err != nil {
Expand Down Expand Up @@ -245,7 +260,7 @@ func resourceManagementGroupRead(d *pluginsdk.ResourceData, meta interface{}) er
}
}
}
d.Set("parent_management_group_id", parentId)
d.Set("parent_tenant_scoped_id", parentId)
}

return nil
Expand Down
45 changes: 44 additions & 1 deletion internal/services/managementgroup/parse/management_group.go
Expand Up @@ -10,7 +10,8 @@ import (
)

type ManagementGroupId struct {
Name string
Name string
TenantID string
}

func ManagementGroupID(input string) (*ManagementGroupId, error) {
Expand Down Expand Up @@ -40,13 +41,55 @@ func ManagementGroupID(input string) (*ManagementGroupId, error) {
return &id, nil
}

func TenantScopedManagementGroupID(input string) (*ManagementGroupId, error) {
regex := regexp.MustCompile(`^/tenants/.*-.*-.*-.*-.*/providers/Microsoft\.Management/managementGroups/`)
if !regex.MatchString(input) {
return nil, fmt.Errorf("Unable to parse Management Group ID for System Topic %q, format should look like '/tenants/<tenantID>/providers/Microsoft.Management/managementGroups/<management_group_name>'", input)
}

segments := strings.Split(input, "/")
if len(segments) != 7 {
return nil, fmt.Errorf("Unable to parse Management Group ID %q: expected id to have seven segments after splitting", input)
}

groupID := segments[len(segments)-1]
if groupID == "" {
return nil, fmt.Errorf("unable to parse Management Group ID %q: management group name is empty", input)
}

tenantID := segments[2]
if tenantID == "" {
return nil, fmt.Errorf("unable to parse Management Group ID %q: tenant id is empty", input)
}

id := ManagementGroupId{
Name: groupID,
TenantID: tenantID,
}

return &id, nil
}

func NewManagementGroupId(managementGroupName string) ManagementGroupId {
return ManagementGroupId{
Name: managementGroupName,
}
}

func NewTenantScopedManagementGroupID(tenantID, groupName string) ManagementGroupId {
return ManagementGroupId{
Name: groupName,
TenantID: tenantID,
}
}

func (r ManagementGroupId) ID() string {
managementGroupIdFmt := "/providers/Microsoft.Management/managementGroups/%s"
return fmt.Sprintf(managementGroupIdFmt, r.Name)
}

func (r ManagementGroupId) TenantScopedID() string {
managementGroupIDForSystemTopicFormat := "/tenants/%s/providers/Microsoft.Management/managementGroups/%s"

return fmt.Sprintf(managementGroupIDForSystemTopicFormat, r.TenantID, r.Name)
}
39 changes: 39 additions & 0 deletions internal/services/managementgroup/parse/management_group_test.go
Expand Up @@ -98,3 +98,42 @@ func TestManagementGroupID(t *testing.T) {
}
}
}

func TestManagementGroupIDForSystemTopic(t *testing.T) {
testData := []struct {
Name string
Input string
Error bool
Expected *ManagementGroupId
}{
{
Name: "Management Group ID for System Topic",
Input: "/tenants/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/providers/Microsoft.Management/managementGroups/00000000-0000-0000-0000-000000000000",
Expected: &ManagementGroupId{
Name: "00000000-0000-0000-0000-000000000000",
TenantID: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
},
},
}

for _, v := range testData {
t.Logf("[DEBUG] Testing %q", v.Name)

actual, err := TenantScopedManagementGroupID(v.Input)
if err != nil {
if v.Error {
continue
}

t.Fatalf("Expected a value but got an error: %s", err)
}

if actual.Name != v.Expected.Name {
t.Fatalf("Expected %q but got %q for Name", v.Expected.Name, actual.Name)
}

if actual.TenantID != v.Expected.TenantID {
t.Fatalf("Expected %q but got %q for TenantID", v.Expected.TenantID, actual.TenantID)
}
}
}
15 changes: 15 additions & 0 deletions internal/services/managementgroup/validate/management_group_id.go
Expand Up @@ -23,3 +23,18 @@ func ManagementGroupID(i interface{}, k string) (warnings []string, errors []err

return
}

func TenantScopedManagementGroupID(i interface{}, k string) (warnings []string, errors []error) {
v, ok := i.(string)
if !ok {
errors = append(errors, fmt.Errorf("expected type of %q to be string", k))
return
}

if _, err := parse.TenantScopedManagementGroupID(v); err != nil {
errors = append(errors, fmt.Errorf("Can not parse %q as a management group id: %v", k, err))
return
}

return
}
4 changes: 3 additions & 1 deletion website/docs/d/management_group.html.markdown
Expand Up @@ -30,14 +30,16 @@ The following arguments are supported:

* `display_name` - Specifies the display name of this Management Group.

~> **NOTE** Whilst multiple management groups may share the same display name, when filtering Terraform expects a single management group to be found with this name.
~> **NOTE** Whilst multiple management groups may share the same display name, when filtering Terraform expects a single management group to be found with this name.

## Attributes Reference

The following attributes are exported:

* `id` - The ID of the Management Group.

* `tenant_scoped_id` - The Management Group ID with the Tenant ID prefix.

* `parent_management_group_id` - The ID of any Parent Management Group.

* `management_group_ids` - A list of Management Group IDs which directly belong to this Management Group.
Expand Down
4 changes: 3 additions & 1 deletion website/docs/r/management_group.html.markdown
Expand Up @@ -45,7 +45,7 @@ The following arguments are supported:

* `display_name` - (Optional) A friendly name for this Management Group. If not specified, this will be the same as the `name`.

* `parent_management_group_id` - (Optional) The ID of the Parent Management Group.
* `parent_management_group_id` - (Optional) The ID of the Parent Management Group.

* `subscription_ids` - (Optional) A list of Subscription GUIDs which should be assigned to the Management Group.

Expand All @@ -57,6 +57,8 @@ In addition to the Arguments listed above - the following Attributes are exporte

* `id` - The ID of the Management Group.

* `tenant_scoped_id` - The Management Group ID with the Tenant ID prefix.

## Timeouts

The `timeouts` block allows you to specify [timeouts](https://www.terraform.io/language/resources/syntax#operation-timeouts) for certain actions:
Expand Down

0 comments on commit 1f996cc

Please sign in to comment.