diff --git a/azurerm/internal/services/managementgroup/management_group_data_source.go b/azurerm/internal/services/managementgroup/management_group_data_source.go index dcd5b2a495cf..38633857f81e 100644 --- a/azurerm/internal/services/managementgroup/management_group_data_source.go +++ b/azurerm/internal/services/managementgroup/management_group_data_source.go @@ -1,6 +1,7 @@ package managementgroup import ( + "context" "fmt" "time" @@ -25,22 +26,24 @@ func dataSourceArmManagementGroup() *schema.Resource { Type: schema.TypeString, Optional: true, Computed: true, - ExactlyOneOf: []string{"name", "group_id"}, + ExactlyOneOf: []string{"name", "group_id", "display_name"}, Deprecated: "Deprecated in favour of `name`", ValidateFunc: validate.ManagementGroupName, }, "name": { Type: schema.TypeString, - Optional: true, // TODO -- change back to required after the deprecation - Computed: true, // TODO -- remove computed after the deprecation - ExactlyOneOf: []string{"name", "group_id"}, + Optional: true, + Computed: true, + ExactlyOneOf: []string{"name", "group_id", "display_name"}, ValidateFunc: validate.ManagementGroupName, }, "display_name": { - Type: schema.TypeString, - Computed: true, + Type: schema.TypeString, + Optional: true, + Computed: true, + ExactlyOneOf: []string{"name", "group_id", "display_name"}, }, "parent_management_group_id": { @@ -70,7 +73,17 @@ func dataSourceArmManagementGroupRead(d *schema.ResourceData, meta interface{}) if v, ok := d.GetOk("group_id"); ok { groupName = v.(string) } + displayName := d.Get("display_name").(string) + // one of displayName and groupName must be non-empty, this is guaranteed by schema + // if the user is retrieving the mgmt group by display name, use the list api to get the group name first + var err error + if displayName != "" { + groupName, err = getManagementGroupNameByDisplayName(ctx, client, displayName) + if err != nil { + return fmt.Errorf("Error reading Management Group (Display Name %q): %+v", displayName, err) + } + } recurse := true resp, err := client.Get(ctx, groupName, "children", &recurse, "", managementGroupCacheControl) if err != nil { @@ -78,7 +91,7 @@ func dataSourceArmManagementGroupRead(d *schema.ResourceData, meta interface{}) return fmt.Errorf("Management Group %q was not found", groupName) } - return fmt.Errorf("Error reading Management Group %q: %+v", d.Id(), err) + return fmt.Errorf("Error reading Management Group %q: %+v", groupName, err) } if resp.ID == nil { @@ -112,6 +125,37 @@ func dataSourceArmManagementGroupRead(d *schema.ResourceData, meta interface{}) return nil } +func getManagementGroupNameByDisplayName(ctx context.Context, client *managementgroups.Client, displayName string) (string, error) { + iterator, err := client.ListComplete(ctx, managementGroupCacheControl, "") + if err != nil { + return "", fmt.Errorf("Error listing Management Groups: %+v", err) + } + + var results []string + for iterator.NotDone() { + group := iterator.Value() + if group.DisplayName != nil && *group.DisplayName == displayName && group.Name != nil && *group.Name != "" { + results = append(results, *group.Name) + } + + if err := iterator.NextWithContext(ctx); err != nil { + return "", fmt.Errorf("Error listing Management Groups: %+v", err) + } + } + + // we found none + if len(results) == 0 { + return "", fmt.Errorf("Management Group (Display Name %q) was not found", displayName) + } + + // we found more than one + if len(results) > 1 { + return "", fmt.Errorf("expected a single Management Group with the Display Name %q but expected one", displayName) + } + + return results[0], nil +} + func flattenArmManagementGroupDataSourceSubscriptionIds(input *[]managementgroups.ChildInfo) (*schema.Set, error) { subscriptionIds := &schema.Set{F: schema.HashString} if input == nil { diff --git a/azurerm/internal/services/managementgroup/tests/management_group_data_source_test.go b/azurerm/internal/services/managementgroup/tests/management_group_data_source_test.go index 405cb9cd4ff2..809014b8a49a 100644 --- a/azurerm/internal/services/managementgroup/tests/management_group_data_source_test.go +++ b/azurerm/internal/services/managementgroup/tests/management_group_data_source_test.go @@ -8,7 +8,7 @@ import ( "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/acceptance" ) -func TestAccDataSourceArmManagementGroup_basic(t *testing.T) { +func TestAccDataSourceArmManagementGroup_basicByName(t *testing.T) { data := acceptance.BuildTestData(t, "data.azurerm_management_group", "test") resource.ParallelTest(t, resource.TestCase{ @@ -16,7 +16,7 @@ func TestAccDataSourceArmManagementGroup_basic(t *testing.T) { Providers: acceptance.SupportedProviders, Steps: []resource.TestStep{ { - Config: testAccDataSourceArmManagementGroup_basic(data), + Config: testAccDataSourceArmManagementGroup_basicByName(data), Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttr(data.ResourceName, "display_name", fmt.Sprintf("acctestmg-%d", data.RandomInteger)), resource.TestCheckResourceAttr(data.ResourceName, "subscription_ids.#", "0"), @@ -26,7 +26,25 @@ func TestAccDataSourceArmManagementGroup_basic(t *testing.T) { }) } -func testAccDataSourceArmManagementGroup_basic(data acceptance.TestData) string { +func TestAccDataSourceArmManagementGroup_basicByDisplayName(t *testing.T) { + data := acceptance.BuildTestData(t, "data.azurerm_management_group", "test") + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acceptance.PreCheck(t) }, + Providers: acceptance.SupportedProviders, + Steps: []resource.TestStep{ + { + Config: testAccDataSourceArmManagementGroup_basicByDisplayName(data), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(data.ResourceName, "display_name", fmt.Sprintf("acctest Management Group %d", data.RandomInteger)), + resource.TestCheckResourceAttr(data.ResourceName, "subscription_ids.#", "0"), + ), + }, + }, + }) +} + +func testAccDataSourceArmManagementGroup_basicByName(data acceptance.TestData) string { return fmt.Sprintf(` provider "azurerm" { features {} @@ -41,3 +59,19 @@ data "azurerm_management_group" "test" { } `, data.RandomInteger) } + +func testAccDataSourceArmManagementGroup_basicByDisplayName(data acceptance.TestData) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +resource "azurerm_management_group" "test" { + display_name = "acctest Management Group %d" +} + +data "azurerm_management_group" "test" { + display_name = azurerm_management_group.test.display_name +} +`, data.RandomInteger) +} diff --git a/website/docs/d/management_group.html.markdown b/website/docs/d/management_group.html.markdown index 668836809ad3..b5ad6c03d3f1 100644 --- a/website/docs/d/management_group.html.markdown +++ b/website/docs/d/management_group.html.markdown @@ -32,17 +32,19 @@ The following arguments are supported: ~> **NOTE:** The field `group_id` has been deprecated in favour of `name`. +* `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. + ## Attributes Reference The following attributes are exported: * `id` - The ID of the Management Group. -* `display_name` - A friendly name for the Management Group. - * `parent_management_group_id` - The ID of any Parent Management Group. -* `subscription_ids` - A list of Subscription ID's which are assigned to the Management Group. +* `subscription_ids` - A list of Subscription IDs which are assigned to the Management Group. ## Timeouts