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

data/resource: azurerm_management_group - now exports tenant_scoped_id #25555

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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