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

azuread_application & azuread_group: Duplicate name detection #279

Merged
merged 2 commits into from
Jun 23, 2020
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions azuread/helpers/graph/application.go
Original file line number Diff line number Diff line change
Expand Up @@ -262,3 +262,31 @@ func ApplicationAddOwners(client graphrbac.ApplicationsClient, ctx context.Conte

return nil
}

func ApplicationFindByName(client graphrbac.ApplicationsClient, ctx context.Context, name string) (*graphrbac.Application, error) {
nameFilter := fmt.Sprintf("displayName eq '%s'", name)
resp, err := client.List(ctx, nameFilter)

if err != nil {
return nil, fmt.Errorf("unable to list Applications with filter %q: %+v", nameFilter, err)
}

for _, app := range resp.Values() {
if *app.DisplayName == name {
return &app, nil
}
}

return nil, nil
}

func ApplicationCheckNameAvailability(client graphrbac.ApplicationsClient, ctx context.Context, name string) error {
existingApp, err := ApplicationFindByName(client, ctx, name)
if err != nil {
return err
}
if existingApp != nil {
return fmt.Errorf("existing Application with name %q (AppID: %q) was found and `prevent_duplicate_names` was specified", name, *existingApp.AppID)
}
return nil
}
28 changes: 28 additions & 0 deletions azuread/helpers/graph/group.go
Original file line number Diff line number Diff line change
Expand Up @@ -182,3 +182,31 @@ func GroupAddOwners(client graphrbac.GroupsClient, ctx context.Context, groupId

return nil
}

func GroupFindByName(client graphrbac.GroupsClient, ctx context.Context, name string) (*graphrbac.ADGroup, error) {
nameFilter := fmt.Sprintf("displayName eq '%s'", name)
resp, err := client.List(ctx, nameFilter)

if err != nil {
return nil, fmt.Errorf("unable to list Groups with filter %q: %+v", nameFilter, err)
}

for _, group := range resp.Values() {
if *group.DisplayName == name {
return &group, nil
}
}

return nil, nil
}

func GroupCheckNameAvailability(client graphrbac.GroupsClient, ctx context.Context, name string) error {
existingGroup, err := GroupFindByName(client, ctx, name)
if err != nil {
return err
}
if existingGroup != nil {
return fmt.Errorf("existing Azure Active Directory Group with name %q (ObjID: %q) was found and `prevent_duplicate_names` was specified", name, *existingGroup.ObjectID)
}
return nil
}
24 changes: 24 additions & 0 deletions azuread/resource_application.go
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,11 @@ func resourceApplication() *schema.Resource {
},
},
},
"prevent_duplicate_names": {
Type: schema.TypeBool,
Optional: true,
Default: false,
},
},
}
}
Expand All @@ -294,6 +299,14 @@ func resourceApplicationCreate(d *schema.ResourceData, meta interface{}) error {
ctx := meta.(*ArmClient).StopContext

name := d.Get("name").(string)

if d.Get("prevent_duplicate_names").(bool) {
err := graph.ApplicationCheckNameAvailability(client, ctx, name)
if err != nil {
return err
}
}

appType := d.Get("type")
identUrls, hasIdentUrls := d.GetOk("identifier_uris")
if appType == "native" {
Expand Down Expand Up @@ -400,6 +413,13 @@ func resourceApplicationUpdate(d *schema.ResourceData, meta interface{}) error {

name := d.Get("name").(string)

if d.Get("prevent_duplicate_names").(bool) {
err := graph.ApplicationCheckNameAvailability(client, ctx, name)
if err != nil {
return err
}
}

var properties graphrbac.ApplicationUpdateParameters

if d.HasChange("name") {
Expand Down Expand Up @@ -591,6 +611,10 @@ func resourceApplicationRead(d *schema.ResourceData, meta interface{}) error {
return fmt.Errorf("setting `owners`: %+v", err)
}

if preventDuplicates := d.Get("prevent_duplicate_names").(bool); !preventDuplicates {
d.Set("prevent_duplicate_names", false)
}

return nil
}

Expand Down
27 changes: 27 additions & 0 deletions azuread/resource_application_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -584,6 +584,22 @@ func TestAccAzureADApplication_oauth2PermissionsUpdate(t *testing.T) {
})
}

func TestAccAzureADApplication_preventDuplicateNames(t *testing.T) {
ri := tf.AccRandTimeInt()

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testCheckADApplicationDestroy,
Steps: []resource.TestStep{
{
Config: testAccADApplication_duplicateName(ri),
ExpectError: regexp.MustCompile("existing Application .+ was found"),
},
},
})
}

func testCheckADApplicationExists(name string) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[name]
Expand Down Expand Up @@ -913,3 +929,14 @@ resource "azuread_application" "test" {
}
`, ri)
}

func testAccADApplication_duplicateName(ri int) string {
return fmt.Sprintf(`
%s

resource "azuread_application" "duplicate" {
name = azuread_application.test.name
prevent_duplicate_names = true
}
`, testAccADApplication_basic(ri))
}
16 changes: 16 additions & 0 deletions azuread/resource_group.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,11 @@ func resourceGroup() *schema.Resource {
Type: schema.TypeString,
Computed: true,
},
"prevent_duplicate_names": {
Type: schema.TypeBool,
Optional: true,
Default: false,
},
},
}
}
Expand All @@ -78,6 +83,13 @@ func resourceGroupCreate(d *schema.ResourceData, meta interface{}) error {

name := d.Get("name").(string)

if d.Get("prevent_duplicate_names").(bool) {
err := graph.GroupCheckNameAvailability(client, ctx, name)
if err != nil {
return err
}
}

properties := graphrbac.GroupCreateParameters{
DisplayName: &name,
MailEnabled: p.Bool(false), // we're defaulting to false, as the API currently only supports the creation of non-mail enabled security groups.
Expand Down Expand Up @@ -168,6 +180,10 @@ func resourceGroupRead(d *schema.ResourceData, meta interface{}) error {
}
d.Set("owners", owners)

if preventDuplicates := d.Get("prevent_duplicate_names").(bool); !preventDuplicates {
d.Set("prevent_duplicate_names", false)
}

return nil
}

Expand Down
28 changes: 28 additions & 0 deletions azuread/resource_group_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package azuread

import (
"fmt"
"regexp"
"testing"

"github.com/hashicorp/terraform-plugin-sdk/helper/acctest"
Expand Down Expand Up @@ -286,6 +287,22 @@ func TestAccAzureADGroup_ownersUpdate(t *testing.T) {
})
}

func TestAccAzureADGroup_preventDuplicateNames(t *testing.T) {
ri := tf.AccRandTimeInt()

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testCheckADApplicationDestroy,
Steps: []resource.TestStep{
{
Config: testAccAzureADGroup_duplicateName(ri),
ExpectError: regexp.MustCompile("existing Azure Active Directory Group .+ was found"),
},
},
})
}

func testCheckAzureADGroupExists(name string) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[name]
Expand Down Expand Up @@ -503,3 +520,14 @@ resource "azuread_group" "test" {
}
`, id)
}

func testAccAzureADGroup_duplicateName(id int) string {
return fmt.Sprintf(`
%s

resource "azuread_group" "duplicate" {
name = azuread_group.test.name
prevent_duplicate_names = true
}
`, testAccAzureADGroup_basic(id))
}
2 changes: 2 additions & 0 deletions website/docs/r/application.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,8 @@ The following arguments are supported:

* `oauth2_permissions` - (Optional) A collection of OAuth 2.0 permission scopes that the web API (resource) app exposes to client apps. Each permission is covered by `oauth2_permissions` blocks as documented below.

* `prevent_duplicate_names` - (Optional) If `true`, will return an error when an existing Application is found with the same name. Defaults to `false`.

---

`required_resource_access` supports the following:
Expand Down
1 change: 1 addition & 0 deletions website/docs/r/group.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ The following arguments are supported:
* `description` - (Optional) The description for the Group. Changing this forces a new resource to be created.
* `members` (Optional) A set of members who should be present in this Group. Supported Object types are Users, Groups or Service Principals.
* `owners` (Optional) A set of owners who own this Group. Supported Object types are Users or Service Principals.
* `prevent_duplicate_names` - (Optional) If `true`, will return an error when an existing Group is found with the same name. Defaults to `false`.

-> **NOTE:** Group names are not unique within Azure Active Directory.

Expand Down