From 237d8b8133924aeb842ee957b0d0e9b18ce40a89 Mon Sep 17 00:00:00 2001 From: twendt Date: Sun, 26 May 2019 21:10:55 +0200 Subject: [PATCH 01/45] New resource 'azuread_application_password' (#71) --- azuread/data_application.go | 4 +- azuread/helpers/graph/credentials.go | 211 ++++++++++++++++ azuread/helpers/guid/guid.go | 13 - azuread/helpers/p/p.go | 5 +- azuread/helpers/tf/marshall.go | 4 +- azuread/locks.go | 6 +- azuread/provider.go | 1 + azuread/resource_application.go | 16 +- azuread/resource_application_password.go | 145 +++++++++++ azuread/resource_application_password_test.go | 238 ++++++++++++++++++ azuread/resource_group.go | 4 +- azuread/resource_service_principal.go | 6 +- .../resource_service_principal_password.go | 216 +++------------- ...esource_service_principal_password_test.go | 164 ++++++------ website/azuread.erb | 4 + .../docs/r/application_password.html.markdown | 68 +++++ 16 files changed, 812 insertions(+), 293 deletions(-) create mode 100644 azuread/helpers/graph/credentials.go delete mode 100644 azuread/helpers/guid/guid.go create mode 100644 azuread/resource_application_password.go create mode 100644 azuread/resource_application_password_test.go create mode 100644 website/docs/r/application_password.html.markdown diff --git a/azuread/data_application.go b/azuread/data_application.go index d141035c23..1ce102f633 100644 --- a/azuread/data_application.go +++ b/azuread/data_application.go @@ -220,11 +220,11 @@ func dataApplicationRead(d *schema.ResourceData, meta interface{}) error { d.Set("available_to_other_tenants", app.AvailableToOtherTenants) d.Set("oauth2_allow_implicit_flow", app.Oauth2AllowImplicitFlow) - if err := d.Set("identifier_uris", tf.FlattenStringArrayPtr(app.IdentifierUris)); err != nil { + if err := d.Set("identifier_uris", tf.FlattenStringSlicePtr(app.IdentifierUris)); err != nil { return fmt.Errorf("Error setting `identifier_uris`: %+v", err) } - if err := d.Set("reply_urls", tf.FlattenStringArrayPtr(app.ReplyUrls)); err != nil { + if err := d.Set("reply_urls", tf.FlattenStringSlicePtr(app.ReplyUrls)); err != nil { return fmt.Errorf("Error setting `reply_urls`: %+v", err) } diff --git a/azuread/helpers/graph/credentials.go b/azuread/helpers/graph/credentials.go new file mode 100644 index 0000000000..032d086c7a --- /dev/null +++ b/azuread/helpers/graph/credentials.go @@ -0,0 +1,211 @@ +package graph + +import ( + "fmt" + "strings" + "time" + + "github.com/Azure/azure-sdk-for-go/services/graphrbac/1.6/graphrbac" + "github.com/Azure/go-autorest/autorest/date" + "github.com/hashicorp/go-uuid" + "github.com/hashicorp/terraform/helper/schema" + "github.com/hashicorp/terraform/helper/validation" + "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/p" + "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/validate" +) + +// valid types are `application` and `service_principal` +func PasswordResourceSchema(object_type string) map[string]*schema.Schema { + return map[string]*schema.Schema{ + object_type + "_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validate.UUID, + }, + + "key_id": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + ValidateFunc: validate.UUID, + }, + + "value": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Sensitive: true, + ValidateFunc: validate.NoEmptyStrings, + }, + + "start_date": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + ValidateFunc: validation.ValidateRFC3339TimeString, + }, + + "end_date": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + ConflictsWith: []string{"end_date_relative"}, + ValidateFunc: validation.ValidateRFC3339TimeString, + }, + + "end_date_relative": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + ConflictsWith: []string{"end_date"}, + ValidateFunc: validate.NoEmptyStrings, + }, + } +} + +type PasswordCredentialId struct { + ObjectId string + KeyId string +} + +func (id PasswordCredentialId) String() string { + return id.ObjectId + "/" + id.KeyId +} + +func ParsePasswordCredentialId(id string) (PasswordCredentialId, error) { + parts := strings.Split(id, "/") + if len(parts) != 2 { + return PasswordCredentialId{}, fmt.Errorf("Password Credential ID should be in the format {objectId}/{keyId} - but got %q", id) + } + + if _, err := uuid.ParseUUID(parts[0]); err != nil { + return PasswordCredentialId{}, fmt.Errorf("Object ID isn't a valid UUID (%q): %+v", id[0], err) + } + + if _, err := uuid.ParseUUID(parts[1]); err != nil { + return PasswordCredentialId{}, fmt.Errorf("Object ID isn't a valid UUID (%q): %+v", id[1], err) + } + + return PasswordCredentialId{ + ObjectId: parts[0], + KeyId: parts[1], + }, nil + +} + +func PasswordCredentialIdFrom(objectId, keyId string) PasswordCredentialId { + return PasswordCredentialId{ + ObjectId: objectId, + KeyId: keyId, + } +} + +func PasswordCredentialForResource(d *schema.ResourceData) (*graphrbac.PasswordCredential, error) { + value := d.Get("value").(string) + + // errors should be handled by the validation + var keyId string + if v, ok := d.GetOk("key_id"); ok { + keyId = v.(string) + } else { + kid, err := uuid.GenerateUUID() + if err != nil { + return nil, err + } + + keyId = kid + } + + var endDate time.Time + if v := d.Get("end_date").(string); v != "" { + endDate, _ = time.Parse(time.RFC3339, v) + } else if v := d.Get("end_date_relative").(string); v != "" { + d, err := time.ParseDuration(v) + if err != nil { + return nil, fmt.Errorf("unable to parse `end_date_relative` (%s) as a duration", v) + } + endDate = time.Now().Add(d) + } else { + return nil, fmt.Errorf("one of `end_date` or `end_date_relative` must be specified") + } + + credential := graphrbac.PasswordCredential{ + KeyID: p.String(keyId), + Value: p.String(value), + EndDate: &date.Time{Time: endDate}, + } + + if v, ok := d.GetOk("start_date"); ok { + // errors will be handled by the validation + startDate, _ := time.Parse(time.RFC3339, v.(string)) + credential.StartDate = &date.Time{Time: startDate} + } + + return &credential, nil +} + +func PasswordCredentialResultFindByKeyId(creds graphrbac.PasswordCredentialListResult, keyId string) *graphrbac.PasswordCredential { + var cred *graphrbac.PasswordCredential + + if creds.Value != nil { + for _, c := range *creds.Value { + if c.KeyID == nil { + continue + } + + if *c.KeyID == keyId { + cred = &c + break + } + } + } + + return cred +} + +func PasswordCredentialResultAdd(existing graphrbac.PasswordCredentialListResult, cred *graphrbac.PasswordCredential, errorOnDuplicate bool) (*[]graphrbac.PasswordCredential, error) { + newCreds := make([]graphrbac.PasswordCredential, 0) + + if existing.Value != nil { + if errorOnDuplicate { + for _, v := range *existing.Value { + if v.KeyID == nil { + continue + } + + if *v.KeyID == *cred.KeyID { + return nil, fmt.Errorf("credential already exists found") + } + } + } + + newCreds = *existing.Value + } + newCreds = append(newCreds, *cred) + + return &newCreds, nil +} + +func PasswordCredentialResultRemoveByKeyId(existing graphrbac.PasswordCredentialListResult, keyId string) *[]graphrbac.PasswordCredential { + newCreds := make([]graphrbac.PasswordCredential, 0) + + if existing.Value != nil { + for _, v := range *existing.Value { + if v.KeyID == nil { + continue + } + + if *v.KeyID == keyId { + continue + } + + newCreds = append(newCreds, v) + } + } + + return &newCreds +} diff --git a/azuread/helpers/guid/guid.go b/azuread/helpers/guid/guid.go deleted file mode 100644 index e035569170..0000000000 --- a/azuread/helpers/guid/guid.go +++ /dev/null @@ -1,13 +0,0 @@ -package guid - -import ( - "github.com/gofrs/uuid" -) - -func New() uuid.UUID { - uuid, err := uuid.NewV4() - if err != nil { - panic(err) - } - return uuid -} diff --git a/azuread/helpers/p/p.go b/azuread/helpers/p/p.go index b1d6b0e69e..a1d6a05b34 100644 --- a/azuread/helpers/p/p.go +++ b/azuread/helpers/p/p.go @@ -1,7 +1,6 @@ -package p //or maybe ptr? - -//helper functions to convert to a pointer +package p // or maybe ptr? +// helper functions to convert to a pointer func Bool(input bool) *bool { return &input } diff --git a/azuread/helpers/tf/marshall.go b/azuread/helpers/tf/marshall.go index 24be506f7f..17f8f3f946 100644 --- a/azuread/helpers/tf/marshall.go +++ b/azuread/helpers/tf/marshall.go @@ -1,6 +1,6 @@ package tf -func ExpandStringArrayPtr(input []interface{}) *[]string { +func ExpandStringSlicePtr(input []interface{}) *[]string { result := make([]string, 0) for _, item := range input { result = append(result, item.(string)) @@ -8,7 +8,7 @@ func ExpandStringArrayPtr(input []interface{}) *[]string { return &result } -func FlattenStringArrayPtr(input *[]string) []interface{} { +func FlattenStringSlicePtr(input *[]string) []interface{} { result := make([]interface{}, 0) if input != nil { for _, item := range *input { diff --git a/azuread/locks.go b/azuread/locks.go index c9149385c6..f62b745b0f 100644 --- a/azuread/locks.go +++ b/azuread/locks.go @@ -1,10 +1,10 @@ package azuread -// handle the case of using the same name for different kinds of resources -func azureADLockByName(name string, resourceType string) { +// handles the case of using the same name for different kinds of resources +func azureADLockByName(resourceType string, name string) { armMutexKV.Lock(resourceType + "." + name) } -func azureADUnlockByName(name string, resourceType string) { +func azureADUnlockByName(resourceType string, name string) { armMutexKV.Unlock(resourceType + "." + name) } diff --git a/azuread/provider.go b/azuread/provider.go index 9de604c743..1daa673133 100644 --- a/azuread/provider.go +++ b/azuread/provider.go @@ -83,6 +83,7 @@ func Provider() terraform.ResourceProvider { ResourcesMap: map[string]*schema.Resource{ "azuread_application": resourceApplication(), + "azuread_application_password": resourceApplicationPassword(), "azuread_group": resourceGroup(), "azuread_service_principal": resourceServicePrincipal(), "azuread_service_principal_password": resourceServicePrincipalPassword(), diff --git a/azuread/resource_application.go b/azuread/resource_application.go index 3df1849272..1271b33057 100644 --- a/azuread/resource_application.go +++ b/azuread/resource_application.go @@ -13,6 +13,8 @@ import ( "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/validate" ) +const resourceApplicationName = "azuread_application" + func resourceApplication() *schema.Resource { return &schema.Resource{ Create: resourceApplicationCreate, @@ -192,8 +194,8 @@ func resourceApplicationCreate(d *schema.ResourceData, meta interface{}) error { properties := graphrbac.ApplicationCreateParameters{ AdditionalProperties: make(map[string]interface{}), DisplayName: &name, - IdentifierUris: tf.ExpandStringArrayPtr(identUrls.([]interface{})), - ReplyUrls: tf.ExpandStringArrayPtr(d.Get("reply_urls").(*schema.Set).List()), + IdentifierUris: tf.ExpandStringSlicePtr(identUrls.([]interface{})), + ReplyUrls: tf.ExpandStringSlicePtr(d.Get("reply_urls").(*schema.Set).List()), AvailableToOtherTenants: p.Bool(d.Get("available_to_other_tenants").(bool)), RequiredResourceAccess: expandADApplicationRequiredResourceAccess(d), } @@ -265,11 +267,11 @@ func resourceApplicationUpdate(d *schema.ResourceData, meta interface{}) error { } if d.HasChange("identifier_uris") { - properties.IdentifierUris = tf.ExpandStringArrayPtr(d.Get("identifier_uris").([]interface{})) + properties.IdentifierUris = tf.ExpandStringSlicePtr(d.Get("identifier_uris").([]interface{})) } if d.HasChange("reply_urls") { - properties.ReplyUrls = tf.ExpandStringArrayPtr(d.Get("reply_urls").(*schema.Set).List()) + properties.ReplyUrls = tf.ExpandStringSlicePtr(d.Get("reply_urls").(*schema.Set).List()) } if d.HasChange("available_to_other_tenants") { @@ -300,7 +302,7 @@ func resourceApplicationUpdate(d *schema.ResourceData, meta interface{}) error { switch appType := d.Get("type"); appType { case "webapp/api": properties.AdditionalProperties["publicClient"] = false - properties.IdentifierUris = tf.ExpandStringArrayPtr(d.Get("identifier_uris").([]interface{})) + properties.IdentifierUris = tf.ExpandStringSlicePtr(d.Get("identifier_uris").([]interface{})) case "native": properties.AdditionalProperties["publicClient"] = true properties.IdentifierUris = &[]string{} @@ -349,11 +351,11 @@ func resourceApplicationRead(d *schema.ResourceData, meta interface{}) error { d.Set("type", "webapp/api") } - if err := d.Set("identifier_uris", tf.FlattenStringArrayPtr(resp.IdentifierUris)); err != nil { + if err := d.Set("identifier_uris", tf.FlattenStringSlicePtr(resp.IdentifierUris)); err != nil { return fmt.Errorf("Error setting `identifier_uris`: %+v", err) } - if err := d.Set("reply_urls", tf.FlattenStringArrayPtr(resp.ReplyUrls)); err != nil { + if err := d.Set("reply_urls", tf.FlattenStringSlicePtr(resp.ReplyUrls)); err != nil { return fmt.Errorf("Error setting `reply_urls`: %+v", err) } diff --git a/azuread/resource_application_password.go b/azuread/resource_application_password.go new file mode 100644 index 0000000000..7b3ca146b5 --- /dev/null +++ b/azuread/resource_application_password.go @@ -0,0 +1,145 @@ +package azuread + +import ( + "fmt" + "log" + "time" + + "github.com/Azure/azure-sdk-for-go/services/graphrbac/1.6/graphrbac" + "github.com/hashicorp/terraform/helper/schema" + "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/ar" + "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/graph" + "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/tf" +) + +func resourceApplicationPassword() *schema.Resource { + return &schema.Resource{ + Create: resourceApplicationPasswordCreate, + Read: resourceApplicationPasswordRead, + Delete: resourceApplicationPasswordDelete, + + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Schema: graph.PasswordResourceSchema("application"), + } +} + +func resourceApplicationPasswordCreate(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).applicationsClient + ctx := meta.(*ArmClient).StopContext + + objectId := d.Get("application_id").(string) + + cred, err := graph.PasswordCredentialForResource(d) + if err != nil { + return fmt.Errorf("Error generating Application Credentials for Object ID %q: %+v", objectId, err) + } + id := graph.PasswordCredentialIdFrom(objectId, *cred.KeyID) + + azureADLockByName(resourceApplicationName, id.ObjectId) + defer azureADUnlockByName(resourceApplicationName, id.ObjectId) + + existingCreds, err := client.ListPasswordCredentials(ctx, id.ObjectId) + if err != nil { + return fmt.Errorf("Error Listing Application Credentials for Object ID %q: %+v", id.ObjectId, err) + } + + newCreds, err := graph.PasswordCredentialResultAdd(existingCreds, cred, requireResourcesToBeImported) + if err != nil { + return tf.ImportAsExistsError("azuread_application_password", id.String()) + } + + if _, err = client.UpdatePasswordCredentials(ctx, id.ObjectId, graphrbac.PasswordCredentialsUpdateParameters{Value: newCreds}); err != nil { + return fmt.Errorf("Error creating Application Credentials %q for Object ID %q: %+v", *cred.KeyID, id.ObjectId, err) + } + + d.SetId(id.String()) + + return resourceApplicationPasswordRead(d, meta) +} + +func resourceApplicationPasswordRead(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).applicationsClient + ctx := meta.(*ArmClient).StopContext + + id, err := graph.ParsePasswordCredentialId(d.Id()) + if err != nil { + return fmt.Errorf("Error parsing Application Password ID: %v", err) + } + + // ensure the Application Object exists + app, err := client.Get(ctx, id.ObjectId) + if err != nil { + // the parent Service Principal has been removed - skip it + if ar.ResponseWasNotFound(app.Response) { + log.Printf("[DEBUG] Application with Object ID %q was not found - removing from state!", id.ObjectId) + d.SetId("") + return nil + } + return fmt.Errorf("Error retrieving Application ID %q: %+v", id.ObjectId, err) + } + + credentials, err := client.ListPasswordCredentials(ctx, id.ObjectId) + if err != nil { + return fmt.Errorf("Error Listing Application Credentials for Application with Object ID %q: %+v", id.ObjectId, err) + } + + credential := graph.PasswordCredentialResultFindByKeyId(credentials, id.KeyId) + if credential == nil { + log.Printf("[DEBUG] Application Credentials %q (ID %q) was not found - removing from state!", id.KeyId, id.ObjectId) + d.SetId("") + return nil + } + + // todo, move this into a graph helper function? + d.Set("application_id", id.ObjectId) + d.Set("key_id", id.KeyId) + + if endDate := credential.EndDate; endDate != nil { + d.Set("end_date", endDate.Format(time.RFC3339)) + } + + if startDate := credential.StartDate; startDate != nil { + d.Set("start_date", startDate.Format(time.RFC3339)) + } + + return nil +} + +func resourceApplicationPasswordDelete(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).applicationsClient + ctx := meta.(*ArmClient).StopContext + + id, err := graph.ParsePasswordCredentialId(d.Id()) + if err != nil { + return fmt.Errorf("Error parsing Application Password ID: %v", err) + } + + azureADLockByName(resourceApplicationName, id.ObjectId) + defer azureADUnlockByName(resourceApplicationName, id.ObjectId) + + // ensure the parent Application exists + app, err := client.Get(ctx, id.ObjectId) + if err != nil { + // the parent Service Principal has been removed - skip it + if ar.ResponseWasNotFound(app.Response) { + log.Printf("[DEBUG] Application with Object ID %q was not found - removing from state!", id.ObjectId) + return nil + } + return fmt.Errorf("Error retrieving Application ID %q: %+v", id.ObjectId, err) + } + + existing, err := client.ListPasswordCredentials(ctx, id.ObjectId) + if err != nil { + return fmt.Errorf("Error Listing Application Credentials for %q: %+v", id.ObjectId, err) + } + + newCreds := graph.PasswordCredentialResultRemoveByKeyId(existing, id.KeyId) + if _, err = client.UpdatePasswordCredentials(ctx, id.ObjectId, graphrbac.PasswordCredentialsUpdateParameters{Value: newCreds}); err != nil { + return fmt.Errorf("Error removing Application Credentials %q from Application Object ID %q: %+v", id.KeyId, id.ObjectId, err) + } + + return nil +} diff --git a/azuread/resource_application_password_test.go b/azuread/resource_application_password_test.go new file mode 100644 index 0000000000..405e575bad --- /dev/null +++ b/azuread/resource_application_password_test.go @@ -0,0 +1,238 @@ +package azuread + +import ( + "fmt" + "testing" + + "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/ar" + "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/graph" + + "github.com/google/uuid" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +func testCheckADApplicationPasswordExists(name string) resource.TestCheckFunc { //nolint unparam + return func(s *terraform.State) error { + client := testAccProvider.Meta().(*ArmClient).applicationsClient + ctx := testAccProvider.Meta().(*ArmClient).StopContext + + rs, ok := s.RootModule().Resources[name] + if !ok { + return fmt.Errorf("Not found: %q", name) + } + + id, err := graph.ParsePasswordCredentialId(rs.Primary.ID) + if err != nil { + return fmt.Errorf("error parsing Application Password Credential ID: %v", err) + } + resp, err := client.Get(ctx, id.ObjectId) + if err != nil { + if ar.ResponseWasNotFound(resp.Response) { + return fmt.Errorf("Bad: Azure AD Application %q does not exist", id.ObjectId) + } + return fmt.Errorf("Bad: Get on Azure AD applicationsClient: %+v", err) + } + + credentials, err := client.ListPasswordCredentials(ctx, id.ObjectId) + if err != nil { + return fmt.Errorf("Error Listing Password Credentials for Application %q: %+v", id.ObjectId, err) + } + + cred := graph.PasswordCredentialResultFindByKeyId(credentials, id.KeyId) + if cred != nil { + return nil + } + + return fmt.Errorf("Password Credential %q was not found in Aplication %q", id.KeyId, id.ObjectId) + } +} + +func testCheckADApplicationPasswordCheckDestroy(s *terraform.State) error { + for _, rs := range s.RootModule().Resources { + client := testAccProvider.Meta().(*ArmClient).applicationsClient + ctx := testAccProvider.Meta().(*ArmClient).StopContext + + if rs.Type != "azuread_application_password" { + continue + } + + id, err := graph.ParsePasswordCredentialId(rs.Primary.ID) + if err != nil { + return fmt.Errorf("error parsing Application Password Credential ID: %v", err) + } + + resp, err := client.Get(ctx, id.ObjectId) + if err != nil { + if ar.ResponseWasNotFound(resp.Response) { + return nil + } + + return err + } + + return fmt.Errorf("Azure AD Application Password Credential still exists:\n%#v", resp) + } + + return nil +} + +func TestAccAzureADApplicationPassword_basic(t *testing.T) { + resourceName := "azuread_application_password.test" + applicationId := uuid.New().String() + value := uuid.New().String() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckADApplicationPasswordCheckDestroy, + Steps: []resource.TestStep{ + { + Config: testAccADObjectPasswordApplication_basic(applicationId, value), + Check: resource.ComposeTestCheckFunc( + // can't assert on Value since it's not returned + testCheckADApplicationPasswordExists(resourceName), + resource.TestCheckResourceAttrSet(resourceName, "start_date"), + resource.TestCheckResourceAttrSet(resourceName, "key_id"), + resource.TestCheckResourceAttr(resourceName, "end_date", "2020-01-01T01:02:03Z"), + ), + }, + }, + }) +} + +func TestAccAzureADApplicationPassword_requiresImport(t *testing.T) { + if !requireResourcesToBeImported { + t.Skip("Skipping since resources aren't required to be imported") + return + } + + resourceName := "azuread_application_password.test" + applicationId := uuid.New().String() + value := uuid.New().String() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckADApplicationPasswordCheckDestroy, + Steps: []resource.TestStep{ + { + Config: testAccADObjectPasswordApplication_basic(applicationId, value), + Check: resource.ComposeTestCheckFunc( + testCheckADApplicationPasswordExists(resourceName), + ), + }, + { + Config: testAccADApplicationPassword_requiresImport(applicationId, value), + ExpectError: testRequiresImportError("azuread_application_password"), + }, + }, + }) +} + +func TestAccAzureADApplicationPassword_customKeyId(t *testing.T) { + resourceName := "azuread_application_password.test" + applicationId := uuid.New().String() + keyId := uuid.New().String() + value := uuid.New().String() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckADApplicationPasswordCheckDestroy, + Steps: []resource.TestStep{ + { + Config: testAccADApplicationPassword_customKeyId(applicationId, keyId, value), + Check: resource.ComposeTestCheckFunc( + testCheckADApplicationPasswordExists(resourceName), + resource.TestCheckResourceAttrSet(resourceName, "start_date"), + resource.TestCheckResourceAttr(resourceName, "key_id", keyId), + resource.TestCheckResourceAttr(resourceName, "end_date", "2020-01-01T01:02:03Z"), + ), + }, + }, + }) +} + +func TestAccAzureADApplicationPassword_relativeEndDate(t *testing.T) { + resourceName := "azuread_application_password.test" + applicationId := uuid.New().String() + value := uuid.New().String() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckADApplicationPasswordCheckDestroy, + Steps: []resource.TestStep{ + { + Config: testAccADApplicationPassword_relativeEndDate(applicationId, value), + Check: resource.ComposeTestCheckFunc( + // can't assert on Value since it's not returned + testCheckADApplicationPasswordExists(resourceName), + resource.TestCheckResourceAttrSet(resourceName, "start_date"), + resource.TestCheckResourceAttrSet(resourceName, "key_id"), + resource.TestCheckResourceAttrSet(resourceName, "end_date"), + ), + }, + }, + }) +} + +func testAccADApplicationPassword_template(applicationId string) string { + return fmt.Sprintf(` +resource "azuread_application" "test" { + name = "acctestspa%s" +} +`, applicationId) +} + +func testAccADObjectPasswordApplication_basic(applicationId, value string) string { + return fmt.Sprintf(` +%s + +resource "azuread_application_password" "test" { + application_id = "${azuread_application.test.id}" + value = "%s" + end_date = "2020-01-01T01:02:03Z" +} +`, testAccADApplicationPassword_template(applicationId), value) +} + +func testAccADApplicationPassword_requiresImport(applicationId, value string) string { + template := testAccADObjectPasswordApplication_basic(applicationId, value) + return fmt.Sprintf(` +%s + +resource "azuread_application_password" "import" { + application_id = "${azuread_application_password.test.application_id}" + key_id = "${azuread_application_password.test.key_id}" + value = "${azuread_application_password.test.value}" + end_date = "${azuread_application_password.test.end_date}" +} +`, template) +} + +func testAccADApplicationPassword_customKeyId(applicationId, keyId, value string) string { + return fmt.Sprintf(` +%s + +resource "azuread_application_password" "test" { + application_id = "${azuread_application.test.id}" + key_id = "%s" + value = "%s" + end_date = "2020-01-01T01:02:03Z" +} +`, testAccADApplicationPassword_template(applicationId), keyId, value) +} + +func testAccADApplicationPassword_relativeEndDate(applicationId, value string) string { + return fmt.Sprintf(` +%s + +resource "azuread_application_password" "test" { + application_id = "${azuread_application.test.id}" + value = "%s" + end_date_relative = "8760h" +} +`, testAccADApplicationPassword_template(applicationId), value) +} diff --git a/azuread/resource_group.go b/azuread/resource_group.go index 9dc00a0b97..ba474785bd 100644 --- a/azuread/resource_group.go +++ b/azuread/resource_group.go @@ -5,10 +5,10 @@ import ( "log" "github.com/Azure/azure-sdk-for-go/services/graphrbac/1.6/graphrbac" + "github.com/google/uuid" "github.com/hashicorp/terraform/helper/schema" "github.com/hashicorp/terraform/helper/validation" "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/ar" - "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/guid" "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/p" ) @@ -41,7 +41,7 @@ func resourceGroupCreate(d *schema.ResourceData, meta interface{}) error { 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. - MailNickname: p.String(guid.New().String()), //this matches the portal behavior + MailNickname: p.String(uuid.New().String()), //this matches the portal behavior SecurityEnabled: p.Bool(true), //we're defaulting to true, as the API currently only supports the creation of non-mail enabled security groups. } diff --git a/azuread/resource_service_principal.go b/azuread/resource_service_principal.go index a1c2cbe485..0345e51764 100644 --- a/azuread/resource_service_principal.go +++ b/azuread/resource_service_principal.go @@ -16,7 +16,7 @@ import ( "github.com/hashicorp/terraform/helper/schema" ) -var servicePrincipalResourceName = "azuread_service_principal" +const servicePrincipalResourceName = "azuread_service_principal" func resourceServicePrincipal() *schema.Resource { return &schema.Resource{ @@ -66,7 +66,7 @@ func resourceServicePrincipalCreate(d *schema.ResourceData, meta interface{}) er AccountEnabled: p.Bool(true), } if v, ok := d.GetOk("tags"); ok { - properties.Tags = tf.ExpandStringArrayPtr(v.(*schema.Set).List()) + properties.Tags = tf.ExpandStringSlicePtr(v.(*schema.Set).List()) } sp, err := client.Create(ctx, properties) @@ -114,7 +114,7 @@ func resourceServicePrincipalRead(d *schema.ResourceData, meta interface{}) erro // tags doesn't exist as a property, so extract it if iTags, ok := app.AdditionalProperties["tags"]; ok { if tags, ok := iTags.([]interface{}); ok { - if err := d.Set("tags", tf.ExpandStringArrayPtr(tags)); err != nil { + if err := d.Set("tags", tf.ExpandStringSlicePtr(tags)); err != nil { return fmt.Errorf("Error setting `tags`: %+v", err) } } diff --git a/azuread/resource_service_principal_password.go b/azuread/resource_service_principal_password.go index 6b4d62bba7..b52b8238e2 100644 --- a/azuread/resource_service_principal_password.go +++ b/azuread/resource_service_principal_password.go @@ -3,18 +3,13 @@ package azuread import ( "fmt" "log" - "strings" "time" "github.com/Azure/azure-sdk-for-go/services/graphrbac/1.6/graphrbac" - "github.com/Azure/go-autorest/autorest/date" - "github.com/hashicorp/go-uuid" "github.com/hashicorp/terraform/helper/schema" - "github.com/hashicorp/terraform/helper/validation" "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/ar" - "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/p" + "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/graph" "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/tf" - "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/validate" ) func resourceServicePrincipalPassword() *schema.Resource { @@ -22,59 +17,12 @@ func resourceServicePrincipalPassword() *schema.Resource { Create: resourceServicePrincipalPasswordCreate, Read: resourceServicePrincipalPasswordRead, Delete: resourceServicePrincipalPasswordDelete, + Importer: &schema.ResourceImporter{ State: schema.ImportStatePassthrough, }, - Schema: map[string]*schema.Schema{ - "service_principal_id": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - ValidateFunc: validate.UUID, - }, - - "key_id": { - Type: schema.TypeString, - Optional: true, - Computed: true, - ForceNew: true, - ValidateFunc: validate.UUID, - }, - - "value": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - Sensitive: true, - ValidateFunc: validate.NoEmptyStrings, - }, - - "start_date": { - Type: schema.TypeString, - Optional: true, - Computed: true, - ForceNew: true, - ValidateFunc: validation.ValidateRFC3339TimeString, - }, - - "end_date": { - Type: schema.TypeString, - Optional: true, - Computed: true, - ForceNew: true, - ConflictsWith: []string{"end_date_relative"}, - ValidateFunc: validation.ValidateRFC3339TimeString, - }, - - "end_date_relative": { - Type: schema.TypeString, - Optional: true, - ForceNew: true, - ConflictsWith: []string{"end_date"}, - ValidateFunc: validate.NoEmptyStrings, - }, - }, + Schema: graph.PasswordResourceSchema("service_principal"), } } @@ -83,82 +31,31 @@ func resourceServicePrincipalPasswordCreate(d *schema.ResourceData, meta interfa ctx := meta.(*ArmClient).StopContext objectId := d.Get("service_principal_id").(string) - value := d.Get("value").(string) - // errors will be handled by the validation - - var keyId string - if v, ok := d.GetOk("key_id"); ok { - keyId = v.(string) - } else { - kid, err := uuid.GenerateUUID() - if err != nil { - return err - } - - keyId = kid - } - - var endDate time.Time - if v := d.Get("end_date").(string); v != "" { - endDate, _ = time.Parse(time.RFC3339, v) - } else if v := d.Get("end_date_relative").(string); v != "" { - d, err := time.ParseDuration(v) - if err != nil { - return fmt.Errorf("unable to parse `end_date_relative` (%s) as a duration", v) - } - endDate = time.Now().Add(d) - } else { - return fmt.Errorf("one of `end_date` or `end_date_relative` must be specified") - } - - credential := graphrbac.PasswordCredential{ - KeyID: p.String(keyId), - Value: p.String(value), - EndDate: &date.Time{Time: endDate}, - } - - if v, ok := d.GetOk("start_date"); ok { - // errors will be handled by the validation - startDate, _ := time.Parse(time.RFC3339, v.(string)) - credential.StartDate = &date.Time{Time: startDate} - } - azureADLockByName(objectId, servicePrincipalResourceName) - defer azureADUnlockByName(objectId, servicePrincipalResourceName) - - existingCredentials, err := client.ListPasswordCredentials(ctx, objectId) + cred, err := graph.PasswordCredentialForResource(d) if err != nil { - return fmt.Errorf("Error Listing Password Credentials for Service Principal %q: %+v", objectId, err) + return fmt.Errorf("Error generating Service Principal Credentials for Object ID %q: %+v", objectId, err) } + id := graph.PasswordCredentialIdFrom(objectId, *cred.KeyID) - id := fmt.Sprintf("%s/%s", objectId, keyId) - updatedCredentials := make([]graphrbac.PasswordCredential, 0) - if existingCredentials.Value != nil { - if requireResourcesToBeImported { - for _, v := range *existingCredentials.Value { - if v.KeyID == nil { - continue - } - - if *v.KeyID == keyId { - return tf.ImportAsExistsError("azuread_service_principal_password", id) - } - } - } + azureADLockByName(servicePrincipalResourceName, id.ObjectId) + defer azureADUnlockByName(servicePrincipalResourceName, id.ObjectId) - updatedCredentials = *existingCredentials.Value + existingCreds, err := client.ListPasswordCredentials(ctx, id.ObjectId) + if err != nil { + return fmt.Errorf("Error Listing Password Credentials for Service Principal %q: %+v", id.ObjectId, err) } - updatedCredentials = append(updatedCredentials, credential) - parameters := graphrbac.PasswordCredentialsUpdateParameters{ - Value: &updatedCredentials, + newCreds, err := graph.PasswordCredentialResultAdd(existingCreds, cred, requireResourcesToBeImported) + if err != nil { + return tf.ImportAsExistsError("azuread_service_principal_password", id.String()) } - if _, err = client.UpdatePasswordCredentials(ctx, objectId, parameters); err != nil { - return fmt.Errorf("Error creating Password Credential %q for Service Principal %q: %+v", keyId, objectId, err) + if _, err = client.UpdatePasswordCredentials(ctx, objectId, graphrbac.PasswordCredentialsUpdateParameters{Value: newCreds}); err != nil { + return fmt.Errorf("Error creating Password Credential %q for Service Principal %q: %+v", id.KeyId, id.ObjectId, err) } - d.SetId(id) + d.SetId(id.String()) return resourceServicePrincipalPasswordRead(d, meta) } @@ -167,52 +64,38 @@ func resourceServicePrincipalPasswordRead(d *schema.ResourceData, meta interface client := meta.(*ArmClient).servicePrincipalsClient ctx := meta.(*ArmClient).StopContext - id := strings.Split(d.Id(), "/") - if len(id) != 2 { - return fmt.Errorf("ID should be in the format {objectId}/{keyId} - but got %q", d.Id()) + id, err := graph.ParsePasswordCredentialId(d.Id()) + if err != nil { + return fmt.Errorf("Error parsing Application Password ID: %v", err) } - objectId := id[0] - keyId := id[1] - // ensure the parent Service Principal exists - servicePrincipal, err := client.Get(ctx, objectId) + servicePrincipal, err := client.Get(ctx, id.ObjectId) if err != nil { // the parent Service Principal has been removed - skip it if ar.ResponseWasNotFound(servicePrincipal.Response) { - log.Printf("[DEBUG] Service Principal with Object ID %q was not found - removing from state!", objectId) + log.Printf("[DEBUG] Service Principal with Object ID %q was not found - removing from state!", id.ObjectId) d.SetId("") return nil } - return fmt.Errorf("Error retrieving Service Principal ID %q: %+v", objectId, err) + return fmt.Errorf("Error retrieving Service Principal ID %q: %+v", id.ObjectId, err) } - credentials, err := client.ListPasswordCredentials(ctx, objectId) + credentials, err := client.ListPasswordCredentials(ctx, id.ObjectId) if err != nil { - return fmt.Errorf("Error Listing Password Credentials for Service Principal with Object ID %q: %+v", objectId, err) - } - - var credential *graphrbac.PasswordCredential - for _, c := range *credentials.Value { - if c.KeyID == nil { - continue - } - - if *c.KeyID == keyId { - credential = &c - break - } + return fmt.Errorf("Error Listing Password Credentials for Service Principal with Object ID %q: %+v", id.ObjectId, err) } + credential := graph.PasswordCredentialResultFindByKeyId(credentials, id.KeyId) if credential == nil { - log.Printf("[DEBUG] Service Principal Password %q (Object ID %q) was not found - removing from state!", keyId, objectId) + log.Printf("[DEBUG] Service Principal %q (ID %q) was not found - removing from state!", id.KeyId, id.ObjectId) d.SetId("") return nil } // value is available in the SDK but isn't returned from the API d.Set("key_id", credential.KeyID) - d.Set("service_principal_id", objectId) + d.Set("service_principal_id", id.ObjectId) if endDate := credential.EndDate; endDate != nil { d.Set("end_date", endDate.Format(time.RFC3339)) @@ -229,50 +112,33 @@ func resourceServicePrincipalPasswordDelete(d *schema.ResourceData, meta interfa client := meta.(*ArmClient).servicePrincipalsClient ctx := meta.(*ArmClient).StopContext - id := strings.Split(d.Id(), "/") - if len(id) != 2 { - return fmt.Errorf("ID should be in the format {objectId}/{keyId} - but got %q", d.Id()) + id, err := graph.ParsePasswordCredentialId(d.Id()) + if err != nil { + return fmt.Errorf("Error parsing Application Password ID: %v", err) } - objectId := id[0] - keyId := id[1] - - azureADLockByName(objectId, servicePrincipalResourceName) - defer azureADUnlockByName(objectId, servicePrincipalResourceName) + azureADLockByName(servicePrincipalResourceName, id.ObjectId) + defer azureADUnlockByName(servicePrincipalResourceName, id.ObjectId) // ensure the parent Service Principal exists - servicePrincipal, err := client.Get(ctx, objectId) + servicePrincipal, err := client.Get(ctx, id.ObjectId) if err != nil { // the parent Service Principal was removed - skip it if ar.ResponseWasNotFound(servicePrincipal.Response) { + log.Printf("[DEBUG] Service Principal with Object ID %q was not found - removing from state!", id.ObjectId) return nil } - - return fmt.Errorf("Error retrieving Service Principal ID %q: %+v", objectId, err) + return fmt.Errorf("Error retrieving Service Principal ID %q: %+v", id.ObjectId, err) } - existing, err := client.ListPasswordCredentials(ctx, objectId) + existing, err := client.ListPasswordCredentials(ctx, id.ObjectId) if err != nil { - return fmt.Errorf("Error Listing Password Credentials for Service Principal with Object ID %q: %+v", objectId, err) - } - - updatedCredentials := make([]graphrbac.PasswordCredential, 0) - for _, credential := range *existing.Value { - if credential.KeyID == nil { - continue - } - - if *credential.KeyID != keyId { - updatedCredentials = append(updatedCredentials, credential) - } + return fmt.Errorf("Error Listing Password Credentials for Service Principal with Object ID %q: %+v", id.ObjectId, err) } - parameters := graphrbac.PasswordCredentialsUpdateParameters{ - Value: &updatedCredentials, - } - _, err = client.UpdatePasswordCredentials(ctx, objectId, parameters) - if err != nil { - return fmt.Errorf("Error removing Password %q from Service Principal %q: %+v", keyId, objectId, err) + newCreds := graph.PasswordCredentialResultRemoveByKeyId(existing, id.KeyId) + if _, err = client.UpdatePasswordCredentials(ctx, id.ObjectId, graphrbac.PasswordCredentialsUpdateParameters{Value: newCreds}); err != nil { + return fmt.Errorf("Error removing Password %q from Service Principal %q: %+v", id.KeyId, id.ObjectId, err) } return nil diff --git a/azuread/resource_service_principal_password_test.go b/azuread/resource_service_principal_password_test.go index e6131a4a2b..ccdfad41d5 100644 --- a/azuread/resource_service_principal_password_test.go +++ b/azuread/resource_service_principal_password_test.go @@ -2,31 +2,91 @@ package azuread import ( "fmt" - "strings" "testing" "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/ar" + "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/graph" - "github.com/hashicorp/go-uuid" + "github.com/google/uuid" "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" ) -func TestAccAzureADServicePrincipalPassword_basic(t *testing.T) { - resourceName := "azuread_service_principal_password.test" - applicationId, err := uuid.GenerateUUID() - if err != nil { - t.Fatal(err) +func testCheckADServicePrincipalPasswordExists(name string) resource.TestCheckFunc { //nolint unparam + return func(s *terraform.State) error { + client := testAccProvider.Meta().(*ArmClient).servicePrincipalsClient + ctx := testAccProvider.Meta().(*ArmClient).StopContext + + rs, ok := s.RootModule().Resources[name] + if !ok { + return fmt.Errorf("Not found: %q", name) + } + + id, err := graph.ParsePasswordCredentialId(rs.Primary.ID) + if err != nil { + return fmt.Errorf("error Service Principal Password Credential ID: %v", err) + } + + resp, err := client.Get(ctx, id.ObjectId) + if err != nil { + if ar.ResponseWasNotFound(resp.Response) { + return fmt.Errorf("Bad: Azure AD Service Principal %q does not exist", id.ObjectId) + } + return fmt.Errorf("Bad: Get on Azure AD servicePrincipalsClient: %+v", err) + } + + credentials, err := client.ListPasswordCredentials(ctx, id.ObjectId) + if err != nil { + return fmt.Errorf("Error Listing Password Credentials for Service Principal %q: %+v", id.ObjectId, err) + } + + cred := graph.PasswordCredentialResultFindByKeyId(credentials, id.KeyId) + if cred != nil { + return nil + } + + return fmt.Errorf("Password Credential %q was not found in Service Principal %q", id.KeyId, id.ObjectId) } - value, err := uuid.GenerateUUID() - if err != nil { - t.Fatal(err) +} + +func testCheckADServicePrincipalPasswordCheckDestroy(s *terraform.State) error { + for _, rs := range s.RootModule().Resources { + client := testAccProvider.Meta().(*ArmClient).applicationsClient + ctx := testAccProvider.Meta().(*ArmClient).StopContext + + if rs.Type != "azuread_service_principal_password" { + continue + } + + id, err := graph.ParsePasswordCredentialId(rs.Primary.ID) + if err != nil { + return fmt.Errorf("error parsing Service Principal Password Credential ID: %v", err) + } + + resp, err := client.Get(ctx, id.ObjectId) + if err != nil { + if ar.ResponseWasNotFound(resp.Response) { + return nil + } + + return err + } + + return fmt.Errorf("Azure AD Service Principal Password Credential still exists:\n%#v", resp) } + return nil +} + +func TestAccAzureADServicePrincipalPassword_basic(t *testing.T) { + resourceName := "azuread_service_principal_password.test" + applicationId := uuid.New().String() + value := uuid.New().String() + resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, Providers: testAccProviders, - CheckDestroy: testCheckADServicePrincipalDestroy, + CheckDestroy: testCheckADServicePrincipalPasswordCheckDestroy, Steps: []resource.TestStep{ { Config: testAccADServicePrincipalPassword_basic(applicationId, value), @@ -49,19 +109,13 @@ func TestAccAzureADServicePrincipalPassword_requiresImport(t *testing.T) { } resourceName := "azuread_service_principal_password.test" - applicationId, err := uuid.GenerateUUID() - if err != nil { - t.Fatal(err) - } - value, err := uuid.GenerateUUID() - if err != nil { - t.Fatal(err) - } + applicationId := uuid.New().String() + value := uuid.New().String() resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, Providers: testAccProviders, - CheckDestroy: testCheckADServicePrincipalDestroy, + CheckDestroy: testCheckADServicePrincipalPasswordCheckDestroy, Steps: []resource.TestStep{ { Config: testAccADServicePrincipalPassword_basic(applicationId, value), @@ -79,23 +133,14 @@ func TestAccAzureADServicePrincipalPassword_requiresImport(t *testing.T) { func TestAccAzureADServicePrincipalPassword_customKeyId(t *testing.T) { resourceName := "azuread_service_principal_password.test" - applicationId, err := uuid.GenerateUUID() - if err != nil { - t.Fatal(err) - } - keyId, err := uuid.GenerateUUID() - if err != nil { - t.Fatal(err) - } - value, err := uuid.GenerateUUID() - if err != nil { - t.Fatal(err) - } + applicationId := uuid.New().String() + keyId := uuid.New().String() + value := uuid.New().String() resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, Providers: testAccProviders, - CheckDestroy: testCheckADServicePrincipalDestroy, + CheckDestroy: testCheckADServicePrincipalPasswordCheckDestroy, Steps: []resource.TestStep{ { Config: testAccADServicePrincipalPassword_customKeyId(applicationId, keyId, value), @@ -113,19 +158,13 @@ func TestAccAzureADServicePrincipalPassword_customKeyId(t *testing.T) { func TestAccAzureADServicePrincipalPassword_relativeEndDate(t *testing.T) { resourceName := "azuread_service_principal_password.test" - applicationId, err := uuid.GenerateUUID() - if err != nil { - t.Fatal(err) - } - value, err := uuid.GenerateUUID() - if err != nil { - t.Fatal(err) - } + applicationId := uuid.New().String() + value := uuid.New().String() resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, Providers: testAccProviders, - CheckDestroy: testCheckADServicePrincipalDestroy, + CheckDestroy: testCheckADServicePrincipalPasswordCheckDestroy, Steps: []resource.TestStep{ { Config: testAccADServicePrincipalPassword_relativeEndDate(applicationId, value), @@ -141,47 +180,6 @@ func TestAccAzureADServicePrincipalPassword_relativeEndDate(t *testing.T) { }) } -func testCheckADServicePrincipalPasswordExists(name string) resource.TestCheckFunc { //nolint unparam - return func(s *terraform.State) error { - rs, ok := s.RootModule().Resources[name] - if !ok { - return fmt.Errorf("Not found: %q", name) - } - - client := testAccProvider.Meta().(*ArmClient).servicePrincipalsClient - ctx := testAccProvider.Meta().(*ArmClient).StopContext - - id := strings.Split(rs.Primary.ID, "/") - objectId := id[0] - keyId := id[1] - resp, err := client.Get(ctx, objectId) - - if err != nil { - if ar.ResponseWasNotFound(resp.Response) { - return fmt.Errorf("Bad: Azure AD Service Principal %q does not exist", objectId) - } - return fmt.Errorf("Bad: Get on Azure AD servicePrincipalsClient: %+v", err) - } - - credentials, err := client.ListPasswordCredentials(ctx, objectId) - if err != nil { - return fmt.Errorf("Error Listing Password Credentials for Service Principal %q: %+v", objectId, err) - } - - for _, credential := range *credentials.Value { - if credential.KeyID == nil { - continue - } - - if *credential.KeyID == keyId { - return nil - } - } - - return fmt.Errorf("Password Credential %q was not found in Service Principal %q", keyId, objectId) - } -} - func testAccADServicePrincipalPassword_template(applicationId string) string { return fmt.Sprintf(` resource "azuread_application" "test" { diff --git a/website/azuread.erb b/website/azuread.erb index 8a4692bd27..0fc53de4d5 100644 --- a/website/azuread.erb +++ b/website/azuread.erb @@ -81,6 +81,10 @@ azuread_application + > + azuread_application_password + + > azuread_group diff --git a/website/docs/r/application_password.html.markdown b/website/docs/r/application_password.html.markdown new file mode 100644 index 0000000000..bcc383d894 --- /dev/null +++ b/website/docs/r/application_password.html.markdown @@ -0,0 +1,68 @@ +--- +layout: "azuread" +page_title: "Azure Active Directory: azuread_application_password" +sidebar_current: "docs-azuread-resource-azuread-application-password" +description: |- + Manages a Password associated with an Application within Azure Active Directory. + +--- + +# azuread_application_password + +Manages a Password associated with an Application within Azure Active Directory. + +-> **NOTE:** If you're authenticating using a Service Principal then it must have permissions to both `Read and write all applications` and `Sign in and read user profile` within the `Windows Azure Active Directory` API. + +## Example Usage + +```hcl +resource "azuread_application" "example" { + name = "example" + homepage = "http://homepage" + identifier_uris = ["http://uri"] + reply_urls = ["http://replyurl"] + available_to_other_tenants = false + oauth2_allow_implicit_flow = true +} + +resource "azuread_application_password" "example" { + application_id = "${azuread_application.example.id}" + value = "VT=uSgbTanZhyz@%nL9Hpd+Tfay_MRV#" + end_date = "2020-01-01T01:02:03Z" +} +``` + +## Argument Reference + +The following arguments are supported: + +* `object_id` - (Required) The Object ID of the Application for which this password should be created. Changing this field forces a new resource to be created. + +* `value` - (Required) The Password for this Application . + +* `end_date` - (Optional) The End Date which the Password is valid until, formatted as a RFC3339 date string (e.g. `2018-01-01T01:02:03Z`). Changing this field forces a new resource to be created. + +* `end_date_relative` - (Optional) A relative duration for which the Password is valid until, for example `240h` (10 days) or `2400h30m`. Changing this field forces a new resource to be created. + +-> **NOTE:** One of `end_date` or `end_date_relative` must be set. + +* `key_id` - (Optional) A GUID used to uniquely identify this Password. If not specified a GUID will be created. Changing this field forces a new resource to be created. + +* `start_date` - (Optional) The Start Date which the Password is valid from, formatted as a RFC3339 date string (e.g. `2018-01-01T01:02:03Z`). If this isn't specified, the current date is used. Changing this field forces a new resource to be created. + + +## Attributes Reference + +The following attributes are exported: + +* `id` - The Key ID for the Password. + +## Import + +Passwords can be imported using the `object id` of an Application, e.g. + +```shell +terraform import azuread_application_password.test 00000000-0000-0000-0000-000000000000/11111111-1111-1111-1111-111111111111 +``` + +-> **NOTE:** This ID format is unique to Terraform and is composed of the Application's Object ID and the Password's Key ID in the format `{ObjectId}/{PasswordKeyId}`. From fc5481e7ce89e2ff411690e002265d0a61521fa7 Mon Sep 17 00:00:00 2001 From: kt Date: Sun, 26 May 2019 12:11:52 -0700 Subject: [PATCH 02/45] Update CHANGELOG,md to include #71 --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 12ae1e5bbb..29cc0878ca 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ ## 0.4.0 (Unreleased) + +FEATURES: + +* **New Resource:** `azuread_application_password` [GH-71] + IMPROVEMENTS: * dependencies: upgrading to `v0.12.0` of `github.com/hashicorp/terraform` [GH-82] From ac258382a782bf22786e8c4a3e663dc965c5584d Mon Sep 17 00:00:00 2001 From: kt Date: Sun, 26 May 2019 12:33:37 -0700 Subject: [PATCH 03/45] travis enhancements (#85) --- .travis.yml | 33 ++++++++++++++++++++------------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/.travis.yml b/.travis.yml index 1f7a4d34cf..9ea72be7c1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,10 +1,19 @@ dist: trusty sudo: required services: -- docker + - docker language: go go: -- "1.11.x" + - "1.12.x" + +branches: + only: + - master + +matrix: + fast_finish: true + allow_failures: + - go: tip install: # This script is used by the Travis build to install a cookie for @@ -14,15 +23,13 @@ install: - bash scripts/gogetcookie.sh - make tools -script: -- make test -- make lint -- make website-test +env: + matrix: + - MODE=unit-tests + - MODE=linters + - MODE=website -branches: - only: - - master -matrix: - fast_finish: true - allow_failures: - - go: tip +script: + - if [[ $MODE == 'unit-tests' ]]; then make test; fi + - if [[ $MODE == 'linters' ]]; then make lint; fi + - if [[ $MODE == 'website' ]]; then make website-test; fi From 4db152590d725865370e273082dea614690c88ff Mon Sep 17 00:00:00 2001 From: Eugene Chuvyrov Date: Wed, 29 May 2019 09:06:35 -0700 Subject: [PATCH 04/45] Added appId output to the documentation - esp. important for AKS (#87) --- website/docs/r/service_principal.html.markdown | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/website/docs/r/service_principal.html.markdown b/website/docs/r/service_principal.html.markdown index 7dd5e40092..4c6d934130 100644 --- a/website/docs/r/service_principal.html.markdown +++ b/website/docs/r/service_principal.html.markdown @@ -44,7 +44,9 @@ The following arguments are supported: The following attributes are exported: -* `id` - The Object ID for the Service Principal. +* `id` - The Object ID (internal ID) for the Service Principal. + +* `application_id` - The Application ID (appId) for the Service Principal. * `display_name` - The Display Name of the Azure Active Directory Application associated with this Service Principal. From b1d365e220fb5c102a27a05b7e88c9582cf557b4 Mon Sep 17 00:00:00 2001 From: kt Date: Wed, 29 May 2019 09:15:34 -0700 Subject: [PATCH 05/45] Application & Service Principal Creation should now wait on replication (#86) Should fix #4 (or at least help) --- azuread/data_application.go | 8 +++----- azuread/resource_application.go | 17 ++++++++++++++--- azuread/resource_service_principal.go | 22 ++++++++++++---------- azuread/resource_user.go | 1 - 4 files changed, 29 insertions(+), 19 deletions(-) diff --git a/azuread/data_application.go b/azuread/data_application.go index 1ce102f633..3480a20d15 100644 --- a/azuread/data_application.go +++ b/azuread/data_application.go @@ -3,12 +3,12 @@ package azuread import ( "fmt" + "github.com/Azure/azure-sdk-for-go/services/graphrbac/1.6/graphrbac" + "github.com/hashicorp/terraform/helper/schema" + "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/ar" "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/tf" "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/validate" - - "github.com/Azure/azure-sdk-for-go/services/graphrbac/1.6/graphrbac" - "github.com/hashicorp/terraform/helper/schema" ) func dataApplication() *schema.Resource { @@ -168,7 +168,6 @@ func dataApplicationRead(d *schema.ResourceData, meta interface{}) error { var app graphrbac.Application if oId, ok := d.GetOk("object_id"); ok { - // use the object_id to find the Azure AD application objectId := oId.(string) resp, err := client.Get(ctx, objectId) @@ -182,7 +181,6 @@ func dataApplicationRead(d *schema.ResourceData, meta interface{}) error { app = resp } else { - // use the name to find the Azure AD application name := d.Get("name").(string) filter := fmt.Sprintf("displayName eq '%s'", name) diff --git a/azuread/resource_application.go b/azuread/resource_application.go index 1271b33057..53fde6760f 100644 --- a/azuread/resource_application.go +++ b/azuread/resource_application.go @@ -3,8 +3,10 @@ package azuread import ( "fmt" "log" + "time" "github.com/Azure/azure-sdk-for-go/services/graphrbac/1.6/graphrbac" + "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/helper/schema" "github.com/hashicorp/terraform/helper/validation" "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/ar" @@ -222,10 +224,21 @@ func resourceApplicationCreate(d *schema.ResourceData, meta interface{}) error { if err != nil { return err } - if app.ObjectID == nil { return fmt.Errorf("Application objectId is nil") } + d.SetId(*app.ObjectID) + + // mimicking the behaviour of az tool retry until a successful get + if err := resource.Retry(3*time.Minute, func() *resource.RetryError { + if _, err := client.Get(ctx, *app.ObjectID); err != nil { + return resource.RetryableError(err) + } + + return nil + }); err != nil { + return fmt.Errorf("Error waiting for Application %q to become available: %+v", name, err) + } // follow suggested hack for azure-cli // AAD graph doesn't have the API to create a native app, aka public client, the recommended hack is @@ -244,8 +257,6 @@ func resourceApplicationCreate(d *schema.ResourceData, meta interface{}) error { } } - d.SetId(*app.ObjectID) - return resourceApplicationRead(d, meta) } diff --git a/azuread/resource_service_principal.go b/azuread/resource_service_principal.go index 0345e51764..db189a0555 100644 --- a/azuread/resource_service_principal.go +++ b/azuread/resource_service_principal.go @@ -3,7 +3,9 @@ package azuread import ( "fmt" "log" + "time" + "github.com/hashicorp/terraform/helper/resource" "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/tf" "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/validate" @@ -73,21 +75,21 @@ func resourceServicePrincipalCreate(d *schema.ResourceData, meta interface{}) er if err != nil { return fmt.Errorf("Error creating Service Principal for application %q: %+v", applicationId, err) } - if sp.ObjectID == nil { - return fmt.Errorf("Create returned a nil object id for application %q", applicationId) + return fmt.Errorf("Service Principal objectID is nil") } - objectId := *sp.ObjectID + d.SetId(*sp.ObjectID) - resp, err := client.Get(ctx, objectId) - if err != nil { - return fmt.Errorf("Error retrieving Service Principal with ID %q: %+v", objectId, err) - } + // mimicking the behaviour of az tool retry until a successful get + if err := resource.Retry(3*time.Minute, func() *resource.RetryError { + if _, err := client.Get(ctx, *sp.ObjectID); err != nil { + return resource.RetryableError(err) + } - if resp.ObjectID == nil { - return fmt.Errorf("Get returned a nil object ID for %q", objectId) + return nil + }); err != nil { + return fmt.Errorf("Error waiting for Service Principal %q to become available: %+v", applicationId, err) } - d.SetId(*resp.ObjectID) return resourceServicePrincipalRead(d, meta) } diff --git a/azuread/resource_user.go b/azuread/resource_user.go index 21c4c20bf3..0d09abda85 100644 --- a/azuread/resource_user.go +++ b/azuread/resource_user.go @@ -108,7 +108,6 @@ func resourceUserCreate(d *schema.ResourceData, meta interface{}) error { if err != nil { return fmt.Errorf("Error retrieving User (%q) with ObjectID %q: %+v", userPrincipalName, *objectId, err) } - if resp.ObjectID == nil { return fmt.Errorf("User objectId is nil") } From ac94dea6922c985d71f42fb4dbb10021023addff Mon Sep 17 00:00:00 2001 From: kt Date: Wed, 29 May 2019 09:20:30 -0700 Subject: [PATCH 06/45] Update CHANGELOG.md to include #86 --- CHANGELOG.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 29cc0878ca..40193fb991 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ FEATURES: * **New Resource:** `azuread_application_password` [GH-71] -IMPROVEMENTS: +IMPROVEMENTS:Application & Service Principal Creation should now wait on replication #86 * dependencies: upgrading to `v0.12.0` of `github.com/hashicorp/terraform` [GH-82] * Data Source `azuread_application` - now exports the `group_membership_claims` property [GH-78] @@ -13,6 +13,8 @@ IMPROVEMENTS: * `azuread_application` - support for the `group_membership_claims` property [GH-78] * `azuread_application` - now exports the `oauth2_permissions` property [GH-79] * `azuread_application` - support for the `type` property enabling the creation of `native` applications [GH-74] +* `azuread_application` - will now wait for replication by waiting for a successful get [GH-86] +* `azuread_service_principal` - will now wait for replication by waiting for a successful get [GH-86] * `azuread_user` - increase the maximum allowed lengh of `password` to 256 [GH-81] ## 0.3.1 (April 18, 2019) From 97126510681eb3ad8405fae623ad4c75b3ca2380 Mon Sep 17 00:00:00 2001 From: kt Date: Wed, 29 May 2019 09:21:44 -0700 Subject: [PATCH 07/45] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 40193fb991..03ff3adc0b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ FEATURES: * **New Resource:** `azuread_application_password` [GH-71] -IMPROVEMENTS:Application & Service Principal Creation should now wait on replication #86 +IMPROVEMENTS: * dependencies: upgrading to `v0.12.0` of `github.com/hashicorp/terraform` [GH-82] * Data Source `azuread_application` - now exports the `group_membership_claims` property [GH-78] From b39d6fa06b0730a0196312c00dcc6e0358adfb9b Mon Sep 17 00:00:00 2001 From: Steve Hawkins Date: Fri, 31 May 2019 13:30:41 +0100 Subject: [PATCH 08/45] removed reply_urls validation (#89) fixes #88 --- azuread/resource_application.go | 2 +- azuread/resource_application_test.go | 42 ++++++++++++++++++++++++++-- 2 files changed, 41 insertions(+), 3 deletions(-) diff --git a/azuread/resource_application.go b/azuread/resource_application.go index 53fde6760f..6b831e1929 100644 --- a/azuread/resource_application.go +++ b/azuread/resource_application.go @@ -58,7 +58,7 @@ func resourceApplication() *schema.Resource { Computed: true, Elem: &schema.Schema{ Type: schema.TypeString, - ValidateFunc: validate.URLIsHTTPOrHTTPS, + ValidateFunc: validate.NoEmptyStrings, }, }, diff --git a/azuread/resource_application_test.go b/azuread/resource_application_test.go index b0713cf7a1..2152c8514f 100644 --- a/azuread/resource_application_test.go +++ b/azuread/resource_application_test.go @@ -5,11 +5,10 @@ import ( "regexp" "testing" - "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/ar" - "github.com/google/uuid" "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" + "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/ar" ) func TestAccAzureADApplication_basic(t *testing.T) { @@ -203,6 +202,35 @@ func TestAccAzureADApplication_native(t *testing.T) { }) } +func TestAccAzureADApplication_nativeReplyUrls(t *testing.T) { + resourceName := "azuread_application.test" + id := uuid.New().String() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckADApplicationDestroy, + Steps: []resource.TestStep{ + { + Config: testAccADApplication_nativeReplyUrls(id), + Check: resource.ComposeTestCheckFunc( + testCheckADApplicationExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "name", fmt.Sprintf("acctest%s", id)), + resource.TestCheckResourceAttr(resourceName, "type", "native"), + resource.TestCheckResourceAttr(resourceName, "reply_urls.#", "1"), + resource.TestCheckResourceAttr(resourceName, "reply_urls.3637476042", "urn:ietf:wg:oauth:2.0:oob"), + resource.TestCheckResourceAttrSet(resourceName, "application_id"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + func TestAccAzureADApplication_nativeUpdate(t *testing.T) { resourceName := "azuread_application.test" id := uuid.New().String() @@ -414,6 +442,16 @@ resource "azuread_application" "test" { `, id) } +func testAccADApplication_nativeReplyUrls(id string) string { + return fmt.Sprintf(` +resource "azuread_application" "test" { + name = "acctest%s" + type = "native" + reply_urls = ["urn:ietf:wg:oauth:2.0:oob"] +} +`, id) +} + func testAccADApplication_native_app_does_not_allow_identifier_uris(id string) string { return fmt.Sprintf(` resource "azuread_application" "test" { From a5aa644f92e67b57f860707bf3e16ffb75ed0e92 Mon Sep 17 00:00:00 2001 From: kt Date: Tue, 4 Jun 2019 11:21:17 -0700 Subject: [PATCH 09/45] enhance application and SP replication wait (#93) refinement of #86 --- azuread/provider.go | 4 ++++ azuread/resource_application.go | 30 +++++++++++++++++++-------- azuread/resource_service_principal.go | 28 ++++++++++++++++++------- 3 files changed, 45 insertions(+), 17 deletions(-) diff --git a/azuread/provider.go b/azuread/provider.go index 1daa673133..4144e8118d 100644 --- a/azuread/provider.go +++ b/azuread/provider.go @@ -2,6 +2,7 @@ package azuread import ( "fmt" + "time" "github.com/hashicorp/go-azure-helpers/authentication" "github.com/hashicorp/terraform/helper/mutexkv" @@ -12,6 +13,9 @@ import ( // armMutexKV is the instance of MutexKV for ARM resources var armMutexKV = mutexkv.NewMutexKV() +const azureAdReplicationTimeout = 5 * time.Minute +const azureAdReplicationTargetOccurence = 10 + // Provider returns a terraform.ResourceProvider. func Provider() terraform.ResourceProvider { p := &schema.Provider{ diff --git a/azuread/resource_application.go b/azuread/resource_application.go index 6b831e1929..66aaad53d3 100644 --- a/azuread/resource_application.go +++ b/azuread/resource_application.go @@ -229,16 +229,28 @@ func resourceApplicationCreate(d *schema.ResourceData, meta interface{}) error { } d.SetId(*app.ObjectID) - // mimicking the behaviour of az tool retry until a successful get - if err := resource.Retry(3*time.Minute, func() *resource.RetryError { - if _, err := client.Get(ctx, *app.ObjectID); err != nil { - return resource.RetryableError(err) - } - - return nil - }); err != nil { - return fmt.Errorf("Error waiting for Application %q to become available: %+v", name, err) + i, err := (&resource.StateChangeConf{ + Pending: []string{"404"}, + Target: []string{"Found"}, + Timeout: azureAdReplicationTimeout, + MinTimeout: 1 * time.Second, + ContinuousTargetOccurence: azureAdReplicationTargetOccurence, + Refresh: func() (interface{}, string, error) { + resp, err2 := client.Get(ctx, *app.ObjectID) + if err2 != nil { + if ar.ResponseWasNotFound(resp.Response) { + return resp, "404", nil + } + return resp, "Error", fmt.Errorf("Error retrieving Application ID %q: %+v", *app.ObjectID, err2) + } + + return resp, "Found", nil + }, + }).WaitForState() + if err != nil { + return fmt.Errorf("Error waiting for application: %+v", err) } + app = i.(graphrbac.Application) // follow suggested hack for azure-cli // AAD graph doesn't have the API to create a native app, aka public client, the recommended hack is diff --git a/azuread/resource_service_principal.go b/azuread/resource_service_principal.go index db189a0555..c405cd905a 100644 --- a/azuread/resource_service_principal.go +++ b/azuread/resource_service_principal.go @@ -80,16 +80,28 @@ func resourceServicePrincipalCreate(d *schema.ResourceData, meta interface{}) er } d.SetId(*sp.ObjectID) - // mimicking the behaviour of az tool retry until a successful get - if err := resource.Retry(3*time.Minute, func() *resource.RetryError { - if _, err := client.Get(ctx, *sp.ObjectID); err != nil { - return resource.RetryableError(err) - } + i, err := (&resource.StateChangeConf{ + Pending: []string{"404"}, + Target: []string{"Found"}, + Timeout: azureAdReplicationTimeout, + MinTimeout: 1 * time.Second, + ContinuousTargetOccurence: azureAdReplicationTargetOccurence, + Refresh: func() (interface{}, string, error) { + resp, err2 := client.Get(ctx, *sp.ObjectID) + if err2 != nil { + if ar.ResponseWasNotFound(resp.Response) { + return resp, "404", nil + } + return resp, "Error", fmt.Errorf("Error retrieving Service Principal ID %q: %+v", *sp.ObjectID, err2) + } - return nil - }); err != nil { - return fmt.Errorf("Error waiting for Service Principal %q to become available: %+v", applicationId, err) + return resp, "Found", nil + }, + }).WaitForState() + if err != nil { + return fmt.Errorf("Error waiting for application: %+v", err) } + sp = i.(graphrbac.ServicePrincipal) return resourceServicePrincipalRead(d, meta) } From dc0f5c5b27fd410de1613d0b22fb1e51b01c4aff Mon Sep 17 00:00:00 2001 From: kt Date: Tue, 4 Jun 2019 11:38:19 -0700 Subject: [PATCH 10/45] azuread_[group|user]: wait for replication (#91) fixs #72 --- azuread/helpers/graph/replication.go | 35 +++++++++++++++++++++++++++ azuread/provider.go | 4 --- azuread/resource_application.go | 28 +++++---------------- azuread/resource_group.go | 16 +++++++++--- azuread/resource_service_principal.go | 27 ++++----------------- azuread/resource_user.go | 28 ++++++++++----------- 6 files changed, 73 insertions(+), 65 deletions(-) create mode 100644 azuread/helpers/graph/replication.go diff --git a/azuread/helpers/graph/replication.go b/azuread/helpers/graph/replication.go new file mode 100644 index 0000000000..20ee76f8b5 --- /dev/null +++ b/azuread/helpers/graph/replication.go @@ -0,0 +1,35 @@ +package graph + +import ( + "fmt" + "time" + + "github.com/Azure/go-autorest/autorest" + "github.com/hashicorp/terraform/helper/resource" + "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/ar" +) + +func WaitForReplication(f func() (interface{}, error)) (interface{}, error) { + return (&resource.StateChangeConf{ + Pending: []string{"404", "BadCast"}, + Target: []string{"Found"}, + Timeout: 5 * time.Minute, + MinTimeout: 1 * time.Second, + ContinuousTargetOccurence: 10, + Refresh: func() (interface{}, string, error) { + i, err := f() + if err != nil { + r, ok := i.(autorest.Response) + if !ok { + return i, "BadCast", nil // sometimes the SDK bubbles up an entirely empty object + } + if ar.ResponseWasNotFound(r) { + return i, "404", nil + } + return i, "Error", fmt.Errorf("Error calling f, response was not 404 (%d): %v", r.StatusCode, err) + } + + return i, "Found", nil + }, + }).WaitForState() +} diff --git a/azuread/provider.go b/azuread/provider.go index 4144e8118d..1daa673133 100644 --- a/azuread/provider.go +++ b/azuread/provider.go @@ -2,7 +2,6 @@ package azuread import ( "fmt" - "time" "github.com/hashicorp/go-azure-helpers/authentication" "github.com/hashicorp/terraform/helper/mutexkv" @@ -13,9 +12,6 @@ import ( // armMutexKV is the instance of MutexKV for ARM resources var armMutexKV = mutexkv.NewMutexKV() -const azureAdReplicationTimeout = 5 * time.Minute -const azureAdReplicationTargetOccurence = 10 - // Provider returns a terraform.ResourceProvider. func Provider() terraform.ResourceProvider { p := &schema.Provider{ diff --git a/azuread/resource_application.go b/azuread/resource_application.go index 66aaad53d3..079d924a6a 100644 --- a/azuread/resource_application.go +++ b/azuread/resource_application.go @@ -3,13 +3,13 @@ package azuread import ( "fmt" "log" - "time" "github.com/Azure/azure-sdk-for-go/services/graphrbac/1.6/graphrbac" - "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/helper/schema" "github.com/hashicorp/terraform/helper/validation" "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/ar" + "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/graph" "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/p" "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/tf" "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/validate" @@ -229,28 +229,12 @@ func resourceApplicationCreate(d *schema.ResourceData, meta interface{}) error { } d.SetId(*app.ObjectID) - i, err := (&resource.StateChangeConf{ - Pending: []string{"404"}, - Target: []string{"Found"}, - Timeout: azureAdReplicationTimeout, - MinTimeout: 1 * time.Second, - ContinuousTargetOccurence: azureAdReplicationTargetOccurence, - Refresh: func() (interface{}, string, error) { - resp, err2 := client.Get(ctx, *app.ObjectID) - if err2 != nil { - if ar.ResponseWasNotFound(resp.Response) { - return resp, "404", nil - } - return resp, "Error", fmt.Errorf("Error retrieving Application ID %q: %+v", *app.ObjectID, err2) - } - - return resp, "Found", nil - }, - }).WaitForState() + _, err = graph.WaitForReplication(func() (interface{}, error) { + return client.Get(ctx, *app.ObjectID) + }) if err != nil { - return fmt.Errorf("Error waiting for application: %+v", err) + return fmt.Errorf("Error waiting for Application with ObjectId %q: %+v", *app.ObjectID, err) } - app = i.(graphrbac.Application) // follow suggested hack for azure-cli // AAD graph doesn't have the API to create a native app, aka public client, the recommended hack is diff --git a/azuread/resource_group.go b/azuread/resource_group.go index ba474785bd..c9d890be1f 100644 --- a/azuread/resource_group.go +++ b/azuread/resource_group.go @@ -9,6 +9,7 @@ import ( "github.com/hashicorp/terraform/helper/schema" "github.com/hashicorp/terraform/helper/validation" "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/ar" + "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/graph" "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/p" ) @@ -17,6 +18,7 @@ func resourceGroup() *schema.Resource { Create: resourceGroupCreate, Read: resourceGroupRead, Delete: resourceGroupDelete, + Importer: &schema.ResourceImporter{ State: schema.ImportStatePassthrough, }, @@ -47,11 +49,20 @@ func resourceGroupCreate(d *schema.ResourceData, meta interface{}) error { group, err := client.Create(ctx, properties) if err != nil { - return err + return fmt.Errorf("Error creating Group (%q): %+v", name, err) + } + if group.ObjectID == nil { + return fmt.Errorf("nil Group ID for %q: %+v", name, err) } - d.SetId(*group.ObjectID) + _, err = graph.WaitForReplication(func() (interface{}, error) { + return client.Get(ctx, *group.ObjectID) + }) + if err != nil { + return fmt.Errorf("Error waiting for Group (%s) with ObjectId %q: %+v", name, *group.ObjectID, err) + } + return resourceGroupRead(d, meta) } @@ -71,7 +82,6 @@ func resourceGroupRead(d *schema.ResourceData, meta interface{}) error { } d.Set("name", resp.DisplayName) - return nil } diff --git a/azuread/resource_service_principal.go b/azuread/resource_service_principal.go index c405cd905a..615b849dbe 100644 --- a/azuread/resource_service_principal.go +++ b/azuread/resource_service_principal.go @@ -3,9 +3,8 @@ package azuread import ( "fmt" "log" - "time" - "github.com/hashicorp/terraform/helper/resource" + "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/graph" "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/tf" "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/validate" @@ -80,28 +79,12 @@ func resourceServicePrincipalCreate(d *schema.ResourceData, meta interface{}) er } d.SetId(*sp.ObjectID) - i, err := (&resource.StateChangeConf{ - Pending: []string{"404"}, - Target: []string{"Found"}, - Timeout: azureAdReplicationTimeout, - MinTimeout: 1 * time.Second, - ContinuousTargetOccurence: azureAdReplicationTargetOccurence, - Refresh: func() (interface{}, string, error) { - resp, err2 := client.Get(ctx, *sp.ObjectID) - if err2 != nil { - if ar.ResponseWasNotFound(resp.Response) { - return resp, "404", nil - } - return resp, "Error", fmt.Errorf("Error retrieving Service Principal ID %q: %+v", *sp.ObjectID, err2) - } - - return resp, "Found", nil - }, - }).WaitForState() + _, err = graph.WaitForReplication(func() (interface{}, error) { + return client.Get(ctx, *sp.ObjectID) + }) if err != nil { - return fmt.Errorf("Error waiting for application: %+v", err) + return fmt.Errorf("Error waiting for Service Pricipal with ObjectId %q: %+v", *sp.ObjectID, err) } - sp = i.(graphrbac.ServicePrincipal) return resourceServicePrincipalRead(d, meta) } diff --git a/azuread/resource_user.go b/azuread/resource_user.go index 0d09abda85..fc0205ba62 100644 --- a/azuread/resource_user.go +++ b/azuread/resource_user.go @@ -9,6 +9,7 @@ import ( "github.com/hashicorp/terraform/helper/schema" "github.com/hashicorp/terraform/helper/validation" "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/ar" + "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/graph" "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/p" "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/validate" ) @@ -19,6 +20,7 @@ func resourceUser() *schema.Resource { Read: resourceUserRead, Update: resourceUserUpdate, Delete: resourceUserDelete, + Importer: &schema.ResourceImporter{ State: schema.ImportStatePassthrough, }, @@ -74,7 +76,7 @@ func resourceUserCreate(d *schema.ResourceData, meta interface{}) error { client := meta.(*ArmClient).usersClient ctx := meta.(*ArmClient).StopContext - userPrincipalName := d.Get("user_principal_name").(string) + upn := d.Get("user_principal_name").(string) displayName := d.Get("display_name").(string) mailNickName := d.Get("mail_nickname").(string) accountEnabled := d.Get("account_enabled").(bool) @@ -83,7 +85,7 @@ func resourceUserCreate(d *schema.ResourceData, meta interface{}) error { //default mail nickname to the first part of the UPN (matches the portal) if mailNickName == "" { - mailNickName = strings.Split(userPrincipalName, "@")[0] + mailNickName = strings.Split(upn, "@")[0] } userCreateParameters := graphrbac.UserCreateParameters{ @@ -94,25 +96,24 @@ func resourceUserCreate(d *schema.ResourceData, meta interface{}) error { ForceChangePasswordNextLogin: &forcePasswordChange, Password: &password, }, - UserPrincipalName: &userPrincipalName, + UserPrincipalName: &upn, } user, err := client.Create(ctx, userCreateParameters) if err != nil { - return fmt.Errorf("Error creating User %q: %+v", userPrincipalName, err) + return fmt.Errorf("Error creating User (%q): %+v", upn, err) } + if user.ObjectID == nil { + return fmt.Errorf("nil User ID for %q: %+v", upn, err) + } + d.SetId(*user.ObjectID) - objectId := user.ObjectID - - resp, err := client.Get(ctx, *objectId) + _, err = graph.WaitForReplication(func() (interface{}, error) { + return client.Get(ctx, *user.ObjectID) + }) if err != nil { - return fmt.Errorf("Error retrieving User (%q) with ObjectID %q: %+v", userPrincipalName, *objectId, err) + return fmt.Errorf("Error waiting for User (%s) with ObjectId %q: %+v", upn, *user.ObjectID, err) } - if resp.ObjectID == nil { - return fmt.Errorf("User objectId is nil") - } - - d.SetId(*resp.ObjectID) return resourceUserRead(d, meta) } @@ -138,7 +139,6 @@ func resourceUserRead(d *schema.ResourceData, meta interface{}) error { d.Set("mail", user.Mail) d.Set("mail_nickname", user.MailNickname) d.Set("account_enabled", user.AccountEnabled) - return nil } From e8ee1889ce9b0d782b20722854b8487f9f5c6733 Mon Sep 17 00:00:00 2001 From: kt Date: Tue, 4 Jun 2019 17:25:20 -0700 Subject: [PATCH 11/45] data.azuread_[group|user]: add lookup by object_id (#90) --- azuread/data_application.go | 43 +++++----- azuread/data_domains.go | 2 +- azuread/data_group.go | 80 ++++++++++++------- azuread/data_group_test.go | 46 ++++++++--- azuread/data_user.go | 67 +++++++++++++--- azuread/data_user_test.go | 36 ++++++++- website/docs/d/group.html.markdown | 6 +- .../docs/d/service_principal.html.markdown | 2 +- website/docs/d/user.html.markdown | 4 + 9 files changed, 211 insertions(+), 75 deletions(-) diff --git a/azuread/data_application.go b/azuread/data_application.go index 3480a20d15..13cbb1d7fd 100644 --- a/azuread/data_application.go +++ b/azuread/data_application.go @@ -167,43 +167,46 @@ func dataApplicationRead(d *schema.ResourceData, meta interface{}) error { var app graphrbac.Application - if oId, ok := d.GetOk("object_id"); ok { + if oId, ok := d.Get("object_id").(string); ok && oId != "" { // use the object_id to find the Azure AD application - objectId := oId.(string) - resp, err := client.Get(ctx, objectId) + resp, err := client.Get(ctx, oId) if err != nil { if ar.ResponseWasNotFound(resp.Response) { - return fmt.Errorf("Error: AzureAD Application with ID %q was not found", objectId) + return fmt.Errorf("Error: AzureAD Application with ID %q was not found", oId) } - return fmt.Errorf("Error making Read request on AzureAD Application with ID %q: %+v", objectId, err) + return fmt.Errorf("Error making Read request on AzureAD Application with ID %q: %+v", oId, err) } app = resp - } else { - // use the name to find the Azure AD application - name := d.Get("name").(string) + } else if name, ok := d.Get("name").(string); ok { filter := fmt.Sprintf("displayName eq '%s'", name) resp, err := client.ListComplete(ctx, filter) if err != nil { - return fmt.Errorf("Error listing Azure AD Applications: %+v", err) + return fmt.Errorf("Error listing Azure AD Applications for filter %q: %+v", filter, err) } - var a *graphrbac.Application - for _, v := range *resp.Response().Value { - if v.DisplayName != nil { - if *v.DisplayName == name { - a = &v - break - } - } + values := resp.Response().Value + if values == nil { + return fmt.Errorf("nil values for AD Applications matching %q", filter) + } + if len(*values) == 0 { + return fmt.Errorf("Found no AD Applications matching %q", filter) + } + if len(*values) > 2 { + return fmt.Errorf("Found multiple AD Applications matching %q", filter) } - if a == nil { - return fmt.Errorf("Couldn't locate an Azure AD Application with a name of %q", name) + app = (*values)[0] + if app.DisplayName == nil { + return fmt.Errorf("nil DisplayName for AD Applications matching %q", filter) + } + if *app.DisplayName != name { + return fmt.Errorf("displayname for AD Applications matching %q does is does not match(%q!=%q)", filter, *app.DisplayName, name) } - app = *a + } else { + return fmt.Errorf("one of `object_id` or `name` must be supplied") } if app.ObjectID == nil { diff --git a/azuread/data_domains.go b/azuread/data_domains.go index 643ec2d297..e5de44beda 100644 --- a/azuread/data_domains.go +++ b/azuread/data_domains.go @@ -74,7 +74,7 @@ func dataSourceActiveDirectoryDomainsRead(d *schema.ResourceData, meta interface return fmt.Errorf("Error listing Azure AD Domains: %+v", err) } - d.SetId("domains-" + tenantId) + d.SetId("domains-" + tenantId) // todo this should be more unique domains := flattenDomains(results.Value, includeUnverified, onlyDefault, onlyInitial) if len(domains) == 0 { diff --git a/azuread/data_group.go b/azuread/data_group.go index d4113fc60a..11ad008f2c 100644 --- a/azuread/data_group.go +++ b/azuread/data_group.go @@ -2,10 +2,10 @@ package azuread import ( "fmt" - "log" "github.com/Azure/azure-sdk-for-go/services/graphrbac/1.6/graphrbac" "github.com/hashicorp/terraform/helper/schema" + "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/ar" "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/validate" ) @@ -17,10 +17,20 @@ func dataGroup() *schema.Resource { }, Schema: map[string]*schema.Schema{ + "object_id": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ValidateFunc: validate.UUID, + ConflictsWith: []string{"name"}, + }, + "name": { - Type: schema.TypeString, - Required: true, - ValidateFunc: validate.NoEmptyStrings, + Type: schema.TypeString, + Optional: true, + Computed: true, + ValidateFunc: validate.NoEmptyStrings, + ConflictsWith: []string{"object_id"}, }, }, } @@ -30,40 +40,56 @@ func dataSourceActiveDirectoryGroupRead(d *schema.ResourceData, meta interface{} client := meta.(*ArmClient).groupsClient ctx := meta.(*ArmClient).StopContext - var groupObj *graphrbac.ADGroup + var group graphrbac.ADGroup - // use the name to find the Azure AD group - name := d.Get("name").(string) - filter := fmt.Sprintf("displayName eq '%s'", name) - log.Printf("[DEBUG] Using filter %q", filter) + if oId, ok := d.Get("object_id").(string); ok && oId != "" { + // use the object_id to find the Azure AD application + resp, err := client.Get(ctx, oId) + if err != nil { + if ar.ResponseWasNotFound(resp.Response) { + return fmt.Errorf("Error: AzureAD Group with ID %q was not found", oId) + } - resp, err := client.ListComplete(ctx, filter) - if err != nil { - return fmt.Errorf("Error listing Azure AD groups: %+v", err) - } + return fmt.Errorf("Error making Read request on AzureAD Group with ID %q: %+v", oId, err) + } + + group = resp + } else if name, ok := d.Get("name").(string); ok && name != "" { + filter := fmt.Sprintf("displayName eq '%s'", name) - for _, v := range *resp.Response().Value { - if v.DisplayName == nil { - continue + resp, err := client.ListComplete(ctx, filter) + if err != nil { + return fmt.Errorf("Error listing Azure AD Groups for filter %q: %+v", filter, err) } - if *v.DisplayName == name { - log.Printf("[DEBUG] %q (API result) matches %q (given value)", *v.DisplayName, name) - groupObj = &v - break - } else { - log.Printf("[DEBUG] %q (API result) does not match %q (given value)", *v.DisplayName, name) + values := resp.Response().Value + if values == nil { + return fmt.Errorf("nil values for AD Groups matching %q", filter) + } + if len(*values) == 0 { + return fmt.Errorf("Found no AD Groups matching %q", filter) + } + if len(*values) > 2 { + return fmt.Errorf("Found multiple AD Groups matching %q", filter) } - } - if groupObj == nil { - return fmt.Errorf("Couldn't locate a Azure AD group with a name of %q", name) + group = (*values)[0] + if group.DisplayName == nil { + return fmt.Errorf("nil DisplayName for AD Groups matching %q", filter) + } + if *group.DisplayName != name { + return fmt.Errorf("displayname for AD Groups matching %q does is does not match(%q!=%q)", filter, *group.DisplayName, name) + } + } else { + return fmt.Errorf("one of `object_id` or `name` must be supplied") } - if groupObj.ObjectID == nil { + if group.ObjectID == nil { return fmt.Errorf("Group objectId is nil") } - d.SetId(*groupObj.ObjectID) + d.SetId(*group.ObjectID) + d.Set("object_id", group.ObjectID) + d.Set("name", group.DisplayName) return nil } diff --git a/azuread/data_group_test.go b/azuread/data_group_test.go index 5edc6f9da4..3bd3f4c570 100644 --- a/azuread/data_group_test.go +++ b/azuread/data_group_test.go @@ -4,17 +4,13 @@ import ( "fmt" "testing" - "github.com/hashicorp/go-uuid" + "github.com/google/uuid" "github.com/hashicorp/terraform/helper/resource" ) func TestAccDataSourceAzureADGroup_byName(t *testing.T) { dataSourceName := "data.azuread_group.test" - id, err := uuid.GenerateUUID() - if err != nil { - t.Fatal(err) - } - config := testAccDataSourceAzureADGroup_name(id) + id := uuid.New().String() resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -25,7 +21,30 @@ func TestAccDataSourceAzureADGroup_byName(t *testing.T) { Config: testAccAzureADGroup(id), }, { - Config: config, + Config: testAccDataSourceAzureADGroup_name(id), + Check: resource.ComposeTestCheckFunc( + testCheckAzureADGroupExists(dataSourceName), + resource.TestCheckResourceAttr(dataSourceName, "name", fmt.Sprintf("acctest%s", id)), + ), + }, + }, + }) +} + +func TestAccDataSourceAzureADGroup_byObjectId(t *testing.T) { + dataSourceName := "data.azuread_group.test" + id := uuid.New().String() + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureADGroupDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureADGroup(id), + }, + { + Config: testAccDataSourceAzureADGroup_objectId(id), Check: resource.ComposeTestCheckFunc( testCheckAzureADGroupExists(dataSourceName), resource.TestCheckResourceAttr(dataSourceName, "name", fmt.Sprintf("acctest%s", id)), @@ -36,12 +55,21 @@ func TestAccDataSourceAzureADGroup_byName(t *testing.T) { } func testAccDataSourceAzureADGroup_name(id string) string { - template := testAccAzureADGroup(id) return fmt.Sprintf(` %s data "azuread_group" "test" { name = "${azuread_group.test.name}" } -`, template) +`, testAccAzureADGroup(id)) +} + +func testAccDataSourceAzureADGroup_objectId(id string) string { + return fmt.Sprintf(` +%s + +data "azuread_group" "test" { + object_id = "${azuread_group.test.id}" +} +`, testAccAzureADGroup(id)) } diff --git a/azuread/data_user.go b/azuread/data_user.go index f711ea0804..afa1f37f29 100644 --- a/azuread/data_user.go +++ b/azuread/data_user.go @@ -2,7 +2,6 @@ package azuread import ( "fmt" - "log" "github.com/Azure/azure-sdk-for-go/services/graphrbac/1.6/graphrbac" "github.com/hashicorp/terraform/helper/schema" @@ -18,10 +17,20 @@ func dataSourceUser() *schema.Resource { }, Schema: map[string]*schema.Schema{ + "object_id": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ValidateFunc: validate.UUID, + ConflictsWith: []string{"user_principal_name"}, + }, + "user_principal_name": { - Type: schema.TypeString, - Required: true, - ValidateFunc: validate.NoEmptyStrings, + Type: schema.TypeString, + Optional: true, + Computed: true, + ValidateFunc: validate.NoEmptyStrings, + ConflictsWith: []string{"object_id"}, }, "account_enabled": { @@ -53,22 +62,56 @@ func dataSourceUserRead(d *schema.ResourceData, meta interface{}) error { var user graphrbac.User - queryString := d.Get("user_principal_name").(string) + if upn, ok := d.Get("user_principal_name").(string); ok && upn != "" { + + // use the object_id to find the Azure AD application + resp, err := client.Get(ctx, upn) + if err != nil { + if ar.ResponseWasNotFound(resp.Response) { + return fmt.Errorf("Error: AzureAD User with ID %q was not found", upn) + } + + return fmt.Errorf("Error making Read request on AzureAD User with ID %q: %+v", upn, err) + } + + user = resp + } else if oId, ok := d.Get("object_id").(string); ok && oId != "" { + filter := fmt.Sprintf("objectId eq '%s'", oId) + + resp, err := client.ListComplete(ctx, filter) + if err != nil { + return fmt.Errorf("Error listing Azure AD Users for filter %q: %+v", filter, err) + } - log.Printf("[DEBUG] Using Get with the following query string: %q", queryString) - user, err := client.Get(ctx, queryString) - if err != nil { - if ar.ResponseWasNotFound(user.Response) { - return fmt.Errorf("Error: No AzureAD User found with the following query string: %q", queryString) + values := resp.Response().Value + if values == nil { + return fmt.Errorf("nil values for AD Users matching %q", filter) + } + if len(*values) == 0 { + return fmt.Errorf("Found no AD Users matching %q", filter) } - return fmt.Errorf("Error making Read request on AzureAD User the following query string: %q: %+v", queryString, err) + if len(*values) > 2 { + return fmt.Errorf("Found multiple AD Users matching %q", filter) + } + + user = (*values)[0] + if user.DisplayName == nil { + return fmt.Errorf("nil DisplayName for AD Users matching %q", filter) + } + if *user.ObjectID != oId { + return fmt.Errorf("objectID for AD Users matching %q does is does not match(%q!=%q)", filter, *user.ObjectID, oId) + } + } else { + return fmt.Errorf("one of `object_id` or `user_principal_name` must be supplied") } if user.ObjectID == nil { - return fmt.Errorf("User objectId is nil") + return fmt.Errorf("Group objectId is nil") } + d.SetId(*user.ObjectID) d.SetId(*user.ObjectID) + d.Set("object_id", user.ObjectID) d.Set("user_principal_name", user.UserPrincipalName) d.Set("account_enabled", user.AccountEnabled) d.Set("display_name", user.DisplayName) diff --git a/azuread/data_user_test.go b/azuread/data_user_test.go index 906c23489c..daa365a75f 100644 --- a/azuread/data_user_test.go +++ b/azuread/data_user_test.go @@ -31,14 +31,44 @@ func TestAccDataSourceAzureADUser_byUserPrincipalName(t *testing.T) { }) } +func TestAccDataSourceAzureADUser_byObjectId(t *testing.T) { + dataSourceName := "data.azuread_user.test" + id := acctest.RandStringFromCharSet(7, acctest.CharSetAlphaNum) + password := id + "p@$$wR2" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccAzureADUserDataSource_byObjectId(id, password), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet(dataSourceName, "user_principal_name"), + resource.TestCheckResourceAttrSet(dataSourceName, "account_enabled"), + resource.TestCheckResourceAttrSet(dataSourceName, "display_name"), + resource.TestCheckResourceAttrSet(dataSourceName, "mail_nickname"), + ), + }, + }, + }) +} + func testAccAzureADUserDataSource_byUserPrincipalName(id, password string) string { - template := testAccADUser_basic(id, password) return fmt.Sprintf(` - %s data "azuread_user" "test" { user_principal_name = "${azuread_user.test.user_principal_name}" } -`, template) +`, testAccADUser_basic(id, password)) +} + +func testAccAzureADUserDataSource_byObjectId(id, password string) string { + return fmt.Sprintf(` +%s + +data "azuread_user" "test" { + object_id = "${azuread_user.test.id}" +} +`, testAccADUser_basic(id, password)) } diff --git a/website/docs/d/group.html.markdown b/website/docs/d/group.html.markdown index d263789951..a64500f2eb 100644 --- a/website/docs/d/group.html.markdown +++ b/website/docs/d/group.html.markdown @@ -25,9 +25,11 @@ data "azuread_group" "test_group" { The following arguments are supported: -* `name` - (Required) The Name of the Azure AD Group we want to lookup. +* `name` - (Optional) The Name of the AD Group we want to lookup. -~> **WARNING:** `name` is not unique within Azure Active Directory. The data source will only return the first Group found. +* `object_id` - (Optional) Specifies the Object ID of the AD Group within Azure Active Directory. + +-> **NOTE:** Either a `name` or an `object_id` must be specified. ## Attributes Reference diff --git a/website/docs/d/service_principal.html.markdown b/website/docs/d/service_principal.html.markdown index fe8af7fee5..c11ba4f923 100644 --- a/website/docs/d/service_principal.html.markdown +++ b/website/docs/d/service_principal.html.markdown @@ -41,7 +41,7 @@ data "azuread_service_principal" "test" { The following arguments are supported: -* `application_id` - (Optional) The ID of the Azure AD Application for which to create a Service Principal. +* `application_id` - (Optional) The ID of the Azure AD Application. * `object_id` - (Optional) The ID of the Azure AD Service Principal. diff --git a/website/docs/d/user.html.markdown b/website/docs/d/user.html.markdown index 93109ac072..f93b29549a 100644 --- a/website/docs/d/user.html.markdown +++ b/website/docs/d/user.html.markdown @@ -27,6 +27,10 @@ The following arguments are supported: * `user_principal_name` - (Required) The User Principal Name of the Azure AD User. +* `object_id` - (Optional) Specifies the Object ID of the Application within Azure Active Directory. + +-> **NOTE:** Either a `user_principal_name` or an `object_id` must be specified. + ## Attributes Reference The following attributes are exported: From c3aa174939feb92716928becc9ae7851cff130cd Mon Sep 17 00:00:00 2001 From: Radek Simko Date: Thu, 6 Jun 2019 16:53:28 +0100 Subject: [PATCH 12/45] go mod tidy (#97) --- go.mod | 1 - go.sum | 2 - vendor/github.com/gofrs/uuid/.gitignore | 15 -- vendor/github.com/gofrs/uuid/.travis.yml | 23 -- vendor/github.com/gofrs/uuid/LICENSE | 20 -- vendor/github.com/gofrs/uuid/README.md | 109 -------- vendor/github.com/gofrs/uuid/codec.go | 212 --------------- vendor/github.com/gofrs/uuid/fuzz.go | 47 ---- vendor/github.com/gofrs/uuid/generator.go | 299 ---------------------- vendor/github.com/gofrs/uuid/sql.go | 109 -------- vendor/github.com/gofrs/uuid/uuid.go | 189 -------------- vendor/modules.txt | 22 +- 12 files changed, 10 insertions(+), 1038 deletions(-) delete mode 100644 vendor/github.com/gofrs/uuid/.gitignore delete mode 100644 vendor/github.com/gofrs/uuid/.travis.yml delete mode 100644 vendor/github.com/gofrs/uuid/LICENSE delete mode 100644 vendor/github.com/gofrs/uuid/README.md delete mode 100644 vendor/github.com/gofrs/uuid/codec.go delete mode 100644 vendor/github.com/gofrs/uuid/fuzz.go delete mode 100644 vendor/github.com/gofrs/uuid/generator.go delete mode 100644 vendor/github.com/gofrs/uuid/sql.go delete mode 100644 vendor/github.com/gofrs/uuid/uuid.go diff --git a/go.mod b/go.mod index 3b2adfcc4d..3be9b0aa4f 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,6 @@ require ( contrib.go.opencensus.io/exporter/ocagent v0.4.2 // indirect github.com/Azure/azure-sdk-for-go v24.1.0+incompatible github.com/Azure/go-autorest v11.2.8+incompatible - github.com/gofrs/uuid v3.2.0+incompatible github.com/google/uuid v0.0.0-20170814143639-7e072fc3a7be github.com/hashicorp/go-azure-helpers v0.0.0-20190129193224-166dfd221bb2 github.com/hashicorp/go-uuid v1.0.1 diff --git a/go.sum b/go.sum index 18a6d79e90..94741fcc25 100644 --- a/go.sum +++ b/go.sum @@ -97,8 +97,6 @@ github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeME github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= github.com/go-test/deep v1.0.1 h1:UQhStjbkDClarlmv0am7OXXO4/GaPdCGiUiMTvi28sg= github.com/go-test/deep v1.0.1/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= -github.com/gofrs/uuid v3.2.0+incompatible h1:y12jRkkFxsd7GpqdSZ+/KCs/fJbqpEXSGd4+jfEaewE= -github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.0 h1:xU6/SpYbvkNYiptHJYEDRseDLvYE7wSqhYYNy0QSUzI= github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= diff --git a/vendor/github.com/gofrs/uuid/.gitignore b/vendor/github.com/gofrs/uuid/.gitignore deleted file mode 100644 index 666dbbb5bc..0000000000 --- a/vendor/github.com/gofrs/uuid/.gitignore +++ /dev/null @@ -1,15 +0,0 @@ -# Binaries for programs and plugins -*.exe -*.exe~ -*.dll -*.so -*.dylib - -# Test binary, build with `go test -c` -*.test - -# Output of the go coverage tool, specifically when used with LiteIDE -*.out - -# binary bundle generated by go-fuzz -uuid-fuzz.zip diff --git a/vendor/github.com/gofrs/uuid/.travis.yml b/vendor/github.com/gofrs/uuid/.travis.yml deleted file mode 100644 index ee1e4fa003..0000000000 --- a/vendor/github.com/gofrs/uuid/.travis.yml +++ /dev/null @@ -1,23 +0,0 @@ -language: go -sudo: false -go: - - 1.7.x - - 1.8.x - - 1.9.x - - 1.10.x - - 1.11.x - - tip -matrix: - allow_failures: - - go: tip - fast_finish: true -env: - - GO111MODULE=on -before_install: - - go get golang.org/x/tools/cmd/cover -script: - - go test ./... -race -coverprofile=coverage.txt -covermode=atomic -after_success: - - bash <(curl -s https://codecov.io/bash) -notifications: - email: false diff --git a/vendor/github.com/gofrs/uuid/LICENSE b/vendor/github.com/gofrs/uuid/LICENSE deleted file mode 100644 index 926d549870..0000000000 --- a/vendor/github.com/gofrs/uuid/LICENSE +++ /dev/null @@ -1,20 +0,0 @@ -Copyright (C) 2013-2018 by Maxim Bublis - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/vendor/github.com/gofrs/uuid/README.md b/vendor/github.com/gofrs/uuid/README.md deleted file mode 100644 index efc3204fc0..0000000000 --- a/vendor/github.com/gofrs/uuid/README.md +++ /dev/null @@ -1,109 +0,0 @@ -# UUID - -[![License](https://img.shields.io/github/license/gofrs/uuid.svg)](https://github.com/gofrs/uuid/blob/master/LICENSE) -[![Build Status](https://travis-ci.org/gofrs/uuid.svg?branch=master)](https://travis-ci.org/gofrs/uuid) -[![GoDoc](http://godoc.org/github.com/gofrs/uuid?status.svg)](http://godoc.org/github.com/gofrs/uuid) -[![Coverage Status](https://codecov.io/gh/gofrs/uuid/branch/master/graphs/badge.svg?branch=master)](https://codecov.io/gh/gofrs/uuid/) -[![Go Report Card](https://goreportcard.com/badge/github.com/gofrs/uuid)](https://goreportcard.com/report/github.com/gofrs/uuid) - -Package uuid provides a pure Go implementation of Universally Unique Identifiers -(UUID) variant as defined in RFC-4122. This package supports both the creation -and parsing of UUIDs in different formats. - -This package supports the following UUID versions: -* Version 1, based on timestamp and MAC address (RFC-4122) -* Version 2, based on timestamp, MAC address and POSIX UID/GID (DCE 1.1) -* Version 3, based on MD5 hashing of a named value (RFC-4122) -* Version 4, based on random numbers (RFC-4122) -* Version 5, based on SHA-1 hashing of a named value (RFC-4122) - -## Project History - -This project was originally forked from the -[github.com/satori/go.uuid](https://github.com/satori/go.uuid) repository after -it appeared to be no longer maintained, while exhibiting [critical -flaws](https://github.com/satori/go.uuid/issues/73). We have decided to take -over this project to ensure it receives regular maintenance for the benefit of -the larger Go community. - -We'd like to thank Maxim Bublis for his hard work on the original iteration of -the package. - -## License - -This source code of this package is released under the MIT License. Please see -the [LICENSE](https://github.com/gofrs/uuid/blob/master/LICENSE) for the full -content of the license. - -## Recommended Package Version - -We recommend using v2.0.0+ of this package, as versions prior to 2.0.0 were -created before our fork of the original package and have some known -deficiencies. - -## Installation - -It is recommended to use a package manager like `dep` that understands tagged -releases of a package, as well as semantic versioning. - -If you are unable to make use of a dependency manager with your project, you can -use the `go get` command to download it directly: - -```Shell -$ go get github.com/gofrs/uuid -``` - -## Requirements - -Due to subtests not being supported in older versions of Go, this package is -only regularly tested against Go 1.7+. This package may work perfectly fine with -Go 1.2+, but support for these older versions is not actively maintained. - -## Go 1.11 Modules - -As of v3.2.0, this repository no longer adopts Go modules, and v3.2.0 no longer has a `go.mod` file. As a result, v3.2.0 also drops support for the `github.com/gofrs/uuid/v3` import path. Only module-based consumers are impacted. With the v3.2.0 release, _all_ gofrs/uuid consumers should use the `github.com/gofrs/uuid` import path. - -An existing module-based consumer will continue to be able to build using the `github.com/gofrs/uuid/v3` import path using any valid consumer `go.mod` that worked prior to the publishing of v3.2.0, but any module-based consumer should start using the `github.com/gofrs/uuid` import path when possible and _must_ use the `github.com/gofrs/uuid` import path prior to upgrading to v3.2.0. - -Please refer to [Issue #61](https://github.com/gofrs/uuid/issues/61) and [Issue #66](https://github.com/gofrs/uuid/issues/66) for more details. - -## Usage - -Here is a quick overview of how to use this package. For more detailed -documentation, please see the [GoDoc Page](http://godoc.org/github.com/gofrs/uuid). - -```go -package main - -import ( - "log" - - "github.com/gofrs/uuid" -) - -// Create a Version 4 UUID, panicking on error. -// Use this form to initialize package-level variables. -var u1 = uuid.Must(uuid.NewV4()) - -func main() { - // Create a Version 4 UUID. - u2, err := uuid.NewV4() - if err != nil { - log.Fatalf("failed to generate UUID: %v", err) - } - log.Printf("generated Version 4 UUID %v", u2) - - // Parse a UUID from a string. - s := "6ba7b810-9dad-11d1-80b4-00c04fd430c8" - u3, err := uuid.FromString(s) - if err != nil { - log.Fatalf("failed to parse UUID %q: %v", s, err) - } - log.Printf("successfully parsed UUID %v", u3) -} -``` - -## References - -* [RFC-4122](https://tools.ietf.org/html/rfc4122) -* [DCE 1.1: Authentication and Security Services](http://pubs.opengroup.org/onlinepubs/9696989899/chap5.htm#tagcjh_08_02_01_01) diff --git a/vendor/github.com/gofrs/uuid/codec.go b/vendor/github.com/gofrs/uuid/codec.go deleted file mode 100644 index e3d8cfb4d0..0000000000 --- a/vendor/github.com/gofrs/uuid/codec.go +++ /dev/null @@ -1,212 +0,0 @@ -// Copyright (C) 2013-2018 by Maxim Bublis -// -// Permission is hereby granted, free of charge, to any person obtaining -// a copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to -// permit persons to whom the Software is furnished to do so, subject to -// the following conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -package uuid - -import ( - "bytes" - "encoding/hex" - "fmt" -) - -// FromBytes returns a UUID generated from the raw byte slice input. -// It will return an error if the slice isn't 16 bytes long. -func FromBytes(input []byte) (UUID, error) { - u := UUID{} - err := u.UnmarshalBinary(input) - return u, err -} - -// FromBytesOrNil returns a UUID generated from the raw byte slice input. -// Same behavior as FromBytes(), but returns uuid.Nil instead of an error. -func FromBytesOrNil(input []byte) UUID { - uuid, err := FromBytes(input) - if err != nil { - return Nil - } - return uuid -} - -// FromString returns a UUID parsed from the input string. -// Input is expected in a form accepted by UnmarshalText. -func FromString(input string) (UUID, error) { - u := UUID{} - err := u.UnmarshalText([]byte(input)) - return u, err -} - -// FromStringOrNil returns a UUID parsed from the input string. -// Same behavior as FromString(), but returns uuid.Nil instead of an error. -func FromStringOrNil(input string) UUID { - uuid, err := FromString(input) - if err != nil { - return Nil - } - return uuid -} - -// MarshalText implements the encoding.TextMarshaler interface. -// The encoding is the same as returned by the String() method. -func (u UUID) MarshalText() ([]byte, error) { - return []byte(u.String()), nil -} - -// UnmarshalText implements the encoding.TextUnmarshaler interface. -// Following formats are supported: -// -// "6ba7b810-9dad-11d1-80b4-00c04fd430c8", -// "{6ba7b810-9dad-11d1-80b4-00c04fd430c8}", -// "urn:uuid:6ba7b810-9dad-11d1-80b4-00c04fd430c8" -// "6ba7b8109dad11d180b400c04fd430c8" -// "{6ba7b8109dad11d180b400c04fd430c8}", -// "urn:uuid:6ba7b8109dad11d180b400c04fd430c8" -// -// ABNF for supported UUID text representation follows: -// -// URN := 'urn' -// UUID-NID := 'uuid' -// -// hexdig := '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' | -// 'a' | 'b' | 'c' | 'd' | 'e' | 'f' | -// 'A' | 'B' | 'C' | 'D' | 'E' | 'F' -// -// hexoct := hexdig hexdig -// 2hexoct := hexoct hexoct -// 4hexoct := 2hexoct 2hexoct -// 6hexoct := 4hexoct 2hexoct -// 12hexoct := 6hexoct 6hexoct -// -// hashlike := 12hexoct -// canonical := 4hexoct '-' 2hexoct '-' 2hexoct '-' 6hexoct -// -// plain := canonical | hashlike -// uuid := canonical | hashlike | braced | urn -// -// braced := '{' plain '}' | '{' hashlike '}' -// urn := URN ':' UUID-NID ':' plain -// -func (u *UUID) UnmarshalText(text []byte) error { - switch len(text) { - case 32: - return u.decodeHashLike(text) - case 34, 38: - return u.decodeBraced(text) - case 36: - return u.decodeCanonical(text) - case 41, 45: - return u.decodeURN(text) - default: - return fmt.Errorf("uuid: incorrect UUID length: %s", text) - } -} - -// decodeCanonical decodes UUID strings that are formatted as defined in RFC-4122 (section 3): -// "6ba7b810-9dad-11d1-80b4-00c04fd430c8". -func (u *UUID) decodeCanonical(t []byte) error { - if t[8] != '-' || t[13] != '-' || t[18] != '-' || t[23] != '-' { - return fmt.Errorf("uuid: incorrect UUID format %s", t) - } - - src := t - dst := u[:] - - for i, byteGroup := range byteGroups { - if i > 0 { - src = src[1:] // skip dash - } - _, err := hex.Decode(dst[:byteGroup/2], src[:byteGroup]) - if err != nil { - return err - } - src = src[byteGroup:] - dst = dst[byteGroup/2:] - } - - return nil -} - -// decodeHashLike decodes UUID strings that are using the following format: -// "6ba7b8109dad11d180b400c04fd430c8". -func (u *UUID) decodeHashLike(t []byte) error { - src := t[:] - dst := u[:] - - _, err := hex.Decode(dst, src) - return err -} - -// decodeBraced decodes UUID strings that are using the following formats: -// "{6ba7b810-9dad-11d1-80b4-00c04fd430c8}" -// "{6ba7b8109dad11d180b400c04fd430c8}". -func (u *UUID) decodeBraced(t []byte) error { - l := len(t) - - if t[0] != '{' || t[l-1] != '}' { - return fmt.Errorf("uuid: incorrect UUID format %s", t) - } - - return u.decodePlain(t[1 : l-1]) -} - -// decodeURN decodes UUID strings that are using the following formats: -// "urn:uuid:6ba7b810-9dad-11d1-80b4-00c04fd430c8" -// "urn:uuid:6ba7b8109dad11d180b400c04fd430c8". -func (u *UUID) decodeURN(t []byte) error { - total := len(t) - - urnUUIDPrefix := t[:9] - - if !bytes.Equal(urnUUIDPrefix, urnPrefix) { - return fmt.Errorf("uuid: incorrect UUID format: %s", t) - } - - return u.decodePlain(t[9:total]) -} - -// decodePlain decodes UUID strings that are using the following formats: -// "6ba7b810-9dad-11d1-80b4-00c04fd430c8" or in hash-like format -// "6ba7b8109dad11d180b400c04fd430c8". -func (u *UUID) decodePlain(t []byte) error { - switch len(t) { - case 32: - return u.decodeHashLike(t) - case 36: - return u.decodeCanonical(t) - default: - return fmt.Errorf("uuid: incorrect UUID length: %s", t) - } -} - -// MarshalBinary implements the encoding.BinaryMarshaler interface. -func (u UUID) MarshalBinary() ([]byte, error) { - return u.Bytes(), nil -} - -// UnmarshalBinary implements the encoding.BinaryUnmarshaler interface. -// It will return an error if the slice isn't 16 bytes long. -func (u *UUID) UnmarshalBinary(data []byte) error { - if len(data) != Size { - return fmt.Errorf("uuid: UUID must be exactly 16 bytes long, got %d bytes", len(data)) - } - copy(u[:], data) - - return nil -} diff --git a/vendor/github.com/gofrs/uuid/fuzz.go b/vendor/github.com/gofrs/uuid/fuzz.go deleted file mode 100644 index afaefbc8e3..0000000000 --- a/vendor/github.com/gofrs/uuid/fuzz.go +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright (c) 2018 Andrei Tudor Călin -// -// Permission is hereby granted, free of charge, to any person obtaining -// a copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to -// permit persons to whom the Software is furnished to do so, subject to -// the following conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -// +build gofuzz - -package uuid - -// Fuzz implements a simple fuzz test for FromString / UnmarshalText. -// -// To run: -// -// $ go get github.com/dvyukov/go-fuzz/... -// $ cd $GOPATH/src/github.com/gofrs/uuid -// $ go-fuzz-build github.com/gofrs/uuid -// $ go-fuzz -bin=uuid-fuzz.zip -workdir=./testdata -// -// If you make significant changes to FromString / UnmarshalText and add -// new cases to fromStringTests (in codec_test.go), please run -// -// $ go test -seed_fuzz_corpus -// -// to seed the corpus with the new interesting inputs, then run the fuzzer. -func Fuzz(data []byte) int { - _, err := FromString(string(data)) - if err != nil { - return 0 - } - return 1 -} diff --git a/vendor/github.com/gofrs/uuid/generator.go b/vendor/github.com/gofrs/uuid/generator.go deleted file mode 100644 index 4257761f15..0000000000 --- a/vendor/github.com/gofrs/uuid/generator.go +++ /dev/null @@ -1,299 +0,0 @@ -// Copyright (C) 2013-2018 by Maxim Bublis -// -// Permission is hereby granted, free of charge, to any person obtaining -// a copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to -// permit persons to whom the Software is furnished to do so, subject to -// the following conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -package uuid - -import ( - "crypto/md5" - "crypto/rand" - "crypto/sha1" - "encoding/binary" - "fmt" - "hash" - "io" - "net" - "os" - "sync" - "time" -) - -// Difference in 100-nanosecond intervals between -// UUID epoch (October 15, 1582) and Unix epoch (January 1, 1970). -const epochStart = 122192928000000000 - -type epochFunc func() time.Time - -// HWAddrFunc is the function type used to provide hardware (MAC) addresses. -type HWAddrFunc func() (net.HardwareAddr, error) - -// DefaultGenerator is the default UUID Generator used by this package. -var DefaultGenerator Generator = NewGen() - -var ( - posixUID = uint32(os.Getuid()) - posixGID = uint32(os.Getgid()) -) - -// NewV1 returns a UUID based on the current timestamp and MAC address. -func NewV1() (UUID, error) { - return DefaultGenerator.NewV1() -} - -// NewV2 returns a DCE Security UUID based on the POSIX UID/GID. -func NewV2(domain byte) (UUID, error) { - return DefaultGenerator.NewV2(domain) -} - -// NewV3 returns a UUID based on the MD5 hash of the namespace UUID and name. -func NewV3(ns UUID, name string) UUID { - return DefaultGenerator.NewV3(ns, name) -} - -// NewV4 returns a randomly generated UUID. -func NewV4() (UUID, error) { - return DefaultGenerator.NewV4() -} - -// NewV5 returns a UUID based on SHA-1 hash of the namespace UUID and name. -func NewV5(ns UUID, name string) UUID { - return DefaultGenerator.NewV5(ns, name) -} - -// Generator provides an interface for generating UUIDs. -type Generator interface { - NewV1() (UUID, error) - NewV2(domain byte) (UUID, error) - NewV3(ns UUID, name string) UUID - NewV4() (UUID, error) - NewV5(ns UUID, name string) UUID -} - -// Gen is a reference UUID generator based on the specifications laid out in -// RFC-4122 and DCE 1.1: Authentication and Security Services. This type -// satisfies the Generator interface as defined in this package. -// -// For consumers who are generating V1 UUIDs, but don't want to expose the MAC -// address of the node generating the UUIDs, the NewGenWithHWAF() function has been -// provided as a convenience. See the function's documentation for more info. -// -// The authors of this package do not feel that the majority of users will need -// to obfuscate their MAC address, and so we recommend using NewGen() to create -// a new generator. -type Gen struct { - clockSequenceOnce sync.Once - hardwareAddrOnce sync.Once - storageMutex sync.Mutex - - rand io.Reader - - epochFunc epochFunc - hwAddrFunc HWAddrFunc - lastTime uint64 - clockSequence uint16 - hardwareAddr [6]byte -} - -// interface check -- build will fail if *Gen doesn't satisfy Generator -var _ Generator = (*Gen)(nil) - -// NewGen returns a new instance of Gen with some default values set. Most -// people should use this. -func NewGen() *Gen { - return NewGenWithHWAF(defaultHWAddrFunc) -} - -// NewGenWithHWAF builds a new UUID generator with the HWAddrFunc provided. Most -// consumers should use NewGen() instead. -// -// This is used so that consumers can generate their own MAC addresses, for use -// in the generated UUIDs, if there is some concern about exposing the physical -// address of the machine generating the UUID. -// -// The Gen generator will only invoke the HWAddrFunc once, and cache that MAC -// address for all the future UUIDs generated by it. If you'd like to switch the -// MAC address being used, you'll need to create a new generator using this -// function. -func NewGenWithHWAF(hwaf HWAddrFunc) *Gen { - return &Gen{ - epochFunc: time.Now, - hwAddrFunc: hwaf, - rand: rand.Reader, - } -} - -// NewV1 returns a UUID based on the current timestamp and MAC address. -func (g *Gen) NewV1() (UUID, error) { - u := UUID{} - - timeNow, clockSeq, err := g.getClockSequence() - if err != nil { - return Nil, err - } - binary.BigEndian.PutUint32(u[0:], uint32(timeNow)) - binary.BigEndian.PutUint16(u[4:], uint16(timeNow>>32)) - binary.BigEndian.PutUint16(u[6:], uint16(timeNow>>48)) - binary.BigEndian.PutUint16(u[8:], clockSeq) - - hardwareAddr, err := g.getHardwareAddr() - if err != nil { - return Nil, err - } - copy(u[10:], hardwareAddr) - - u.SetVersion(V1) - u.SetVariant(VariantRFC4122) - - return u, nil -} - -// NewV2 returns a DCE Security UUID based on the POSIX UID/GID. -func (g *Gen) NewV2(domain byte) (UUID, error) { - u, err := g.NewV1() - if err != nil { - return Nil, err - } - - switch domain { - case DomainPerson: - binary.BigEndian.PutUint32(u[:], posixUID) - case DomainGroup: - binary.BigEndian.PutUint32(u[:], posixGID) - } - - u[9] = domain - - u.SetVersion(V2) - u.SetVariant(VariantRFC4122) - - return u, nil -} - -// NewV3 returns a UUID based on the MD5 hash of the namespace UUID and name. -func (g *Gen) NewV3(ns UUID, name string) UUID { - u := newFromHash(md5.New(), ns, name) - u.SetVersion(V3) - u.SetVariant(VariantRFC4122) - - return u -} - -// NewV4 returns a randomly generated UUID. -func (g *Gen) NewV4() (UUID, error) { - u := UUID{} - if _, err := io.ReadFull(g.rand, u[:]); err != nil { - return Nil, err - } - u.SetVersion(V4) - u.SetVariant(VariantRFC4122) - - return u, nil -} - -// NewV5 returns a UUID based on SHA-1 hash of the namespace UUID and name. -func (g *Gen) NewV5(ns UUID, name string) UUID { - u := newFromHash(sha1.New(), ns, name) - u.SetVersion(V5) - u.SetVariant(VariantRFC4122) - - return u -} - -// Returns the epoch and clock sequence. -func (g *Gen) getClockSequence() (uint64, uint16, error) { - var err error - g.clockSequenceOnce.Do(func() { - buf := make([]byte, 2) - if _, err = io.ReadFull(g.rand, buf); err != nil { - return - } - g.clockSequence = binary.BigEndian.Uint16(buf) - }) - if err != nil { - return 0, 0, err - } - - g.storageMutex.Lock() - defer g.storageMutex.Unlock() - - timeNow := g.getEpoch() - // Clock didn't change since last UUID generation. - // Should increase clock sequence. - if timeNow <= g.lastTime { - g.clockSequence++ - } - g.lastTime = timeNow - - return timeNow, g.clockSequence, nil -} - -// Returns the hardware address. -func (g *Gen) getHardwareAddr() ([]byte, error) { - var err error - g.hardwareAddrOnce.Do(func() { - var hwAddr net.HardwareAddr - if hwAddr, err = g.hwAddrFunc(); err == nil { - copy(g.hardwareAddr[:], hwAddr) - return - } - - // Initialize hardwareAddr randomly in case - // of real network interfaces absence. - if _, err = io.ReadFull(g.rand, g.hardwareAddr[:]); err != nil { - return - } - // Set multicast bit as recommended by RFC-4122 - g.hardwareAddr[0] |= 0x01 - }) - if err != nil { - return []byte{}, err - } - return g.hardwareAddr[:], nil -} - -// Returns the difference between UUID epoch (October 15, 1582) -// and current time in 100-nanosecond intervals. -func (g *Gen) getEpoch() uint64 { - return epochStart + uint64(g.epochFunc().UnixNano()/100) -} - -// Returns the UUID based on the hashing of the namespace UUID and name. -func newFromHash(h hash.Hash, ns UUID, name string) UUID { - u := UUID{} - h.Write(ns[:]) - h.Write([]byte(name)) - copy(u[:], h.Sum(nil)) - - return u -} - -// Returns the hardware address. -func defaultHWAddrFunc() (net.HardwareAddr, error) { - ifaces, err := net.Interfaces() - if err != nil { - return []byte{}, err - } - for _, iface := range ifaces { - if len(iface.HardwareAddr) >= 6 { - return iface.HardwareAddr, nil - } - } - return []byte{}, fmt.Errorf("uuid: no HW address found") -} diff --git a/vendor/github.com/gofrs/uuid/sql.go b/vendor/github.com/gofrs/uuid/sql.go deleted file mode 100644 index 6f254a4fd1..0000000000 --- a/vendor/github.com/gofrs/uuid/sql.go +++ /dev/null @@ -1,109 +0,0 @@ -// Copyright (C) 2013-2018 by Maxim Bublis -// -// Permission is hereby granted, free of charge, to any person obtaining -// a copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to -// permit persons to whom the Software is furnished to do so, subject to -// the following conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -package uuid - -import ( - "bytes" - "database/sql/driver" - "encoding/json" - "fmt" -) - -// Value implements the driver.Valuer interface. -func (u UUID) Value() (driver.Value, error) { - return u.String(), nil -} - -// Scan implements the sql.Scanner interface. -// A 16-byte slice will be handled by UnmarshalBinary, while -// a longer byte slice or a string will be handled by UnmarshalText. -func (u *UUID) Scan(src interface{}) error { - switch src := src.(type) { - case UUID: // support gorm convert from UUID to NullUUID - *u = src - return nil - - case []byte: - if len(src) == Size { - return u.UnmarshalBinary(src) - } - return u.UnmarshalText(src) - - case string: - return u.UnmarshalText([]byte(src)) - } - - return fmt.Errorf("uuid: cannot convert %T to UUID", src) -} - -// NullUUID can be used with the standard sql package to represent a -// UUID value that can be NULL in the database. -type NullUUID struct { - UUID UUID - Valid bool -} - -// Value implements the driver.Valuer interface. -func (u NullUUID) Value() (driver.Value, error) { - if !u.Valid { - return nil, nil - } - // Delegate to UUID Value function - return u.UUID.Value() -} - -// Scan implements the sql.Scanner interface. -func (u *NullUUID) Scan(src interface{}) error { - if src == nil { - u.UUID, u.Valid = Nil, false - return nil - } - - // Delegate to UUID Scan function - u.Valid = true - return u.UUID.Scan(src) -} - -// MarshalJSON marshals the NullUUID as null or the nested UUID -func (u NullUUID) MarshalJSON() ([]byte, error) { - if !u.Valid { - return json.Marshal(nil) - } - - return json.Marshal(u.UUID) -} - -// UnmarshalJSON unmarshals a NullUUID -func (u *NullUUID) UnmarshalJSON(b []byte) error { - if bytes.Equal(b, []byte("null")) { - u.UUID, u.Valid = Nil, false - return nil - } - - if err := json.Unmarshal(b, &u.UUID); err != nil { - return err - } - - u.Valid = true - - return nil -} diff --git a/vendor/github.com/gofrs/uuid/uuid.go b/vendor/github.com/gofrs/uuid/uuid.go deleted file mode 100644 index 29ef440597..0000000000 --- a/vendor/github.com/gofrs/uuid/uuid.go +++ /dev/null @@ -1,189 +0,0 @@ -// Copyright (C) 2013-2018 by Maxim Bublis -// -// Permission is hereby granted, free of charge, to any person obtaining -// a copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to -// permit persons to whom the Software is furnished to do so, subject to -// the following conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -// Package uuid provides implementations of the Universally Unique Identifier (UUID), as specified in RFC-4122 and DCE 1.1. -// -// RFC-4122[1] provides the specification for versions 1, 3, 4, and 5. -// -// DCE 1.1[2] provides the specification for version 2. -// -// [1] https://tools.ietf.org/html/rfc4122 -// [2] http://pubs.opengroup.org/onlinepubs/9696989899/chap5.htm#tagcjh_08_02_01_01 -package uuid - -import ( - "encoding/binary" - "encoding/hex" - "fmt" - "time" -) - -// Size of a UUID in bytes. -const Size = 16 - -// UUID is an array type to represent the value of a UUID, as defined in RFC-4122. -type UUID [Size]byte - -// UUID versions. -const ( - _ byte = iota - V1 // Version 1 (date-time and MAC address) - V2 // Version 2 (date-time and MAC address, DCE security version) - V3 // Version 3 (namespace name-based) - V4 // Version 4 (random) - V5 // Version 5 (namespace name-based) -) - -// UUID layout variants. -const ( - VariantNCS byte = iota - VariantRFC4122 - VariantMicrosoft - VariantFuture -) - -// UUID DCE domains. -const ( - DomainPerson = iota - DomainGroup - DomainOrg -) - -// Timestamp is the count of 100-nanosecond intervals since 00:00:00.00, -// 15 October 1582 within a V1 UUID. This type has no meaning for V2-V5 -// UUIDs since they don't have an embedded timestamp. -type Timestamp uint64 - -const _100nsPerSecond = 10000000 - -// Time returns the UTC time.Time representation of a Timestamp -func (t Timestamp) Time() (time.Time, error) { - secs := uint64(t) / _100nsPerSecond - nsecs := 100 * (uint64(t) % _100nsPerSecond) - return time.Unix(int64(secs)-(epochStart/_100nsPerSecond), int64(nsecs)), nil -} - -// TimestampFromV1 returns the Timestamp embedded within a V1 UUID. -// Returns an error if the UUID is any version other than 1. -func TimestampFromV1(u UUID) (Timestamp, error) { - if u.Version() != 1 { - err := fmt.Errorf("uuid: %s is version %d, not version 1", u, u.Version()) - return 0, err - } - low := binary.BigEndian.Uint32(u[0:4]) - mid := binary.BigEndian.Uint16(u[4:6]) - hi := binary.BigEndian.Uint16(u[6:8]) & 0xfff - return Timestamp(uint64(low) + (uint64(mid) << 32) + (uint64(hi) << 48)), nil -} - -// String parse helpers. -var ( - urnPrefix = []byte("urn:uuid:") - byteGroups = []int{8, 4, 4, 4, 12} -) - -// Nil is the nil UUID, as specified in RFC-4122, that has all 128 bits set to -// zero. -var Nil = UUID{} - -// Predefined namespace UUIDs. -var ( - NamespaceDNS = Must(FromString("6ba7b810-9dad-11d1-80b4-00c04fd430c8")) - NamespaceURL = Must(FromString("6ba7b811-9dad-11d1-80b4-00c04fd430c8")) - NamespaceOID = Must(FromString("6ba7b812-9dad-11d1-80b4-00c04fd430c8")) - NamespaceX500 = Must(FromString("6ba7b814-9dad-11d1-80b4-00c04fd430c8")) -) - -// Version returns the algorithm version used to generate the UUID. -func (u UUID) Version() byte { - return u[6] >> 4 -} - -// Variant returns the UUID layout variant. -func (u UUID) Variant() byte { - switch { - case (u[8] >> 7) == 0x00: - return VariantNCS - case (u[8] >> 6) == 0x02: - return VariantRFC4122 - case (u[8] >> 5) == 0x06: - return VariantMicrosoft - case (u[8] >> 5) == 0x07: - fallthrough - default: - return VariantFuture - } -} - -// Bytes returns a byte slice representation of the UUID. -func (u UUID) Bytes() []byte { - return u[:] -} - -// String returns a canonical RFC-4122 string representation of the UUID: -// xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx. -func (u UUID) String() string { - buf := make([]byte, 36) - - hex.Encode(buf[0:8], u[0:4]) - buf[8] = '-' - hex.Encode(buf[9:13], u[4:6]) - buf[13] = '-' - hex.Encode(buf[14:18], u[6:8]) - buf[18] = '-' - hex.Encode(buf[19:23], u[8:10]) - buf[23] = '-' - hex.Encode(buf[24:], u[10:]) - - return string(buf) -} - -// SetVersion sets the version bits. -func (u *UUID) SetVersion(v byte) { - u[6] = (u[6] & 0x0f) | (v << 4) -} - -// SetVariant sets the variant bits. -func (u *UUID) SetVariant(v byte) { - switch v { - case VariantNCS: - u[8] = (u[8]&(0xff>>1) | (0x00 << 7)) - case VariantRFC4122: - u[8] = (u[8]&(0xff>>2) | (0x02 << 6)) - case VariantMicrosoft: - u[8] = (u[8]&(0xff>>3) | (0x06 << 5)) - case VariantFuture: - fallthrough - default: - u[8] = (u[8]&(0xff>>3) | (0x07 << 5)) - } -} - -// Must is a helper that wraps a call to a function returning (UUID, error) -// and panics if the error is non-nil. It is intended for use in variable -// initializations such as -// var packageUUID = uuid.Must(uuid.FromString("123e4567-e89b-12d3-a456-426655440000")) -func Must(u UUID, err error) UUID { - if err != nil { - panic(err) - } - return u -} diff --git a/vendor/modules.txt b/vendor/modules.txt index 11524798ac..27b6c0d0b4 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -85,8 +85,6 @@ github.com/dgrijalva/jwt-go github.com/dimchansky/utfbom # github.com/fatih/color v1.7.0 github.com/fatih/color -# github.com/gofrs/uuid v3.2.0+incompatible -github.com/gofrs/uuid # github.com/golang/protobuf v1.3.0 github.com/golang/protobuf/proto github.com/golang/protobuf/ptypes @@ -152,9 +150,9 @@ github.com/hashicorp/hcl2/gohcl github.com/hashicorp/hcl2/hclparse github.com/hashicorp/hcl2/ext/typeexpr github.com/hashicorp/hcl2/ext/dynblock +github.com/hashicorp/hcl2/hcled github.com/hashicorp/hcl2/hclwrite github.com/hashicorp/hcl2/hcl/json -github.com/hashicorp/hcl2/hcled # github.com/hashicorp/hil v0.0.0-20190212112733-ab17b08d6590 github.com/hashicorp/hil github.com/hashicorp/hil/ast @@ -169,6 +167,7 @@ github.com/hashicorp/terraform/helper/schema github.com/hashicorp/terraform/helper/validation github.com/hashicorp/terraform/httpclient github.com/hashicorp/terraform/terraform +github.com/hashicorp/terraform/helper/resource github.com/hashicorp/terraform/configs/configschema github.com/hashicorp/terraform/helper/plugin github.com/hashicorp/terraform/internal/tfplugin5 @@ -195,7 +194,11 @@ github.com/hashicorp/terraform/plans/objchange github.com/hashicorp/terraform/states github.com/hashicorp/terraform/states/statefile github.com/hashicorp/terraform/helper/acctest -github.com/hashicorp/terraform/helper/resource +github.com/hashicorp/terraform/command/format +github.com/hashicorp/terraform/configs/configload +github.com/hashicorp/terraform/helper/config +github.com/hashicorp/terraform/helper/logging +github.com/hashicorp/terraform/internal/initwd github.com/hashicorp/terraform/registry github.com/hashicorp/terraform/registry/regsrc github.com/hashicorp/terraform/registry/response @@ -203,15 +206,10 @@ github.com/hashicorp/terraform/svchost/disco github.com/hashicorp/terraform/helper/hilmapstructure github.com/hashicorp/terraform/lang/blocktoattr github.com/hashicorp/terraform/lang/funcs -github.com/hashicorp/terraform/command/format -github.com/hashicorp/terraform/configs/configload -github.com/hashicorp/terraform/helper/config -github.com/hashicorp/terraform/helper/logging -github.com/hashicorp/terraform/internal/initwd -github.com/hashicorp/terraform/svchost -github.com/hashicorp/terraform/svchost/auth github.com/hashicorp/terraform/internal/modsdir github.com/hashicorp/terraform/internal/earlyconfig +github.com/hashicorp/terraform/svchost +github.com/hashicorp/terraform/svchost/auth # github.com/hashicorp/terraform-config-inspect v0.0.0-20190327195015-8022a2663a70 github.com/hashicorp/terraform-config-inspect/tfconfig # github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb @@ -359,6 +357,7 @@ google.golang.org/genproto/googleapis/rpc/code google.golang.org/genproto/googleapis/api/annotations # google.golang.org/grpc v1.18.0 google.golang.org/grpc +google.golang.org/grpc/test/bufconn google.golang.org/grpc/credentials google.golang.org/grpc/health google.golang.org/grpc/health/grpc_health_v1 @@ -387,7 +386,6 @@ google.golang.org/grpc/resolver/passthrough google.golang.org/grpc/stats google.golang.org/grpc/status google.golang.org/grpc/tap -google.golang.org/grpc/test/bufconn google.golang.org/grpc/credentials/internal google.golang.org/grpc/balancer/base google.golang.org/grpc/binarylog/grpc_binarylog_v1 From d39844fe8940a1b93ffc9103796eb9d9911dcf72 Mon Sep 17 00:00:00 2001 From: kt Date: Thu, 6 Jun 2019 09:14:29 -0700 Subject: [PATCH 13/45] Update CHANGELOG.md to include #91 --- CHANGELOG.md | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 03ff3adc0b..f9e07bbd82 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ ## 0.4.0 (Unreleased) +NOTES: + +* Applies with this release could potentially take longer as the provider will now attempt to wait for replication when objets are created. FEATURES: @@ -13,8 +16,10 @@ IMPROVEMENTS: * `azuread_application` - support for the `group_membership_claims` property [GH-78] * `azuread_application` - now exports the `oauth2_permissions` property [GH-79] * `azuread_application` - support for the `type` property enabling the creation of `native` applications [GH-74] -* `azuread_application` - will now wait for replication by waiting for a successful get [GH-86] -* `azuread_service_principal` - will now wait for replication by waiting for a successful get [GH-86] +* `azuread_application` - will now wait for replication by waiting for 10 successful reads after creation [GH-93] +* `azuread_group` - will now wait for replication by waiting for 10 successful reads after creation [GH-91] +* `azuread_service_principal` - will now wait for replication by waiting for 10 successful reads after creation [GH-93] +* `azuread_user` - will now wait for replication by waiting for 10 successful reads after creation [GH-91] * `azuread_user` - increase the maximum allowed lengh of `password` to 256 [GH-81] ## 0.3.1 (April 18, 2019) From 1739172d14e2815b0eb304d5c13f74a320d68689 Mon Sep 17 00:00:00 2001 From: kt Date: Thu, 6 Jun 2019 11:37:45 -0700 Subject: [PATCH 14/45] add explicit object_id property to resources (#99) --- azuread/data_application_test.go | 4 ++-- azuread/data_group_test.go | 2 +- azuread/data_service_principal_test.go | 2 +- azuread/data_user_test.go | 2 +- azuread/resource_application.go | 6 ++++++ azuread/resource_application_test.go | 2 ++ azuread/resource_group.go | 12 +++++++++--- azuread/resource_group_test.go | 2 ++ azuread/resource_service_principal.go | 6 ++++++ azuread/resource_service_principal_test.go | 2 ++ azuread/resource_user.go | 6 ++++++ azuread/resource_user_test.go | 4 ++++ website/docs/d/group.html.markdown | 1 + website/docs/r/application.html.markdown | 2 ++ website/docs/r/service_principal.html.markdown | 2 ++ website/docs/r/user.html.markdown | 1 + 16 files changed, 48 insertions(+), 8 deletions(-) diff --git a/azuread/data_application_test.go b/azuread/data_application_test.go index 97766ae1ad..e3722b4639 100644 --- a/azuread/data_application_test.go +++ b/azuread/data_application_test.go @@ -105,7 +105,7 @@ func testAccAzureADApplicationDataSource_objectId(id string) string { %s data "azuread_application" "test" { - object_id = "${azuread_application.test.id}" + object_id = "${azuread_application.test.object_id}" } `, template) } @@ -116,7 +116,7 @@ func testAccAzureADApplicationDataSource_objectIdComplete(id string) string { %s data "azuread_application" "test" { - object_id = "${azuread_application.test.id}" + object_id = "${azuread_application.test.object_id}" } `, template) } diff --git a/azuread/data_group_test.go b/azuread/data_group_test.go index 3bd3f4c570..eaa077d146 100644 --- a/azuread/data_group_test.go +++ b/azuread/data_group_test.go @@ -69,7 +69,7 @@ func testAccDataSourceAzureADGroup_objectId(id string) string { %s data "azuread_group" "test" { - object_id = "${azuread_group.test.id}" + object_id = "${azuread_group.test.object_id}" } `, testAccAzureADGroup(id)) } diff --git a/azuread/data_service_principal_test.go b/azuread/data_service_principal_test.go index 1328f2bcd2..f0a166d287 100644 --- a/azuread/data_service_principal_test.go +++ b/azuread/data_service_principal_test.go @@ -102,7 +102,7 @@ func testAccAzureADServicePrincipalDataSource_byObjectId(id string) string { %s data "azuread_service_principal" "test" { - object_id = "${azuread_service_principal.test.id}" + object_id = "${azuread_service_principal.test.object_id}" } `, template) } diff --git a/azuread/data_user_test.go b/azuread/data_user_test.go index daa365a75f..88d7bb523c 100644 --- a/azuread/data_user_test.go +++ b/azuread/data_user_test.go @@ -68,7 +68,7 @@ func testAccAzureADUserDataSource_byObjectId(id, password string) string { %s data "azuread_user" "test" { - object_id = "${azuread_user.test.id}" + object_id = "${azuread_user.test.object_id}" } `, testAccADUser_basic(id, password)) } diff --git a/azuread/resource_application.go b/azuread/resource_application.go index 079d924a6a..9e6a73b1fe 100644 --- a/azuread/resource_application.go +++ b/azuread/resource_application.go @@ -176,6 +176,11 @@ func resourceApplication() *schema.Resource { }, }, }, + + "object_id": { + Type: schema.TypeString, + Computed: true, + }, }, } } @@ -346,6 +351,7 @@ func resourceApplicationRead(d *schema.ResourceData, meta interface{}) error { d.Set("homepage", resp.Homepage) d.Set("available_to_other_tenants", resp.AvailableToOtherTenants) d.Set("oauth2_allow_implicit_flow", resp.Oauth2AllowImplicitFlow) + d.Set("object_id", resp.ObjectID) if groupMembershipClaims, ok := resp.AdditionalProperties["groupMembershipClaims"]; ok { d.Set("group_membership_claims", groupMembershipClaims) diff --git a/azuread/resource_application_test.go b/azuread/resource_application_test.go index 2152c8514f..7796ead99c 100644 --- a/azuread/resource_application_test.go +++ b/azuread/resource_application_test.go @@ -30,6 +30,7 @@ func TestAccAzureADApplication_basic(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "oauth2_permissions.#", "1"), resource.TestCheckResourceAttr(resourceName, "oauth2_permissions.0.admin_consent_description", fmt.Sprintf("Access %s", fmt.Sprintf("acctest%s", id))), resource.TestCheckResourceAttrSet(resourceName, "application_id"), + resource.TestCheckResourceAttrSet(resourceName, "object_id"), ), }, { @@ -62,6 +63,7 @@ func TestAccAzureADApplication_complete(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "group_membership_claims", "All"), resource.TestCheckResourceAttr(resourceName, "required_resource_access.#", "2"), resource.TestCheckResourceAttrSet(resourceName, "application_id"), + resource.TestCheckResourceAttrSet(resourceName, "object_id"), ), }, { diff --git a/azuread/resource_group.go b/azuread/resource_group.go index c9d890be1f..66ff3d6794 100644 --- a/azuread/resource_group.go +++ b/azuread/resource_group.go @@ -30,6 +30,11 @@ func resourceGroup() *schema.Resource { ForceNew: true, ValidateFunc: validation.NoZeroValues, }, + + "object_id": { + Type: schema.TypeString, + Computed: true, + }, }, } } @@ -42,9 +47,9 @@ func resourceGroupCreate(d *schema.ResourceData, meta interface{}) error { 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. - MailNickname: p.String(uuid.New().String()), //this matches the portal behavior - SecurityEnabled: p.Bool(true), //we're defaulting to true, as the API currently only supports the creation of non-mail enabled security groups. + MailEnabled: p.Bool(false), // we're defaulting to false, as the API currently only supports the creation of non-mail enabled security groups. + MailNickname: p.String(uuid.New().String()), // this matches the portal behavior + SecurityEnabled: p.Bool(true), // we're defaulting to true, as the API currently only supports the creation of non-mail enabled security groups. } group, err := client.Create(ctx, properties) @@ -82,6 +87,7 @@ func resourceGroupRead(d *schema.ResourceData, meta interface{}) error { } d.Set("name", resp.DisplayName) + d.Set("object_id", resp.ObjectID) return nil } diff --git a/azuread/resource_group_test.go b/azuread/resource_group_test.go index 1a15009539..e210c2e666 100644 --- a/azuread/resource_group_test.go +++ b/azuread/resource_group_test.go @@ -28,6 +28,7 @@ func TestAccAzureADGroup_basic(t *testing.T) { Check: resource.ComposeTestCheckFunc( testCheckAzureADGroupExists(resourceName), resource.TestCheckResourceAttr(resourceName, "name", fmt.Sprintf("acctest%s", id)), + resource.TestCheckResourceAttrSet(resourceName, "object_id"), ), }, { @@ -57,6 +58,7 @@ func TestAccAzureADGroup_complete(t *testing.T) { Check: resource.ComposeTestCheckFunc( testCheckAzureADGroupExists(resourceName), resource.TestCheckResourceAttr(resourceName, "name", fmt.Sprintf("acctest%s", id)), + resource.TestCheckResourceAttrSet(resourceName, "object_id"), ), }, { diff --git a/azuread/resource_service_principal.go b/azuread/resource_service_principal.go index 615b849dbe..fcf0b6e7be 100644 --- a/azuread/resource_service_principal.go +++ b/azuread/resource_service_principal.go @@ -50,6 +50,11 @@ func resourceServicePrincipal() *schema.Resource { Type: schema.TypeString, Computed: true, }, + + "object_id": { + Type: schema.TypeString, + Computed: true, + }, }, } } @@ -107,6 +112,7 @@ func resourceServicePrincipalRead(d *schema.ResourceData, meta interface{}) erro d.Set("application_id", app.AppID) d.Set("display_name", app.DisplayName) + d.Set("object_id", app.ObjectID) // tags doesn't exist as a property, so extract it if iTags, ok := app.AdditionalProperties["tags"]; ok { diff --git a/azuread/resource_service_principal_test.go b/azuread/resource_service_principal_test.go index 18e31db61f..32cbba8381 100644 --- a/azuread/resource_service_principal_test.go +++ b/azuread/resource_service_principal_test.go @@ -26,6 +26,7 @@ func TestAccAzureADServicePrincipal_basic(t *testing.T) { testCheckADServicePrincipalExists(resourceName), resource.TestCheckResourceAttrSet(resourceName, "display_name"), resource.TestCheckResourceAttrSet(resourceName, "application_id"), + resource.TestCheckResourceAttrSet(resourceName, "object_id"), ), }, { @@ -51,6 +52,7 @@ func TestAccAzureADServicePrincipal_complete(t *testing.T) { Check: resource.ComposeTestCheckFunc( testCheckADServicePrincipalExists(resourceName), resource.TestCheckResourceAttr(resourceName, "tags.#", "3"), + resource.TestCheckResourceAttrSet(resourceName, "object_id"), ), }, { diff --git a/azuread/resource_user.go b/azuread/resource_user.go index fc0205ba62..d38b2ade9f 100644 --- a/azuread/resource_user.go +++ b/azuread/resource_user.go @@ -68,6 +68,11 @@ func resourceUser() *schema.Resource { Type: schema.TypeString, Computed: true, }, + + "object_id": { + Type: schema.TypeString, + Computed: true, + }, }, } } @@ -139,6 +144,7 @@ func resourceUserRead(d *schema.ResourceData, meta interface{}) error { d.Set("mail", user.Mail) d.Set("mail_nickname", user.MailNickname) d.Set("account_enabled", user.AccountEnabled) + d.Set("object_id", user.ObjectID) return nil } diff --git a/azuread/resource_user_test.go b/azuread/resource_user_test.go index c41f06a644..f26ec2ff0a 100644 --- a/azuread/resource_user_test.go +++ b/azuread/resource_user_test.go @@ -26,6 +26,7 @@ func TestAccAzureADUser_basic(t *testing.T) { Check: resource.ComposeTestCheckFunc( testCheckADUserExists(resourceName), resource.TestCheckResourceAttrSet(resourceName, "user_principal_name"), + resource.TestCheckResourceAttrSet(resourceName, "object_id"), resource.TestCheckResourceAttr(resourceName, "display_name", fmt.Sprintf("acctest%s", id)), resource.TestCheckResourceAttr(resourceName, "mail_nickname", fmt.Sprintf("acctest%s", id)), resource.TestCheckResourceAttr(resourceName, "account_enabled", "true"), @@ -59,6 +60,7 @@ func TestAccAzureADUser_complete(t *testing.T) { Check: resource.ComposeTestCheckFunc( testCheckADUserExists(resourceName), resource.TestCheckResourceAttrSet(resourceName, "user_principal_name"), + resource.TestCheckResourceAttrSet(resourceName, "object_id"), resource.TestCheckResourceAttr(resourceName, "display_name", fmt.Sprintf("acctestupdate%s", id)), resource.TestCheckResourceAttr(resourceName, "mail_nickname", fmt.Sprintf("acctestupdate%s", id)), resource.TestCheckResourceAttr(resourceName, "account_enabled", "false"), @@ -93,6 +95,7 @@ func TestAccAzureADUser_update(t *testing.T) { Check: resource.ComposeTestCheckFunc( testCheckADUserExists(resourceName), resource.TestCheckResourceAttrSet(resourceName, "user_principal_name"), + resource.TestCheckResourceAttrSet(resourceName, "object_id"), resource.TestCheckResourceAttr(resourceName, "display_name", fmt.Sprintf("acctest%s", id)), resource.TestCheckResourceAttr(resourceName, "mail_nickname", fmt.Sprintf("acctest%s", id)), resource.TestCheckResourceAttr(resourceName, "account_enabled", "true"), @@ -103,6 +106,7 @@ func TestAccAzureADUser_update(t *testing.T) { Check: resource.ComposeTestCheckFunc( testCheckADUserExists(resourceName), resource.TestCheckResourceAttrSet(resourceName, "user_principal_name"), + resource.TestCheckResourceAttrSet(resourceName, "object_id"), resource.TestCheckResourceAttr(resourceName, "display_name", fmt.Sprintf("acctestupdate%s", id)), resource.TestCheckResourceAttr(resourceName, "mail_nickname", fmt.Sprintf("acctestupdate%s", id)), resource.TestCheckResourceAttr(resourceName, "account_enabled", "false"), diff --git a/website/docs/d/group.html.markdown b/website/docs/d/group.html.markdown index a64500f2eb..6ee37b08a4 100644 --- a/website/docs/d/group.html.markdown +++ b/website/docs/d/group.html.markdown @@ -36,3 +36,4 @@ The following arguments are supported: The following attributes are exported: * `id` - The Object ID of the Azure AD Group. + diff --git a/website/docs/r/application.html.markdown b/website/docs/r/application.html.markdown index e33b57b038..2b43fb11b1 100644 --- a/website/docs/r/application.html.markdown +++ b/website/docs/r/application.html.markdown @@ -98,6 +98,8 @@ The following attributes are exported: * `application_id` - The Application ID. +* `object_id` - The Application's Object ID. + * `oauth2_permissions` - A collection of OAuth 2.0 permission scopes that the web API (resource) app exposes to client apps. Each permission is covered by a `oauth2_permission` block as documented below. --- diff --git a/website/docs/r/service_principal.html.markdown b/website/docs/r/service_principal.html.markdown index 4c6d934130..a41b3cee52 100644 --- a/website/docs/r/service_principal.html.markdown +++ b/website/docs/r/service_principal.html.markdown @@ -48,6 +48,8 @@ The following attributes are exported: * `application_id` - The Application ID (appId) for the Service Principal. +* `object_id` - The Service Principal's Object ID. + * `display_name` - The Display Name of the Azure Active Directory Application associated with this Service Principal. ## Import diff --git a/website/docs/r/user.html.markdown b/website/docs/r/user.html.markdown index f1111d645e..2afce8393b 100644 --- a/website/docs/r/user.html.markdown +++ b/website/docs/r/user.html.markdown @@ -39,5 +39,6 @@ The following arguments are supported: The following attributes are exported: +* `object_id` - The Object ID of the Azure AD User. * `id` - The Object ID of the Azure AD User. * `mail` - The primary email address of the Azure AD User. From b86a50884e3e9da9476dce11a9b98019e9ce074c Mon Sep 17 00:00:00 2001 From: kt Date: Thu, 6 Jun 2019 11:38:48 -0700 Subject: [PATCH 15/45] Update CHANGELOG.md to include #99 --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f9e07bbd82..2c213921ce 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,12 +15,16 @@ IMPROVEMENTS: * Data Source `azuread_application` - now exports the `oauth2_permissions` property [GH-79] * `azuread_application` - support for the `group_membership_claims` property [GH-78] * `azuread_application` - now exports the `oauth2_permissions` property [GH-79] +* `azuread_application` - now exports the `object_id` property [GH-99] * `azuread_application` - support for the `type` property enabling the creation of `native` applications [GH-74] * `azuread_application` - will now wait for replication by waiting for 10 successful reads after creation [GH-93] * `azuread_group` - will now wait for replication by waiting for 10 successful reads after creation [GH-91] +* `azuread_group` - now exports the `object_id` property [GH-99] * `azuread_service_principal` - will now wait for replication by waiting for 10 successful reads after creation [GH-93] +* `azuread_service_principal` - now exports the `object_id` property [GH-99] * `azuread_user` - will now wait for replication by waiting for 10 successful reads after creation [GH-91] * `azuread_user` - increase the maximum allowed lengh of `password` to 256 [GH-81] +* `azuread_user` - now exports the `object_id` property [GH-99] ## 0.3.1 (April 18, 2019) From 239b9741e0989334f9e075a14b437838afb10c9c Mon Sep 17 00:00:00 2001 From: kt Date: Thu, 6 Jun 2019 13:23:39 -0700 Subject: [PATCH 16/45] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2c213921ce..94de6a7000 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ NOTES: -* Applies with this release could potentially take longer as the provider will now attempt to wait for replication when objets are created. +* Resource creation potentially could take longer after this release as the provider will now attempt to wait for replication like the az cli tool. FEATURES: From 0ed698531d3dd13fed4e51469fda2d517f98aa9d Mon Sep 17 00:00:00 2001 From: tf-release-bot Date: Thu, 6 Jun 2019 20:32:19 +0000 Subject: [PATCH 17/45] v0.4.0 --- CHANGELOG.md | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 94de6a7000..5488d8c145 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -## 0.4.0 (Unreleased) +## 0.4.0 (June 06, 2019) NOTES: @@ -6,25 +6,25 @@ NOTES: FEATURES: -* **New Resource:** `azuread_application_password` [GH-71] +* **New Resource:** `azuread_application_password` ([#71](https://github.com/terraform-providers/terraform-provider-azuread/issues/71)) IMPROVEMENTS: -* dependencies: upgrading to `v0.12.0` of `github.com/hashicorp/terraform` [GH-82] -* Data Source `azuread_application` - now exports the `group_membership_claims` property [GH-78] -* Data Source `azuread_application` - now exports the `oauth2_permissions` property [GH-79] -* `azuread_application` - support for the `group_membership_claims` property [GH-78] -* `azuread_application` - now exports the `oauth2_permissions` property [GH-79] -* `azuread_application` - now exports the `object_id` property [GH-99] -* `azuread_application` - support for the `type` property enabling the creation of `native` applications [GH-74] -* `azuread_application` - will now wait for replication by waiting for 10 successful reads after creation [GH-93] -* `azuread_group` - will now wait for replication by waiting for 10 successful reads after creation [GH-91] -* `azuread_group` - now exports the `object_id` property [GH-99] -* `azuread_service_principal` - will now wait for replication by waiting for 10 successful reads after creation [GH-93] -* `azuread_service_principal` - now exports the `object_id` property [GH-99] -* `azuread_user` - will now wait for replication by waiting for 10 successful reads after creation [GH-91] -* `azuread_user` - increase the maximum allowed lengh of `password` to 256 [GH-81] -* `azuread_user` - now exports the `object_id` property [GH-99] +* dependencies: upgrading to `v0.12.0` of `github.com/hashicorp/terraform` ([#82](https://github.com/terraform-providers/terraform-provider-azuread/issues/82)) +* Data Source `azuread_application` - now exports the `group_membership_claims` property ([#78](https://github.com/terraform-providers/terraform-provider-azuread/issues/78)) +* Data Source `azuread_application` - now exports the `oauth2_permissions` property ([#79](https://github.com/terraform-providers/terraform-provider-azuread/issues/79)) +* `azuread_application` - support for the `group_membership_claims` property ([#78](https://github.com/terraform-providers/terraform-provider-azuread/issues/78)) +* `azuread_application` - now exports the `oauth2_permissions` property ([#79](https://github.com/terraform-providers/terraform-provider-azuread/issues/79)) +* `azuread_application` - now exports the `object_id` property ([#99](https://github.com/terraform-providers/terraform-provider-azuread/issues/99)) +* `azuread_application` - support for the `type` property enabling the creation of `native` applications ([#74](https://github.com/terraform-providers/terraform-provider-azuread/issues/74)) +* `azuread_application` - will now wait for replication by waiting for 10 successful reads after creation ([#93](https://github.com/terraform-providers/terraform-provider-azuread/issues/93)) +* `azuread_group` - will now wait for replication by waiting for 10 successful reads after creation ([#91](https://github.com/terraform-providers/terraform-provider-azuread/issues/91)) +* `azuread_group` - now exports the `object_id` property ([#99](https://github.com/terraform-providers/terraform-provider-azuread/issues/99)) +* `azuread_service_principal` - will now wait for replication by waiting for 10 successful reads after creation ([#93](https://github.com/terraform-providers/terraform-provider-azuread/issues/93)) +* `azuread_service_principal` - now exports the `object_id` property ([#99](https://github.com/terraform-providers/terraform-provider-azuread/issues/99)) +* `azuread_user` - will now wait for replication by waiting for 10 successful reads after creation ([#91](https://github.com/terraform-providers/terraform-provider-azuread/issues/91)) +* `azuread_user` - increase the maximum allowed lengh of `password` to 256 ([#81](https://github.com/terraform-providers/terraform-provider-azuread/issues/81)) +* `azuread_user` - now exports the `object_id` property ([#99](https://github.com/terraform-providers/terraform-provider-azuread/issues/99)) ## 0.3.1 (April 18, 2019) From 2e0864bfb0c7698bd54cfc0e3922109a6d9e24d6 Mon Sep 17 00:00:00 2001 From: kt Date: Mon, 10 Jun 2019 08:29:54 -0700 Subject: [PATCH 18/45] Upgrade azure-sdk-for-go to v29.0.0 (#102) --- azuread/config.go | 5 +- azuread/data_application.go | 13 +- azuread/data_application_test.go | 2 +- azuread/helpers/ar/sender.go | 57 + azuread/resource_application.go | 88 +- azuread/resource_application_test.go | 10 +- azuread/resource_service_principal.go | 29 +- go.mod | 8 +- go.sum | 14 +- .../graphrbac/1.6/graphrbac/applications.go | 79 +- .../graphrbac/1.6/graphrbac/models.go | 1844 +++++++++-------- .../graphrbac/1.6/graphrbac/oauth2.go | 197 -- .../1.6/graphrbac/oauth2permissiongrant.go | 369 ++++ .../Azure/azure-sdk-for-go/version/version.go | 2 +- .../Azure/go-autorest/autorest/adal/token.go | 2 +- .../go-autorest/autorest/adal/version.go | 12 +- .../go-autorest/autorest/authorization.go | 27 + .../Azure/go-autorest/autorest/azure/async.go | 10 +- .../go-autorest/autorest/azure/cli/token.go | 8 +- .../autorest/azure/environments.go | 13 +- .../Azure/go-autorest/autorest/client.go | 6 + .../Azure/go-autorest/autorest/version.go | 2 +- vendor/github.com/google/uuid/README.md | 4 - vendor/github.com/google/uuid/go.mod | 1 + vendor/github.com/google/uuid/hash.go | 2 +- vendor/github.com/google/uuid/marshal.go | 2 - vendor/github.com/google/uuid/node.go | 33 +- vendor/github.com/google/uuid/node_js.go | 12 + vendor/github.com/google/uuid/node_net.go | 33 + vendor/github.com/google/uuid/time.go | 6 +- vendor/github.com/google/uuid/uuid.go | 92 +- vendor/github.com/google/uuid/version4.go | 2 +- .../authentication/auth_method.go | 2 +- .../auth_method_azure_cli_token.go | 2 +- .../authentication/auth_method_client_cert.go | 4 +- .../auth_method_client_secret.go | 3 +- .../authentication/auth_method_msi.go | 5 +- .../go-azure-helpers/authentication/config.go | 4 +- vendor/modules.txt | 8 +- 39 files changed, 1794 insertions(+), 1218 deletions(-) create mode 100644 azuread/helpers/ar/sender.go delete mode 100644 vendor/github.com/Azure/azure-sdk-for-go/services/graphrbac/1.6/graphrbac/oauth2.go create mode 100644 vendor/github.com/Azure/azure-sdk-for-go/services/graphrbac/1.6/graphrbac/oauth2permissiongrant.go create mode 100644 vendor/github.com/google/uuid/go.mod create mode 100644 vendor/github.com/google/uuid/node_js.go create mode 100644 vendor/github.com/google/uuid/node_net.go diff --git a/azuread/config.go b/azuread/config.go index d5d9d70e70..014ced5dca 100644 --- a/azuread/config.go +++ b/azuread/config.go @@ -15,6 +15,7 @@ import ( "github.com/hashicorp/go-azure-helpers/authentication" "github.com/hashicorp/go-azure-helpers/sender" "github.com/hashicorp/terraform/httpclient" + "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/ar" "github.com/terraform-providers/terraform-provider-azuread/version" ) @@ -50,6 +51,8 @@ func getArmClient(authCfg *authentication.Config) (*ArmClient, error) { environment: *env, } + sender := ar.BuildSender() + oauthConfig, err := adal.NewOAuthConfig(env.ActiveDirectoryEndpoint, client.tenantID) if err != nil { return nil, err @@ -62,7 +65,7 @@ func getArmClient(authCfg *authentication.Config) (*ArmClient, error) { // Graph Endpoints graphEndpoint := env.GraphEndpoint - graphAuthorizer, err := authCfg.GetAuthorizationToken(oauthConfig, graphEndpoint) + graphAuthorizer, err := authCfg.GetAuthorizationToken(sender, oauthConfig, graphEndpoint) if err != nil { return nil, err } diff --git a/azuread/data_application.go b/azuread/data_application.go index 13cbb1d7fd..6ef2267e4d 100644 --- a/azuread/data_application.go +++ b/azuread/data_application.go @@ -233,19 +233,18 @@ func dataApplicationRead(d *schema.ResourceData, meta interface{}) error { return fmt.Errorf("Error setting `required_resource_access`: %+v", err) } - switch appType := app.AdditionalProperties["publicClient"]; appType { - case true: + if v := app.PublicClient; v != nil && *v { d.Set("type", "native") - default: + } else { d.Set("type", "webapp/api") } - if groupMembershipClaims, ok := app.AdditionalProperties["groupMembershipClaims"]; ok { - d.Set("group_membership_claims", groupMembershipClaims) + if err := d.Set("group_membership_claims", app.GroupMembershipClaims); err != nil { + return fmt.Errorf("Error setting `group_membership_claims`: %+v", err) } - if oauth2Permissions, ok := app.AdditionalProperties["oauth2Permissions"].([]interface{}); ok { - d.Set("oauth2_permissions", flattenADApplicationOauth2Permissions(oauth2Permissions)) + if err := d.Set("oauth2_permissions", flattenADApplicationOauth2Permissions(app.Oauth2Permissions)); err != nil { + return fmt.Errorf("Error setting `oauth2_permissions`: %+v", err) } return nil diff --git a/azuread/data_application_test.go b/azuread/data_application_test.go index e3722b4639..bef04cd035 100644 --- a/azuread/data_application_test.go +++ b/azuread/data_application_test.go @@ -32,7 +32,7 @@ func TestAccAzureADApplicationDataSource_byObjectId(t *testing.T) { resource.TestCheckResourceAttr(dataSourceName, "type", "webapp/api"), resource.TestCheckResourceAttr(dataSourceName, "oauth2_allow_implicit_flow", "false"), resource.TestCheckResourceAttr(dataSourceName, "oauth2_permissions.#", "1"), - resource.TestCheckResourceAttr(dataSourceName, "oauth2_permissions.0.admin_consent_description", fmt.Sprintf("Access %s", fmt.Sprintf("acctest%s", id))), + resource.TestCheckResourceAttr(dataSourceName, "oauth2_permissions.0.admin_consent_description", fmt.Sprintf("Allow the application to access %s on behalf of the signed-in user.", fmt.Sprintf("acctest%s", id))), resource.TestCheckResourceAttrSet(dataSourceName, "application_id"), ), }, diff --git a/azuread/helpers/ar/sender.go b/azuread/helpers/ar/sender.go new file mode 100644 index 0000000000..55be052378 --- /dev/null +++ b/azuread/helpers/ar/sender.go @@ -0,0 +1,57 @@ +package ar + +import ( + "log" + "net/http" + "net/http/httputil" + + "github.com/Azure/go-autorest/autorest" +) + +func BuildSender() autorest.Sender { + return autorest.DecorateSender(&http.Client{ + Transport: &http.Transport{ + Proxy: http.ProxyFromEnvironment, + }, + }, withRequestLogging()) +} + +func withRequestLogging() autorest.SendDecorator { + return func(s autorest.Sender) autorest.Sender { + return autorest.SenderFunc(func(r *http.Request) (*http.Response, error) { + // strip the authorization header prior to printing + authHeaderName := "Authorization" + auth := r.Header.Get(authHeaderName) + if auth != "" { + r.Header.Del(authHeaderName) + } + + // dump request to wire format + if dump, err := httputil.DumpRequestOut(r, true); err == nil { + log.Printf("[DEBUG] AzureAD Request: \n%s\n", dump) + } else { + // fallback to basic message + log.Printf("[DEBUG] AzureAD Request: %s to %s\n", r.Method, r.URL) + } + + // add the auth header back + if auth != "" { + r.Header.Add(authHeaderName, auth) + } + + resp, err := s.Do(r) + if resp != nil { + // dump response to wire format + if dump, err2 := httputil.DumpResponse(resp, true); err2 == nil { + log.Printf("[DEBUG] AzureAD Response for %s: \n%s\n", r.URL, dump) + } else { + // fallback to basic message + log.Printf("[DEBUG] AzureAD Response: %s for %s\n", resp.Status, r.URL) + } + } else { + log.Printf("[DEBUG] Request to %s completed with no response", r.URL) + } + return resp, err + }) + } +} diff --git a/azuread/resource_application.go b/azuread/resource_application.go index 9e6a73b1fe..4f2e45c13c 100644 --- a/azuread/resource_application.go +++ b/azuread/resource_application.go @@ -81,7 +81,7 @@ func resourceApplication() *schema.Resource { Type: schema.TypeString, Optional: true, ValidateFunc: validation.StringInSlice( - []string{"None", "SecurityGroup", "All"}, + []string{"All", "None", "SecurityGroup", "DirectoryRole", "DistributionGroup"}, false, ), }, @@ -199,7 +199,6 @@ func resourceApplicationCreate(d *schema.ResourceData, meta interface{}) error { } properties := graphrbac.ApplicationCreateParameters{ - AdditionalProperties: make(map[string]interface{}), DisplayName: &name, IdentifierUris: tf.ExpandStringSlicePtr(identUrls.([]interface{})), ReplyUrls: tf.ExpandStringSlicePtr(d.Get("reply_urls").(*schema.Set).List()), @@ -222,7 +221,7 @@ func resourceApplicationCreate(d *schema.ResourceData, meta interface{}) error { } if v, ok := d.GetOk("group_membership_claims"); ok { - properties.AdditionalProperties["groupMembershipClaims"] = v + properties.GroupMembershipClaims = v } app, err := client.Create(ctx, properties) @@ -249,9 +248,7 @@ func resourceApplicationCreate(d *schema.ResourceData, meta interface{}) error { properties := graphrbac.ApplicationUpdateParameters{ Homepage: nil, IdentifierUris: &[]string{}, - AdditionalProperties: map[string]interface{}{ - "publicClient": true, - }, + PublicClient: p.Bool(true), } if _, err := client.Patch(ctx, *app.ObjectID, properties); err != nil { return err @@ -268,7 +265,6 @@ func resourceApplicationUpdate(d *schema.ResourceData, meta interface{}) error { name := d.Get("name").(string) var properties graphrbac.ApplicationUpdateParameters - properties.AdditionalProperties = make(map[string]interface{}) if d.HasChange("name") { properties.DisplayName = &name @@ -301,22 +297,16 @@ func resourceApplicationUpdate(d *schema.ResourceData, meta interface{}) error { } if d.HasChange("group_membership_claims") { - groupMembershipClaims := d.Get("group_membership_claims").(string) - - if len(groupMembershipClaims) == 0 { - properties.AdditionalProperties["groupMembershipClaims"] = nil - } else { - properties.AdditionalProperties["groupMembershipClaims"] = groupMembershipClaims - } + properties.GroupMembershipClaims = d.Get("group_membership_claims") } if d.HasChange("type") { switch appType := d.Get("type"); appType { case "webapp/api": - properties.AdditionalProperties["publicClient"] = false + properties.PublicClient = p.Bool(false) properties.IdentifierUris = tf.ExpandStringSlicePtr(d.Get("identifier_uris").([]interface{})) case "native": - properties.AdditionalProperties["publicClient"] = true + properties.PublicClient = p.Bool(true) properties.IdentifierUris = &[]string{} default: return fmt.Errorf("Error paching Azure AD Application with ID %q: Unknow application type %v. Supported types are [webapp/api, native]", d.Id(), appType) @@ -335,9 +325,9 @@ func resourceApplicationRead(d *schema.ResourceData, meta interface{}) error { client := meta.(*ArmClient).applicationsClient ctx := meta.(*ArmClient).StopContext - resp, err := client.Get(ctx, d.Id()) + app, err := client.Get(ctx, d.Id()) if err != nil { - if ar.ResponseWasNotFound(resp.Response) { + if ar.ResponseWasNotFound(app.Response) { log.Printf("[DEBUG] Azure AD Application with ID %q was not found - removing from state", d.Id()) d.SetId("") return nil @@ -346,38 +336,37 @@ func resourceApplicationRead(d *schema.ResourceData, meta interface{}) error { return fmt.Errorf("Error retrieving Azure AD Application with ID %q: %+v", d.Id(), err) } - d.Set("name", resp.DisplayName) - d.Set("application_id", resp.AppID) - d.Set("homepage", resp.Homepage) - d.Set("available_to_other_tenants", resp.AvailableToOtherTenants) - d.Set("oauth2_allow_implicit_flow", resp.Oauth2AllowImplicitFlow) - d.Set("object_id", resp.ObjectID) - - if groupMembershipClaims, ok := resp.AdditionalProperties["groupMembershipClaims"]; ok { - d.Set("group_membership_claims", groupMembershipClaims) - } + d.Set("name", app.DisplayName) + d.Set("application_id", app.AppID) + d.Set("homepage", app.Homepage) + d.Set("available_to_other_tenants", app.AvailableToOtherTenants) + d.Set("oauth2_allow_implicit_flow", app.Oauth2AllowImplicitFlow) + d.Set("object_id", app.ObjectID) - switch appType := resp.AdditionalProperties["publicClient"]; appType { - case true: + if v := app.PublicClient; v != nil && *v { d.Set("type", "native") - default: + } else { d.Set("type", "webapp/api") } - if err := d.Set("identifier_uris", tf.FlattenStringSlicePtr(resp.IdentifierUris)); err != nil { + if err := d.Set("group_membership_claims", app.GroupMembershipClaims); err != nil { + return fmt.Errorf("Error setting `group_membership_claims`: %+v", err) + } + + if err := d.Set("identifier_uris", tf.FlattenStringSlicePtr(app.IdentifierUris)); err != nil { return fmt.Errorf("Error setting `identifier_uris`: %+v", err) } - if err := d.Set("reply_urls", tf.FlattenStringSlicePtr(resp.ReplyUrls)); err != nil { + if err := d.Set("reply_urls", tf.FlattenStringSlicePtr(app.ReplyUrls)); err != nil { return fmt.Errorf("Error setting `reply_urls`: %+v", err) } - if err := d.Set("required_resource_access", flattenADApplicationRequiredResourceAccess(resp.RequiredResourceAccess)); err != nil { + if err := d.Set("required_resource_access", flattenADApplicationRequiredResourceAccess(app.RequiredResourceAccess)); err != nil { return fmt.Errorf("Error setting `required_resource_access`: %+v", err) } - if oauth2Permissions, ok := resp.AdditionalProperties["oauth2Permissions"].([]interface{}); ok { - d.Set("oauth2_permissions", flattenADApplicationOauth2Permissions(oauth2Permissions)) + if err := d.Set("oauth2_permissions", flattenADApplicationOauth2Permissions(app.Oauth2Permissions)); err != nil { + return fmt.Errorf("Error setting `oauth2_permissions`: %+v", err) } return nil @@ -489,37 +478,36 @@ func flattenADApplicationResourceAccess(in *[]graphrbac.ResourceAccess) []interf return accesses } -func flattenADApplicationOauth2Permissions(in []interface{}) []map[string]interface{} { +func flattenADApplicationOauth2Permissions(in *[]graphrbac.OAuth2Permission) []map[string]interface{} { if in == nil { return []map[string]interface{}{} } - result := make([]map[string]interface{}, 0, len(in)) - for _, oauth2Permissions := range in { - rawPermission := oauth2Permissions.(map[string]interface{}) + result := make([]map[string]interface{}, 0) + for _, p := range *in { permission := make(map[string]interface{}) - if v := rawPermission["adminConsentDescription"]; v != nil { + if v := p.AdminConsentDescription; v != nil { permission["admin_consent_description"] = v } - if v := rawPermission["adminConsentDisplayName"]; v != nil { - permission["admin_consent_description"] = v + if v := p.AdminConsentDisplayName; v != nil { + permission["admin_consent_display_name"] = v } - if v := rawPermission["id"]; v != nil { + if v := p.ID; v != nil { permission["id"] = v } - if v := rawPermission["isEnabled"]; v != nil { - permission["is_enabled"] = v.(bool) + if v := p.IsEnabled; v != nil { + permission["is_enabled"] = *v } - if v := rawPermission["type"]; v != nil { + if v := p.Type; v != nil { permission["type"] = v } - if v := rawPermission["userConsentDescription"]; v != nil { + if v := p.UserConsentDescription; v != nil { permission["user_consent_description"] = v } - if v := rawPermission["userConsentDisplayName"]; v != nil { + if v := p.UserConsentDisplayName; v != nil { permission["user_consent_display_name"] = v } - if v := rawPermission["value"]; v != nil { + if v := p.Value; v != nil { permission["value"] = v } diff --git a/azuread/resource_application_test.go b/azuread/resource_application_test.go index 7796ead99c..8a992164e4 100644 --- a/azuread/resource_application_test.go +++ b/azuread/resource_application_test.go @@ -28,7 +28,7 @@ func TestAccAzureADApplication_basic(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "homepage", fmt.Sprintf("https://acctest%s", id)), resource.TestCheckResourceAttr(resourceName, "type", "webapp/api"), resource.TestCheckResourceAttr(resourceName, "oauth2_permissions.#", "1"), - resource.TestCheckResourceAttr(resourceName, "oauth2_permissions.0.admin_consent_description", fmt.Sprintf("Access %s", fmt.Sprintf("acctest%s", id))), + resource.TestCheckResourceAttr(resourceName, "oauth2_permissions.0.admin_consent_description", fmt.Sprintf("Allow the application to access %s on behalf of the signed-in user.", fmt.Sprintf("acctest%s", id))), resource.TestCheckResourceAttrSet(resourceName, "application_id"), resource.TestCheckResourceAttrSet(resourceName, "object_id"), ), @@ -153,10 +153,10 @@ func TestAccAzureADApplication_groupMembershipClaimsUpdate(t *testing.T) { ), }, { - Config: testAccADApplication_withGroupMembershipClaimsAll(id), + Config: testAccADApplication_withGroupMembershipClaimsDirectoryRole(id), Check: resource.ComposeTestCheckFunc( testCheckADApplicationExists(resourceName), - resource.TestCheckResourceAttr(resourceName, "group_membership_claims", "All"), + resource.TestCheckResourceAttr(resourceName, "group_membership_claims", "DirectoryRole"), ), }, { @@ -376,11 +376,11 @@ resource "azuread_application" "test" { `, id, id) } -func testAccADApplication_withGroupMembershipClaimsAll(id string) string { +func testAccADApplication_withGroupMembershipClaimsDirectoryRole(id string) string { return fmt.Sprintf(` resource "azuread_application" "test" { name = "acctest%s" - group_membership_claims = "All" + group_membership_claims = "DirectoryRole" } `, id) } diff --git a/azuread/resource_service_principal.go b/azuread/resource_service_principal.go index fcf0b6e7be..35cbaf3f36 100644 --- a/azuread/resource_service_principal.go +++ b/azuread/resource_service_principal.go @@ -36,16 +36,6 @@ func resourceServicePrincipal() *schema.Resource { ValidateFunc: validate.UUID, }, - "tags": { - Type: schema.TypeSet, - Optional: true, - Set: schema.HashString, - ForceNew: true, - Elem: &schema.Schema{ - Type: schema.TypeString, - }, - }, - "display_name": { Type: schema.TypeString, Computed: true, @@ -55,6 +45,16 @@ func resourceServicePrincipal() *schema.Resource { Type: schema.TypeString, Computed: true, }, + + "tags": { + Type: schema.TypeSet, + Optional: true, + Set: schema.HashString, + ForceNew: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, }, } } @@ -113,14 +113,9 @@ func resourceServicePrincipalRead(d *schema.ResourceData, meta interface{}) erro d.Set("application_id", app.AppID) d.Set("display_name", app.DisplayName) d.Set("object_id", app.ObjectID) - // tags doesn't exist as a property, so extract it - if iTags, ok := app.AdditionalProperties["tags"]; ok { - if tags, ok := iTags.([]interface{}); ok { - if err := d.Set("tags", tf.ExpandStringSlicePtr(tags)); err != nil { - return fmt.Errorf("Error setting `tags`: %+v", err) - } - } + if err := d.Set("tags", app.Tags); err != nil { + return fmt.Errorf("Error setting `tags`: %+v", err) } return nil diff --git a/go.mod b/go.mod index 3be9b0aa4f..a3ba06eff6 100644 --- a/go.mod +++ b/go.mod @@ -2,10 +2,10 @@ module github.com/terraform-providers/terraform-provider-azuread require ( contrib.go.opencensus.io/exporter/ocagent v0.4.2 // indirect - github.com/Azure/azure-sdk-for-go v24.1.0+incompatible - github.com/Azure/go-autorest v11.2.8+incompatible - github.com/google/uuid v0.0.0-20170814143639-7e072fc3a7be - github.com/hashicorp/go-azure-helpers v0.0.0-20190129193224-166dfd221bb2 + github.com/Azure/azure-sdk-for-go v29.0.0+incompatible + github.com/Azure/go-autorest v11.7.0+incompatible + github.com/google/uuid v1.1.1 + github.com/hashicorp/go-azure-helpers v0.4.1 github.com/hashicorp/go-uuid v1.0.1 github.com/hashicorp/terraform v0.12.0 ) diff --git a/go.sum b/go.sum index 94741fcc25..e254623a03 100644 --- a/go.sum +++ b/go.sum @@ -11,11 +11,11 @@ dmitri.shuralyov.com/service/change v0.0.0-20181023043359-a85b471d5412/go.mod h1 dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D6DFvNNtx+9ybjezNCa8XF0xaYcETyp6rHWU= git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg= github.com/Azure/azure-sdk-for-go v21.3.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= -github.com/Azure/azure-sdk-for-go v24.1.0+incompatible h1:P7GocB7bhkyGbRL1tCy0m9FDqb1V/dqssch3jZieUHk= -github.com/Azure/azure-sdk-for-go v24.1.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= +github.com/Azure/azure-sdk-for-go v29.0.0+incompatible h1:CYPU39ULbGjQBo3gXIqiWouK0C4F+Pt2Zx5CqGvqknE= +github.com/Azure/azure-sdk-for-go v29.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= github.com/Azure/go-autorest v10.15.4+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= -github.com/Azure/go-autorest v11.2.8+incompatible h1:Q2feRPMlcfVcqz3pF87PJzkm5lZrL+x6BDtzhODzNJM= -github.com/Azure/go-autorest v11.2.8+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= +github.com/Azure/go-autorest v11.7.0+incompatible h1:gzma19dc9ejB75D90E5S+/wXouzpZyA+CV+/MJPSD/k= +github.com/Azure/go-autorest v11.7.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= github.com/Azure/go-ntlmssp v0.0.0-20180810175552-4a21cbd618b4 h1:pSm8mp0T2OH2CPmPDPtwHPr3VAQaOwVF/JbllOPP4xA= github.com/Azure/go-ntlmssp v0.0.0-20180810175552-4a21cbd618b4/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= @@ -122,8 +122,8 @@ github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/uuid v0.0.0-20170814143639-7e072fc3a7be h1:JX31ns0WPRsUGmZXMlMoJta76MW+0UM7+JnCmqxDUVs= -github.com/google/uuid v0.0.0-20170814143639-7e072fc3a7be/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= +github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go v2.0.0+incompatible h1:j0GKcs05QVmm7yesiZq2+9cxHkNK9YM6zKx4D2qucQU= github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY= github.com/googleapis/gax-go/v2 v2.0.3 h1:siORttZ36U2R/WjiJuDz8znElWBiAlO9rVt+mqJt0Cc= @@ -150,6 +150,8 @@ github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/U github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-azure-helpers v0.0.0-20190129193224-166dfd221bb2 h1:VBRx+yPYUZaobnn5ANBcOUf4hhWpTHSQgftG4TcDkhI= github.com/hashicorp/go-azure-helpers v0.0.0-20190129193224-166dfd221bb2/go.mod h1:lu62V//auUow6k0IykxLK2DCNW8qTmpm8KqhYVWattA= +github.com/hashicorp/go-azure-helpers v0.4.1 h1:aEWYW4hxAVVmxmq7nPXGK8F44A6HBXQ4m0vB1M3/20g= +github.com/hashicorp/go-azure-helpers v0.4.1/go.mod h1:lu62V//auUow6k0IykxLK2DCNW8qTmpm8KqhYVWattA= github.com/hashicorp/go-checkpoint v0.5.0 h1:MFYpPZCnQqQTE18jFwSII6eUQrD/oxMFp3mlgcqk5mU= github.com/hashicorp/go-checkpoint v0.5.0/go.mod h1:7nfLNL10NsxqO4iWuW6tWW0HjZuDrwkBuEQsVcpCOgg= github.com/hashicorp/go-cleanhttp v0.5.0 h1:wvCrVc9TjDls6+YGAF2hAifE1E5U1+b4tH6KdvN3Gig= diff --git a/vendor/github.com/Azure/azure-sdk-for-go/services/graphrbac/1.6/graphrbac/applications.go b/vendor/github.com/Azure/azure-sdk-for-go/services/graphrbac/1.6/graphrbac/applications.go index fc6dfc571d..36a3275fda 100644 --- a/vendor/github.com/Azure/azure-sdk-for-go/services/graphrbac/1.6/graphrbac/applications.go +++ b/vendor/github.com/Azure/azure-sdk-for-go/services/graphrbac/1.6/graphrbac/applications.go @@ -142,9 +142,7 @@ func (client ApplicationsClient) Create(ctx context.Context, parameters Applicat } if err := validation.Validate([]validation.Validation{ {TargetValue: parameters, - Constraints: []validation.Constraint{{Target: "parameters.AvailableToOtherTenants", Name: validation.Null, Rule: true, Chain: nil}, - {Target: "parameters.DisplayName", Name: validation.Null, Rule: true, Chain: nil}, - {Target: "parameters.IdentifierUris", Name: validation.Null, Rule: true, Chain: nil}}}}); err != nil { + Constraints: []validation.Constraint{{Target: "parameters.DisplayName", Name: validation.Null, Rule: true, Chain: nil}}}}); err != nil { return result, validation.NewError("graphrbac.ApplicationsClient", "Create", err.Error()) } @@ -359,6 +357,81 @@ func (client ApplicationsClient) GetResponder(resp *http.Response) (result Appli return } +// GetServicePrincipalsIDByAppID gets an object id for a given application id from the current tenant. +// Parameters: +// applicationID - the application ID. +func (client ApplicationsClient) GetServicePrincipalsIDByAppID(ctx context.Context, applicationID string) (result ServicePrincipalObjectResult, err error) { + if tracing.IsEnabled() { + ctx = tracing.StartSpan(ctx, fqdn+"/ApplicationsClient.GetServicePrincipalsIDByAppID") + defer func() { + sc := -1 + if result.Response.Response != nil { + sc = result.Response.Response.StatusCode + } + tracing.EndSpan(ctx, sc, err) + }() + } + req, err := client.GetServicePrincipalsIDByAppIDPreparer(ctx, applicationID) + if err != nil { + err = autorest.NewErrorWithError(err, "graphrbac.ApplicationsClient", "GetServicePrincipalsIDByAppID", nil, "Failure preparing request") + return + } + + resp, err := client.GetServicePrincipalsIDByAppIDSender(req) + if err != nil { + result.Response = autorest.Response{Response: resp} + err = autorest.NewErrorWithError(err, "graphrbac.ApplicationsClient", "GetServicePrincipalsIDByAppID", resp, "Failure sending request") + return + } + + result, err = client.GetServicePrincipalsIDByAppIDResponder(resp) + if err != nil { + err = autorest.NewErrorWithError(err, "graphrbac.ApplicationsClient", "GetServicePrincipalsIDByAppID", resp, "Failure responding to request") + } + + return +} + +// GetServicePrincipalsIDByAppIDPreparer prepares the GetServicePrincipalsIDByAppID request. +func (client ApplicationsClient) GetServicePrincipalsIDByAppIDPreparer(ctx context.Context, applicationID string) (*http.Request, error) { + pathParameters := map[string]interface{}{ + "applicationID": autorest.Encode("path", applicationID), + "tenantID": autorest.Encode("path", client.TenantID), + } + + const APIVersion = "1.6" + queryParameters := map[string]interface{}{ + "api-version": APIVersion, + } + + preparer := autorest.CreatePreparer( + autorest.AsGet(), + autorest.WithBaseURL(client.BaseURI), + autorest.WithPathParameters("/{tenantID}/servicePrincipalsByAppId/{applicationID}/objectId", pathParameters), + autorest.WithQueryParameters(queryParameters)) + return preparer.Prepare((&http.Request{}).WithContext(ctx)) +} + +// GetServicePrincipalsIDByAppIDSender sends the GetServicePrincipalsIDByAppID request. The method will close the +// http.Response Body if it receives an error. +func (client ApplicationsClient) GetServicePrincipalsIDByAppIDSender(req *http.Request) (*http.Response, error) { + return autorest.SendWithSender(client, req, + autorest.DoRetryForStatusCodes(client.RetryAttempts, client.RetryDuration, autorest.StatusCodesForRetry...)) +} + +// GetServicePrincipalsIDByAppIDResponder handles the response to the GetServicePrincipalsIDByAppID request. The method always +// closes the http.Response Body. +func (client ApplicationsClient) GetServicePrincipalsIDByAppIDResponder(resp *http.Response) (result ServicePrincipalObjectResult, err error) { + err = autorest.Respond( + resp, + client.ByInspecting(), + azure.WithErrorUnlessStatusCode(http.StatusOK), + autorest.ByUnmarshallingJSON(&result), + autorest.ByClosing()) + result.Response = autorest.Response{Response: resp} + return +} + // List lists applications by filter parameters. // Parameters: // filter - the filters to apply to the operation. diff --git a/vendor/github.com/Azure/azure-sdk-for-go/services/graphrbac/1.6/graphrbac/models.go b/vendor/github.com/Azure/azure-sdk-for-go/services/graphrbac/1.6/graphrbac/models.go index 66c76b8ef6..e6b583d96f 100644 --- a/vendor/github.com/Azure/azure-sdk-for-go/services/graphrbac/1.6/graphrbac/models.go +++ b/vendor/github.com/Azure/azure-sdk-for-go/services/graphrbac/1.6/graphrbac/models.go @@ -30,6 +30,21 @@ import ( // The package's fully qualified name. const fqdn = "github.com/Azure/azure-sdk-for-go/services/graphrbac/1.6/graphrbac" +// ConsentType enumerates the values for consent type. +type ConsentType string + +const ( + // AllPrincipals ... + AllPrincipals ConsentType = "AllPrincipals" + // Principal ... + Principal ConsentType = "Principal" +) + +// PossibleConsentTypeValues returns an array of possible values for the ConsentType const type. +func PossibleConsentTypeValues() []ConsentType { + return []ConsentType{AllPrincipals, Principal} +} + // ObjectType enumerates the values for object type. type ObjectType string @@ -137,9 +152,9 @@ type ADGroup struct { Mail *string `json:"mail,omitempty"` // AdditionalProperties - Unmatched properties from the message are deserialized this collection AdditionalProperties map[string]interface{} `json:""` - // ObjectID - The object ID. + // ObjectID - READ-ONLY; The object ID. ObjectID *string `json:"objectId,omitempty"` - // DeletionTimestamp - The time at which the directory object was deleted. + // DeletionTimestamp - READ-ONLY; The time at which the directory object was deleted. DeletionTimestamp *date.Time `json:"deletionTimestamp,omitempty"` // ObjectType - Possible values include: 'ObjectTypeDirectoryObject', 'ObjectTypeApplication', 'ObjectTypeGroup', 'ObjectTypeServicePrincipal', 'ObjectTypeUser' ObjectType ObjectType `json:"objectType,omitempty"` @@ -164,12 +179,6 @@ func (ag ADGroup) MarshalJSON() ([]byte, error) { if ag.Mail != nil { objectMap["mail"] = ag.Mail } - if ag.ObjectID != nil { - objectMap["objectId"] = ag.ObjectID - } - if ag.DeletionTimestamp != nil { - objectMap["deletionTimestamp"] = ag.DeletionTimestamp - } if ag.ObjectType != "" { objectMap["objectType"] = ag.ObjectType } @@ -313,33 +322,72 @@ type Application struct { autorest.Response `json:"-"` // AppID - The application ID. AppID *string `json:"appId,omitempty"` + // AllowGuestsSignIn - A property on the application to indicate if the application accepts other IDPs or not or partially accepts. + AllowGuestsSignIn *bool `json:"allowGuestsSignIn,omitempty"` + // AllowPassthroughUsers - Indicates that the application supports pass through users who have no presence in the resource tenant. + AllowPassthroughUsers *bool `json:"allowPassthroughUsers,omitempty"` + // AppLogoURL - The url for the application logo image stored in a CDN. + AppLogoURL *string `json:"appLogoUrl,omitempty"` // AppRoles - The collection of application roles that an application may declare. These roles can be assigned to users, groups or service principals. AppRoles *[]AppRole `json:"appRoles,omitempty"` // AppPermissions - The application permissions. AppPermissions *[]string `json:"appPermissions,omitempty"` - // AvailableToOtherTenants - Whether the application is be available to other tenants. + // AvailableToOtherTenants - Whether the application is available to other tenants. AvailableToOtherTenants *bool `json:"availableToOtherTenants,omitempty"` // DisplayName - The display name of the application. DisplayName *string `json:"displayName,omitempty"` - // IdentifierUris - A collection of URIs for the application. - IdentifierUris *[]string `json:"identifierUris,omitempty"` - // ReplyUrls - A collection of reply URLs for the application. - ReplyUrls *[]string `json:"replyUrls,omitempty"` + // ErrorURL - A URL provided by the author of the application to report errors when using the application. + ErrorURL *string `json:"errorUrl,omitempty"` + // GroupMembershipClaims - Configures the groups claim issued in a user or OAuth 2.0 access token that the app expects. + GroupMembershipClaims interface{} `json:"groupMembershipClaims,omitempty"` // Homepage - The home page of the application. Homepage *string `json:"homepage,omitempty"` - // Oauth2AllowImplicitFlow - Whether to allow implicit grant flow for OAuth2 - Oauth2AllowImplicitFlow *bool `json:"oauth2AllowImplicitFlow,omitempty"` - // RequiredResourceAccess - Specifies resources that this application requires access to and the set of OAuth permission scopes and application roles that it needs under each of those resources. This pre-configuration of required resource access drives the consent experience. - RequiredResourceAccess *[]RequiredResourceAccess `json:"requiredResourceAccess,omitempty"` + // IdentifierUris - A collection of URIs for the application. + IdentifierUris *[]string `json:"identifierUris,omitempty"` + // InformationalUrls - URLs with more information about the application. + InformationalUrls *InformationalURL `json:"informationalUrls,omitempty"` + // IsDeviceOnlyAuthSupported - Specifies whether this application supports device authentication without a user. The default is false. + IsDeviceOnlyAuthSupported *bool `json:"isDeviceOnlyAuthSupported,omitempty"` // KeyCredentials - A collection of KeyCredential objects. KeyCredentials *[]KeyCredential `json:"keyCredentials,omitempty"` + // KnownClientApplications - Client applications that are tied to this resource application. Consent to any of the known client applications will result in implicit consent to the resource application through a combined consent dialog (showing the OAuth permission scopes required by the client and the resource). + KnownClientApplications *[]string `json:"knownClientApplications,omitempty"` + // LogoutURL - the url of the logout page + LogoutURL *string `json:"logoutUrl,omitempty"` + // Oauth2AllowImplicitFlow - Whether to allow implicit grant flow for OAuth2 + Oauth2AllowImplicitFlow *bool `json:"oauth2AllowImplicitFlow,omitempty"` + // Oauth2AllowURLPathMatching - Specifies whether during a token Request Azure AD will allow path matching of the redirect URI against the applications collection of replyURLs. The default is false. + Oauth2AllowURLPathMatching *bool `json:"oauth2AllowUrlPathMatching,omitempty"` + // Oauth2Permissions - The collection of OAuth 2.0 permission scopes that the web API (resource) application exposes to client applications. These permission scopes may be granted to client applications during consent. + Oauth2Permissions *[]OAuth2Permission `json:"oauth2Permissions,omitempty"` + // Oauth2RequirePostResponse - Specifies whether, as part of OAuth 2.0 token requests, Azure AD will allow POST requests, as opposed to GET requests. The default is false, which specifies that only GET requests will be allowed. + Oauth2RequirePostResponse *bool `json:"oauth2RequirePostResponse,omitempty"` + // OrgRestrictions - A list of tenants allowed to access application. + OrgRestrictions *[]string `json:"orgRestrictions,omitempty"` + OptionalClaims *OptionalClaims `json:"optionalClaims,omitempty"` // PasswordCredentials - A collection of PasswordCredential objects PasswordCredentials *[]PasswordCredential `json:"passwordCredentials,omitempty"` + // PreAuthorizedApplications - list of pre-authorized applications. + PreAuthorizedApplications *[]PreAuthorizedApplication `json:"preAuthorizedApplications,omitempty"` + // PublicClient - Specifies whether this application is a public client (such as an installed application running on a mobile device). Default is false. + PublicClient *bool `json:"publicClient,omitempty"` + // PublisherDomain - Reliable domain which can be used to identify an application. + PublisherDomain *string `json:"publisherDomain,omitempty"` + // ReplyUrls - A collection of reply URLs for the application. + ReplyUrls *[]string `json:"replyUrls,omitempty"` + // RequiredResourceAccess - Specifies resources that this application requires access to and the set of OAuth permission scopes and application roles that it needs under each of those resources. This pre-configuration of required resource access drives the consent experience. + RequiredResourceAccess *[]RequiredResourceAccess `json:"requiredResourceAccess,omitempty"` + // SamlMetadataURL - The URL to the SAML metadata for the application. + SamlMetadataURL *string `json:"samlMetadataUrl,omitempty"` + // SignInAudience - Audience for signing in to the application (AzureADMyOrganization, AzureADAllOrganizations, AzureADAndMicrosoftAccounts). + SignInAudience *string `json:"signInAudience,omitempty"` + // WwwHomepage - The primary Web page. + WwwHomepage *string `json:"wwwHomepage,omitempty"` // AdditionalProperties - Unmatched properties from the message are deserialized this collection AdditionalProperties map[string]interface{} `json:""` - // ObjectID - The object ID. + // ObjectID - READ-ONLY; The object ID. ObjectID *string `json:"objectId,omitempty"` - // DeletionTimestamp - The time at which the directory object was deleted. + // DeletionTimestamp - READ-ONLY; The time at which the directory object was deleted. DeletionTimestamp *date.Time `json:"deletionTimestamp,omitempty"` // ObjectType - Possible values include: 'ObjectTypeDirectoryObject', 'ObjectTypeApplication', 'ObjectTypeGroup', 'ObjectTypeServicePrincipal', 'ObjectTypeUser' ObjectType ObjectType `json:"objectType,omitempty"` @@ -352,6 +400,15 @@ func (a Application) MarshalJSON() ([]byte, error) { if a.AppID != nil { objectMap["appId"] = a.AppID } + if a.AllowGuestsSignIn != nil { + objectMap["allowGuestsSignIn"] = a.AllowGuestsSignIn + } + if a.AllowPassthroughUsers != nil { + objectMap["allowPassthroughUsers"] = a.AllowPassthroughUsers + } + if a.AppLogoURL != nil { + objectMap["appLogoUrl"] = a.AppLogoURL + } if a.AppRoles != nil { objectMap["appRoles"] = a.AppRoles } @@ -364,32 +421,77 @@ func (a Application) MarshalJSON() ([]byte, error) { if a.DisplayName != nil { objectMap["displayName"] = a.DisplayName } - if a.IdentifierUris != nil { - objectMap["identifierUris"] = a.IdentifierUris + if a.ErrorURL != nil { + objectMap["errorUrl"] = a.ErrorURL } - if a.ReplyUrls != nil { - objectMap["replyUrls"] = a.ReplyUrls + if a.GroupMembershipClaims != nil { + objectMap["groupMembershipClaims"] = a.GroupMembershipClaims } if a.Homepage != nil { objectMap["homepage"] = a.Homepage } - if a.Oauth2AllowImplicitFlow != nil { - objectMap["oauth2AllowImplicitFlow"] = a.Oauth2AllowImplicitFlow + if a.IdentifierUris != nil { + objectMap["identifierUris"] = a.IdentifierUris } - if a.RequiredResourceAccess != nil { - objectMap["requiredResourceAccess"] = a.RequiredResourceAccess + if a.InformationalUrls != nil { + objectMap["informationalUrls"] = a.InformationalUrls + } + if a.IsDeviceOnlyAuthSupported != nil { + objectMap["isDeviceOnlyAuthSupported"] = a.IsDeviceOnlyAuthSupported } if a.KeyCredentials != nil { objectMap["keyCredentials"] = a.KeyCredentials } + if a.KnownClientApplications != nil { + objectMap["knownClientApplications"] = a.KnownClientApplications + } + if a.LogoutURL != nil { + objectMap["logoutUrl"] = a.LogoutURL + } + if a.Oauth2AllowImplicitFlow != nil { + objectMap["oauth2AllowImplicitFlow"] = a.Oauth2AllowImplicitFlow + } + if a.Oauth2AllowURLPathMatching != nil { + objectMap["oauth2AllowUrlPathMatching"] = a.Oauth2AllowURLPathMatching + } + if a.Oauth2Permissions != nil { + objectMap["oauth2Permissions"] = a.Oauth2Permissions + } + if a.Oauth2RequirePostResponse != nil { + objectMap["oauth2RequirePostResponse"] = a.Oauth2RequirePostResponse + } + if a.OrgRestrictions != nil { + objectMap["orgRestrictions"] = a.OrgRestrictions + } + if a.OptionalClaims != nil { + objectMap["optionalClaims"] = a.OptionalClaims + } if a.PasswordCredentials != nil { objectMap["passwordCredentials"] = a.PasswordCredentials } - if a.ObjectID != nil { - objectMap["objectId"] = a.ObjectID + if a.PreAuthorizedApplications != nil { + objectMap["preAuthorizedApplications"] = a.PreAuthorizedApplications + } + if a.PublicClient != nil { + objectMap["publicClient"] = a.PublicClient + } + if a.PublisherDomain != nil { + objectMap["publisherDomain"] = a.PublisherDomain + } + if a.ReplyUrls != nil { + objectMap["replyUrls"] = a.ReplyUrls + } + if a.RequiredResourceAccess != nil { + objectMap["requiredResourceAccess"] = a.RequiredResourceAccess + } + if a.SamlMetadataURL != nil { + objectMap["samlMetadataUrl"] = a.SamlMetadataURL } - if a.DeletionTimestamp != nil { - objectMap["deletionTimestamp"] = a.DeletionTimestamp + if a.SignInAudience != nil { + objectMap["signInAudience"] = a.SignInAudience + } + if a.WwwHomepage != nil { + objectMap["wwwHomepage"] = a.WwwHomepage } if a.ObjectType != "" { objectMap["objectType"] = a.ObjectType @@ -448,6 +550,33 @@ func (a *Application) UnmarshalJSON(body []byte) error { } a.AppID = &appID } + case "allowGuestsSignIn": + if v != nil { + var allowGuestsSignIn bool + err = json.Unmarshal(*v, &allowGuestsSignIn) + if err != nil { + return err + } + a.AllowGuestsSignIn = &allowGuestsSignIn + } + case "allowPassthroughUsers": + if v != nil { + var allowPassthroughUsers bool + err = json.Unmarshal(*v, &allowPassthroughUsers) + if err != nil { + return err + } + a.AllowPassthroughUsers = &allowPassthroughUsers + } + case "appLogoUrl": + if v != nil { + var appLogoURL string + err = json.Unmarshal(*v, &appLogoURL) + if err != nil { + return err + } + a.AppLogoURL = &appLogoURL + } case "appRoles": if v != nil { var appRoles []AppRole @@ -484,23 +613,23 @@ func (a *Application) UnmarshalJSON(body []byte) error { } a.DisplayName = &displayName } - case "identifierUris": + case "errorUrl": if v != nil { - var identifierUris []string - err = json.Unmarshal(*v, &identifierUris) + var errorURL string + err = json.Unmarshal(*v, &errorURL) if err != nil { return err } - a.IdentifierUris = &identifierUris + a.ErrorURL = &errorURL } - case "replyUrls": + case "groupMembershipClaims": if v != nil { - var replyUrls []string - err = json.Unmarshal(*v, &replyUrls) + var groupMembershipClaims interface{} + err = json.Unmarshal(*v, &groupMembershipClaims) if err != nil { return err } - a.ReplyUrls = &replyUrls + a.GroupMembershipClaims = groupMembershipClaims } case "homepage": if v != nil { @@ -511,23 +640,32 @@ func (a *Application) UnmarshalJSON(body []byte) error { } a.Homepage = &homepage } - case "oauth2AllowImplicitFlow": + case "identifierUris": if v != nil { - var oauth2AllowImplicitFlow bool - err = json.Unmarshal(*v, &oauth2AllowImplicitFlow) + var identifierUris []string + err = json.Unmarshal(*v, &identifierUris) if err != nil { return err } - a.Oauth2AllowImplicitFlow = &oauth2AllowImplicitFlow + a.IdentifierUris = &identifierUris } - case "requiredResourceAccess": + case "informationalUrls": if v != nil { - var requiredResourceAccess []RequiredResourceAccess - err = json.Unmarshal(*v, &requiredResourceAccess) + var informationalUrls InformationalURL + err = json.Unmarshal(*v, &informationalUrls) if err != nil { return err } - a.RequiredResourceAccess = &requiredResourceAccess + a.InformationalUrls = &informationalUrls + } + case "isDeviceOnlyAuthSupported": + if v != nil { + var isDeviceOnlyAuthSupported bool + err = json.Unmarshal(*v, &isDeviceOnlyAuthSupported) + if err != nil { + return err + } + a.IsDeviceOnlyAuthSupported = &isDeviceOnlyAuthSupported } case "keyCredentials": if v != nil { @@ -538,190 +676,113 @@ func (a *Application) UnmarshalJSON(body []byte) error { } a.KeyCredentials = &keyCredentials } - case "passwordCredentials": + case "knownClientApplications": if v != nil { - var passwordCredentials []PasswordCredential - err = json.Unmarshal(*v, &passwordCredentials) + var knownClientApplications []string + err = json.Unmarshal(*v, &knownClientApplications) if err != nil { return err } - a.PasswordCredentials = &passwordCredentials + a.KnownClientApplications = &knownClientApplications } - default: + case "logoutUrl": if v != nil { - var additionalProperties interface{} - err = json.Unmarshal(*v, &additionalProperties) + var logoutURL string + err = json.Unmarshal(*v, &logoutURL) if err != nil { return err } - if a.AdditionalProperties == nil { - a.AdditionalProperties = make(map[string]interface{}) - } - a.AdditionalProperties[k] = additionalProperties + a.LogoutURL = &logoutURL } - case "objectId": + case "oauth2AllowImplicitFlow": if v != nil { - var objectID string - err = json.Unmarshal(*v, &objectID) + var oauth2AllowImplicitFlow bool + err = json.Unmarshal(*v, &oauth2AllowImplicitFlow) if err != nil { return err } - a.ObjectID = &objectID + a.Oauth2AllowImplicitFlow = &oauth2AllowImplicitFlow } - case "deletionTimestamp": + case "oauth2AllowUrlPathMatching": if v != nil { - var deletionTimestamp date.Time - err = json.Unmarshal(*v, &deletionTimestamp) + var oauth2AllowURLPathMatching bool + err = json.Unmarshal(*v, &oauth2AllowURLPathMatching) if err != nil { return err } - a.DeletionTimestamp = &deletionTimestamp + a.Oauth2AllowURLPathMatching = &oauth2AllowURLPathMatching } - case "objectType": + case "oauth2Permissions": if v != nil { - var objectType ObjectType - err = json.Unmarshal(*v, &objectType) + var oauth2Permissions []OAuth2Permission + err = json.Unmarshal(*v, &oauth2Permissions) if err != nil { return err } - a.ObjectType = objectType + a.Oauth2Permissions = &oauth2Permissions } - } - } - - return nil -} - -// ApplicationCreateParameters request parameters for creating a new application. -type ApplicationCreateParameters struct { - // AdditionalProperties - Unmatched properties from the message are deserialized this collection - AdditionalProperties map[string]interface{} `json:""` - // AppRoles - The collection of application roles that an application may declare. These roles can be assigned to users, groups or service principals. - AppRoles *[]AppRole `json:"appRoles,omitempty"` - // AvailableToOtherTenants - Whether the application is available to other tenants. - AvailableToOtherTenants *bool `json:"availableToOtherTenants,omitempty"` - // DisplayName - The display name of the application. - DisplayName *string `json:"displayName,omitempty"` - // Homepage - The home page of the application. - Homepage *string `json:"homepage,omitempty"` - // IdentifierUris - A collection of URIs for the application. - IdentifierUris *[]string `json:"identifierUris,omitempty"` - // ReplyUrls - A collection of reply URLs for the application. - ReplyUrls *[]string `json:"replyUrls,omitempty"` - // KeyCredentials - The list of KeyCredential objects. - KeyCredentials *[]KeyCredential `json:"keyCredentials,omitempty"` - // PasswordCredentials - The list of PasswordCredential objects. - PasswordCredentials *[]PasswordCredential `json:"passwordCredentials,omitempty"` - // Oauth2AllowImplicitFlow - Whether to allow implicit grant flow for OAuth2 - Oauth2AllowImplicitFlow *bool `json:"oauth2AllowImplicitFlow,omitempty"` - // RequiredResourceAccess - Specifies resources that this application requires access to and the set of OAuth permission scopes and application roles that it needs under each of those resources. This pre-configuration of required resource access drives the consent experience. - RequiredResourceAccess *[]RequiredResourceAccess `json:"requiredResourceAccess,omitempty"` -} - -// MarshalJSON is the custom marshaler for ApplicationCreateParameters. -func (acp ApplicationCreateParameters) MarshalJSON() ([]byte, error) { - objectMap := make(map[string]interface{}) - if acp.AppRoles != nil { - objectMap["appRoles"] = acp.AppRoles - } - if acp.AvailableToOtherTenants != nil { - objectMap["availableToOtherTenants"] = acp.AvailableToOtherTenants - } - if acp.DisplayName != nil { - objectMap["displayName"] = acp.DisplayName - } - if acp.Homepage != nil { - objectMap["homepage"] = acp.Homepage - } - if acp.IdentifierUris != nil { - objectMap["identifierUris"] = acp.IdentifierUris - } - if acp.ReplyUrls != nil { - objectMap["replyUrls"] = acp.ReplyUrls - } - if acp.KeyCredentials != nil { - objectMap["keyCredentials"] = acp.KeyCredentials - } - if acp.PasswordCredentials != nil { - objectMap["passwordCredentials"] = acp.PasswordCredentials - } - if acp.Oauth2AllowImplicitFlow != nil { - objectMap["oauth2AllowImplicitFlow"] = acp.Oauth2AllowImplicitFlow - } - if acp.RequiredResourceAccess != nil { - objectMap["requiredResourceAccess"] = acp.RequiredResourceAccess - } - for k, v := range acp.AdditionalProperties { - objectMap[k] = v - } - return json.Marshal(objectMap) -} - -// UnmarshalJSON is the custom unmarshaler for ApplicationCreateParameters struct. -func (acp *ApplicationCreateParameters) UnmarshalJSON(body []byte) error { - var m map[string]*json.RawMessage - err := json.Unmarshal(body, &m) - if err != nil { - return err - } - for k, v := range m { - switch k { - default: + case "oauth2RequirePostResponse": if v != nil { - var additionalProperties interface{} - err = json.Unmarshal(*v, &additionalProperties) + var oauth2RequirePostResponse bool + err = json.Unmarshal(*v, &oauth2RequirePostResponse) if err != nil { return err } - if acp.AdditionalProperties == nil { - acp.AdditionalProperties = make(map[string]interface{}) + a.Oauth2RequirePostResponse = &oauth2RequirePostResponse + } + case "orgRestrictions": + if v != nil { + var orgRestrictions []string + err = json.Unmarshal(*v, &orgRestrictions) + if err != nil { + return err } - acp.AdditionalProperties[k] = additionalProperties + a.OrgRestrictions = &orgRestrictions } - case "appRoles": + case "optionalClaims": if v != nil { - var appRoles []AppRole - err = json.Unmarshal(*v, &appRoles) + var optionalClaims OptionalClaims + err = json.Unmarshal(*v, &optionalClaims) if err != nil { return err } - acp.AppRoles = &appRoles + a.OptionalClaims = &optionalClaims } - case "availableToOtherTenants": + case "passwordCredentials": if v != nil { - var availableToOtherTenants bool - err = json.Unmarshal(*v, &availableToOtherTenants) + var passwordCredentials []PasswordCredential + err = json.Unmarshal(*v, &passwordCredentials) if err != nil { return err } - acp.AvailableToOtherTenants = &availableToOtherTenants + a.PasswordCredentials = &passwordCredentials } - case "displayName": + case "preAuthorizedApplications": if v != nil { - var displayName string - err = json.Unmarshal(*v, &displayName) + var preAuthorizedApplications []PreAuthorizedApplication + err = json.Unmarshal(*v, &preAuthorizedApplications) if err != nil { return err } - acp.DisplayName = &displayName + a.PreAuthorizedApplications = &preAuthorizedApplications } - case "homepage": + case "publicClient": if v != nil { - var homepage string - err = json.Unmarshal(*v, &homepage) + var publicClient bool + err = json.Unmarshal(*v, &publicClient) if err != nil { return err } - acp.Homepage = &homepage + a.PublicClient = &publicClient } - case "identifierUris": + case "publisherDomain": if v != nil { - var identifierUris []string - err = json.Unmarshal(*v, &identifierUris) + var publisherDomain string + err = json.Unmarshal(*v, &publisherDomain) if err != nil { return err } - acp.IdentifierUris = &identifierUris + a.PublisherDomain = &publisherDomain } case "replyUrls": if v != nil { @@ -730,72 +791,237 @@ func (acp *ApplicationCreateParameters) UnmarshalJSON(body []byte) error { if err != nil { return err } - acp.ReplyUrls = &replyUrls + a.ReplyUrls = &replyUrls } - case "keyCredentials": + case "requiredResourceAccess": if v != nil { - var keyCredentials []KeyCredential - err = json.Unmarshal(*v, &keyCredentials) + var requiredResourceAccess []RequiredResourceAccess + err = json.Unmarshal(*v, &requiredResourceAccess) if err != nil { return err } - acp.KeyCredentials = &keyCredentials + a.RequiredResourceAccess = &requiredResourceAccess } - case "passwordCredentials": + case "samlMetadataUrl": if v != nil { - var passwordCredentials []PasswordCredential - err = json.Unmarshal(*v, &passwordCredentials) + var samlMetadataURL string + err = json.Unmarshal(*v, &samlMetadataURL) if err != nil { return err } - acp.PasswordCredentials = &passwordCredentials + a.SamlMetadataURL = &samlMetadataURL } - case "oauth2AllowImplicitFlow": + case "signInAudience": if v != nil { - var oauth2AllowImplicitFlow bool - err = json.Unmarshal(*v, &oauth2AllowImplicitFlow) + var signInAudience string + err = json.Unmarshal(*v, &signInAudience) if err != nil { return err } - acp.Oauth2AllowImplicitFlow = &oauth2AllowImplicitFlow + a.SignInAudience = &signInAudience } - case "requiredResourceAccess": + case "wwwHomepage": if v != nil { - var requiredResourceAccess []RequiredResourceAccess - err = json.Unmarshal(*v, &requiredResourceAccess) + var wwwHomepage string + err = json.Unmarshal(*v, &wwwHomepage) if err != nil { return err } - acp.RequiredResourceAccess = &requiredResourceAccess + a.WwwHomepage = &wwwHomepage } - } - } - - return nil -} - -// ApplicationListResult application list operation result. -type ApplicationListResult struct { - autorest.Response `json:"-"` - // Value - A collection of applications. - Value *[]Application `json:"value,omitempty"` - // OdataNextLink - The URL to get the next set of results. - OdataNextLink *string `json:"odata.nextLink,omitempty"` -} - -// ApplicationListResultIterator provides access to a complete listing of Application values. -type ApplicationListResultIterator struct { - i int - page ApplicationListResultPage -} - -// NextWithContext advances to the next value. If there was an error making -// the request the iterator does not advance and the error is returned. -func (iter *ApplicationListResultIterator) NextWithContext(ctx context.Context) (err error) { - if tracing.IsEnabled() { - ctx = tracing.StartSpan(ctx, fqdn+"/ApplicationListResultIterator.NextWithContext") - defer func() { - sc := -1 + default: + if v != nil { + var additionalProperties interface{} + err = json.Unmarshal(*v, &additionalProperties) + if err != nil { + return err + } + if a.AdditionalProperties == nil { + a.AdditionalProperties = make(map[string]interface{}) + } + a.AdditionalProperties[k] = additionalProperties + } + case "objectId": + if v != nil { + var objectID string + err = json.Unmarshal(*v, &objectID) + if err != nil { + return err + } + a.ObjectID = &objectID + } + case "deletionTimestamp": + if v != nil { + var deletionTimestamp date.Time + err = json.Unmarshal(*v, &deletionTimestamp) + if err != nil { + return err + } + a.DeletionTimestamp = &deletionTimestamp + } + case "objectType": + if v != nil { + var objectType ObjectType + err = json.Unmarshal(*v, &objectType) + if err != nil { + return err + } + a.ObjectType = objectType + } + } + } + + return nil +} + +// ApplicationBase active Directive Application common properties shared among GET, POST and PATCH +type ApplicationBase struct { + // AllowGuestsSignIn - A property on the application to indicate if the application accepts other IDPs or not or partially accepts. + AllowGuestsSignIn *bool `json:"allowGuestsSignIn,omitempty"` + // AllowPassthroughUsers - Indicates that the application supports pass through users who have no presence in the resource tenant. + AllowPassthroughUsers *bool `json:"allowPassthroughUsers,omitempty"` + // AppLogoURL - The url for the application logo image stored in a CDN. + AppLogoURL *string `json:"appLogoUrl,omitempty"` + // AppRoles - The collection of application roles that an application may declare. These roles can be assigned to users, groups or service principals. + AppRoles *[]AppRole `json:"appRoles,omitempty"` + // AppPermissions - The application permissions. + AppPermissions *[]string `json:"appPermissions,omitempty"` + // AvailableToOtherTenants - Whether the application is available to other tenants. + AvailableToOtherTenants *bool `json:"availableToOtherTenants,omitempty"` + // ErrorURL - A URL provided by the author of the application to report errors when using the application. + ErrorURL *string `json:"errorUrl,omitempty"` + // GroupMembershipClaims - Configures the groups claim issued in a user or OAuth 2.0 access token that the app expects. + GroupMembershipClaims interface{} `json:"groupMembershipClaims,omitempty"` + // Homepage - The home page of the application. + Homepage *string `json:"homepage,omitempty"` + // InformationalUrls - URLs with more information about the application. + InformationalUrls *InformationalURL `json:"informationalUrls,omitempty"` + // IsDeviceOnlyAuthSupported - Specifies whether this application supports device authentication without a user. The default is false. + IsDeviceOnlyAuthSupported *bool `json:"isDeviceOnlyAuthSupported,omitempty"` + // KeyCredentials - A collection of KeyCredential objects. + KeyCredentials *[]KeyCredential `json:"keyCredentials,omitempty"` + // KnownClientApplications - Client applications that are tied to this resource application. Consent to any of the known client applications will result in implicit consent to the resource application through a combined consent dialog (showing the OAuth permission scopes required by the client and the resource). + KnownClientApplications *[]string `json:"knownClientApplications,omitempty"` + // LogoutURL - the url of the logout page + LogoutURL *string `json:"logoutUrl,omitempty"` + // Oauth2AllowImplicitFlow - Whether to allow implicit grant flow for OAuth2 + Oauth2AllowImplicitFlow *bool `json:"oauth2AllowImplicitFlow,omitempty"` + // Oauth2AllowURLPathMatching - Specifies whether during a token Request Azure AD will allow path matching of the redirect URI against the applications collection of replyURLs. The default is false. + Oauth2AllowURLPathMatching *bool `json:"oauth2AllowUrlPathMatching,omitempty"` + // Oauth2Permissions - The collection of OAuth 2.0 permission scopes that the web API (resource) application exposes to client applications. These permission scopes may be granted to client applications during consent. + Oauth2Permissions *[]OAuth2Permission `json:"oauth2Permissions,omitempty"` + // Oauth2RequirePostResponse - Specifies whether, as part of OAuth 2.0 token requests, Azure AD will allow POST requests, as opposed to GET requests. The default is false, which specifies that only GET requests will be allowed. + Oauth2RequirePostResponse *bool `json:"oauth2RequirePostResponse,omitempty"` + // OrgRestrictions - A list of tenants allowed to access application. + OrgRestrictions *[]string `json:"orgRestrictions,omitempty"` + OptionalClaims *OptionalClaims `json:"optionalClaims,omitempty"` + // PasswordCredentials - A collection of PasswordCredential objects + PasswordCredentials *[]PasswordCredential `json:"passwordCredentials,omitempty"` + // PreAuthorizedApplications - list of pre-authorized applications. + PreAuthorizedApplications *[]PreAuthorizedApplication `json:"preAuthorizedApplications,omitempty"` + // PublicClient - Specifies whether this application is a public client (such as an installed application running on a mobile device). Default is false. + PublicClient *bool `json:"publicClient,omitempty"` + // PublisherDomain - Reliable domain which can be used to identify an application. + PublisherDomain *string `json:"publisherDomain,omitempty"` + // ReplyUrls - A collection of reply URLs for the application. + ReplyUrls *[]string `json:"replyUrls,omitempty"` + // RequiredResourceAccess - Specifies resources that this application requires access to and the set of OAuth permission scopes and application roles that it needs under each of those resources. This pre-configuration of required resource access drives the consent experience. + RequiredResourceAccess *[]RequiredResourceAccess `json:"requiredResourceAccess,omitempty"` + // SamlMetadataURL - The URL to the SAML metadata for the application. + SamlMetadataURL *string `json:"samlMetadataUrl,omitempty"` + // SignInAudience - Audience for signing in to the application (AzureADMyOrganization, AzureADAllOrganizations, AzureADAndMicrosoftAccounts). + SignInAudience *string `json:"signInAudience,omitempty"` + // WwwHomepage - The primary Web page. + WwwHomepage *string `json:"wwwHomepage,omitempty"` +} + +// ApplicationCreateParameters request parameters for creating a new application. +type ApplicationCreateParameters struct { + // DisplayName - The display name of the application. + DisplayName *string `json:"displayName,omitempty"` + // IdentifierUris - A collection of URIs for the application. + IdentifierUris *[]string `json:"identifierUris,omitempty"` + // AllowGuestsSignIn - A property on the application to indicate if the application accepts other IDPs or not or partially accepts. + AllowGuestsSignIn *bool `json:"allowGuestsSignIn,omitempty"` + // AllowPassthroughUsers - Indicates that the application supports pass through users who have no presence in the resource tenant. + AllowPassthroughUsers *bool `json:"allowPassthroughUsers,omitempty"` + // AppLogoURL - The url for the application logo image stored in a CDN. + AppLogoURL *string `json:"appLogoUrl,omitempty"` + // AppRoles - The collection of application roles that an application may declare. These roles can be assigned to users, groups or service principals. + AppRoles *[]AppRole `json:"appRoles,omitempty"` + // AppPermissions - The application permissions. + AppPermissions *[]string `json:"appPermissions,omitempty"` + // AvailableToOtherTenants - Whether the application is available to other tenants. + AvailableToOtherTenants *bool `json:"availableToOtherTenants,omitempty"` + // ErrorURL - A URL provided by the author of the application to report errors when using the application. + ErrorURL *string `json:"errorUrl,omitempty"` + // GroupMembershipClaims - Configures the groups claim issued in a user or OAuth 2.0 access token that the app expects. + GroupMembershipClaims interface{} `json:"groupMembershipClaims,omitempty"` + // Homepage - The home page of the application. + Homepage *string `json:"homepage,omitempty"` + // InformationalUrls - URLs with more information about the application. + InformationalUrls *InformationalURL `json:"informationalUrls,omitempty"` + // IsDeviceOnlyAuthSupported - Specifies whether this application supports device authentication without a user. The default is false. + IsDeviceOnlyAuthSupported *bool `json:"isDeviceOnlyAuthSupported,omitempty"` + // KeyCredentials - A collection of KeyCredential objects. + KeyCredentials *[]KeyCredential `json:"keyCredentials,omitempty"` + // KnownClientApplications - Client applications that are tied to this resource application. Consent to any of the known client applications will result in implicit consent to the resource application through a combined consent dialog (showing the OAuth permission scopes required by the client and the resource). + KnownClientApplications *[]string `json:"knownClientApplications,omitempty"` + // LogoutURL - the url of the logout page + LogoutURL *string `json:"logoutUrl,omitempty"` + // Oauth2AllowImplicitFlow - Whether to allow implicit grant flow for OAuth2 + Oauth2AllowImplicitFlow *bool `json:"oauth2AllowImplicitFlow,omitempty"` + // Oauth2AllowURLPathMatching - Specifies whether during a token Request Azure AD will allow path matching of the redirect URI against the applications collection of replyURLs. The default is false. + Oauth2AllowURLPathMatching *bool `json:"oauth2AllowUrlPathMatching,omitempty"` + // Oauth2Permissions - The collection of OAuth 2.0 permission scopes that the web API (resource) application exposes to client applications. These permission scopes may be granted to client applications during consent. + Oauth2Permissions *[]OAuth2Permission `json:"oauth2Permissions,omitempty"` + // Oauth2RequirePostResponse - Specifies whether, as part of OAuth 2.0 token requests, Azure AD will allow POST requests, as opposed to GET requests. The default is false, which specifies that only GET requests will be allowed. + Oauth2RequirePostResponse *bool `json:"oauth2RequirePostResponse,omitempty"` + // OrgRestrictions - A list of tenants allowed to access application. + OrgRestrictions *[]string `json:"orgRestrictions,omitempty"` + OptionalClaims *OptionalClaims `json:"optionalClaims,omitempty"` + // PasswordCredentials - A collection of PasswordCredential objects + PasswordCredentials *[]PasswordCredential `json:"passwordCredentials,omitempty"` + // PreAuthorizedApplications - list of pre-authorized applications. + PreAuthorizedApplications *[]PreAuthorizedApplication `json:"preAuthorizedApplications,omitempty"` + // PublicClient - Specifies whether this application is a public client (such as an installed application running on a mobile device). Default is false. + PublicClient *bool `json:"publicClient,omitempty"` + // PublisherDomain - Reliable domain which can be used to identify an application. + PublisherDomain *string `json:"publisherDomain,omitempty"` + // ReplyUrls - A collection of reply URLs for the application. + ReplyUrls *[]string `json:"replyUrls,omitempty"` + // RequiredResourceAccess - Specifies resources that this application requires access to and the set of OAuth permission scopes and application roles that it needs under each of those resources. This pre-configuration of required resource access drives the consent experience. + RequiredResourceAccess *[]RequiredResourceAccess `json:"requiredResourceAccess,omitempty"` + // SamlMetadataURL - The URL to the SAML metadata for the application. + SamlMetadataURL *string `json:"samlMetadataUrl,omitempty"` + // SignInAudience - Audience for signing in to the application (AzureADMyOrganization, AzureADAllOrganizations, AzureADAndMicrosoftAccounts). + SignInAudience *string `json:"signInAudience,omitempty"` + // WwwHomepage - The primary Web page. + WwwHomepage *string `json:"wwwHomepage,omitempty"` +} + +// ApplicationListResult application list operation result. +type ApplicationListResult struct { + autorest.Response `json:"-"` + // Value - A collection of applications. + Value *[]Application `json:"value,omitempty"` + // OdataNextLink - The URL to get the next set of results. + OdataNextLink *string `json:"odata.nextLink,omitempty"` +} + +// ApplicationListResultIterator provides access to a complete listing of Application values. +type ApplicationListResultIterator struct { + i int + page ApplicationListResultPage +} + +// NextWithContext advances to the next value. If there was an error making +// the request the iterator does not advance and the error is returned. +func (iter *ApplicationListResultIterator) NextWithContext(ctx context.Context) (err error) { + if tracing.IsEnabled() { + ctx = tracing.StartSpan(ctx, fqdn+"/ApplicationListResultIterator.NextWithContext") + defer func() { + sc := -1 if iter.Response().Response.Response != nil { sc = iter.Response().Response.Response.StatusCode } @@ -908,186 +1134,69 @@ func NewApplicationListResultPage(getNextPage func(context.Context, ApplicationL return ApplicationListResultPage{fn: getNextPage} } -// ApplicationUpdateParameters request parameters for updating an existing application. +// ApplicationUpdateParameters request parameters for updating a new application. type ApplicationUpdateParameters struct { - // AdditionalProperties - Unmatched properties from the message are deserialized this collection - AdditionalProperties map[string]interface{} `json:""` + // DisplayName - The display name of the application. + DisplayName *string `json:"displayName,omitempty"` + // IdentifierUris - A collection of URIs for the application. + IdentifierUris *[]string `json:"identifierUris,omitempty"` + // AllowGuestsSignIn - A property on the application to indicate if the application accepts other IDPs or not or partially accepts. + AllowGuestsSignIn *bool `json:"allowGuestsSignIn,omitempty"` + // AllowPassthroughUsers - Indicates that the application supports pass through users who have no presence in the resource tenant. + AllowPassthroughUsers *bool `json:"allowPassthroughUsers,omitempty"` + // AppLogoURL - The url for the application logo image stored in a CDN. + AppLogoURL *string `json:"appLogoUrl,omitempty"` // AppRoles - The collection of application roles that an application may declare. These roles can be assigned to users, groups or service principals. AppRoles *[]AppRole `json:"appRoles,omitempty"` - // AvailableToOtherTenants - Whether the application is available to other tenants + // AppPermissions - The application permissions. + AppPermissions *[]string `json:"appPermissions,omitempty"` + // AvailableToOtherTenants - Whether the application is available to other tenants. AvailableToOtherTenants *bool `json:"availableToOtherTenants,omitempty"` - // DisplayName - The display name of the application. - DisplayName *string `json:"displayName,omitempty"` + // ErrorURL - A URL provided by the author of the application to report errors when using the application. + ErrorURL *string `json:"errorUrl,omitempty"` + // GroupMembershipClaims - Configures the groups claim issued in a user or OAuth 2.0 access token that the app expects. + GroupMembershipClaims interface{} `json:"groupMembershipClaims,omitempty"` // Homepage - The home page of the application. Homepage *string `json:"homepage,omitempty"` - // IdentifierUris - A collection of URIs for the application. - IdentifierUris *[]string `json:"identifierUris,omitempty"` - // ReplyUrls - A collection of reply URLs for the application. - ReplyUrls *[]string `json:"replyUrls,omitempty"` - // KeyCredentials - The list of KeyCredential objects. + // InformationalUrls - URLs with more information about the application. + InformationalUrls *InformationalURL `json:"informationalUrls,omitempty"` + // IsDeviceOnlyAuthSupported - Specifies whether this application supports device authentication without a user. The default is false. + IsDeviceOnlyAuthSupported *bool `json:"isDeviceOnlyAuthSupported,omitempty"` + // KeyCredentials - A collection of KeyCredential objects. KeyCredentials *[]KeyCredential `json:"keyCredentials,omitempty"` - // PasswordCredentials - The list of PasswordCredential objects. - PasswordCredentials *[]PasswordCredential `json:"passwordCredentials,omitempty"` + // KnownClientApplications - Client applications that are tied to this resource application. Consent to any of the known client applications will result in implicit consent to the resource application through a combined consent dialog (showing the OAuth permission scopes required by the client and the resource). + KnownClientApplications *[]string `json:"knownClientApplications,omitempty"` + // LogoutURL - the url of the logout page + LogoutURL *string `json:"logoutUrl,omitempty"` // Oauth2AllowImplicitFlow - Whether to allow implicit grant flow for OAuth2 Oauth2AllowImplicitFlow *bool `json:"oauth2AllowImplicitFlow,omitempty"` + // Oauth2AllowURLPathMatching - Specifies whether during a token Request Azure AD will allow path matching of the redirect URI against the applications collection of replyURLs. The default is false. + Oauth2AllowURLPathMatching *bool `json:"oauth2AllowUrlPathMatching,omitempty"` + // Oauth2Permissions - The collection of OAuth 2.0 permission scopes that the web API (resource) application exposes to client applications. These permission scopes may be granted to client applications during consent. + Oauth2Permissions *[]OAuth2Permission `json:"oauth2Permissions,omitempty"` + // Oauth2RequirePostResponse - Specifies whether, as part of OAuth 2.0 token requests, Azure AD will allow POST requests, as opposed to GET requests. The default is false, which specifies that only GET requests will be allowed. + Oauth2RequirePostResponse *bool `json:"oauth2RequirePostResponse,omitempty"` + // OrgRestrictions - A list of tenants allowed to access application. + OrgRestrictions *[]string `json:"orgRestrictions,omitempty"` + OptionalClaims *OptionalClaims `json:"optionalClaims,omitempty"` + // PasswordCredentials - A collection of PasswordCredential objects + PasswordCredentials *[]PasswordCredential `json:"passwordCredentials,omitempty"` + // PreAuthorizedApplications - list of pre-authorized applications. + PreAuthorizedApplications *[]PreAuthorizedApplication `json:"preAuthorizedApplications,omitempty"` + // PublicClient - Specifies whether this application is a public client (such as an installed application running on a mobile device). Default is false. + PublicClient *bool `json:"publicClient,omitempty"` + // PublisherDomain - Reliable domain which can be used to identify an application. + PublisherDomain *string `json:"publisherDomain,omitempty"` + // ReplyUrls - A collection of reply URLs for the application. + ReplyUrls *[]string `json:"replyUrls,omitempty"` // RequiredResourceAccess - Specifies resources that this application requires access to and the set of OAuth permission scopes and application roles that it needs under each of those resources. This pre-configuration of required resource access drives the consent experience. RequiredResourceAccess *[]RequiredResourceAccess `json:"requiredResourceAccess,omitempty"` -} - -// MarshalJSON is the custom marshaler for ApplicationUpdateParameters. -func (aup ApplicationUpdateParameters) MarshalJSON() ([]byte, error) { - objectMap := make(map[string]interface{}) - if aup.AppRoles != nil { - objectMap["appRoles"] = aup.AppRoles - } - if aup.AvailableToOtherTenants != nil { - objectMap["availableToOtherTenants"] = aup.AvailableToOtherTenants - } - if aup.DisplayName != nil { - objectMap["displayName"] = aup.DisplayName - } - if aup.Homepage != nil { - objectMap["homepage"] = aup.Homepage - } - if aup.IdentifierUris != nil { - objectMap["identifierUris"] = aup.IdentifierUris - } - if aup.ReplyUrls != nil { - objectMap["replyUrls"] = aup.ReplyUrls - } - if aup.KeyCredentials != nil { - objectMap["keyCredentials"] = aup.KeyCredentials - } - if aup.PasswordCredentials != nil { - objectMap["passwordCredentials"] = aup.PasswordCredentials - } - if aup.Oauth2AllowImplicitFlow != nil { - objectMap["oauth2AllowImplicitFlow"] = aup.Oauth2AllowImplicitFlow - } - if aup.RequiredResourceAccess != nil { - objectMap["requiredResourceAccess"] = aup.RequiredResourceAccess - } - for k, v := range aup.AdditionalProperties { - objectMap[k] = v - } - return json.Marshal(objectMap) -} - -// UnmarshalJSON is the custom unmarshaler for ApplicationUpdateParameters struct. -func (aup *ApplicationUpdateParameters) UnmarshalJSON(body []byte) error { - var m map[string]*json.RawMessage - err := json.Unmarshal(body, &m) - if err != nil { - return err - } - for k, v := range m { - switch k { - default: - if v != nil { - var additionalProperties interface{} - err = json.Unmarshal(*v, &additionalProperties) - if err != nil { - return err - } - if aup.AdditionalProperties == nil { - aup.AdditionalProperties = make(map[string]interface{}) - } - aup.AdditionalProperties[k] = additionalProperties - } - case "appRoles": - if v != nil { - var appRoles []AppRole - err = json.Unmarshal(*v, &appRoles) - if err != nil { - return err - } - aup.AppRoles = &appRoles - } - case "availableToOtherTenants": - if v != nil { - var availableToOtherTenants bool - err = json.Unmarshal(*v, &availableToOtherTenants) - if err != nil { - return err - } - aup.AvailableToOtherTenants = &availableToOtherTenants - } - case "displayName": - if v != nil { - var displayName string - err = json.Unmarshal(*v, &displayName) - if err != nil { - return err - } - aup.DisplayName = &displayName - } - case "homepage": - if v != nil { - var homepage string - err = json.Unmarshal(*v, &homepage) - if err != nil { - return err - } - aup.Homepage = &homepage - } - case "identifierUris": - if v != nil { - var identifierUris []string - err = json.Unmarshal(*v, &identifierUris) - if err != nil { - return err - } - aup.IdentifierUris = &identifierUris - } - case "replyUrls": - if v != nil { - var replyUrls []string - err = json.Unmarshal(*v, &replyUrls) - if err != nil { - return err - } - aup.ReplyUrls = &replyUrls - } - case "keyCredentials": - if v != nil { - var keyCredentials []KeyCredential - err = json.Unmarshal(*v, &keyCredentials) - if err != nil { - return err - } - aup.KeyCredentials = &keyCredentials - } - case "passwordCredentials": - if v != nil { - var passwordCredentials []PasswordCredential - err = json.Unmarshal(*v, &passwordCredentials) - if err != nil { - return err - } - aup.PasswordCredentials = &passwordCredentials - } - case "oauth2AllowImplicitFlow": - if v != nil { - var oauth2AllowImplicitFlow bool - err = json.Unmarshal(*v, &oauth2AllowImplicitFlow) - if err != nil { - return err - } - aup.Oauth2AllowImplicitFlow = &oauth2AllowImplicitFlow - } - case "requiredResourceAccess": - if v != nil { - var requiredResourceAccess []RequiredResourceAccess - err = json.Unmarshal(*v, &requiredResourceAccess) - if err != nil { - return err - } - aup.RequiredResourceAccess = &requiredResourceAccess - } - } - } - - return nil + // SamlMetadataURL - The URL to the SAML metadata for the application. + SamlMetadataURL *string `json:"samlMetadataUrl,omitempty"` + // SignInAudience - Audience for signing in to the application (AzureADMyOrganization, AzureADAllOrganizations, AzureADAndMicrosoftAccounts). + SignInAudience *string `json:"signInAudience,omitempty"` + // WwwHomepage - The primary Web page. + WwwHomepage *string `json:"wwwHomepage,omitempty"` } // AppRole ... @@ -1246,9 +1355,9 @@ type BasicDirectoryObject interface { type DirectoryObject struct { // AdditionalProperties - Unmatched properties from the message are deserialized this collection AdditionalProperties map[string]interface{} `json:""` - // ObjectID - The object ID. + // ObjectID - READ-ONLY; The object ID. ObjectID *string `json:"objectId,omitempty"` - // DeletionTimestamp - The time at which the directory object was deleted. + // DeletionTimestamp - READ-ONLY; The time at which the directory object was deleted. DeletionTimestamp *date.Time `json:"deletionTimestamp,omitempty"` // ObjectType - Possible values include: 'ObjectTypeDirectoryObject', 'ObjectTypeApplication', 'ObjectTypeGroup', 'ObjectTypeServicePrincipal', 'ObjectTypeUser' ObjectType ObjectType `json:"objectType,omitempty"` @@ -1307,12 +1416,6 @@ func unmarshalBasicDirectoryObjectArray(body []byte) ([]BasicDirectoryObject, er func (do DirectoryObject) MarshalJSON() ([]byte, error) { do.ObjectType = ObjectTypeDirectoryObject objectMap := make(map[string]interface{}) - if do.ObjectID != nil { - objectMap["objectId"] = do.ObjectID - } - if do.DeletionTimestamp != nil { - objectMap["deletionTimestamp"] = do.DeletionTimestamp - } if do.ObjectType != "" { objectMap["objectType"] = do.ObjectType } @@ -1589,11 +1692,11 @@ type Domain struct { autorest.Response `json:"-"` // AdditionalProperties - Unmatched properties from the message are deserialized this collection AdditionalProperties map[string]interface{} `json:""` - // AuthenticationType - the type of the authentication into the domain. + // AuthenticationType - READ-ONLY; the type of the authentication into the domain. AuthenticationType *string `json:"authenticationType,omitempty"` - // IsDefault - if this is the default domain in the tenant. + // IsDefault - READ-ONLY; if this is the default domain in the tenant. IsDefault *bool `json:"isDefault,omitempty"` - // IsVerified - if this domain's ownership is verified. + // IsVerified - READ-ONLY; if this domain's ownership is verified. IsVerified *bool `json:"isVerified,omitempty"` // Name - the domain name. Name *string `json:"name,omitempty"` @@ -1602,17 +1705,8 @@ type Domain struct { // MarshalJSON is the custom marshaler for Domain. func (d Domain) MarshalJSON() ([]byte, error) { objectMap := make(map[string]interface{}) - if d.AuthenticationType != nil { - objectMap["authenticationType"] = d.AuthenticationType - } - if d.IsDefault != nil { - objectMap["isDefault"] = d.IsDefault - } - if d.IsVerified != nil { - objectMap["isVerified"] = d.IsVerified - } - if d.Name != nil { - objectMap["name"] = d.Name + if d.Name != nil { + objectMap["name"] = d.Name } for k, v := range d.AdditionalProperties { objectMap[k] = v @@ -2170,6 +2264,19 @@ func NewGroupListResultPage(getNextPage func(context.Context, GroupListResult) ( return GroupListResultPage{fn: getNextPage} } +// InformationalURL represents a group of URIs that provide terms of service, marketing, support and +// privacy policy information about an application. The default value for each string is null. +type InformationalURL struct { + // TermsOfService - The terms of service URI + TermsOfService *string `json:"termsOfService,omitempty"` + // Marketing - The marketing URI + Marketing *string `json:"marketing,omitempty"` + // Privacy - The privacy policy URI + Privacy *string `json:"privacy,omitempty"` + // Support - The support URI + Support *string `json:"support,omitempty"` +} + // KeyCredential active Directory Key Credential information. type KeyCredential struct { // AdditionalProperties - Unmatched properties from the message are deserialized this collection @@ -2323,6 +2430,187 @@ type KeyCredentialsUpdateParameters struct { Value *[]KeyCredential `json:"value,omitempty"` } +// OAuth2Permission represents an OAuth 2.0 delegated permission scope. The specified OAuth 2.0 delegated +// permission scopes may be requested by client applications (through the requiredResourceAccess collection +// on the Application object) when calling a resource application. The oauth2Permissions property of the +// ServicePrincipal entity and of the Application entity is a collection of OAuth2Permission. +type OAuth2Permission struct { + // AdminConsentDescription - Permission help text that appears in the admin consent and app assignment experiences. + AdminConsentDescription *string `json:"adminConsentDescription,omitempty"` + // AdminConsentDisplayName - Display name for the permission that appears in the admin consent and app assignment experiences. + AdminConsentDisplayName *string `json:"adminConsentDisplayName,omitempty"` + // ID - Unique scope permission identifier inside the oauth2Permissions collection. + ID *string `json:"id,omitempty"` + // IsEnabled - When creating or updating a permission, this property must be set to true (which is the default). To delete a permission, this property must first be set to false. At that point, in a subsequent call, the permission may be removed. + IsEnabled *bool `json:"isEnabled,omitempty"` + // Type - Specifies whether this scope permission can be consented to by an end user, or whether it is a tenant-wide permission that must be consented to by a Company Administrator. Possible values are "User" or "Admin". + Type *string `json:"type,omitempty"` + // UserConsentDescription - Permission help text that appears in the end user consent experience. + UserConsentDescription *string `json:"userConsentDescription,omitempty"` + // UserConsentDisplayName - Display name for the permission that appears in the end user consent experience. + UserConsentDisplayName *string `json:"userConsentDisplayName,omitempty"` + // Value - The value of the scope claim that the resource application should expect in the OAuth 2.0 access token. + Value *string `json:"value,omitempty"` +} + +// OAuth2PermissionGrant ... +type OAuth2PermissionGrant struct { + autorest.Response `json:"-"` + // OdataType - Microsoft.DirectoryServices.OAuth2PermissionGrant + OdataType *string `json:"odata.type,omitempty"` + // ClientID - The id of the resource's service principal granted consent to impersonate the user when accessing the resource (represented by the resourceId property). + ClientID *string `json:"clientId,omitempty"` + // ObjectID - The id of the permission grant + ObjectID *string `json:"objectId,omitempty"` + // ConsentType - Indicates if consent was provided by the administrator (on behalf of the organization) or by an individual. Possible values include: 'AllPrincipals', 'Principal' + ConsentType ConsentType `json:"consentType,omitempty"` + // PrincipalID - When consent type is Principal, this property specifies the id of the user that granted consent and applies only for that user. + PrincipalID *string `json:"principalId,omitempty"` + // ResourceID - Object Id of the resource you want to grant + ResourceID *string `json:"resourceId,omitempty"` + // Scope - Specifies the value of the scope claim that the resource application should expect in the OAuth 2.0 access token. For example, User.Read + Scope *string `json:"scope,omitempty"` + // StartTime - Start time for TTL + StartTime *string `json:"startTime,omitempty"` + // ExpiryTime - Expiry time for TTL + ExpiryTime *string `json:"expiryTime,omitempty"` +} + +// OAuth2PermissionGrantListResult server response for get oauth2 permissions grants +type OAuth2PermissionGrantListResult struct { + autorest.Response `json:"-"` + // Value - the list of oauth2 permissions grants + Value *[]OAuth2PermissionGrant `json:"value,omitempty"` + // OdataNextLink - the URL to get the next set of results. + OdataNextLink *string `json:"odata.nextLink,omitempty"` +} + +// OAuth2PermissionGrantListResultIterator provides access to a complete listing of OAuth2PermissionGrant +// values. +type OAuth2PermissionGrantListResultIterator struct { + i int + page OAuth2PermissionGrantListResultPage +} + +// NextWithContext advances to the next value. If there was an error making +// the request the iterator does not advance and the error is returned. +func (iter *OAuth2PermissionGrantListResultIterator) NextWithContext(ctx context.Context) (err error) { + if tracing.IsEnabled() { + ctx = tracing.StartSpan(ctx, fqdn+"/OAuth2PermissionGrantListResultIterator.NextWithContext") + defer func() { + sc := -1 + if iter.Response().Response.Response != nil { + sc = iter.Response().Response.Response.StatusCode + } + tracing.EndSpan(ctx, sc, err) + }() + } + iter.i++ + if iter.i < len(iter.page.Values()) { + return nil + } + err = iter.page.NextWithContext(ctx) + if err != nil { + iter.i-- + return err + } + iter.i = 0 + return nil +} + +// Next advances to the next value. If there was an error making +// the request the iterator does not advance and the error is returned. +// Deprecated: Use NextWithContext() instead. +func (iter *OAuth2PermissionGrantListResultIterator) Next() error { + return iter.NextWithContext(context.Background()) +} + +// NotDone returns true if the enumeration should be started or is not yet complete. +func (iter OAuth2PermissionGrantListResultIterator) NotDone() bool { + return iter.page.NotDone() && iter.i < len(iter.page.Values()) +} + +// Response returns the raw server response from the last page request. +func (iter OAuth2PermissionGrantListResultIterator) Response() OAuth2PermissionGrantListResult { + return iter.page.Response() +} + +// Value returns the current value or a zero-initialized value if the +// iterator has advanced beyond the end of the collection. +func (iter OAuth2PermissionGrantListResultIterator) Value() OAuth2PermissionGrant { + if !iter.page.NotDone() { + return OAuth2PermissionGrant{} + } + return iter.page.Values()[iter.i] +} + +// Creates a new instance of the OAuth2PermissionGrantListResultIterator type. +func NewOAuth2PermissionGrantListResultIterator(page OAuth2PermissionGrantListResultPage) OAuth2PermissionGrantListResultIterator { + return OAuth2PermissionGrantListResultIterator{page: page} +} + +// IsEmpty returns true if the ListResult contains no values. +func (oa2pglr OAuth2PermissionGrantListResult) IsEmpty() bool { + return oa2pglr.Value == nil || len(*oa2pglr.Value) == 0 +} + +// OAuth2PermissionGrantListResultPage contains a page of OAuth2PermissionGrant values. +type OAuth2PermissionGrantListResultPage struct { + fn func(context.Context, OAuth2PermissionGrantListResult) (OAuth2PermissionGrantListResult, error) + oa2pglr OAuth2PermissionGrantListResult +} + +// NextWithContext advances to the next page of values. If there was an error making +// the request the page does not advance and the error is returned. +func (page *OAuth2PermissionGrantListResultPage) NextWithContext(ctx context.Context) (err error) { + if tracing.IsEnabled() { + ctx = tracing.StartSpan(ctx, fqdn+"/OAuth2PermissionGrantListResultPage.NextWithContext") + defer func() { + sc := -1 + if page.Response().Response.Response != nil { + sc = page.Response().Response.Response.StatusCode + } + tracing.EndSpan(ctx, sc, err) + }() + } + next, err := page.fn(ctx, page.oa2pglr) + if err != nil { + return err + } + page.oa2pglr = next + return nil +} + +// Next advances to the next page of values. If there was an error making +// the request the page does not advance and the error is returned. +// Deprecated: Use NextWithContext() instead. +func (page *OAuth2PermissionGrantListResultPage) Next() error { + return page.NextWithContext(context.Background()) +} + +// NotDone returns true if the page enumeration should be started or is not yet complete. +func (page OAuth2PermissionGrantListResultPage) NotDone() bool { + return !page.oa2pglr.IsEmpty() +} + +// Response returns the raw server response from the last page request. +func (page OAuth2PermissionGrantListResultPage) Response() OAuth2PermissionGrantListResult { + return page.oa2pglr +} + +// Values returns the slice of values for the current page or nil if there are no values. +func (page OAuth2PermissionGrantListResultPage) Values() []OAuth2PermissionGrant { + if page.oa2pglr.IsEmpty() { + return nil + } + return *page.oa2pglr.Value +} + +// Creates a new instance of the OAuth2PermissionGrantListResultPage type. +func NewOAuth2PermissionGrantListResultPage(getNextPage func(context.Context, OAuth2PermissionGrantListResult) (OAuth2PermissionGrantListResult, error)) OAuth2PermissionGrantListResultPage { + return OAuth2PermissionGrantListResultPage{fn: getNextPage} +} + // OdataError active Directory OData error information. type OdataError struct { // Code - Error code. @@ -2376,6 +2664,27 @@ func (oe *OdataError) UnmarshalJSON(body []byte) error { return nil } +// OptionalClaim specifying the claims to be included in a token. +type OptionalClaim struct { + // Name - Claim name. + Name *string `json:"name,omitempty"` + // Source - Claim source. + Source *string `json:"source,omitempty"` + // Essential - Is this a required claim. + Essential *bool `json:"essential,omitempty"` + AdditionalProperties interface{} `json:"additionalProperties,omitempty"` +} + +// OptionalClaims specifying the claims to be included in the token. +type OptionalClaims struct { + // IDToken - Optional claims requested to be included in the id token. + IDToken *[]OptionalClaim `json:"idToken,omitempty"` + // AccessToken - Optional claims requested to be included in the access token. + AccessToken *[]OptionalClaim `json:"accessToken,omitempty"` + // SamlToken - Optional claims requested to be included in the saml token. + SamlToken *[]OptionalClaim `json:"samlToken,omitempty"` +} + // PasswordCredential active Directory Password Credential information. type PasswordCredential struct { // AdditionalProperties - Unmatched properties from the message are deserialized this collection @@ -2571,25 +2880,29 @@ func (pp *PasswordProfile) UnmarshalJSON(body []byte) error { return nil } -// Permissions ... -type Permissions struct { - autorest.Response `json:"-"` - // OdataType - Microsoft.DirectoryServices.OAuth2PermissionGrant - OdataType *string `json:"odata.type,omitempty"` - // ClientID - The objectId of the Service Principal associated with the app - ClientID *string `json:"clientId,omitempty"` - // ConsentType - Typically set to AllPrincipals - ConsentType *string `json:"consentType,omitempty"` - // PrincipalID - Set to null if AllPrincipals is set - PrincipalID interface{} `json:"principalId,omitempty"` - // ResourceID - Service Principal Id of the resource you want to grant - ResourceID *string `json:"resourceId,omitempty"` - // Scope - Typically set to user_impersonation - Scope *string `json:"scope,omitempty"` - // StartTime - Start time for TTL - StartTime *string `json:"startTime,omitempty"` - // ExpiryTime - Expiry time for TTL - ExpiryTime *string `json:"expiryTime,omitempty"` +// PreAuthorizedApplication contains information about pre authorized client application. +type PreAuthorizedApplication struct { + // AppID - Represents the application id. + AppID *string `json:"appId,omitempty"` + // Permissions - Collection of required app permissions/entitlements from the resource application. + Permissions *[]PreAuthorizedApplicationPermission `json:"permissions,omitempty"` + // Extensions - Collection of extensions from the resource application. + Extensions *[]PreAuthorizedApplicationExtension `json:"extensions,omitempty"` +} + +// PreAuthorizedApplicationExtension representation of an app PreAuthorizedApplicationExtension required by +// a pre authorized client app. +type PreAuthorizedApplicationExtension struct { + // Conditions - The extension's conditions. + Conditions *[]string `json:"conditions,omitempty"` +} + +// PreAuthorizedApplicationPermission contains information about the pre-authorized permissions. +type PreAuthorizedApplicationPermission struct { + // DirectAccessGrant - Indicates whether the permission set is DirectAccess or impersonation. + DirectAccessGrant *bool `json:"directAccessGrant,omitempty"` + // AccessGrants - The list of permissions. + AccessGrants *[]string `json:"accessGrants,omitempty"` } // RequiredResourceAccess specifies the set of OAuth 2.0 permission scopes and app roles under the @@ -2740,19 +3053,53 @@ func (ra *ResourceAccess) UnmarshalJSON(body []byte) error { // ServicePrincipal active Directory service principal information. type ServicePrincipal struct { autorest.Response `json:"-"` - // DisplayName - The display name of the service principal. - DisplayName *string `json:"displayName,omitempty"` + // AccountEnabled - whether or not the service principal account is enabled + AccountEnabled *bool `json:"accountEnabled,omitempty"` + // AlternativeNames - alternative names + AlternativeNames *[]string `json:"alternativeNames,omitempty"` + // AppDisplayName - READ-ONLY; The display name exposed by the associated application. + AppDisplayName *string `json:"appDisplayName,omitempty"` // AppID - The application ID. AppID *string `json:"appId,omitempty"` + // AppOwnerTenantID - READ-ONLY + AppOwnerTenantID *string `json:"appOwnerTenantId,omitempty"` + // AppRoleAssignmentRequired - Specifies whether an AppRoleAssignment to a user or group is required before Azure AD will issue a user or access token to the application. + AppRoleAssignmentRequired *bool `json:"appRoleAssignmentRequired,omitempty"` // AppRoles - The collection of application roles that an application may declare. These roles can be assigned to users, groups or service principals. AppRoles *[]AppRole `json:"appRoles,omitempty"` + // DisplayName - The display name of the service principal. + DisplayName *string `json:"displayName,omitempty"` + // ErrorURL - A URL provided by the author of the associated application to report errors when using the application. + ErrorURL *string `json:"errorUrl,omitempty"` + // Homepage - The URL to the homepage of the associated application. + Homepage *string `json:"homepage,omitempty"` + // KeyCredentials - The collection of key credentials associated with the service principal. + KeyCredentials *[]KeyCredential `json:"keyCredentials,omitempty"` + // LogoutURL - A URL provided by the author of the associated application to logout + LogoutURL *string `json:"logoutUrl,omitempty"` + // Oauth2Permissions - READ-ONLY; The OAuth 2.0 permissions exposed by the associated application. + Oauth2Permissions *[]OAuth2Permission `json:"oauth2Permissions,omitempty"` + // PasswordCredentials - The collection of password credentials associated with the service principal. + PasswordCredentials *[]PasswordCredential `json:"passwordCredentials,omitempty"` + // PreferredTokenSigningKeyThumbprint - The thumbprint of preferred certificate to sign the token + PreferredTokenSigningKeyThumbprint *string `json:"preferredTokenSigningKeyThumbprint,omitempty"` + // PublisherName - The publisher's name of the associated application + PublisherName *string `json:"publisherName,omitempty"` + // ReplyUrls - The URLs that user tokens are sent to for sign in with the associated application. The redirect URIs that the oAuth 2.0 authorization code and access tokens are sent to for the associated application. + ReplyUrls *[]string `json:"replyUrls,omitempty"` + // SamlMetadataURL - The URL to the SAML metadata of the associated application + SamlMetadataURL *string `json:"samlMetadataUrl,omitempty"` // ServicePrincipalNames - A collection of service principal names. ServicePrincipalNames *[]string `json:"servicePrincipalNames,omitempty"` + // ServicePrincipalType - the type of the service principal + ServicePrincipalType *string `json:"servicePrincipalType,omitempty"` + // Tags - Optional list of tags that you can apply to your service principals. Not nullable. + Tags *[]string `json:"tags,omitempty"` // AdditionalProperties - Unmatched properties from the message are deserialized this collection AdditionalProperties map[string]interface{} `json:""` - // ObjectID - The object ID. + // ObjectID - READ-ONLY; The object ID. ObjectID *string `json:"objectId,omitempty"` - // DeletionTimestamp - The time at which the directory object was deleted. + // DeletionTimestamp - READ-ONLY; The time at which the directory object was deleted. DeletionTimestamp *date.Time `json:"deletionTimestamp,omitempty"` // ObjectType - Possible values include: 'ObjectTypeDirectoryObject', 'ObjectTypeApplication', 'ObjectTypeGroup', 'ObjectTypeServicePrincipal', 'ObjectTypeUser' ObjectType ObjectType `json:"objectType,omitempty"` @@ -2762,23 +3109,59 @@ type ServicePrincipal struct { func (sp ServicePrincipal) MarshalJSON() ([]byte, error) { sp.ObjectType = ObjectTypeServicePrincipal objectMap := make(map[string]interface{}) - if sp.DisplayName != nil { - objectMap["displayName"] = sp.DisplayName + if sp.AccountEnabled != nil { + objectMap["accountEnabled"] = sp.AccountEnabled + } + if sp.AlternativeNames != nil { + objectMap["alternativeNames"] = sp.AlternativeNames } if sp.AppID != nil { objectMap["appId"] = sp.AppID } + if sp.AppRoleAssignmentRequired != nil { + objectMap["appRoleAssignmentRequired"] = sp.AppRoleAssignmentRequired + } if sp.AppRoles != nil { objectMap["appRoles"] = sp.AppRoles } + if sp.DisplayName != nil { + objectMap["displayName"] = sp.DisplayName + } + if sp.ErrorURL != nil { + objectMap["errorUrl"] = sp.ErrorURL + } + if sp.Homepage != nil { + objectMap["homepage"] = sp.Homepage + } + if sp.KeyCredentials != nil { + objectMap["keyCredentials"] = sp.KeyCredentials + } + if sp.LogoutURL != nil { + objectMap["logoutUrl"] = sp.LogoutURL + } + if sp.PasswordCredentials != nil { + objectMap["passwordCredentials"] = sp.PasswordCredentials + } + if sp.PreferredTokenSigningKeyThumbprint != nil { + objectMap["preferredTokenSigningKeyThumbprint"] = sp.PreferredTokenSigningKeyThumbprint + } + if sp.PublisherName != nil { + objectMap["publisherName"] = sp.PublisherName + } + if sp.ReplyUrls != nil { + objectMap["replyUrls"] = sp.ReplyUrls + } + if sp.SamlMetadataURL != nil { + objectMap["samlMetadataUrl"] = sp.SamlMetadataURL + } if sp.ServicePrincipalNames != nil { objectMap["servicePrincipalNames"] = sp.ServicePrincipalNames } - if sp.ObjectID != nil { - objectMap["objectId"] = sp.ObjectID + if sp.ServicePrincipalType != nil { + objectMap["servicePrincipalType"] = sp.ServicePrincipalType } - if sp.DeletionTimestamp != nil { - objectMap["deletionTimestamp"] = sp.DeletionTimestamp + if sp.Tags != nil { + objectMap["tags"] = sp.Tags } if sp.ObjectType != "" { objectMap["objectType"] = sp.ObjectType @@ -2828,14 +3211,32 @@ func (sp *ServicePrincipal) UnmarshalJSON(body []byte) error { } for k, v := range m { switch k { - case "displayName": + case "accountEnabled": if v != nil { - var displayName string - err = json.Unmarshal(*v, &displayName) + var accountEnabled bool + err = json.Unmarshal(*v, &accountEnabled) if err != nil { return err } - sp.DisplayName = &displayName + sp.AccountEnabled = &accountEnabled + } + case "alternativeNames": + if v != nil { + var alternativeNames []string + err = json.Unmarshal(*v, &alternativeNames) + if err != nil { + return err + } + sp.AlternativeNames = &alternativeNames + } + case "appDisplayName": + if v != nil { + var appDisplayName string + err = json.Unmarshal(*v, &appDisplayName) + if err != nil { + return err + } + sp.AppDisplayName = &appDisplayName } case "appId": if v != nil { @@ -2846,6 +3247,24 @@ func (sp *ServicePrincipal) UnmarshalJSON(body []byte) error { } sp.AppID = &appID } + case "appOwnerTenantId": + if v != nil { + var appOwnerTenantID string + err = json.Unmarshal(*v, &appOwnerTenantID) + if err != nil { + return err + } + sp.AppOwnerTenantID = &appOwnerTenantID + } + case "appRoleAssignmentRequired": + if v != nil { + var appRoleAssignmentRequired bool + err = json.Unmarshal(*v, &appRoleAssignmentRequired) + if err != nil { + return err + } + sp.AppRoleAssignmentRequired = &appRoleAssignmentRequired + } case "appRoles": if v != nil { var appRoles []AppRole @@ -2855,274 +3274,170 @@ func (sp *ServicePrincipal) UnmarshalJSON(body []byte) error { } sp.AppRoles = &appRoles } - case "servicePrincipalNames": + case "displayName": if v != nil { - var servicePrincipalNames []string - err = json.Unmarshal(*v, &servicePrincipalNames) + var displayName string + err = json.Unmarshal(*v, &displayName) if err != nil { return err } - sp.ServicePrincipalNames = &servicePrincipalNames + sp.DisplayName = &displayName } - default: + case "errorUrl": if v != nil { - var additionalProperties interface{} - err = json.Unmarshal(*v, &additionalProperties) + var errorURL string + err = json.Unmarshal(*v, &errorURL) if err != nil { return err } - if sp.AdditionalProperties == nil { - sp.AdditionalProperties = make(map[string]interface{}) + sp.ErrorURL = &errorURL + } + case "homepage": + if v != nil { + var homepage string + err = json.Unmarshal(*v, &homepage) + if err != nil { + return err } - sp.AdditionalProperties[k] = additionalProperties + sp.Homepage = &homepage } - case "objectId": + case "keyCredentials": if v != nil { - var objectID string - err = json.Unmarshal(*v, &objectID) + var keyCredentials []KeyCredential + err = json.Unmarshal(*v, &keyCredentials) if err != nil { return err } - sp.ObjectID = &objectID + sp.KeyCredentials = &keyCredentials } - case "deletionTimestamp": + case "logoutUrl": if v != nil { - var deletionTimestamp date.Time - err = json.Unmarshal(*v, &deletionTimestamp) + var logoutURL string + err = json.Unmarshal(*v, &logoutURL) if err != nil { return err } - sp.DeletionTimestamp = &deletionTimestamp + sp.LogoutURL = &logoutURL } - case "objectType": + case "oauth2Permissions": if v != nil { - var objectType ObjectType - err = json.Unmarshal(*v, &objectType) + var oauth2Permissions []OAuth2Permission + err = json.Unmarshal(*v, &oauth2Permissions) if err != nil { return err } - sp.ObjectType = objectType + sp.Oauth2Permissions = &oauth2Permissions } - } - } - - return nil -} - -// ServicePrincipalCreateParameters request parameters for creating a new service principal. -type ServicePrincipalCreateParameters struct { - // AdditionalProperties - Unmatched properties from the message are deserialized this collection - AdditionalProperties map[string]interface{} `json:""` - // AccountEnabled - Whether the account is enabled - AccountEnabled *bool `json:"accountEnabled,omitempty"` - // AppID - application Id - AppID *string `json:"appId,omitempty"` - // AppRoleAssignmentRequired - Specifies whether an AppRoleAssignment to a user or group is required before Azure AD will issue a user or access token to the application. - AppRoleAssignmentRequired *bool `json:"appRoleAssignmentRequired,omitempty"` - // DisplayName - The display name for the service principal. - DisplayName *string `json:"displayName,omitempty"` - ErrorURL *string `json:"errorUrl,omitempty"` - // Homepage - The URL to the homepage of the associated application. - Homepage *string `json:"homepage,omitempty"` - // KeyCredentials - A collection of KeyCredential objects. - KeyCredentials *[]KeyCredential `json:"keyCredentials,omitempty"` - // PasswordCredentials - A collection of PasswordCredential objects - PasswordCredentials *[]PasswordCredential `json:"passwordCredentials,omitempty"` - // PublisherName - The display name of the tenant in which the associated application is specified. - PublisherName *string `json:"publisherName,omitempty"` - // ReplyUrls - A collection of reply URLs for the service principal. - ReplyUrls *[]string `json:"replyUrls,omitempty"` - SamlMetadataURL *string `json:"samlMetadataUrl,omitempty"` - // ServicePrincipalNames - A collection of service principal names. - ServicePrincipalNames *[]string `json:"servicePrincipalNames,omitempty"` - Tags *[]string `json:"tags,omitempty"` -} - -// MarshalJSON is the custom marshaler for ServicePrincipalCreateParameters. -func (spcp ServicePrincipalCreateParameters) MarshalJSON() ([]byte, error) { - objectMap := make(map[string]interface{}) - if spcp.AccountEnabled != nil { - objectMap["accountEnabled"] = spcp.AccountEnabled - } - if spcp.AppID != nil { - objectMap["appId"] = spcp.AppID - } - if spcp.AppRoleAssignmentRequired != nil { - objectMap["appRoleAssignmentRequired"] = spcp.AppRoleAssignmentRequired - } - if spcp.DisplayName != nil { - objectMap["displayName"] = spcp.DisplayName - } - if spcp.ErrorURL != nil { - objectMap["errorUrl"] = spcp.ErrorURL - } - if spcp.Homepage != nil { - objectMap["homepage"] = spcp.Homepage - } - if spcp.KeyCredentials != nil { - objectMap["keyCredentials"] = spcp.KeyCredentials - } - if spcp.PasswordCredentials != nil { - objectMap["passwordCredentials"] = spcp.PasswordCredentials - } - if spcp.PublisherName != nil { - objectMap["publisherName"] = spcp.PublisherName - } - if spcp.ReplyUrls != nil { - objectMap["replyUrls"] = spcp.ReplyUrls - } - if spcp.SamlMetadataURL != nil { - objectMap["samlMetadataUrl"] = spcp.SamlMetadataURL - } - if spcp.ServicePrincipalNames != nil { - objectMap["servicePrincipalNames"] = spcp.ServicePrincipalNames - } - if spcp.Tags != nil { - objectMap["tags"] = spcp.Tags - } - for k, v := range spcp.AdditionalProperties { - objectMap[k] = v - } - return json.Marshal(objectMap) -} - -// UnmarshalJSON is the custom unmarshaler for ServicePrincipalCreateParameters struct. -func (spcp *ServicePrincipalCreateParameters) UnmarshalJSON(body []byte) error { - var m map[string]*json.RawMessage - err := json.Unmarshal(body, &m) - if err != nil { - return err - } - for k, v := range m { - switch k { - default: - if v != nil { - var additionalProperties interface{} - err = json.Unmarshal(*v, &additionalProperties) - if err != nil { - return err - } - if spcp.AdditionalProperties == nil { - spcp.AdditionalProperties = make(map[string]interface{}) - } - spcp.AdditionalProperties[k] = additionalProperties - } - case "accountEnabled": + case "passwordCredentials": if v != nil { - var accountEnabled bool - err = json.Unmarshal(*v, &accountEnabled) + var passwordCredentials []PasswordCredential + err = json.Unmarshal(*v, &passwordCredentials) if err != nil { return err } - spcp.AccountEnabled = &accountEnabled + sp.PasswordCredentials = &passwordCredentials } - case "appId": + case "preferredTokenSigningKeyThumbprint": if v != nil { - var appID string - err = json.Unmarshal(*v, &appID) + var preferredTokenSigningKeyThumbprint string + err = json.Unmarshal(*v, &preferredTokenSigningKeyThumbprint) if err != nil { return err } - spcp.AppID = &appID + sp.PreferredTokenSigningKeyThumbprint = &preferredTokenSigningKeyThumbprint } - case "appRoleAssignmentRequired": + case "publisherName": if v != nil { - var appRoleAssignmentRequired bool - err = json.Unmarshal(*v, &appRoleAssignmentRequired) + var publisherName string + err = json.Unmarshal(*v, &publisherName) if err != nil { return err } - spcp.AppRoleAssignmentRequired = &appRoleAssignmentRequired + sp.PublisherName = &publisherName } - case "displayName": + case "replyUrls": if v != nil { - var displayName string - err = json.Unmarshal(*v, &displayName) + var replyUrls []string + err = json.Unmarshal(*v, &replyUrls) if err != nil { return err } - spcp.DisplayName = &displayName + sp.ReplyUrls = &replyUrls } - case "errorUrl": + case "samlMetadataUrl": if v != nil { - var errorURL string - err = json.Unmarshal(*v, &errorURL) + var samlMetadataURL string + err = json.Unmarshal(*v, &samlMetadataURL) if err != nil { return err } - spcp.ErrorURL = &errorURL + sp.SamlMetadataURL = &samlMetadataURL } - case "homepage": + case "servicePrincipalNames": if v != nil { - var homepage string - err = json.Unmarshal(*v, &homepage) + var servicePrincipalNames []string + err = json.Unmarshal(*v, &servicePrincipalNames) if err != nil { return err } - spcp.Homepage = &homepage + sp.ServicePrincipalNames = &servicePrincipalNames } - case "keyCredentials": + case "servicePrincipalType": if v != nil { - var keyCredentials []KeyCredential - err = json.Unmarshal(*v, &keyCredentials) + var servicePrincipalType string + err = json.Unmarshal(*v, &servicePrincipalType) if err != nil { return err } - spcp.KeyCredentials = &keyCredentials + sp.ServicePrincipalType = &servicePrincipalType } - case "passwordCredentials": + case "tags": if v != nil { - var passwordCredentials []PasswordCredential - err = json.Unmarshal(*v, &passwordCredentials) + var tags []string + err = json.Unmarshal(*v, &tags) if err != nil { return err } - spcp.PasswordCredentials = &passwordCredentials + sp.Tags = &tags } - case "publisherName": + default: if v != nil { - var publisherName string - err = json.Unmarshal(*v, &publisherName) + var additionalProperties interface{} + err = json.Unmarshal(*v, &additionalProperties) if err != nil { return err } - spcp.PublisherName = &publisherName - } - case "replyUrls": - if v != nil { - var replyUrls []string - err = json.Unmarshal(*v, &replyUrls) - if err != nil { - return err + if sp.AdditionalProperties == nil { + sp.AdditionalProperties = make(map[string]interface{}) } - spcp.ReplyUrls = &replyUrls + sp.AdditionalProperties[k] = additionalProperties } - case "samlMetadataUrl": + case "objectId": if v != nil { - var samlMetadataURL string - err = json.Unmarshal(*v, &samlMetadataURL) + var objectID string + err = json.Unmarshal(*v, &objectID) if err != nil { return err } - spcp.SamlMetadataURL = &samlMetadataURL + sp.ObjectID = &objectID } - case "servicePrincipalNames": + case "deletionTimestamp": if v != nil { - var servicePrincipalNames []string - err = json.Unmarshal(*v, &servicePrincipalNames) + var deletionTimestamp date.Time + err = json.Unmarshal(*v, &deletionTimestamp) if err != nil { return err } - spcp.ServicePrincipalNames = &servicePrincipalNames + sp.DeletionTimestamp = &deletionTimestamp } - case "tags": + case "objectType": if v != nil { - var tags []string - err = json.Unmarshal(*v, &tags) + var objectType ObjectType + err = json.Unmarshal(*v, &objectType) if err != nil { return err } - spcp.Tags = &tags + sp.ObjectType = objectType } } } @@ -3130,6 +3445,41 @@ func (spcp *ServicePrincipalCreateParameters) UnmarshalJSON(body []byte) error { return nil } +// ServicePrincipalBase active Directory service principal common properties shared among GET, POST and +// PATCH +type ServicePrincipalBase struct { + // AccountEnabled - whether or not the service principal account is enabled + AccountEnabled *bool `json:"accountEnabled,omitempty"` + // AppRoleAssignmentRequired - Specifies whether an AppRoleAssignment to a user or group is required before Azure AD will issue a user or access token to the application. + AppRoleAssignmentRequired *bool `json:"appRoleAssignmentRequired,omitempty"` + // KeyCredentials - The collection of key credentials associated with the service principal. + KeyCredentials *[]KeyCredential `json:"keyCredentials,omitempty"` + // PasswordCredentials - The collection of password credentials associated with the service principal. + PasswordCredentials *[]PasswordCredential `json:"passwordCredentials,omitempty"` + // ServicePrincipalType - the type of the service principal + ServicePrincipalType *string `json:"servicePrincipalType,omitempty"` + // Tags - Optional list of tags that you can apply to your service principals. Not nullable. + Tags *[]string `json:"tags,omitempty"` +} + +// ServicePrincipalCreateParameters request parameters for creating a new service principal. +type ServicePrincipalCreateParameters struct { + // AppID - The application ID. + AppID *string `json:"appId,omitempty"` + // AccountEnabled - whether or not the service principal account is enabled + AccountEnabled *bool `json:"accountEnabled,omitempty"` + // AppRoleAssignmentRequired - Specifies whether an AppRoleAssignment to a user or group is required before Azure AD will issue a user or access token to the application. + AppRoleAssignmentRequired *bool `json:"appRoleAssignmentRequired,omitempty"` + // KeyCredentials - The collection of key credentials associated with the service principal. + KeyCredentials *[]KeyCredential `json:"keyCredentials,omitempty"` + // PasswordCredentials - The collection of password credentials associated with the service principal. + PasswordCredentials *[]PasswordCredential `json:"passwordCredentials,omitempty"` + // ServicePrincipalType - the type of the service principal + ServicePrincipalType *string `json:"servicePrincipalType,omitempty"` + // Tags - Optional list of tags that you can apply to your service principals. Not nullable. + Tags *[]string `json:"tags,omitempty"` +} + // ServicePrincipalListResult server response for get tenant service principals API call. type ServicePrincipalListResult struct { autorest.Response `json:"-"` @@ -3264,225 +3614,29 @@ func NewServicePrincipalListResultPage(getNextPage func(context.Context, Service return ServicePrincipalListResultPage{fn: getNextPage} } -// ServicePrincipalUpdateParameters request parameters for creating a new service principal. +// ServicePrincipalObjectResult service Principal Object Result. +type ServicePrincipalObjectResult struct { + autorest.Response `json:"-"` + // Value - The Object ID of the service principal with the specified application ID. + Value *string `json:"value,omitempty"` + // OdataMetadata - The URL representing edm equivalent. + OdataMetadata *string `json:"odata.metadata,omitempty"` +} + +// ServicePrincipalUpdateParameters request parameters for update an existing service principal. type ServicePrincipalUpdateParameters struct { - // AdditionalProperties - Unmatched properties from the message are deserialized this collection - AdditionalProperties map[string]interface{} `json:""` - // AccountEnabled - Whether the account is enabled + // AccountEnabled - whether or not the service principal account is enabled AccountEnabled *bool `json:"accountEnabled,omitempty"` - // AppID - application Id - AppID *string `json:"appId,omitempty"` // AppRoleAssignmentRequired - Specifies whether an AppRoleAssignment to a user or group is required before Azure AD will issue a user or access token to the application. AppRoleAssignmentRequired *bool `json:"appRoleAssignmentRequired,omitempty"` - // DisplayName - The display name for the service principal. - DisplayName *string `json:"displayName,omitempty"` - ErrorURL *string `json:"errorUrl,omitempty"` - // Homepage - The URL to the homepage of the associated application. - Homepage *string `json:"homepage,omitempty"` - // KeyCredentials - A collection of KeyCredential objects. + // KeyCredentials - The collection of key credentials associated with the service principal. KeyCredentials *[]KeyCredential `json:"keyCredentials,omitempty"` - // PasswordCredentials - A collection of PasswordCredential objects + // PasswordCredentials - The collection of password credentials associated with the service principal. PasswordCredentials *[]PasswordCredential `json:"passwordCredentials,omitempty"` - // PublisherName - The display name of the tenant in which the associated application is specified. - PublisherName *string `json:"publisherName,omitempty"` - // ReplyUrls - A collection of reply URLs for the service principal. - ReplyUrls *[]string `json:"replyUrls,omitempty"` - SamlMetadataURL *string `json:"samlMetadataUrl,omitempty"` - // ServicePrincipalNames - A collection of service principal names. - ServicePrincipalNames *[]string `json:"servicePrincipalNames,omitempty"` - Tags *[]string `json:"tags,omitempty"` -} - -// MarshalJSON is the custom marshaler for ServicePrincipalUpdateParameters. -func (spup ServicePrincipalUpdateParameters) MarshalJSON() ([]byte, error) { - objectMap := make(map[string]interface{}) - if spup.AccountEnabled != nil { - objectMap["accountEnabled"] = spup.AccountEnabled - } - if spup.AppID != nil { - objectMap["appId"] = spup.AppID - } - if spup.AppRoleAssignmentRequired != nil { - objectMap["appRoleAssignmentRequired"] = spup.AppRoleAssignmentRequired - } - if spup.DisplayName != nil { - objectMap["displayName"] = spup.DisplayName - } - if spup.ErrorURL != nil { - objectMap["errorUrl"] = spup.ErrorURL - } - if spup.Homepage != nil { - objectMap["homepage"] = spup.Homepage - } - if spup.KeyCredentials != nil { - objectMap["keyCredentials"] = spup.KeyCredentials - } - if spup.PasswordCredentials != nil { - objectMap["passwordCredentials"] = spup.PasswordCredentials - } - if spup.PublisherName != nil { - objectMap["publisherName"] = spup.PublisherName - } - if spup.ReplyUrls != nil { - objectMap["replyUrls"] = spup.ReplyUrls - } - if spup.SamlMetadataURL != nil { - objectMap["samlMetadataUrl"] = spup.SamlMetadataURL - } - if spup.ServicePrincipalNames != nil { - objectMap["servicePrincipalNames"] = spup.ServicePrincipalNames - } - if spup.Tags != nil { - objectMap["tags"] = spup.Tags - } - for k, v := range spup.AdditionalProperties { - objectMap[k] = v - } - return json.Marshal(objectMap) -} - -// UnmarshalJSON is the custom unmarshaler for ServicePrincipalUpdateParameters struct. -func (spup *ServicePrincipalUpdateParameters) UnmarshalJSON(body []byte) error { - var m map[string]*json.RawMessage - err := json.Unmarshal(body, &m) - if err != nil { - return err - } - for k, v := range m { - switch k { - default: - if v != nil { - var additionalProperties interface{} - err = json.Unmarshal(*v, &additionalProperties) - if err != nil { - return err - } - if spup.AdditionalProperties == nil { - spup.AdditionalProperties = make(map[string]interface{}) - } - spup.AdditionalProperties[k] = additionalProperties - } - case "accountEnabled": - if v != nil { - var accountEnabled bool - err = json.Unmarshal(*v, &accountEnabled) - if err != nil { - return err - } - spup.AccountEnabled = &accountEnabled - } - case "appId": - if v != nil { - var appID string - err = json.Unmarshal(*v, &appID) - if err != nil { - return err - } - spup.AppID = &appID - } - case "appRoleAssignmentRequired": - if v != nil { - var appRoleAssignmentRequired bool - err = json.Unmarshal(*v, &appRoleAssignmentRequired) - if err != nil { - return err - } - spup.AppRoleAssignmentRequired = &appRoleAssignmentRequired - } - case "displayName": - if v != nil { - var displayName string - err = json.Unmarshal(*v, &displayName) - if err != nil { - return err - } - spup.DisplayName = &displayName - } - case "errorUrl": - if v != nil { - var errorURL string - err = json.Unmarshal(*v, &errorURL) - if err != nil { - return err - } - spup.ErrorURL = &errorURL - } - case "homepage": - if v != nil { - var homepage string - err = json.Unmarshal(*v, &homepage) - if err != nil { - return err - } - spup.Homepage = &homepage - } - case "keyCredentials": - if v != nil { - var keyCredentials []KeyCredential - err = json.Unmarshal(*v, &keyCredentials) - if err != nil { - return err - } - spup.KeyCredentials = &keyCredentials - } - case "passwordCredentials": - if v != nil { - var passwordCredentials []PasswordCredential - err = json.Unmarshal(*v, &passwordCredentials) - if err != nil { - return err - } - spup.PasswordCredentials = &passwordCredentials - } - case "publisherName": - if v != nil { - var publisherName string - err = json.Unmarshal(*v, &publisherName) - if err != nil { - return err - } - spup.PublisherName = &publisherName - } - case "replyUrls": - if v != nil { - var replyUrls []string - err = json.Unmarshal(*v, &replyUrls) - if err != nil { - return err - } - spup.ReplyUrls = &replyUrls - } - case "samlMetadataUrl": - if v != nil { - var samlMetadataURL string - err = json.Unmarshal(*v, &samlMetadataURL) - if err != nil { - return err - } - spup.SamlMetadataURL = &samlMetadataURL - } - case "servicePrincipalNames": - if v != nil { - var servicePrincipalNames []string - err = json.Unmarshal(*v, &servicePrincipalNames) - if err != nil { - return err - } - spup.ServicePrincipalNames = &servicePrincipalNames - } - case "tags": - if v != nil { - var tags []string - err = json.Unmarshal(*v, &tags) - if err != nil { - return err - } - spup.Tags = &tags - } - } - } - - return nil + // ServicePrincipalType - the type of the service principal + ServicePrincipalType *string `json:"servicePrincipalType,omitempty"` + // Tags - Optional list of tags that you can apply to your service principals. Not nullable. + Tags *[]string `json:"tags,omitempty"` } // SignInName contains information about a sign-in name of a local account user in an Azure Active @@ -3583,9 +3737,9 @@ type User struct { SignInNames *[]SignInName `json:"signInNames,omitempty"` // AdditionalProperties - Unmatched properties from the message are deserialized this collection AdditionalProperties map[string]interface{} `json:""` - // ObjectID - The object ID. + // ObjectID - READ-ONLY; The object ID. ObjectID *string `json:"objectId,omitempty"` - // DeletionTimestamp - The time at which the directory object was deleted. + // DeletionTimestamp - READ-ONLY; The time at which the directory object was deleted. DeletionTimestamp *date.Time `json:"deletionTimestamp,omitempty"` // ObjectType - Possible values include: 'ObjectTypeDirectoryObject', 'ObjectTypeApplication', 'ObjectTypeGroup', 'ObjectTypeServicePrincipal', 'ObjectTypeUser' ObjectType ObjectType `json:"objectType,omitempty"` @@ -3628,12 +3782,6 @@ func (u User) MarshalJSON() ([]byte, error) { if u.SignInNames != nil { objectMap["signInNames"] = u.SignInNames } - if u.ObjectID != nil { - objectMap["objectId"] = u.ObjectID - } - if u.DeletionTimestamp != nil { - objectMap["deletionTimestamp"] = u.DeletionTimestamp - } if u.ObjectType != "" { objectMap["objectType"] = u.ObjectType } diff --git a/vendor/github.com/Azure/azure-sdk-for-go/services/graphrbac/1.6/graphrbac/oauth2.go b/vendor/github.com/Azure/azure-sdk-for-go/services/graphrbac/1.6/graphrbac/oauth2.go deleted file mode 100644 index 97e79c76dd..0000000000 --- a/vendor/github.com/Azure/azure-sdk-for-go/services/graphrbac/1.6/graphrbac/oauth2.go +++ /dev/null @@ -1,197 +0,0 @@ -package graphrbac - -// Copyright (c) Microsoft and contributors. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// -// See the License for the specific language governing permissions and -// limitations under the License. -// -// Code generated by Microsoft (R) AutoRest Code Generator. -// Changes may cause incorrect behavior and will be lost if the code is regenerated. - -import ( - "context" - "github.com/Azure/go-autorest/autorest" - "github.com/Azure/go-autorest/autorest/azure" - "github.com/Azure/go-autorest/tracing" - "net/http" -) - -// OAuth2Client is the the Graph RBAC Management Client -type OAuth2Client struct { - BaseClient -} - -// NewOAuth2Client creates an instance of the OAuth2Client client. -func NewOAuth2Client(tenantID string) OAuth2Client { - return NewOAuth2ClientWithBaseURI(DefaultBaseURI, tenantID) -} - -// NewOAuth2ClientWithBaseURI creates an instance of the OAuth2Client client. -func NewOAuth2ClientWithBaseURI(baseURI string, tenantID string) OAuth2Client { - return OAuth2Client{NewWithBaseURI(baseURI, tenantID)} -} - -// Get queries OAuth2 permissions for the relevant SP ObjectId of an app. -// Parameters: -// filter - this is the Service Principal ObjectId associated with the app -func (client OAuth2Client) Get(ctx context.Context, filter string) (result Permissions, err error) { - if tracing.IsEnabled() { - ctx = tracing.StartSpan(ctx, fqdn+"/OAuth2Client.Get") - defer func() { - sc := -1 - if result.Response.Response != nil { - sc = result.Response.Response.StatusCode - } - tracing.EndSpan(ctx, sc, err) - }() - } - req, err := client.GetPreparer(ctx, filter) - if err != nil { - err = autorest.NewErrorWithError(err, "graphrbac.OAuth2Client", "Get", nil, "Failure preparing request") - return - } - - resp, err := client.GetSender(req) - if err != nil { - result.Response = autorest.Response{Response: resp} - err = autorest.NewErrorWithError(err, "graphrbac.OAuth2Client", "Get", resp, "Failure sending request") - return - } - - result, err = client.GetResponder(resp) - if err != nil { - err = autorest.NewErrorWithError(err, "graphrbac.OAuth2Client", "Get", resp, "Failure responding to request") - } - - return -} - -// GetPreparer prepares the Get request. -func (client OAuth2Client) GetPreparer(ctx context.Context, filter string) (*http.Request, error) { - pathParameters := map[string]interface{}{ - "tenantID": autorest.Encode("path", client.TenantID), - } - - const APIVersion = "1.6" - queryParameters := map[string]interface{}{ - "api-version": APIVersion, - } - if len(filter) > 0 { - queryParameters["$filter"] = autorest.Encode("query", filter) - } - - preparer := autorest.CreatePreparer( - autorest.AsGet(), - autorest.WithBaseURL(client.BaseURI), - autorest.WithPathParameters("/{tenantID}/oauth2PermissionGrants", pathParameters), - autorest.WithQueryParameters(queryParameters)) - return preparer.Prepare((&http.Request{}).WithContext(ctx)) -} - -// GetSender sends the Get request. The method will close the -// http.Response Body if it receives an error. -func (client OAuth2Client) GetSender(req *http.Request) (*http.Response, error) { - return autorest.SendWithSender(client, req, - autorest.DoRetryForStatusCodes(client.RetryAttempts, client.RetryDuration, autorest.StatusCodesForRetry...)) -} - -// GetResponder handles the response to the Get request. The method always -// closes the http.Response Body. -func (client OAuth2Client) GetResponder(resp *http.Response) (result Permissions, err error) { - err = autorest.Respond( - resp, - client.ByInspecting(), - azure.WithErrorUnlessStatusCode(http.StatusOK), - autorest.ByUnmarshallingJSON(&result), - autorest.ByClosing()) - result.Response = autorest.Response{Response: resp} - return -} - -// Grant grants OAuth2 permissions for the relevant resource Ids of an app. -// Parameters: -// body - the relevant app Service Principal Object Id and the Service Principal Object Id you want to grant. -func (client OAuth2Client) Grant(ctx context.Context, body *Permissions) (result Permissions, err error) { - if tracing.IsEnabled() { - ctx = tracing.StartSpan(ctx, fqdn+"/OAuth2Client.Grant") - defer func() { - sc := -1 - if result.Response.Response != nil { - sc = result.Response.Response.StatusCode - } - tracing.EndSpan(ctx, sc, err) - }() - } - req, err := client.GrantPreparer(ctx, body) - if err != nil { - err = autorest.NewErrorWithError(err, "graphrbac.OAuth2Client", "Grant", nil, "Failure preparing request") - return - } - - resp, err := client.GrantSender(req) - if err != nil { - result.Response = autorest.Response{Response: resp} - err = autorest.NewErrorWithError(err, "graphrbac.OAuth2Client", "Grant", resp, "Failure sending request") - return - } - - result, err = client.GrantResponder(resp) - if err != nil { - err = autorest.NewErrorWithError(err, "graphrbac.OAuth2Client", "Grant", resp, "Failure responding to request") - } - - return -} - -// GrantPreparer prepares the Grant request. -func (client OAuth2Client) GrantPreparer(ctx context.Context, body *Permissions) (*http.Request, error) { - pathParameters := map[string]interface{}{ - "tenantID": autorest.Encode("path", client.TenantID), - } - - const APIVersion = "1.6" - queryParameters := map[string]interface{}{ - "api-version": APIVersion, - } - - preparer := autorest.CreatePreparer( - autorest.AsContentType("application/json; charset=utf-8"), - autorest.AsPost(), - autorest.WithBaseURL(client.BaseURI), - autorest.WithPathParameters("/{tenantID}/oauth2PermissionGrants", pathParameters), - autorest.WithQueryParameters(queryParameters)) - if body != nil { - preparer = autorest.DecoratePreparer(preparer, - autorest.WithJSON(body)) - } - return preparer.Prepare((&http.Request{}).WithContext(ctx)) -} - -// GrantSender sends the Grant request. The method will close the -// http.Response Body if it receives an error. -func (client OAuth2Client) GrantSender(req *http.Request) (*http.Response, error) { - return autorest.SendWithSender(client, req, - autorest.DoRetryForStatusCodes(client.RetryAttempts, client.RetryDuration, autorest.StatusCodesForRetry...)) -} - -// GrantResponder handles the response to the Grant request. The method always -// closes the http.Response Body. -func (client OAuth2Client) GrantResponder(resp *http.Response) (result Permissions, err error) { - err = autorest.Respond( - resp, - client.ByInspecting(), - azure.WithErrorUnlessStatusCode(http.StatusOK, http.StatusCreated), - autorest.ByUnmarshallingJSON(&result), - autorest.ByClosing()) - result.Response = autorest.Response{Response: resp} - return -} diff --git a/vendor/github.com/Azure/azure-sdk-for-go/services/graphrbac/1.6/graphrbac/oauth2permissiongrant.go b/vendor/github.com/Azure/azure-sdk-for-go/services/graphrbac/1.6/graphrbac/oauth2permissiongrant.go new file mode 100644 index 0000000000..3d1ec66eb6 --- /dev/null +++ b/vendor/github.com/Azure/azure-sdk-for-go/services/graphrbac/1.6/graphrbac/oauth2permissiongrant.go @@ -0,0 +1,369 @@ +package graphrbac + +// Copyright (c) Microsoft and contributors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Code generated by Microsoft (R) AutoRest Code Generator. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +import ( + "context" + "github.com/Azure/go-autorest/autorest" + "github.com/Azure/go-autorest/autorest/azure" + "github.com/Azure/go-autorest/autorest/to" + "github.com/Azure/go-autorest/tracing" + "net/http" +) + +// OAuth2PermissionGrantClient is the the Graph RBAC Management Client +type OAuth2PermissionGrantClient struct { + BaseClient +} + +// NewOAuth2PermissionGrantClient creates an instance of the OAuth2PermissionGrantClient client. +func NewOAuth2PermissionGrantClient(tenantID string) OAuth2PermissionGrantClient { + return NewOAuth2PermissionGrantClientWithBaseURI(DefaultBaseURI, tenantID) +} + +// NewOAuth2PermissionGrantClientWithBaseURI creates an instance of the OAuth2PermissionGrantClient client. +func NewOAuth2PermissionGrantClientWithBaseURI(baseURI string, tenantID string) OAuth2PermissionGrantClient { + return OAuth2PermissionGrantClient{NewWithBaseURI(baseURI, tenantID)} +} + +// Create grants OAuth2 permissions for the relevant resource Ids of an app. +// Parameters: +// body - the relevant app Service Principal Object Id and the Service Principal Object Id you want to grant. +func (client OAuth2PermissionGrantClient) Create(ctx context.Context, body *OAuth2PermissionGrant) (result OAuth2PermissionGrant, err error) { + if tracing.IsEnabled() { + ctx = tracing.StartSpan(ctx, fqdn+"/OAuth2PermissionGrantClient.Create") + defer func() { + sc := -1 + if result.Response.Response != nil { + sc = result.Response.Response.StatusCode + } + tracing.EndSpan(ctx, sc, err) + }() + } + req, err := client.CreatePreparer(ctx, body) + if err != nil { + err = autorest.NewErrorWithError(err, "graphrbac.OAuth2PermissionGrantClient", "Create", nil, "Failure preparing request") + return + } + + resp, err := client.CreateSender(req) + if err != nil { + result.Response = autorest.Response{Response: resp} + err = autorest.NewErrorWithError(err, "graphrbac.OAuth2PermissionGrantClient", "Create", resp, "Failure sending request") + return + } + + result, err = client.CreateResponder(resp) + if err != nil { + err = autorest.NewErrorWithError(err, "graphrbac.OAuth2PermissionGrantClient", "Create", resp, "Failure responding to request") + } + + return +} + +// CreatePreparer prepares the Create request. +func (client OAuth2PermissionGrantClient) CreatePreparer(ctx context.Context, body *OAuth2PermissionGrant) (*http.Request, error) { + pathParameters := map[string]interface{}{ + "tenantID": autorest.Encode("path", client.TenantID), + } + + const APIVersion = "1.6" + queryParameters := map[string]interface{}{ + "api-version": APIVersion, + } + + preparer := autorest.CreatePreparer( + autorest.AsContentType("application/json; charset=utf-8"), + autorest.AsPost(), + autorest.WithBaseURL(client.BaseURI), + autorest.WithPathParameters("/{tenantID}/oauth2PermissionGrants", pathParameters), + autorest.WithQueryParameters(queryParameters)) + if body != nil { + preparer = autorest.DecoratePreparer(preparer, + autorest.WithJSON(body)) + } + return preparer.Prepare((&http.Request{}).WithContext(ctx)) +} + +// CreateSender sends the Create request. The method will close the +// http.Response Body if it receives an error. +func (client OAuth2PermissionGrantClient) CreateSender(req *http.Request) (*http.Response, error) { + return autorest.SendWithSender(client, req, + autorest.DoRetryForStatusCodes(client.RetryAttempts, client.RetryDuration, autorest.StatusCodesForRetry...)) +} + +// CreateResponder handles the response to the Create request. The method always +// closes the http.Response Body. +func (client OAuth2PermissionGrantClient) CreateResponder(resp *http.Response) (result OAuth2PermissionGrant, err error) { + err = autorest.Respond( + resp, + client.ByInspecting(), + azure.WithErrorUnlessStatusCode(http.StatusOK, http.StatusCreated), + autorest.ByUnmarshallingJSON(&result), + autorest.ByClosing()) + result.Response = autorest.Response{Response: resp} + return +} + +// Delete delete a OAuth2 permission grant for the relevant resource Ids of an app. +// Parameters: +// objectID - the object ID of a permission grant. +func (client OAuth2PermissionGrantClient) Delete(ctx context.Context, objectID string) (result autorest.Response, err error) { + if tracing.IsEnabled() { + ctx = tracing.StartSpan(ctx, fqdn+"/OAuth2PermissionGrantClient.Delete") + defer func() { + sc := -1 + if result.Response != nil { + sc = result.Response.StatusCode + } + tracing.EndSpan(ctx, sc, err) + }() + } + req, err := client.DeletePreparer(ctx, objectID) + if err != nil { + err = autorest.NewErrorWithError(err, "graphrbac.OAuth2PermissionGrantClient", "Delete", nil, "Failure preparing request") + return + } + + resp, err := client.DeleteSender(req) + if err != nil { + result.Response = resp + err = autorest.NewErrorWithError(err, "graphrbac.OAuth2PermissionGrantClient", "Delete", resp, "Failure sending request") + return + } + + result, err = client.DeleteResponder(resp) + if err != nil { + err = autorest.NewErrorWithError(err, "graphrbac.OAuth2PermissionGrantClient", "Delete", resp, "Failure responding to request") + } + + return +} + +// DeletePreparer prepares the Delete request. +func (client OAuth2PermissionGrantClient) DeletePreparer(ctx context.Context, objectID string) (*http.Request, error) { + pathParameters := map[string]interface{}{ + "objectId": autorest.Encode("path", objectID), + "tenantID": autorest.Encode("path", client.TenantID), + } + + const APIVersion = "1.6" + queryParameters := map[string]interface{}{ + "api-version": APIVersion, + } + + preparer := autorest.CreatePreparer( + autorest.AsDelete(), + autorest.WithBaseURL(client.BaseURI), + autorest.WithPathParameters("/{tenantID}/oauth2PermissionGrants/{objectId}", pathParameters), + autorest.WithQueryParameters(queryParameters)) + return preparer.Prepare((&http.Request{}).WithContext(ctx)) +} + +// DeleteSender sends the Delete request. The method will close the +// http.Response Body if it receives an error. +func (client OAuth2PermissionGrantClient) DeleteSender(req *http.Request) (*http.Response, error) { + return autorest.SendWithSender(client, req, + autorest.DoRetryForStatusCodes(client.RetryAttempts, client.RetryDuration, autorest.StatusCodesForRetry...)) +} + +// DeleteResponder handles the response to the Delete request. The method always +// closes the http.Response Body. +func (client OAuth2PermissionGrantClient) DeleteResponder(resp *http.Response) (result autorest.Response, err error) { + err = autorest.Respond( + resp, + client.ByInspecting(), + azure.WithErrorUnlessStatusCode(http.StatusOK, http.StatusNoContent), + autorest.ByClosing()) + result.Response = resp + return +} + +// List queries OAuth2 permissions grants for the relevant SP ObjectId of an app. +// Parameters: +// filter - this is the Service Principal ObjectId associated with the app +func (client OAuth2PermissionGrantClient) List(ctx context.Context, filter string) (result OAuth2PermissionGrantListResultPage, err error) { + if tracing.IsEnabled() { + ctx = tracing.StartSpan(ctx, fqdn+"/OAuth2PermissionGrantClient.List") + defer func() { + sc := -1 + if result.oa2pglr.Response.Response != nil { + sc = result.oa2pglr.Response.Response.StatusCode + } + tracing.EndSpan(ctx, sc, err) + }() + } + result.fn = func(ctx context.Context, lastResult OAuth2PermissionGrantListResult) (OAuth2PermissionGrantListResult, error) { + if lastResult.OdataNextLink == nil || len(to.String(lastResult.OdataNextLink)) < 1 { + return OAuth2PermissionGrantListResult{}, nil + } + return client.ListNext(ctx, *lastResult.OdataNextLink) + } + req, err := client.ListPreparer(ctx, filter) + if err != nil { + err = autorest.NewErrorWithError(err, "graphrbac.OAuth2PermissionGrantClient", "List", nil, "Failure preparing request") + return + } + + resp, err := client.ListSender(req) + if err != nil { + result.oa2pglr.Response = autorest.Response{Response: resp} + err = autorest.NewErrorWithError(err, "graphrbac.OAuth2PermissionGrantClient", "List", resp, "Failure sending request") + return + } + + result.oa2pglr, err = client.ListResponder(resp) + if err != nil { + err = autorest.NewErrorWithError(err, "graphrbac.OAuth2PermissionGrantClient", "List", resp, "Failure responding to request") + } + + return +} + +// ListPreparer prepares the List request. +func (client OAuth2PermissionGrantClient) ListPreparer(ctx context.Context, filter string) (*http.Request, error) { + pathParameters := map[string]interface{}{ + "tenantID": autorest.Encode("path", client.TenantID), + } + + const APIVersion = "1.6" + queryParameters := map[string]interface{}{ + "api-version": APIVersion, + } + if len(filter) > 0 { + queryParameters["$filter"] = autorest.Encode("query", filter) + } + + preparer := autorest.CreatePreparer( + autorest.AsGet(), + autorest.WithBaseURL(client.BaseURI), + autorest.WithPathParameters("/{tenantID}/oauth2PermissionGrants", pathParameters), + autorest.WithQueryParameters(queryParameters)) + return preparer.Prepare((&http.Request{}).WithContext(ctx)) +} + +// ListSender sends the List request. The method will close the +// http.Response Body if it receives an error. +func (client OAuth2PermissionGrantClient) ListSender(req *http.Request) (*http.Response, error) { + return autorest.SendWithSender(client, req, + autorest.DoRetryForStatusCodes(client.RetryAttempts, client.RetryDuration, autorest.StatusCodesForRetry...)) +} + +// ListResponder handles the response to the List request. The method always +// closes the http.Response Body. +func (client OAuth2PermissionGrantClient) ListResponder(resp *http.Response) (result OAuth2PermissionGrantListResult, err error) { + err = autorest.Respond( + resp, + client.ByInspecting(), + azure.WithErrorUnlessStatusCode(http.StatusOK), + autorest.ByUnmarshallingJSON(&result), + autorest.ByClosing()) + result.Response = autorest.Response{Response: resp} + return +} + +// ListComplete enumerates all values, automatically crossing page boundaries as required. +func (client OAuth2PermissionGrantClient) ListComplete(ctx context.Context, filter string) (result OAuth2PermissionGrantListResultIterator, err error) { + if tracing.IsEnabled() { + ctx = tracing.StartSpan(ctx, fqdn+"/OAuth2PermissionGrantClient.List") + defer func() { + sc := -1 + if result.Response().Response.Response != nil { + sc = result.page.Response().Response.Response.StatusCode + } + tracing.EndSpan(ctx, sc, err) + }() + } + result.page, err = client.List(ctx, filter) + return +} + +// ListNext gets the next page of OAuth2 permission grants +// Parameters: +// nextLink - next link for the list operation. +func (client OAuth2PermissionGrantClient) ListNext(ctx context.Context, nextLink string) (result OAuth2PermissionGrantListResult, err error) { + if tracing.IsEnabled() { + ctx = tracing.StartSpan(ctx, fqdn+"/OAuth2PermissionGrantClient.ListNext") + defer func() { + sc := -1 + if result.Response.Response != nil { + sc = result.Response.Response.StatusCode + } + tracing.EndSpan(ctx, sc, err) + }() + } + req, err := client.ListNextPreparer(ctx, nextLink) + if err != nil { + err = autorest.NewErrorWithError(err, "graphrbac.OAuth2PermissionGrantClient", "ListNext", nil, "Failure preparing request") + return + } + + resp, err := client.ListNextSender(req) + if err != nil { + result.Response = autorest.Response{Response: resp} + err = autorest.NewErrorWithError(err, "graphrbac.OAuth2PermissionGrantClient", "ListNext", resp, "Failure sending request") + return + } + + result, err = client.ListNextResponder(resp) + if err != nil { + err = autorest.NewErrorWithError(err, "graphrbac.OAuth2PermissionGrantClient", "ListNext", resp, "Failure responding to request") + } + + return +} + +// ListNextPreparer prepares the ListNext request. +func (client OAuth2PermissionGrantClient) ListNextPreparer(ctx context.Context, nextLink string) (*http.Request, error) { + pathParameters := map[string]interface{}{ + "nextLink": nextLink, + "tenantID": autorest.Encode("path", client.TenantID), + } + + const APIVersion = "1.6" + queryParameters := map[string]interface{}{ + "api-version": APIVersion, + } + + preparer := autorest.CreatePreparer( + autorest.AsGet(), + autorest.WithBaseURL(client.BaseURI), + autorest.WithPathParameters("/{tenantID}/{nextLink}", pathParameters), + autorest.WithQueryParameters(queryParameters)) + return preparer.Prepare((&http.Request{}).WithContext(ctx)) +} + +// ListNextSender sends the ListNext request. The method will close the +// http.Response Body if it receives an error. +func (client OAuth2PermissionGrantClient) ListNextSender(req *http.Request) (*http.Response, error) { + return autorest.SendWithSender(client, req, + autorest.DoRetryForStatusCodes(client.RetryAttempts, client.RetryDuration, autorest.StatusCodesForRetry...)) +} + +// ListNextResponder handles the response to the ListNext request. The method always +// closes the http.Response Body. +func (client OAuth2PermissionGrantClient) ListNextResponder(resp *http.Response) (result OAuth2PermissionGrantListResult, err error) { + err = autorest.Respond( + resp, + client.ByInspecting(), + azure.WithErrorUnlessStatusCode(http.StatusOK), + autorest.ByUnmarshallingJSON(&result), + autorest.ByClosing()) + result.Response = autorest.Response{Response: resp} + return +} diff --git a/vendor/github.com/Azure/azure-sdk-for-go/version/version.go b/vendor/github.com/Azure/azure-sdk-for-go/version/version.go index fcc40b733b..2f58cd8cfd 100644 --- a/vendor/github.com/Azure/azure-sdk-for-go/version/version.go +++ b/vendor/github.com/Azure/azure-sdk-for-go/version/version.go @@ -18,4 +18,4 @@ package version // Changes may cause incorrect behavior and will be lost if the code is regenerated. // Number contains the semantic version of this SDK. -const Number = "v24.1.0" +const Number = "v29.0.0" diff --git a/vendor/github.com/Azure/go-autorest/autorest/adal/token.go b/vendor/github.com/Azure/go-autorest/autorest/adal/token.go index 52ca378667..effa87ab2f 100644 --- a/vendor/github.com/Azure/go-autorest/autorest/adal/token.go +++ b/vendor/github.com/Azure/go-autorest/autorest/adal/token.go @@ -796,7 +796,7 @@ func (spt *ServicePrincipalToken) refreshInternal(ctx context.Context, resource if err != nil { return fmt.Errorf("adal: Failed to build the refresh request. Error = '%v'", err) } - req.Header.Add("User-Agent", userAgent()) + req.Header.Add("User-Agent", UserAgent()) req = req.WithContext(ctx) if !isIMDS(spt.inner.OauthConfig.TokenEndpoint) { v := url.Values{} diff --git a/vendor/github.com/Azure/go-autorest/autorest/adal/version.go b/vendor/github.com/Azure/go-autorest/autorest/adal/version.go index 3944edf051..c867b34843 100644 --- a/vendor/github.com/Azure/go-autorest/autorest/adal/version.go +++ b/vendor/github.com/Azure/go-autorest/autorest/adal/version.go @@ -30,6 +30,16 @@ var ( ) ) -func userAgent() string { +// UserAgent returns a string containing the Go version, system architecture and OS, and the adal version. +func UserAgent() string { return ua } + +// AddToUserAgent adds an extension to the current user agent +func AddToUserAgent(extension string) error { + if extension != "" { + ua = fmt.Sprintf("%s %s", ua, extension) + return nil + } + return fmt.Errorf("Extension was empty, User Agent remained as '%s'", ua) +} diff --git a/vendor/github.com/Azure/go-autorest/autorest/authorization.go b/vendor/github.com/Azure/go-autorest/autorest/authorization.go index bc474b406a..2e24b4b397 100644 --- a/vendor/github.com/Azure/go-autorest/autorest/authorization.go +++ b/vendor/github.com/Azure/go-autorest/autorest/authorization.go @@ -15,6 +15,7 @@ package autorest // limitations under the License. import ( + "encoding/base64" "fmt" "net/http" "net/url" @@ -31,6 +32,8 @@ const ( apiKeyAuthorizerHeader = "Ocp-Apim-Subscription-Key" bingAPISdkHeader = "X-BingApis-SDK-Client" golangBingAPISdkHeaderValue = "Go-SDK" + authorization = "Authorization" + basic = "Basic" ) // Authorizer is the interface that provides a PrepareDecorator used to supply request @@ -258,3 +261,27 @@ func (egta EventGridKeyAuthorizer) WithAuthorization() PrepareDecorator { } return NewAPIKeyAuthorizerWithHeaders(headers).WithAuthorization() } + +// BasicAuthorizer implements basic HTTP authorization by adding the Authorization HTTP header +// with the value "Basic " where is a base64-encoded username:password tuple. +type BasicAuthorizer struct { + userName string + password string +} + +// NewBasicAuthorizer creates a new BasicAuthorizer with the specified username and password. +func NewBasicAuthorizer(userName, password string) *BasicAuthorizer { + return &BasicAuthorizer{ + userName: userName, + password: password, + } +} + +// WithAuthorization returns a PrepareDecorator that adds an HTTP Authorization header whose +// value is "Basic " followed by the base64-encoded username:password tuple. +func (ba *BasicAuthorizer) WithAuthorization() PrepareDecorator { + headers := make(map[string]interface{}) + headers[authorization] = basic + " " + base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("%s:%s", ba.userName, ba.password))) + + return NewAPIKeyAuthorizerWithHeaders(headers).WithAuthorization() +} diff --git a/vendor/github.com/Azure/go-autorest/autorest/azure/async.go b/vendor/github.com/Azure/go-autorest/autorest/azure/async.go index 3f6a2c097f..0041eacf75 100644 --- a/vendor/github.com/Azure/go-autorest/autorest/azure/async.go +++ b/vendor/github.com/Azure/go-autorest/autorest/azure/async.go @@ -181,6 +181,10 @@ func (f Future) WaitForCompletion(ctx context.Context, client autorest.Client) e // running operation has completed, the provided context is cancelled, or the client's // polling duration has been exceeded. It will retry failed polling attempts based on // the retry value defined in the client up to the maximum retry attempts. +// If no deadline is specified in the context then the client.PollingDuration will be +// used to determine if a default deadline should be used. +// If PollingDuration is greater than zero the value will be used as the context's timeout. +// If PollingDuration is zero then no default deadline will be used. func (f *Future) WaitForCompletionRef(ctx context.Context, client autorest.Client) (err error) { ctx = tracing.StartSpan(ctx, "github.com/Azure/go-autorest/autorest/azure/async.WaitForCompletionRef") defer func() { @@ -192,7 +196,9 @@ func (f *Future) WaitForCompletionRef(ctx context.Context, client autorest.Clien tracing.EndSpan(ctx, sc, err) }() cancelCtx := ctx - if d := client.PollingDuration; d != 0 { + // if the provided context already has a deadline don't override it + _, hasDeadline := ctx.Deadline() + if d := client.PollingDuration; !hasDeadline && d != 0 { var cancel context.CancelFunc cancelCtx, cancel = context.WithTimeout(ctx, d) defer cancel() @@ -824,8 +830,6 @@ func (pt *pollingTrackerPut) updatePollingMethod() error { pt.URI = lh pt.Pm = PollingLocation } - // when both headers are returned we use the value in the Location header for the final GET - pt.FinalGetURI = lh } // make sure a polling URL was found if pt.URI == "" { diff --git a/vendor/github.com/Azure/go-autorest/autorest/azure/cli/token.go b/vendor/github.com/Azure/go-autorest/autorest/azure/cli/token.go index dece9ec631..810075ba61 100644 --- a/vendor/github.com/Azure/go-autorest/autorest/azure/cli/token.go +++ b/vendor/github.com/Azure/go-autorest/autorest/azure/cli/token.go @@ -126,7 +126,7 @@ func GetTokenFromCLI(resource string) (*Token, error) { azureCLIDefaultPathWindows := fmt.Sprintf("%s\\Microsoft SDKs\\Azure\\CLI2\\wbin; %s\\Microsoft SDKs\\Azure\\CLI2\\wbin", os.Getenv("ProgramFiles(x86)"), os.Getenv("ProgramFiles")) // Default path for non-Windows. - const azureCLIDefaultPath = "/usr/bin:/usr/local/bin" + const azureCLIDefaultPath = "/bin:/sbin:/usr/bin:/usr/local/bin" // Validate resource, since it gets sent as a command line argument to Azure CLI const invalidResourceErrorTemplate = "Resource %s is not in expected format. Only alphanumeric characters, [dot], [colon], [hyphen], and [forward slash] are allowed." @@ -144,13 +144,13 @@ func GetTokenFromCLI(resource string) (*Token, error) { cliCmd = exec.Command(fmt.Sprintf("%s\\system32\\cmd.exe", os.Getenv("windir"))) cliCmd.Env = os.Environ() cliCmd.Env = append(cliCmd.Env, fmt.Sprintf("PATH=%s;%s", os.Getenv(azureCLIPath), azureCLIDefaultPathWindows)) - cliCmd.Args = append(cliCmd.Args, "/c") + cliCmd.Args = append(cliCmd.Args, "/c", "az") } else { - cliCmd = exec.Command(os.Getenv("SHELL")) + cliCmd = exec.Command("az") cliCmd.Env = os.Environ() cliCmd.Env = append(cliCmd.Env, fmt.Sprintf("PATH=%s:%s", os.Getenv(azureCLIPath), azureCLIDefaultPath)) } - cliCmd.Args = append(cliCmd.Args, "az", "account", "get-access-token", "-o", "json", "--resource", resource) + cliCmd.Args = append(cliCmd.Args, "account", "get-access-token", "-o", "json", "--resource", resource) var stderr bytes.Buffer cliCmd.Stderr = &stderr diff --git a/vendor/github.com/Azure/go-autorest/autorest/azure/environments.go b/vendor/github.com/Azure/go-autorest/autorest/azure/environments.go index 7e41f7fd99..85d3202afe 100644 --- a/vendor/github.com/Azure/go-autorest/autorest/azure/environments.go +++ b/vendor/github.com/Azure/go-autorest/autorest/azure/environments.go @@ -54,6 +54,7 @@ type Environment struct { ServiceManagementVMDNSSuffix string `json:"serviceManagementVMDNSSuffix"` ResourceManagerVMDNSSuffix string `json:"resourceManagerVMDNSSuffix"` ContainerRegistryDNSSuffix string `json:"containerRegistryDNSSuffix"` + CosmosDBDNSSuffix string `json:"cosmosDBDNSSuffix"` TokenAudience string `json:"tokenAudience"` } @@ -79,6 +80,7 @@ var ( ServiceManagementVMDNSSuffix: "cloudapp.net", ResourceManagerVMDNSSuffix: "cloudapp.azure.com", ContainerRegistryDNSSuffix: "azurecr.io", + CosmosDBDNSSuffix: "documents.azure.com", TokenAudience: "https://management.azure.com/", } @@ -102,7 +104,8 @@ var ( ServiceBusEndpointSuffix: "servicebus.usgovcloudapi.net", ServiceManagementVMDNSSuffix: "usgovcloudapp.net", ResourceManagerVMDNSSuffix: "cloudapp.windowsazure.us", - ContainerRegistryDNSSuffix: "azurecr.io", + ContainerRegistryDNSSuffix: "azurecr.us", + CosmosDBDNSSuffix: "documents.azure.us", TokenAudience: "https://management.usgovcloudapi.net/", } @@ -126,7 +129,8 @@ var ( ServiceBusEndpointSuffix: "servicebus.chinacloudapi.cn", ServiceManagementVMDNSSuffix: "chinacloudapp.cn", ResourceManagerVMDNSSuffix: "cloudapp.azure.cn", - ContainerRegistryDNSSuffix: "azurecr.io", + ContainerRegistryDNSSuffix: "azurecr.cn", + CosmosDBDNSSuffix: "documents.azure.cn", TokenAudience: "https://management.chinacloudapi.cn/", } @@ -150,8 +154,9 @@ var ( ServiceBusEndpointSuffix: "servicebus.cloudapi.de", ServiceManagementVMDNSSuffix: "azurecloudapp.de", ResourceManagerVMDNSSuffix: "cloudapp.microsoftazure.de", - ContainerRegistryDNSSuffix: "azurecr.io", - TokenAudience: "https://management.microsoftazure.de/", + // ContainerRegistryDNSSuffix: "", ACR not present yet in the German Cloud + CosmosDBDNSSuffix: "documents.microsoftazure.de", + TokenAudience: "https://management.microsoftazure.de/", } ) diff --git a/vendor/github.com/Azure/go-autorest/autorest/client.go b/vendor/github.com/Azure/go-autorest/autorest/client.go index 4874e6e82d..3496415b27 100644 --- a/vendor/github.com/Azure/go-autorest/autorest/client.go +++ b/vendor/github.com/Azure/go-autorest/autorest/client.go @@ -16,6 +16,7 @@ package autorest import ( "bytes" + "crypto/tls" "fmt" "io" "io/ioutil" @@ -230,6 +231,11 @@ func (c Client) Do(r *http.Request) (*http.Response, error) { func (c Client) sender() Sender { if c.Sender == nil { j, _ := cookiejar.New(nil) + tracing.Transport.Base = &http.Transport{ + TLSClientConfig: &tls.Config{ + MinVersion: tls.VersionTLS12, + }, + } client := &http.Client{Jar: j, Transport: tracing.Transport} return client } diff --git a/vendor/github.com/Azure/go-autorest/autorest/version.go b/vendor/github.com/Azure/go-autorest/autorest/version.go index 32c4e00cf9..9ecc348708 100644 --- a/vendor/github.com/Azure/go-autorest/autorest/version.go +++ b/vendor/github.com/Azure/go-autorest/autorest/version.go @@ -19,7 +19,7 @@ import ( "runtime" ) -const number = "v11.2.8" +const number = "v11.7.0" var ( userAgent = fmt.Sprintf("Go/%s (%s-%s) go-autorest/%s", diff --git a/vendor/github.com/google/uuid/README.md b/vendor/github.com/google/uuid/README.md index 21205eaeb5..9d92c11f16 100644 --- a/vendor/github.com/google/uuid/README.md +++ b/vendor/github.com/google/uuid/README.md @@ -1,7 +1,3 @@ -**This package is currently in development and the API may not be stable.** - -The API will become stable with v1. - # uuid ![build status](https://travis-ci.org/google/uuid.svg?branch=master) The uuid package generates and inspects UUIDs based on [RFC 4122](http://tools.ietf.org/html/rfc4122) diff --git a/vendor/github.com/google/uuid/go.mod b/vendor/github.com/google/uuid/go.mod new file mode 100644 index 0000000000..fc84cd79d4 --- /dev/null +++ b/vendor/github.com/google/uuid/go.mod @@ -0,0 +1 @@ +module github.com/google/uuid diff --git a/vendor/github.com/google/uuid/hash.go b/vendor/github.com/google/uuid/hash.go index 4fc5a77df5..b174616315 100644 --- a/vendor/github.com/google/uuid/hash.go +++ b/vendor/github.com/google/uuid/hash.go @@ -27,7 +27,7 @@ var ( func NewHash(h hash.Hash, space UUID, data []byte, version int) UUID { h.Reset() h.Write(space[:]) - h.Write([]byte(data)) + h.Write(data) s := h.Sum(nil) var uuid UUID copy(uuid[:], s) diff --git a/vendor/github.com/google/uuid/marshal.go b/vendor/github.com/google/uuid/marshal.go index 84bbc5880b..7f9e0c6c0e 100644 --- a/vendor/github.com/google/uuid/marshal.go +++ b/vendor/github.com/google/uuid/marshal.go @@ -15,8 +15,6 @@ func (uuid UUID) MarshalText() ([]byte, error) { // UnmarshalText implements encoding.TextUnmarshaler. func (uuid *UUID) UnmarshalText(data []byte) error { - // See comment in ParseBytes why we do this. - // id, err := ParseBytes(data) id, err := ParseBytes(data) if err == nil { *uuid = id diff --git a/vendor/github.com/google/uuid/node.go b/vendor/github.com/google/uuid/node.go index 5f0156a2e6..d651a2b061 100644 --- a/vendor/github.com/google/uuid/node.go +++ b/vendor/github.com/google/uuid/node.go @@ -5,16 +5,14 @@ package uuid import ( - "net" "sync" ) var ( - nodeMu sync.Mutex - interfaces []net.Interface // cached list of interfaces - ifname string // name of interface being used - nodeID [6]byte // hardware for version 1 UUIDs - zeroID [6]byte // nodeID with only 0's + nodeMu sync.Mutex + ifname string // name of interface being used + nodeID [6]byte // hardware for version 1 UUIDs + zeroID [6]byte // nodeID with only 0's ) // NodeInterface returns the name of the interface from which the NodeID was @@ -39,26 +37,18 @@ func SetNodeInterface(name string) bool { } func setNodeInterface(name string) bool { - if interfaces == nil { - var err error - interfaces, err = net.Interfaces() - if err != nil && name != "" { - return false - } - } - - for _, ifs := range interfaces { - if len(ifs.HardwareAddr) >= 6 && (name == "" || name == ifs.Name) { - copy(nodeID[:], ifs.HardwareAddr) - ifname = ifs.Name - return true - } + iname, addr := getHardwareInterface(name) // null implementation for js + if iname != "" && addr != nil { + ifname = iname + copy(nodeID[:], addr) + return true } // We found no interfaces with a valid hardware address. If name // does not specify a specific interface generate a random Node ID // (section 4.1.6) if name == "" { + ifname = "random" randomBits(nodeID[:]) return true } @@ -94,9 +84,6 @@ func SetNodeID(id []byte) bool { // NodeID returns the 6 byte node id encoded in uuid. It returns nil if uuid is // not valid. The NodeID is only well defined for version 1 and 2 UUIDs. func (uuid UUID) NodeID() []byte { - if len(uuid) != 16 { - return nil - } var node [6]byte copy(node[:], uuid[10:]) return node[:] diff --git a/vendor/github.com/google/uuid/node_js.go b/vendor/github.com/google/uuid/node_js.go new file mode 100644 index 0000000000..24b78edc90 --- /dev/null +++ b/vendor/github.com/google/uuid/node_js.go @@ -0,0 +1,12 @@ +// Copyright 2017 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build js + +package uuid + +// getHardwareInterface returns nil values for the JS version of the code. +// This remvoves the "net" dependency, because it is not used in the browser. +// Using the "net" library inflates the size of the transpiled JS code by 673k bytes. +func getHardwareInterface(name string) (string, []byte) { return "", nil } diff --git a/vendor/github.com/google/uuid/node_net.go b/vendor/github.com/google/uuid/node_net.go new file mode 100644 index 0000000000..0cbbcddbd6 --- /dev/null +++ b/vendor/github.com/google/uuid/node_net.go @@ -0,0 +1,33 @@ +// Copyright 2017 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !js + +package uuid + +import "net" + +var interfaces []net.Interface // cached list of interfaces + +// getHardwareInterface returns the name and hardware address of interface name. +// If name is "" then the name and hardware address of one of the system's +// interfaces is returned. If no interfaces are found (name does not exist or +// there are no interfaces) then "", nil is returned. +// +// Only addresses of at least 6 bytes are returned. +func getHardwareInterface(name string) (string, []byte) { + if interfaces == nil { + var err error + interfaces, err = net.Interfaces() + if err != nil { + return "", nil + } + } + for _, ifs := range interfaces { + if len(ifs.HardwareAddr) >= 6 && (name == "" || name == ifs.Name) { + return ifs.Name, ifs.HardwareAddr + } + } + return "", nil +} diff --git a/vendor/github.com/google/uuid/time.go b/vendor/github.com/google/uuid/time.go index fd7fe0ac46..e6ef06cdc8 100644 --- a/vendor/github.com/google/uuid/time.go +++ b/vendor/github.com/google/uuid/time.go @@ -86,7 +86,7 @@ func clockSequence() int { return int(clockSeq & 0x3fff) } -// SetClockSeq sets the clock sequence to the lower 14 bits of seq. Setting to +// SetClockSequence sets the clock sequence to the lower 14 bits of seq. Setting to // -1 causes a new sequence to be generated. func SetClockSequence(seq int) { defer timeMu.Unlock() @@ -100,9 +100,9 @@ func setClockSequence(seq int) { randomBits(b[:]) // clock sequence seq = int(b[0])<<8 | int(b[1]) } - old_seq := clockSeq + oldSeq := clockSeq clockSeq = uint16(seq&0x3fff) | 0x8000 // Set our variant - if old_seq != clockSeq { + if oldSeq != clockSeq { lasttime = 0 } } diff --git a/vendor/github.com/google/uuid/uuid.go b/vendor/github.com/google/uuid/uuid.go index 23161a86c0..524404cc52 100644 --- a/vendor/github.com/google/uuid/uuid.go +++ b/vendor/github.com/google/uuid/uuid.go @@ -1,4 +1,4 @@ -// Copyright 2016 Google Inc. All rights reserved. +// Copyright 2018 Google Inc. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. @@ -35,20 +35,43 @@ const ( var rander = rand.Reader // random function -// Parse decodes s into a UUID or returns an error. Both the UUID form of -// xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx and -// urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx are decoded. +// Parse decodes s into a UUID or returns an error. Both the standard UUID +// forms of xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx and +// urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx are decoded as well as the +// Microsoft encoding {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx} and the raw hex +// encoding: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx. func Parse(s string) (UUID, error) { var uuid UUID - if len(s) != 36 { - if len(s) != 36+9 { - return uuid, fmt.Errorf("invalid UUID length: %d", len(s)) - } + switch len(s) { + // xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx + case 36: + + // urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx + case 36 + 9: if strings.ToLower(s[:9]) != "urn:uuid:" { return uuid, fmt.Errorf("invalid urn prefix: %q", s[:9]) } s = s[9:] + + // {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx} + case 36 + 2: + s = s[1:] + + // xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + case 32: + var ok bool + for i := range uuid { + uuid[i], ok = xtob(s[i*2], s[i*2+1]) + if !ok { + return uuid, errors.New("invalid UUID format") + } + } + return uuid, nil + default: + return uuid, fmt.Errorf("invalid UUID length: %d", len(s)) } + // s is now at least 36 bytes long + // it must be of the form xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx if s[8] != '-' || s[13] != '-' || s[18] != '-' || s[23] != '-' { return uuid, errors.New("invalid UUID format") } @@ -58,11 +81,11 @@ func Parse(s string) (UUID, error) { 14, 16, 19, 21, 24, 26, 28, 30, 32, 34} { - if v, ok := xtob(s[x], s[x+1]); !ok { + v, ok := xtob(s[x], s[x+1]) + if !ok { return uuid, errors.New("invalid UUID format") - } else { - uuid[i] = v } + uuid[i] = v } return uuid, nil } @@ -70,15 +93,29 @@ func Parse(s string) (UUID, error) { // ParseBytes is like Parse, except it parses a byte slice instead of a string. func ParseBytes(b []byte) (UUID, error) { var uuid UUID - if len(b) != 36 { - if len(b) != 36+9 { - return uuid, fmt.Errorf("invalid UUID length: %d", len(b)) - } + switch len(b) { + case 36: // xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx + case 36 + 9: // urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx if !bytes.Equal(bytes.ToLower(b[:9]), []byte("urn:uuid:")) { return uuid, fmt.Errorf("invalid urn prefix: %q", b[:9]) } b = b[9:] + case 36 + 2: // {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx} + b = b[1:] + case 32: // xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + var ok bool + for i := 0; i < 32; i += 2 { + uuid[i/2], ok = xtob(b[i], b[i+1]) + if !ok { + return uuid, errors.New("invalid UUID format") + } + } + return uuid, nil + default: + return uuid, fmt.Errorf("invalid UUID length: %d", len(b)) } + // s is now at least 36 bytes long + // it must be of the form xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx if b[8] != '-' || b[13] != '-' || b[18] != '-' || b[23] != '-' { return uuid, errors.New("invalid UUID format") } @@ -88,15 +125,32 @@ func ParseBytes(b []byte) (UUID, error) { 14, 16, 19, 21, 24, 26, 28, 30, 32, 34} { - if v, ok := xtob(b[x], b[x+1]); !ok { + v, ok := xtob(b[x], b[x+1]) + if !ok { return uuid, errors.New("invalid UUID format") - } else { - uuid[i] = v } + uuid[i] = v } return uuid, nil } +// MustParse is like Parse but panics if the string cannot be parsed. +// It simplifies safe initialization of global variables holding compiled UUIDs. +func MustParse(s string) UUID { + uuid, err := Parse(s) + if err != nil { + panic(`uuid: Parse(` + s + `): ` + err.Error()) + } + return uuid +} + +// FromBytes creates a new UUID from a byte slice. Returns an error if the slice +// does not have a length of 16. The bytes are copied from the slice. +func FromBytes(b []byte) (uuid UUID, err error) { + err = uuid.UnmarshalBinary(b) + return uuid, err +} + // Must returns uuid if err is nil and panics otherwise. func Must(uuid UUID, err error) UUID { if err != nil { @@ -123,7 +177,7 @@ func (uuid UUID) URN() string { } func encodeHex(dst []byte, uuid UUID) { - hex.Encode(dst[:], uuid[:4]) + hex.Encode(dst, uuid[:4]) dst[8] = '-' hex.Encode(dst[9:13], uuid[4:6]) dst[13] = '-' diff --git a/vendor/github.com/google/uuid/version4.go b/vendor/github.com/google/uuid/version4.go index 74c4e6c9f5..84af91c9f5 100644 --- a/vendor/github.com/google/uuid/version4.go +++ b/vendor/github.com/google/uuid/version4.go @@ -14,7 +14,7 @@ func New() UUID { return Must(NewRandom()) } -// NewRandom returns a Random (Version 4) UUID or panics. +// NewRandom returns a Random (Version 4) UUID. // // The strength of the UUIDs is based on the strength of the crypto/rand // package. diff --git a/vendor/github.com/hashicorp/go-azure-helpers/authentication/auth_method.go b/vendor/github.com/hashicorp/go-azure-helpers/authentication/auth_method.go index d599b2eeec..cbc73d562e 100644 --- a/vendor/github.com/hashicorp/go-azure-helpers/authentication/auth_method.go +++ b/vendor/github.com/hashicorp/go-azure-helpers/authentication/auth_method.go @@ -10,7 +10,7 @@ type authMethod interface { isApplicable(b Builder) bool - getAuthorizationToken(oauthConfig *adal.OAuthConfig, endpoint string) (*autorest.BearerAuthorizer, error) + getAuthorizationToken(sender autorest.Sender, oauthConfig *adal.OAuthConfig, endpoint string) (*autorest.BearerAuthorizer, error) name() string diff --git a/vendor/github.com/hashicorp/go-azure-helpers/authentication/auth_method_azure_cli_token.go b/vendor/github.com/hashicorp/go-azure-helpers/authentication/auth_method_azure_cli_token.go index 8f0927527a..6f854d5ea0 100644 --- a/vendor/github.com/hashicorp/go-azure-helpers/authentication/auth_method_azure_cli_token.go +++ b/vendor/github.com/hashicorp/go-azure-helpers/authentication/auth_method_azure_cli_token.go @@ -55,7 +55,7 @@ func (a azureCliTokenAuth) isApplicable(b Builder) bool { return b.SupportsAzureCliToken } -func (a azureCliTokenAuth) getAuthorizationToken(oauthConfig *adal.OAuthConfig, endpoint string) (*autorest.BearerAuthorizer, error) { +func (a azureCliTokenAuth) getAuthorizationToken(sender autorest.Sender, oauthConfig *adal.OAuthConfig, endpoint string) (*autorest.BearerAuthorizer, error) { // the Azure CLI appears to cache these, so to maintain compatibility with the interface this method is intentionally not on the pointer token, err := obtainAuthorizationToken(endpoint, a.profile.subscriptionId) if err != nil { diff --git a/vendor/github.com/hashicorp/go-azure-helpers/authentication/auth_method_client_cert.go b/vendor/github.com/hashicorp/go-azure-helpers/authentication/auth_method_client_cert.go index 00a0d27944..f6dd9b77e6 100644 --- a/vendor/github.com/hashicorp/go-azure-helpers/authentication/auth_method_client_cert.go +++ b/vendor/github.com/hashicorp/go-azure-helpers/authentication/auth_method_client_cert.go @@ -41,7 +41,7 @@ func (a servicePrincipalClientCertificateAuth) name() string { return "Service Principal / Client Certificate" } -func (a servicePrincipalClientCertificateAuth) getAuthorizationToken(oauthConfig *adal.OAuthConfig, endpoint string) (*autorest.BearerAuthorizer, error) { +func (a servicePrincipalClientCertificateAuth) getAuthorizationToken(sender autorest.Sender, oauthConfig *adal.OAuthConfig, endpoint string) (*autorest.BearerAuthorizer, error) { certificateData, err := ioutil.ReadFile(a.clientCertPath) if err != nil { return nil, fmt.Errorf("Error reading Client Certificate %q: %v", a.clientCertPath, err) @@ -58,6 +58,8 @@ func (a servicePrincipalClientCertificateAuth) getAuthorizationToken(oauthConfig return nil, err } + spt.SetSender(sender) + err = spt.Refresh() if err != nil { return nil, err diff --git a/vendor/github.com/hashicorp/go-azure-helpers/authentication/auth_method_client_secret.go b/vendor/github.com/hashicorp/go-azure-helpers/authentication/auth_method_client_secret.go index 4e41d5a935..f31bc20e8d 100644 --- a/vendor/github.com/hashicorp/go-azure-helpers/authentication/auth_method_client_secret.go +++ b/vendor/github.com/hashicorp/go-azure-helpers/authentication/auth_method_client_secret.go @@ -33,11 +33,12 @@ func (a servicePrincipalClientSecretAuth) name() string { return "Service Principal / Client Secret" } -func (a servicePrincipalClientSecretAuth) getAuthorizationToken(oauthConfig *adal.OAuthConfig, endpoint string) (*autorest.BearerAuthorizer, error) { +func (a servicePrincipalClientSecretAuth) getAuthorizationToken(sender autorest.Sender, oauthConfig *adal.OAuthConfig, endpoint string) (*autorest.BearerAuthorizer, error) { spt, err := adal.NewServicePrincipalToken(*oauthConfig, a.clientId, a.clientSecret, endpoint) if err != nil { return nil, err } + spt.SetSender(sender) auth := autorest.NewBearerAuthorizer(spt) return auth, nil diff --git a/vendor/github.com/hashicorp/go-azure-helpers/authentication/auth_method_msi.go b/vendor/github.com/hashicorp/go-azure-helpers/authentication/auth_method_msi.go index 9b0de8f5d4..883df2607b 100644 --- a/vendor/github.com/hashicorp/go-azure-helpers/authentication/auth_method_msi.go +++ b/vendor/github.com/hashicorp/go-azure-helpers/authentication/auth_method_msi.go @@ -39,11 +39,14 @@ func (a managedServiceIdentityAuth) name() string { return "Managed Service Identity" } -func (a managedServiceIdentityAuth) getAuthorizationToken(oauthConfig *adal.OAuthConfig, endpoint string) (*autorest.BearerAuthorizer, error) { +func (a managedServiceIdentityAuth) getAuthorizationToken(sender autorest.Sender, oauthConfig *adal.OAuthConfig, endpoint string) (*autorest.BearerAuthorizer, error) { spt, err := adal.NewServicePrincipalTokenFromMSI(a.endpoint, endpoint) if err != nil { return nil, err } + + spt.SetSender(sender) + auth := autorest.NewBearerAuthorizer(spt) return auth, nil } diff --git a/vendor/github.com/hashicorp/go-azure-helpers/authentication/config.go b/vendor/github.com/hashicorp/go-azure-helpers/authentication/config.go index c3068152f8..90b78fce02 100644 --- a/vendor/github.com/hashicorp/go-azure-helpers/authentication/config.go +++ b/vendor/github.com/hashicorp/go-azure-helpers/authentication/config.go @@ -22,8 +22,8 @@ type Config struct { } // GetAuthorizationToken returns an authorization token for the authentication method defined in the Config -func (c Config) GetAuthorizationToken(oauthConfig *adal.OAuthConfig, endpoint string) (*autorest.BearerAuthorizer, error) { - return c.authMethod.getAuthorizationToken(oauthConfig, endpoint) +func (c Config) GetAuthorizationToken(sender autorest.Sender, oauthConfig *adal.OAuthConfig, endpoint string) (*autorest.BearerAuthorizer, error) { + return c.authMethod.getAuthorizationToken(sender, oauthConfig, endpoint) } func (c Config) validate() (*Config, error) { diff --git a/vendor/modules.txt b/vendor/modules.txt index 27b6c0d0b4..faa8d00953 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -8,10 +8,10 @@ cloud.google.com/go/internal/version cloud.google.com/go/compute/metadata # contrib.go.opencensus.io/exporter/ocagent v0.4.2 contrib.go.opencensus.io/exporter/ocagent -# github.com/Azure/azure-sdk-for-go v24.1.0+incompatible +# github.com/Azure/azure-sdk-for-go v29.0.0+incompatible github.com/Azure/azure-sdk-for-go/services/graphrbac/1.6/graphrbac github.com/Azure/azure-sdk-for-go/version -# github.com/Azure/go-autorest v11.2.8+incompatible +# github.com/Azure/go-autorest v11.7.0+incompatible github.com/Azure/go-autorest/autorest github.com/Azure/go-autorest/autorest/adal github.com/Azure/go-autorest/autorest/azure @@ -100,7 +100,7 @@ github.com/google/go-cmp/cmp github.com/google/go-cmp/cmp/internal/diff github.com/google/go-cmp/cmp/internal/function github.com/google/go-cmp/cmp/internal/value -# github.com/google/uuid v0.0.0-20170814143639-7e072fc3a7be +# github.com/google/uuid v1.1.1 github.com/google/uuid # github.com/googleapis/gax-go/v2 v2.0.3 github.com/googleapis/gax-go/v2 @@ -110,7 +110,7 @@ github.com/grpc-ecosystem/grpc-gateway/utilities github.com/grpc-ecosystem/grpc-gateway/runtime/internal # github.com/hashicorp/errwrap v1.0.0 github.com/hashicorp/errwrap -# github.com/hashicorp/go-azure-helpers v0.0.0-20190129193224-166dfd221bb2 +# github.com/hashicorp/go-azure-helpers v0.4.1 github.com/hashicorp/go-azure-helpers/authentication github.com/hashicorp/go-azure-helpers/response github.com/hashicorp/go-azure-helpers/sender From 1f58b34c39e422fa72c1ff76ab475df1807926d5 Mon Sep 17 00:00:00 2001 From: kt Date: Mon, 10 Jun 2019 10:55:01 -0700 Subject: [PATCH 19/45] tune goimports linting (#105) --- .golangci.yml | 2 ++ GNUmakefile | 4 ++-- azuread/config.go | 3 ++- azuread/data_group.go | 1 + azuread/data_user.go | 1 + azuread/helpers/ar/response_test.go | 2 +- azuread/helpers/graph/credentials.go | 1 + azuread/helpers/graph/replication.go | 1 + azuread/resource_application.go | 1 + azuread/resource_application_password.go | 1 + azuread/resource_application_password_test.go | 2 +- azuread/resource_application_test.go | 1 + azuread/resource_group.go | 3 ++- azuread/resource_group_test.go | 1 + azuread/resource_service_principal.go | 16 +++++++--------- azuread/resource_service_principal_password.go | 1 + azuread/resource_user.go | 1 + azuread/resource_user_test.go | 1 + examples/service_principal/basic/main.tf | 2 +- .../service_principal/create-for-rbac/main.tf | 2 +- ...service_principal_configuration.html.markdown | 2 +- 21 files changed, 31 insertions(+), 18 deletions(-) diff --git a/.golangci.yml b/.golangci.yml index c468a41e30..abe4f0dab2 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -28,6 +28,8 @@ linters: linters-settings: errcheck: ignore: github.com/hashicorp/terraform/helper/schema:ForceNew|Set,fmt:.*,io:Close + misspell: + locale: UK run: modules-download-mode: vendor \ No newline at end of file diff --git a/GNUmakefile b/GNUmakefile index f54cec9e13..4082308ab4 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -31,9 +31,9 @@ fmt: fmtcheck: @sh "$(CURDIR)/scripts/gofmtcheck.sh" -goimport: +goimports: @echo "==> Fixing imports code with goimports..." - goimports -w $(PKG_NAME)/ + goimports -local "github.com/terraform-providers/terraform-provider-azuread/azuread" -w $(PKG_NAME)/ lint: @echo "==> Checking source code against linters..." diff --git a/azuread/config.go b/azuread/config.go index 014ced5dca..6a7a30d01c 100644 --- a/azuread/config.go +++ b/azuread/config.go @@ -15,8 +15,9 @@ import ( "github.com/hashicorp/go-azure-helpers/authentication" "github.com/hashicorp/go-azure-helpers/sender" "github.com/hashicorp/terraform/httpclient" - "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/ar" "github.com/terraform-providers/terraform-provider-azuread/version" + + "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/ar" ) // ArmClient contains the handles to all the specific Azure ADger resource classes' respective clients. diff --git a/azuread/data_group.go b/azuread/data_group.go index 11ad008f2c..af2557ab88 100644 --- a/azuread/data_group.go +++ b/azuread/data_group.go @@ -5,6 +5,7 @@ import ( "github.com/Azure/azure-sdk-for-go/services/graphrbac/1.6/graphrbac" "github.com/hashicorp/terraform/helper/schema" + "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/ar" "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/validate" ) diff --git a/azuread/data_user.go b/azuread/data_user.go index afa1f37f29..d34c0dfcca 100644 --- a/azuread/data_user.go +++ b/azuread/data_user.go @@ -5,6 +5,7 @@ import ( "github.com/Azure/azure-sdk-for-go/services/graphrbac/1.6/graphrbac" "github.com/hashicorp/terraform/helper/schema" + "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/ar" "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/validate" ) diff --git a/azuread/helpers/ar/response_test.go b/azuread/helpers/ar/response_test.go index bb2e095f03..f0c9228222 100644 --- a/azuread/helpers/ar/response_test.go +++ b/azuread/helpers/ar/response_test.go @@ -44,7 +44,7 @@ type testNetError struct { temporary bool } -// testNetError fulfills net.Error interface +// testNetError fulfils net.Error interface func (e testNetError) Error() string { return "testError" } func (e testNetError) Timeout() bool { return e.timeout } func (e testNetError) Temporary() bool { return e.temporary } diff --git a/azuread/helpers/graph/credentials.go b/azuread/helpers/graph/credentials.go index 032d086c7a..2b47835c40 100644 --- a/azuread/helpers/graph/credentials.go +++ b/azuread/helpers/graph/credentials.go @@ -10,6 +10,7 @@ import ( "github.com/hashicorp/go-uuid" "github.com/hashicorp/terraform/helper/schema" "github.com/hashicorp/terraform/helper/validation" + "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/p" "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/validate" ) diff --git a/azuread/helpers/graph/replication.go b/azuread/helpers/graph/replication.go index 20ee76f8b5..fb29e8e170 100644 --- a/azuread/helpers/graph/replication.go +++ b/azuread/helpers/graph/replication.go @@ -6,6 +6,7 @@ import ( "github.com/Azure/go-autorest/autorest" "github.com/hashicorp/terraform/helper/resource" + "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/ar" ) diff --git a/azuread/resource_application.go b/azuread/resource_application.go index 4f2e45c13c..d0fd8c3d2a 100644 --- a/azuread/resource_application.go +++ b/azuread/resource_application.go @@ -8,6 +8,7 @@ import ( "github.com/hashicorp/terraform/helper/schema" "github.com/hashicorp/terraform/helper/validation" + "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/ar" "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/graph" "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/p" diff --git a/azuread/resource_application_password.go b/azuread/resource_application_password.go index 7b3ca146b5..3bf0d9338f 100644 --- a/azuread/resource_application_password.go +++ b/azuread/resource_application_password.go @@ -7,6 +7,7 @@ import ( "github.com/Azure/azure-sdk-for-go/services/graphrbac/1.6/graphrbac" "github.com/hashicorp/terraform/helper/schema" + "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/ar" "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/graph" "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/tf" diff --git a/azuread/resource_application_password_test.go b/azuread/resource_application_password_test.go index 405e575bad..e034681532 100644 --- a/azuread/resource_application_password_test.go +++ b/azuread/resource_application_password_test.go @@ -44,7 +44,7 @@ func testCheckADApplicationPasswordExists(name string) resource.TestCheckFunc { return nil } - return fmt.Errorf("Password Credential %q was not found in Aplication %q", id.KeyId, id.ObjectId) + return fmt.Errorf("Password Credential %q was not found in Application %q", id.KeyId, id.ObjectId) } } diff --git a/azuread/resource_application_test.go b/azuread/resource_application_test.go index 8a992164e4..6edf137309 100644 --- a/azuread/resource_application_test.go +++ b/azuread/resource_application_test.go @@ -8,6 +8,7 @@ import ( "github.com/google/uuid" "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" + "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/ar" ) diff --git a/azuread/resource_group.go b/azuread/resource_group.go index 66ff3d6794..82c686d3fb 100644 --- a/azuread/resource_group.go +++ b/azuread/resource_group.go @@ -8,6 +8,7 @@ import ( "github.com/google/uuid" "github.com/hashicorp/terraform/helper/schema" "github.com/hashicorp/terraform/helper/validation" + "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/ar" "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/graph" "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/p" @@ -48,7 +49,7 @@ func resourceGroupCreate(d *schema.ResourceData, meta interface{}) error { 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. - MailNickname: p.String(uuid.New().String()), // this matches the portal behavior + MailNickname: p.String(uuid.New().String()), // this matches the portal behaviour SecurityEnabled: p.Bool(true), // we're defaulting to true, as the API currently only supports the creation of non-mail enabled security groups. } diff --git a/azuread/resource_group_test.go b/azuread/resource_group_test.go index e210c2e666..75e923e514 100644 --- a/azuread/resource_group_test.go +++ b/azuread/resource_group_test.go @@ -7,6 +7,7 @@ import ( "github.com/hashicorp/go-uuid" "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" + "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/ar" ) diff --git a/azuread/resource_service_principal.go b/azuread/resource_service_principal.go index 35cbaf3f36..b3cd008fb8 100644 --- a/azuread/resource_service_principal.go +++ b/azuread/resource_service_principal.go @@ -4,17 +4,15 @@ import ( "fmt" "log" - "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/graph" - "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/tf" - - "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/validate" - + "github.com/Azure/azure-sdk-for-go/services/graphrbac/1.6/graphrbac" "github.com/hashicorp/go-azure-helpers/response" + "github.com/hashicorp/terraform/helper/schema" + "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/ar" + "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/graph" "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/p" - - "github.com/Azure/azure-sdk-for-go/services/graphrbac/1.6/graphrbac" - "github.com/hashicorp/terraform/helper/schema" + "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/tf" + "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/validate" ) const servicePrincipalResourceName = "azuread_service_principal" @@ -88,7 +86,7 @@ func resourceServicePrincipalCreate(d *schema.ResourceData, meta interface{}) er return client.Get(ctx, *sp.ObjectID) }) if err != nil { - return fmt.Errorf("Error waiting for Service Pricipal with ObjectId %q: %+v", *sp.ObjectID, err) + return fmt.Errorf("Error waiting for Service Principal with ObjectId %q: %+v", *sp.ObjectID, err) } return resourceServicePrincipalRead(d, meta) diff --git a/azuread/resource_service_principal_password.go b/azuread/resource_service_principal_password.go index b52b8238e2..c94a918d99 100644 --- a/azuread/resource_service_principal_password.go +++ b/azuread/resource_service_principal_password.go @@ -7,6 +7,7 @@ import ( "github.com/Azure/azure-sdk-for-go/services/graphrbac/1.6/graphrbac" "github.com/hashicorp/terraform/helper/schema" + "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/ar" "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/graph" "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/tf" diff --git a/azuread/resource_user.go b/azuread/resource_user.go index d38b2ade9f..d7d60a01fa 100644 --- a/azuread/resource_user.go +++ b/azuread/resource_user.go @@ -8,6 +8,7 @@ import ( "github.com/Azure/azure-sdk-for-go/services/graphrbac/1.6/graphrbac" "github.com/hashicorp/terraform/helper/schema" "github.com/hashicorp/terraform/helper/validation" + "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/ar" "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/graph" "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/p" diff --git a/azuread/resource_user_test.go b/azuread/resource_user_test.go index f26ec2ff0a..6f520a2a29 100644 --- a/azuread/resource_user_test.go +++ b/azuread/resource_user_test.go @@ -8,6 +8,7 @@ import ( "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" + "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/ar" ) diff --git a/examples/service_principal/basic/main.tf b/examples/service_principal/basic/main.tf index 17037e0c8a..10468a7b3b 100644 --- a/examples/service_principal/basic/main.tf +++ b/examples/service_principal/basic/main.tf @@ -1,5 +1,5 @@ ## This example creates a basic application and Service Principal using the Azure Active Directory resources. -# WARNING: the service pricipal password will be presisted to state +# WARNING: the service Principal password will be presisted to state #create a random identifier for the application name resource "random_id" "app_name" { diff --git a/examples/service_principal/create-for-rbac/main.tf b/examples/service_principal/create-for-rbac/main.tf index 236e19b806..d54425598e 100644 --- a/examples/service_principal/create-for-rbac/main.tf +++ b/examples/service_principal/create-for-rbac/main.tf @@ -1,7 +1,7 @@ ## This example creates a basic application and Service Principal and then assigns a role ## this mimics the behaviour of `az ad sp create-for-rbac --years 2` -# WARNING: the service pricipal password will be presisted to state +# WARNING: the service principal password will be presisted to state data "azurerm_subscription" "main" {} diff --git a/website/docs/auth/service_principal_configuration.html.markdown b/website/docs/auth/service_principal_configuration.html.markdown index 7a0431a229..cc875f3fbf 100644 --- a/website/docs/auth/service_principal_configuration.html.markdown +++ b/website/docs/auth/service_principal_configuration.html.markdown @@ -92,7 +92,7 @@ if ($role -eq $null) { $role = Get-AzureADDirectoryRole | Where-Object {$_.displayName -eq 'Company Administrator'} } -$sp = Get-AzureADServicePrincipal | Where-Object {$_.displayName -eq 'Service Pricipal Name'} +$sp = Get-AzureADServicePrincipal | Where-Object {$_.displayName -eq 'Service Principal Name'} $sp.ObjectId Add-AzureADDirectoryRoleMember -ObjectId $role.ObjectId -RefObjectId $sp.ObjectId From 5a179146b9f9c38e6dc440254f761d5788b846b1 Mon Sep 17 00:00:00 2001 From: kt Date: Mon, 10 Jun 2019 13:23:12 -0700 Subject: [PATCH 20/45] Update CHANGELOG.md --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5488d8c145..c1c65b308f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,5 @@ +## 0.5.0 (Unreleased) + ## 0.4.0 (June 06, 2019) NOTES: From cdc264417f13e91ee0dc58087682b1cb1450b68f Mon Sep 17 00:00:00 2001 From: Joakim Bakke Hellum Date: Tue, 11 Jun 2019 20:06:17 +0200 Subject: [PATCH 21/45] `azuread_service_principal`: export the `oauth2_permissions` property (#103) --- azuread/data_application.go | 52 +--------- azuread/data_service_principal.go | 7 ++ azuread/data_service_principal_test.go | 2 + azuread/helpers/graph/application.go | 96 +++++++++++++++++++ azuread/resource_application.go | 89 +---------------- azuread/resource_service_principal.go | 6 ++ azuread/resource_service_principal_test.go | 2 + .../docs/d/service_principal.html.markdown | 22 +++++ .../docs/r/service_principal.html.markdown | 22 +++++ 9 files changed, 162 insertions(+), 136 deletions(-) create mode 100644 azuread/helpers/graph/application.go diff --git a/azuread/data_application.go b/azuread/data_application.go index 6ef2267e4d..fa31a49e2a 100644 --- a/azuread/data_application.go +++ b/azuread/data_application.go @@ -7,6 +7,7 @@ import ( "github.com/hashicorp/terraform/helper/schema" "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/ar" + "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/graph" "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/tf" "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/validate" ) @@ -109,54 +110,7 @@ func dataApplication() *schema.Resource { }, }, - "oauth2_permissions": { - Type: schema.TypeList, - Optional: true, - Computed: true, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "admin_consent_description": { - Type: schema.TypeString, - Computed: true, - }, - - "admin_consent_display_name": { - Type: schema.TypeString, - Computed: true, - }, - - "id": { - Type: schema.TypeString, - Computed: true, - }, - - "is_enabled": { - Type: schema.TypeBool, - Computed: true, - }, - - "type": { - Type: schema.TypeString, - Computed: true, - }, - - "user_consent_description": { - Type: schema.TypeString, - Computed: true, - }, - - "user_consent_display_name": { - Type: schema.TypeString, - Computed: true, - }, - - "value": { - Type: schema.TypeString, - Computed: true, - }, - }, - }, - }, + "oauth2_permissions": graph.SchemaOauth2Permissions(), }, } } @@ -243,7 +197,7 @@ func dataApplicationRead(d *schema.ResourceData, meta interface{}) error { return fmt.Errorf("Error setting `group_membership_claims`: %+v", err) } - if err := d.Set("oauth2_permissions", flattenADApplicationOauth2Permissions(app.Oauth2Permissions)); err != nil { + if err := d.Set("oauth2_permissions", graph.FlattenOauth2Permissions(app.Oauth2Permissions)); err != nil { return fmt.Errorf("Error setting `oauth2_permissions`: %+v", err) } diff --git a/azuread/data_service_principal.go b/azuread/data_service_principal.go index e9b8afa860..e5ba8de774 100644 --- a/azuread/data_service_principal.go +++ b/azuread/data_service_principal.go @@ -4,6 +4,7 @@ import ( "fmt" "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/ar" + "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/graph" "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/validate" "github.com/Azure/azure-sdk-for-go/services/graphrbac/1.6/graphrbac" @@ -38,6 +39,8 @@ func dataServicePrincipal() *schema.Resource { ValidateFunc: validate.UUID, ConflictsWith: []string{"object_id", "display_name"}, }, + + "oauth2_permissions": graph.SchemaOauth2Permissions(), }, } } @@ -126,5 +129,9 @@ func dataSourceActiveDirectoryServicePrincipalRead(d *schema.ResourceData, meta d.Set("display_name", sp.DisplayName) d.Set("object_id", sp.ObjectID) + if err := d.Set("oauth2_permissions", graph.FlattenOauth2Permissions(sp.Oauth2Permissions)); err != nil { + return fmt.Errorf("Error setting `oauth2_permissions`: %+v", err) + } + return nil } diff --git a/azuread/data_service_principal_test.go b/azuread/data_service_principal_test.go index f0a166d287..bf2a31afd8 100644 --- a/azuread/data_service_principal_test.go +++ b/azuread/data_service_principal_test.go @@ -24,6 +24,8 @@ func TestAccAzureADServicePrincipalDataSource_byApplicationId(t *testing.T) { resource.TestCheckResourceAttrSet(dataSourceName, "application_id"), resource.TestCheckResourceAttrSet(dataSourceName, "object_id"), resource.TestCheckResourceAttrSet(dataSourceName, "display_name"), + resource.TestCheckResourceAttr(dataSourceName, "oauth2_permissions.#", "1"), + resource.TestCheckResourceAttr(dataSourceName, "oauth2_permissions.0.admin_consent_description", fmt.Sprintf("Allow the application to access %s on behalf of the signed-in user.", fmt.Sprintf("acctestspa%s", id))), ), }, }, diff --git a/azuread/helpers/graph/application.go b/azuread/helpers/graph/application.go new file mode 100644 index 0000000000..12f5f2dff5 --- /dev/null +++ b/azuread/helpers/graph/application.go @@ -0,0 +1,96 @@ +package graph + +import ( + "github.com/Azure/azure-sdk-for-go/services/graphrbac/1.6/graphrbac" + "github.com/hashicorp/terraform/helper/schema" +) + +func SchemaOauth2Permissions() *schema.Schema { + return &schema.Schema{ + Type: schema.TypeList, + Optional: true, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "admin_consent_description": { + Type: schema.TypeString, + Computed: true, + }, + + "admin_consent_display_name": { + Type: schema.TypeString, + Computed: true, + }, + + "id": { + Type: schema.TypeString, + Computed: true, + }, + + "is_enabled": { + Type: schema.TypeBool, + Computed: true, + }, + + "type": { + Type: schema.TypeString, + Computed: true, + }, + + "user_consent_description": { + Type: schema.TypeString, + Computed: true, + }, + + "user_consent_display_name": { + Type: schema.TypeString, + Computed: true, + }, + + "value": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + } +} + +func FlattenOauth2Permissions(in *[]graphrbac.OAuth2Permission) []map[string]interface{} { + if in == nil { + return []map[string]interface{}{} + } + + result := make([]map[string]interface{}, 0) + for _, p := range *in { + permission := make(map[string]interface{}) + if v := p.AdminConsentDescription; v != nil { + permission["admin_consent_description"] = v + } + if v := p.AdminConsentDisplayName; v != nil { + permission["admin_consent_display_name"] = v + } + if v := p.ID; v != nil { + permission["id"] = v + } + if v := p.IsEnabled; v != nil { + permission["is_enabled"] = *v + } + if v := p.Type; v != nil { + permission["type"] = v + } + if v := p.UserConsentDescription; v != nil { + permission["user_consent_description"] = v + } + if v := p.UserConsentDisplayName; v != nil { + permission["user_consent_display_name"] = v + } + if v := p.Value; v != nil { + permission["value"] = v + } + + result = append(result, permission) + } + + return result +} diff --git a/azuread/resource_application.go b/azuread/resource_application.go index d0fd8c3d2a..947f30b73f 100644 --- a/azuread/resource_application.go +++ b/azuread/resource_application.go @@ -130,53 +130,7 @@ func resourceApplication() *schema.Resource { }, }, - "oauth2_permissions": { - Type: schema.TypeList, - Computed: true, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "admin_consent_description": { - Type: schema.TypeString, - Computed: true, - }, - - "admin_consent_display_name": { - Type: schema.TypeString, - Computed: true, - }, - - "id": { - Type: schema.TypeString, - Computed: true, - }, - - "is_enabled": { - Type: schema.TypeBool, - Computed: true, - }, - - "type": { - Type: schema.TypeString, - Computed: true, - }, - - "user_consent_description": { - Type: schema.TypeString, - Computed: true, - }, - - "user_consent_display_name": { - Type: schema.TypeString, - Computed: true, - }, - - "value": { - Type: schema.TypeString, - Computed: true, - }, - }, - }, - }, + "oauth2_permissions": graph.SchemaOauth2Permissions(), "object_id": { Type: schema.TypeString, @@ -366,7 +320,7 @@ func resourceApplicationRead(d *schema.ResourceData, meta interface{}) error { return fmt.Errorf("Error setting `required_resource_access`: %+v", err) } - if err := d.Set("oauth2_permissions", flattenADApplicationOauth2Permissions(app.Oauth2Permissions)); err != nil { + if err := d.Set("oauth2_permissions", graph.FlattenOauth2Permissions(app.Oauth2Permissions)); err != nil { return fmt.Errorf("Error setting `oauth2_permissions`: %+v", err) } @@ -478,42 +432,3 @@ func flattenADApplicationResourceAccess(in *[]graphrbac.ResourceAccess) []interf return accesses } - -func flattenADApplicationOauth2Permissions(in *[]graphrbac.OAuth2Permission) []map[string]interface{} { - if in == nil { - return []map[string]interface{}{} - } - - result := make([]map[string]interface{}, 0) - for _, p := range *in { - permission := make(map[string]interface{}) - if v := p.AdminConsentDescription; v != nil { - permission["admin_consent_description"] = v - } - if v := p.AdminConsentDisplayName; v != nil { - permission["admin_consent_display_name"] = v - } - if v := p.ID; v != nil { - permission["id"] = v - } - if v := p.IsEnabled; v != nil { - permission["is_enabled"] = *v - } - if v := p.Type; v != nil { - permission["type"] = v - } - if v := p.UserConsentDescription; v != nil { - permission["user_consent_description"] = v - } - if v := p.UserConsentDisplayName; v != nil { - permission["user_consent_display_name"] = v - } - if v := p.Value; v != nil { - permission["value"] = v - } - - result = append(result, permission) - } - - return result -} diff --git a/azuread/resource_service_principal.go b/azuread/resource_service_principal.go index b3cd008fb8..44b50a381d 100644 --- a/azuread/resource_service_principal.go +++ b/azuread/resource_service_principal.go @@ -39,6 +39,8 @@ func resourceServicePrincipal() *schema.Resource { Computed: true, }, + "oauth2_permissions": graph.SchemaOauth2Permissions(), + "object_id": { Type: schema.TypeString, Computed: true, @@ -116,6 +118,10 @@ func resourceServicePrincipalRead(d *schema.ResourceData, meta interface{}) erro return fmt.Errorf("Error setting `tags`: %+v", err) } + if err := d.Set("oauth2_permissions", graph.FlattenOauth2Permissions(app.Oauth2Permissions)); err != nil { + return fmt.Errorf("Error setting `oauth2_permissions`: %+v", err) + } + return nil } diff --git a/azuread/resource_service_principal_test.go b/azuread/resource_service_principal_test.go index 32cbba8381..d498557593 100644 --- a/azuread/resource_service_principal_test.go +++ b/azuread/resource_service_principal_test.go @@ -26,6 +26,8 @@ func TestAccAzureADServicePrincipal_basic(t *testing.T) { testCheckADServicePrincipalExists(resourceName), resource.TestCheckResourceAttrSet(resourceName, "display_name"), resource.TestCheckResourceAttrSet(resourceName, "application_id"), + resource.TestCheckResourceAttr(resourceName, "oauth2_permissions.#", "1"), + resource.TestCheckResourceAttr(resourceName, "oauth2_permissions.0.admin_consent_description", fmt.Sprintf("Allow the application to access %s on behalf of the signed-in user.", fmt.Sprintf("acctestspa%s", id))), resource.TestCheckResourceAttrSet(resourceName, "object_id"), ), }, diff --git a/website/docs/d/service_principal.html.markdown b/website/docs/d/service_principal.html.markdown index c11ba4f923..0311777ab1 100644 --- a/website/docs/d/service_principal.html.markdown +++ b/website/docs/d/service_principal.html.markdown @@ -49,8 +49,30 @@ The following arguments are supported: -> **NOTE:** At least one of `application_id`, `display_name` or `object_id` must be specified. +* `oauth2_permissions` - A collection of OAuth 2.0 permissions exposed by the associated application. Each permission is covered by a `oauth2_permission` block as documented below. + ## Attributes Reference The following attributes are exported: * `id` - The Object ID for the Service Principal. + +--- + +`oauth2_permission` block exports the following: + +* `id` - The unique identifier for one of the `OAuth2Permission` + +* `type` - The type of the permission + +* `admin_consent_description` - The description of the admin consent + +* `admin_consent_display_name` - The display name of the admin consent + +* `is_enabled` - Is this permission enabled? + +* `user_consent_description` - The description of the user consent + +* `user_consent_display_name` - The display name of the user consent + +* `value` - The name of this permission diff --git a/website/docs/r/service_principal.html.markdown b/website/docs/r/service_principal.html.markdown index a41b3cee52..3f1dcefd3a 100644 --- a/website/docs/r/service_principal.html.markdown +++ b/website/docs/r/service_principal.html.markdown @@ -52,6 +52,28 @@ The following attributes are exported: * `display_name` - The Display Name of the Azure Active Directory Application associated with this Service Principal. +* `oauth2_permissions` - A collection of OAuth 2.0 permissions exposed by the associated application. Each permission is covered by a `oauth2_permission` block as documented below. + +--- + +`oauth2_permission` block exports the following: + +* `id` - The unique identifier for one of the `OAuth2Permission`. + +* `type` - The type of the permission. + +* `admin_consent_description` - The description of the admin consent. + +* `admin_consent_display_name` - The display name of the admin consent. + +* `is_enabled` - Is this permission enabled? + +* `user_consent_description` - The description of the user consent. + +* `user_consent_display_name` - The display name of the user consent. + +* `value` - The name of this permission. + ## Import Azure Active Directory Service Principals can be imported using the `object id`, e.g. From f01cbc702bcfb8847a53bec3fdf2b85b00e9095a Mon Sep 17 00:00:00 2001 From: kt Date: Tue, 11 Jun 2019 11:07:09 -0700 Subject: [PATCH 22/45] Update CHANGELOG.md to include #103 --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c1c65b308f..e6b039c8f2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ ## 0.5.0 (Unreleased) +IMPROVEMENTS: + +* `azuread_service_principal` - export the `oauth2_permissions` property [GH-103] + + ## 0.4.0 (June 06, 2019) NOTES: From d1c18e88bdface37eb0571cbf6a9bd4a2688e160 Mon Sep 17 00:00:00 2001 From: Steve Hawkins Date: Wed, 12 Jun 2019 20:01:13 +0100 Subject: [PATCH 23/45] Feature/application app roles (#98) this should resolve #75 --- azuread/resource_application.go | 158 ++++++++++++++++++++++- azuread/resource_application_test.go | 147 ++++++++++++++++++++- website/docs/r/application.html.markdown | 34 ++++- 3 files changed, 333 insertions(+), 6 deletions(-) diff --git a/azuread/resource_application.go b/azuread/resource_application.go index 947f30b73f..b18c1e1455 100644 --- a/azuread/resource_application.go +++ b/azuread/resource_application.go @@ -5,10 +5,9 @@ import ( "log" "github.com/Azure/azure-sdk-for-go/services/graphrbac/1.6/graphrbac" - + "github.com/google/uuid" "github.com/hashicorp/terraform/helper/schema" "github.com/hashicorp/terraform/helper/validation" - "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/ar" "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/graph" "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/p" @@ -94,6 +93,56 @@ func resourceApplication() *schema.Resource { Default: "webapp/api", }, + "app_role": { + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "id": { + Type: schema.TypeString, + Computed: true, + }, + + "allowed_member_types": { + Type: schema.TypeSet, + Required: true, + MinItems: 1, + Elem: &schema.Schema{ + Type: schema.TypeString, + ValidateFunc: validation.StringInSlice( + []string{"User", "Application"}, + false, + ), + }, + }, + + "description": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validate.NoEmptyStrings, + }, + + "display_name": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validate.NoEmptyStrings, + }, + + "is_enabled": { + Type: schema.TypeBool, + Optional: true, + Default: true, + }, + + "value": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validate.NoEmptyStrings, + }, + }, + }, + }, + "required_resource_access": { Type: schema.TypeSet, Optional: true, @@ -159,6 +208,7 @@ func resourceApplicationCreate(d *schema.ResourceData, meta interface{}) error { ReplyUrls: tf.ExpandStringSlicePtr(d.Get("reply_urls").(*schema.Set).List()), AvailableToOtherTenants: p.Bool(d.Get("available_to_other_tenants").(bool)), RequiredResourceAccess: expandADApplicationRequiredResourceAccess(d), + AppRoles: expandADApplicationAppRoles(d.Get("app_role")), } if v, ok := d.GetOk("homepage"); ok { @@ -251,6 +301,32 @@ func resourceApplicationUpdate(d *schema.ResourceData, meta interface{}) error { properties.RequiredResourceAccess = expandADApplicationRequiredResourceAccess(d) } + if d.HasChange("app_role") { + // if the app role already exists then it must be disabled + // with no other changes before it can be edited or deleted + var app graphrbac.Application + var appRolesProperties graphrbac.ApplicationUpdateParameters + resp, err := client.Get(ctx, d.Id()) + if err != nil { + if ar.ResponseWasNotFound(resp.Response) { + return fmt.Errorf("Error: AzureAD Application with ID %q was not found", d.Id()) + } + + return fmt.Errorf("Error making Read request on AzureAD Application with ID %q: %+v", d.Id(), err) + } + app = resp + for _, appRole := range *app.AppRoles { + *appRole.IsEnabled = false + } + appRolesProperties.AppRoles = app.AppRoles + if _, err := client.Patch(ctx, d.Id(), appRolesProperties); err != nil { + return fmt.Errorf("Error disabling App Roles for Azure AD Application with ID %q: %+v", d.Id(), err) + } + + // now we can set the new state of the app role + properties.AppRoles = expandADApplicationAppRoles(d.Get("app_role")) + } + if d.HasChange("group_membership_claims") { properties.GroupMembershipClaims = d.Get("group_membership_claims") } @@ -265,7 +341,6 @@ func resourceApplicationUpdate(d *schema.ResourceData, meta interface{}) error { properties.IdentifierUris = &[]string{} default: return fmt.Errorf("Error paching Azure AD Application with ID %q: Unknow application type %v. Supported types are [webapp/api, native]", d.Id(), appType) - } } @@ -320,6 +395,10 @@ func resourceApplicationRead(d *schema.ResourceData, meta interface{}) error { return fmt.Errorf("Error setting `required_resource_access`: %+v", err) } + if err := d.Set("app_role", flattenADApplicationAppRoles(app.AppRoles)); err != nil { + return fmt.Errorf("Error setting `app_role`: %+v", err) + } + if err := d.Set("oauth2_permissions", graph.FlattenOauth2Permissions(app.Oauth2Permissions)); err != nil { return fmt.Errorf("Error setting `oauth2_permissions`: %+v", err) } @@ -432,3 +511,76 @@ func flattenADApplicationResourceAccess(in *[]graphrbac.ResourceAccess) []interf return accesses } + +func expandADApplicationAppRoles(i interface{}) *[]graphrbac.AppRole { + input := i.(*schema.Set).List() + if len(input) == 0 { + return nil + } + + var output []graphrbac.AppRole + + for _, appRoleRaw := range input { + appRole := appRoleRaw.(map[string]interface{}) + + appRoleID := appRole["id"].(string) + if appRoleID == "" { + appRoleID = uuid.New().String() + } + + var appRoleAllowedMemberTypes []string + for _, appRoleAllowedMemberType := range appRole["allowed_member_types"].(*schema.Set).List() { + appRoleAllowedMemberTypes = append(appRoleAllowedMemberTypes, appRoleAllowedMemberType.(string)) + } + + appRoleDescription := appRole["description"].(string) + appRoleDisplayName := appRole["display_name"].(string) + appRoleIsEnabled := appRole["is_enabled"].(bool) + appRoleValue := appRole["value"].(string) + + output = append(output, + graphrbac.AppRole{ + ID: &appRoleID, + AllowedMemberTypes: &appRoleAllowedMemberTypes, + Description: &appRoleDescription, + DisplayName: &appRoleDisplayName, + IsEnabled: &appRoleIsEnabled, + Value: &appRoleValue, + }, + ) + } + + return &output +} + +func flattenADApplicationAppRoles(in *[]graphrbac.AppRole) []interface{} { + if in == nil { + return []interface{}{} + } + + appRoles := make([]interface{}, 0) + for _, role := range *in { + appRole := make(map[string]interface{}) + if role.ID != nil { + appRole["id"] = *role.ID + } + if role.AllowedMemberTypes != nil { + appRole["allowed_member_types"] = *role.AllowedMemberTypes + } + if role.Description != nil { + appRole["description"] = *role.Description + } + if role.DisplayName != nil { + appRole["display_name"] = *role.DisplayName + } + if role.IsEnabled != nil { + appRole["is_enabled"] = *role.IsEnabled + } + if role.Value != nil { + appRole["value"] = *role.Value + } + appRoles = append(appRoles, appRole) + } + + return appRoles +} diff --git a/azuread/resource_application_test.go b/azuread/resource_application_test.go index 6edf137309..6974d7d0ac 100644 --- a/azuread/resource_application_test.go +++ b/azuread/resource_application_test.go @@ -8,7 +8,6 @@ import ( "github.com/google/uuid" "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" - "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/ar" ) @@ -138,6 +137,111 @@ func TestAccAzureADApplication_availableToOtherTenants(t *testing.T) { }) } +func TestAccAzureADApplication_appRoles(t *testing.T) { + resourceName := "azuread_application.test" + id := uuid.New().String() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckADApplicationDestroy, + Steps: []resource.TestStep{ + { + Config: testAccADApplication_appRoles(id), + Check: resource.ComposeTestCheckFunc( + testCheckADApplicationExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "app_role.#", "1"), + resource.TestCheckResourceAttr(resourceName, "app_role.3282540397.allowed_member_types.#", "2"), + resource.TestCheckResourceAttr(resourceName, "app_role.3282540397.allowed_member_types.2550101162", "Application"), + resource.TestCheckResourceAttr(resourceName, "app_role.3282540397.allowed_member_types.2906997583", "User"), + resource.TestCheckResourceAttr(resourceName, "app_role.3282540397.description", "Admins can manage roles and perform all task actions"), + resource.TestCheckResourceAttr(resourceName, "app_role.3282540397.display_name", "Admin"), + resource.TestCheckResourceAttr(resourceName, "app_role.3282540397.is_enabled", "true"), + resource.TestCheckResourceAttr(resourceName, "app_role.3282540397.value", "Admin"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAzureADApplication_appRolesUpdate(t *testing.T) { + resourceName := "azuread_application.test" + id := uuid.New().String() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckADApplicationDestroy, + Steps: []resource.TestStep{ + { + Config: testAccADApplication_appRoles(id), + Check: resource.ComposeTestCheckFunc( + testCheckADApplicationExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "app_role.#", "1"), + resource.TestCheckResourceAttr(resourceName, "app_role.3282540397.allowed_member_types.#", "2"), + resource.TestCheckResourceAttr(resourceName, "app_role.3282540397.allowed_member_types.2550101162", "Application"), + resource.TestCheckResourceAttr(resourceName, "app_role.3282540397.allowed_member_types.2906997583", "User"), + resource.TestCheckResourceAttr(resourceName, "app_role.3282540397.description", "Admins can manage roles and perform all task actions"), + resource.TestCheckResourceAttr(resourceName, "app_role.3282540397.display_name", "Admin"), + resource.TestCheckResourceAttr(resourceName, "app_role.3282540397.is_enabled", "true"), + resource.TestCheckResourceAttr(resourceName, "app_role.3282540397.value", "Admin"), + ), + }, + { + Config: testAccADApplication_appRolesUpdate(id), + Check: resource.ComposeTestCheckFunc( + testCheckADApplicationExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "app_role.#", "2"), + resource.TestCheckResourceAttr(resourceName, "app_role.1786747921.allowed_member_types.#", "1"), + resource.TestCheckResourceAttr(resourceName, "app_role.1786747921.allowed_member_types.2906997583", "User"), + resource.TestCheckResourceAttr(resourceName, "app_role.1786747921.description", "ReadOnly roles have limited query access"), + resource.TestCheckResourceAttr(resourceName, "app_role.1786747921.display_name", "ReadOnly"), + resource.TestCheckResourceAttr(resourceName, "app_role.1786747921.is_enabled", "true"), + resource.TestCheckResourceAttr(resourceName, "app_role.1786747921.value", "User"), + resource.TestCheckResourceAttr(resourceName, "app_role.2608972077.allowed_member_types.#", "1"), + resource.TestCheckResourceAttr(resourceName, "app_role.2608972077.allowed_member_types.2906997583", "User"), + resource.TestCheckResourceAttr(resourceName, "app_role.2608972077.description", "Admins can manage roles and perform all task actions"), + resource.TestCheckResourceAttr(resourceName, "app_role.2608972077.display_name", "Admin"), + resource.TestCheckResourceAttr(resourceName, "app_role.2608972077.is_enabled", "true"), + resource.TestCheckResourceAttr(resourceName, "app_role.2608972077.value", "Admin"), + ), + }, + }, + }) +} + +func TestAccAzureADApplication_appRolesDelete(t *testing.T) { + resourceName := "azuread_application.test" + id := uuid.New().String() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckADApplicationDestroy, + Steps: []resource.TestStep{ + { + Config: testAccADApplication_appRolesUpdate(id), + Check: resource.ComposeTestCheckFunc( + testCheckADApplicationExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "app_role.#", "2"), + ), + }, + { + Config: testAccADApplication_appRoles(id), + Check: resource.ComposeTestCheckFunc( + testCheckADApplicationExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "app_role.#", "1"), + ), + }, + }, + }) +} + func TestAccAzureADApplication_groupMembershipClaimsUpdate(t *testing.T) { resourceName := "azuread_application.test" id := uuid.New().String() @@ -436,6 +540,47 @@ resource "azuread_application" "test" { `, id, id, id) } +func testAccADApplication_appRoles(id string) string { + return fmt.Sprintf(` +resource "azuread_application" "test" { + name = "acctest%s" + app_role { + allowed_member_types = [ + "User", + "Application", + ] + description = "Admins can manage roles and perform all task actions" + display_name = "Admin" + is_enabled = true + value = "Admin" + } +} +`, id) +} + +func testAccADApplication_appRolesUpdate(id string) string { + return fmt.Sprintf(` +resource "azuread_application" "test" { + name = "acctest%s" + app_role { + allowed_member_types = ["User"] + description = "Admins can manage roles and perform all task actions" + display_name = "Admin" + is_enabled = true + value = "Admin" + } + + app_role { + allowed_member_types = ["User"] + description = "ReadOnly roles have limited query access" + display_name = "ReadOnly" + is_enabled = true + value = "User" + } +} +`, id) +} + func testAccADApplication_native(id string) string { return fmt.Sprintf(` resource "azuread_application" "test" { diff --git a/website/docs/r/application.html.markdown b/website/docs/r/application.html.markdown index 2b43fb11b1..2ce2f62b23 100644 --- a/website/docs/r/application.html.markdown +++ b/website/docs/r/application.html.markdown @@ -51,6 +51,18 @@ resource "azuread_application" "test" { type = "Scope" } } + + app_role { + allowed_member_types = [ + "User", + "Application", + ] + + description = "Admins can manage roles and perform all task actions" + display_name = "Admin" + is_enabled = true + value = "Admin" + } } ``` @@ -74,7 +86,9 @@ The following arguments are supported: * `required_resource_access` - (Optional) A collection of `required_resource_access` blocks as documented below. -* `type` - (Optional) Type of an application: `webapp/api` or `native`. Defaults to `webapp/api`. For `native` apps type `identifier_uris` property can not not be set. +* `type` - (Optional) Type of an application: `webapp/api` or `native`. Defaults to `webapp/api`. For `native` apps type `identifier_uris` property can not not be set. + +* `app_role` - (Optional) A collection of `app_role` blocks as documented below. For more information https://docs.microsoft.com/en-us/azure/architecture/multitenant-identity/app-roles --- @@ -88,10 +102,26 @@ The following arguments are supported: `resource_access` supports the following: -* `id` - (Required) The unique identifier for one of the `OAuth2Permission` or `AppRole` instances that the resource application exposes. +* `id` - (Required) The unique identifier for one of the `OAuth2Permission` or `AppRole` instances that the resource application exposes. * `type` - (Required) Specifies whether the id property references an `OAuth2Permission` or an `AppRole`. Possible values are `Scope` or `Role`. +--- + +`app_role` supports the following: + +* `id` - The unique identifier of the `app_role`. + +* `allowed_member_types` - (Required) Specifies whether this app role definition can be assigned to users and groups by setting to `User`, or to other applications (that are accessing this application in daemon service scenarios) by setting to `Application`, or to both. + +* `description` - (Required) Permission help text that appears in the admin app assignment and consent experiences. + +* `display_name` - (Required) Display name for the permission that appears in the admin consent and app assignment experiences. + +* `is_enabled` - (Optional) Determines if the app role is enabled: Defaults to `true`. + +* `value` - (Required) Specifies the value of the roles claim that the application should expect in the authentication and access tokens. + ## Attributes Reference The following attributes are exported: From 219815206b0042d735622076dc9118fd64c66dd3 Mon Sep 17 00:00:00 2001 From: kt Date: Wed, 12 Jun 2019 12:02:04 -0700 Subject: [PATCH 24/45] Update CHANGELOG.md to include #98 --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e6b039c8f2..cdaecfac8a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,9 +2,9 @@ IMPROVEMENTS: +* `azuread_application` - support for the `app_roles` property [GH-98] * `azuread_service_principal` - export the `oauth2_permissions` property [GH-103] - ## 0.4.0 (June 06, 2019) NOTES: From 6ebea6278d929b96ce9c6b3265d9535dbf2150c6 Mon Sep 17 00:00:00 2001 From: kt Date: Wed, 12 Jun 2019 12:42:48 -0700 Subject: [PATCH 25/45] azuread_application_password: deprecate application_id in favour of application_object_id (#107) fixes #106 --- azuread/data_service_principal.go | 6 +- azuread/data_user_test.go | 1 - azuread/resource_application.go | 1 + azuread/resource_application_password.go | 76 ++++++++++++++++++- azuread/resource_application_password_test.go | 70 ++++++++++++----- azuread/resource_application_test.go | 1 + azuread/resource_service_principal.go | 5 +- ...esource_service_principal_password_test.go | 6 +- azuread/resource_service_principal_test.go | 4 +- azuread/resource_user_test.go | 1 - .../docs/r/application_password.html.markdown | 2 +- 11 files changed, 140 insertions(+), 33 deletions(-) diff --git a/azuread/data_service_principal.go b/azuread/data_service_principal.go index e5ba8de774..3c1261c806 100644 --- a/azuread/data_service_principal.go +++ b/azuread/data_service_principal.go @@ -3,12 +3,12 @@ package azuread import ( "fmt" + "github.com/Azure/azure-sdk-for-go/services/graphrbac/1.6/graphrbac" + "github.com/hashicorp/terraform/helper/schema" + "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/ar" "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/graph" "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/validate" - - "github.com/Azure/azure-sdk-for-go/services/graphrbac/1.6/graphrbac" - "github.com/hashicorp/terraform/helper/schema" ) func dataServicePrincipal() *schema.Resource { diff --git a/azuread/data_user_test.go b/azuread/data_user_test.go index 88d7bb523c..b48bf3a247 100644 --- a/azuread/data_user_test.go +++ b/azuread/data_user_test.go @@ -5,7 +5,6 @@ import ( "testing" "github.com/hashicorp/terraform/helper/acctest" - "github.com/hashicorp/terraform/helper/resource" ) diff --git a/azuread/resource_application.go b/azuread/resource_application.go index b18c1e1455..ec147af9f0 100644 --- a/azuread/resource_application.go +++ b/azuread/resource_application.go @@ -8,6 +8,7 @@ import ( "github.com/google/uuid" "github.com/hashicorp/terraform/helper/schema" "github.com/hashicorp/terraform/helper/validation" + "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/ar" "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/graph" "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/p" diff --git a/azuread/resource_application_password.go b/azuread/resource_application_password.go index 3bf0d9338f..1387cf735d 100644 --- a/azuread/resource_application_password.go +++ b/azuread/resource_application_password.go @@ -7,10 +7,12 @@ import ( "github.com/Azure/azure-sdk-for-go/services/graphrbac/1.6/graphrbac" "github.com/hashicorp/terraform/helper/schema" + "github.com/hashicorp/terraform/helper/validation" "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/ar" "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/graph" "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/tf" + "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/validate" ) func resourceApplicationPassword() *schema.Resource { @@ -23,7 +25,68 @@ func resourceApplicationPassword() *schema.Resource { State: schema.ImportStatePassthrough, }, - Schema: graph.PasswordResourceSchema("application"), + // Schema: graph.PasswordResourceSchema("application_object"), //todo switch back to this in 1.0 + Schema: map[string]*schema.Schema{ + "application_id": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + Computed: true, + ValidateFunc: validate.UUID, + Deprecated: "Deprecated in favour of `application_object_id` to prevent confusion", + ConflictsWith: []string{"application_id"}, + }, + + "application_object_id": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + ValidateFunc: validate.UUID, + ConflictsWith: []string{"application_object_id"}, + }, + + "key_id": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + ValidateFunc: validate.UUID, + }, + + "value": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Sensitive: true, + ValidateFunc: validate.NoEmptyStrings, + }, + + "start_date": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + ValidateFunc: validation.ValidateRFC3339TimeString, + }, + + "end_date": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + ConflictsWith: []string{"end_date_relative"}, + ValidateFunc: validation.ValidateRFC3339TimeString, + }, + + "end_date_relative": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + ConflictsWith: []string{"end_date"}, + ValidateFunc: validate.NoEmptyStrings, + }, + }, } } @@ -31,7 +94,13 @@ func resourceApplicationPasswordCreate(d *schema.ResourceData, meta interface{}) client := meta.(*ArmClient).applicationsClient ctx := meta.(*ArmClient).StopContext - objectId := d.Get("application_id").(string) + objectId := d.Get("application_object_id").(string) + if objectId == "" { // todo remove in 1.0 + objectId = d.Get("application_id").(string) + } + if objectId == "" { + return fmt.Errorf("one of `application_object_id` or `application_id` must be specified") + } cred, err := graph.PasswordCredentialForResource(d) if err != nil { @@ -95,7 +164,8 @@ func resourceApplicationPasswordRead(d *schema.ResourceData, meta interface{}) e } // todo, move this into a graph helper function? - d.Set("application_id", id.ObjectId) + d.Set("application_object_id", id.ObjectId) + d.Set("application_id", id.ObjectId) //todo remove in 2.0 d.Set("key_id", id.KeyId) if endDate := credential.EndDate; endDate != nil { diff --git a/azuread/resource_application_password_test.go b/azuread/resource_application_password_test.go index e034681532..feb6391b87 100644 --- a/azuread/resource_application_password_test.go +++ b/azuread/resource_application_password_test.go @@ -4,12 +4,12 @@ import ( "fmt" "testing" - "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/ar" - "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/graph" - "github.com/google/uuid" "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" + + "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/ar" + "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/graph" ) func testCheckADApplicationPasswordExists(name string) resource.TestCheckFunc { //nolint unparam @@ -101,6 +101,30 @@ func TestAccAzureADApplicationPassword_basic(t *testing.T) { }) } +func TestAccAzureADApplicationPassword_basicOld(t *testing.T) { + resourceName := "azuread_application_password.test" + applicationId := uuid.New().String() + value := uuid.New().String() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckADApplicationPasswordCheckDestroy, + Steps: []resource.TestStep{ + { + Config: testAccADObjectPasswordApplication_basicOld(applicationId, value), + Check: resource.ComposeTestCheckFunc( + // can't assert on Value since it's not returned + testCheckADApplicationPasswordExists(resourceName), + resource.TestCheckResourceAttrSet(resourceName, "start_date"), + resource.TestCheckResourceAttrSet(resourceName, "key_id"), + resource.TestCheckResourceAttr(resourceName, "end_date", "2020-01-01T01:02:03Z"), + ), + }, + }, + }) +} + func TestAccAzureADApplicationPassword_requiresImport(t *testing.T) { if !requireResourcesToBeImported { t.Skip("Skipping since resources aren't required to be imported") @@ -191,9 +215,21 @@ func testAccADObjectPasswordApplication_basic(applicationId, value string) strin %s resource "azuread_application_password" "test" { - application_id = "${azuread_application.test.id}" - value = "%s" - end_date = "2020-01-01T01:02:03Z" + application_object_id = "${azuread_application.test.id}" + value = "%s" + end_date = "2020-01-01T01:02:03Z" +} +`, testAccADApplicationPassword_template(applicationId), value) +} + +func testAccADObjectPasswordApplication_basicOld(applicationId, value string) string { + return fmt.Sprintf(` +%s + +resource "azuread_application_password" "test" { + application_id = "${azuread_application.test.id}" + value = "%s" + end_date = "2020-01-01T01:02:03Z" } `, testAccADApplicationPassword_template(applicationId), value) } @@ -204,10 +240,10 @@ func testAccADApplicationPassword_requiresImport(applicationId, value string) st %s resource "azuread_application_password" "import" { - application_id = "${azuread_application_password.test.application_id}" - key_id = "${azuread_application_password.test.key_id}" - value = "${azuread_application_password.test.value}" - end_date = "${azuread_application_password.test.end_date}" + application_object_id = "${azuread_application_password.test.application_id}" + key_id = "${azuread_application_password.test.key_id}" + value = "${azuread_application_password.test.value}" + end_date = "${azuread_application_password.test.end_date}" } `, template) } @@ -217,10 +253,10 @@ func testAccADApplicationPassword_customKeyId(applicationId, keyId, value string %s resource "azuread_application_password" "test" { - application_id = "${azuread_application.test.id}" - key_id = "%s" - value = "%s" - end_date = "2020-01-01T01:02:03Z" + application_object_id = "${azuread_application.test.id}" + key_id = "%s" + value = "%s" + end_date = "2020-01-01T01:02:03Z" } `, testAccADApplicationPassword_template(applicationId), keyId, value) } @@ -230,9 +266,9 @@ func testAccADApplicationPassword_relativeEndDate(applicationId, value string) s %s resource "azuread_application_password" "test" { - application_id = "${azuread_application.test.id}" - value = "%s" - end_date_relative = "8760h" + application_object_id = "${azuread_application.test.id}" + value = "%s" + end_date_relative = "8760h" } `, testAccADApplicationPassword_template(applicationId), value) } diff --git a/azuread/resource_application_test.go b/azuread/resource_application_test.go index 6974d7d0ac..77acd7448d 100644 --- a/azuread/resource_application_test.go +++ b/azuread/resource_application_test.go @@ -8,6 +8,7 @@ import ( "github.com/google/uuid" "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" + "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/ar" ) diff --git a/azuread/resource_service_principal.go b/azuread/resource_service_principal.go index 44b50a381d..4de4c03bf8 100644 --- a/azuread/resource_service_principal.go +++ b/azuread/resource_service_principal.go @@ -39,13 +39,13 @@ func resourceServicePrincipal() *schema.Resource { Computed: true, }, - "oauth2_permissions": graph.SchemaOauth2Permissions(), - "object_id": { Type: schema.TypeString, Computed: true, }, + "oauth2_permissions": graph.SchemaOauth2Permissions(), + "tags": { Type: schema.TypeSet, Optional: true, @@ -113,6 +113,7 @@ func resourceServicePrincipalRead(d *schema.ResourceData, meta interface{}) erro d.Set("application_id", app.AppID) d.Set("display_name", app.DisplayName) d.Set("object_id", app.ObjectID) + // tags doesn't exist as a property, so extract it if err := d.Set("tags", app.Tags); err != nil { return fmt.Errorf("Error setting `tags`: %+v", err) diff --git a/azuread/resource_service_principal_password_test.go b/azuread/resource_service_principal_password_test.go index ccdfad41d5..6a18e2182e 100644 --- a/azuread/resource_service_principal_password_test.go +++ b/azuread/resource_service_principal_password_test.go @@ -4,12 +4,12 @@ import ( "fmt" "testing" - "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/ar" - "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/graph" - "github.com/google/uuid" "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" + + "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/ar" + "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/graph" ) func testCheckADServicePrincipalPasswordExists(name string) resource.TestCheckFunc { //nolint unparam diff --git a/azuread/resource_service_principal_test.go b/azuread/resource_service_principal_test.go index d498557593..52bdc3acde 100644 --- a/azuread/resource_service_principal_test.go +++ b/azuread/resource_service_principal_test.go @@ -4,11 +4,11 @@ import ( "fmt" "testing" - "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/ar" - "github.com/google/uuid" "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" + + "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/ar" ) func TestAccAzureADServicePrincipal_basic(t *testing.T) { diff --git a/azuread/resource_user_test.go b/azuread/resource_user_test.go index 6f520a2a29..3776a08347 100644 --- a/azuread/resource_user_test.go +++ b/azuread/resource_user_test.go @@ -5,7 +5,6 @@ import ( "testing" "github.com/hashicorp/terraform/helper/acctest" - "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" diff --git a/website/docs/r/application_password.html.markdown b/website/docs/r/application_password.html.markdown index bcc383d894..36fe1a4c9c 100644 --- a/website/docs/r/application_password.html.markdown +++ b/website/docs/r/application_password.html.markdown @@ -36,7 +36,7 @@ resource "azuread_application_password" "example" { The following arguments are supported: -* `object_id` - (Required) The Object ID of the Application for which this password should be created. Changing this field forces a new resource to be created. +* `application_object_id` - (Required) The Object ID of the Application for which this password should be created. Changing this field forces a new resource to be created. * `value` - (Required) The Password for this Application . From 239b0661c0ed64bb8c5fc484b9352fbeb67a5311 Mon Sep 17 00:00:00 2001 From: kt Date: Wed, 12 Jun 2019 12:44:11 -0700 Subject: [PATCH 26/45] Update CHANGELOG.md to include #107 --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index cdaecfac8a..46ec6bcb4d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,8 @@ IMPROVEMENTS: * `azuread_application` - support for the `app_roles` property [GH-98] +* `azuread_application_password` - deprecation of `application_id` in favour of `application_object_id` [GH-107] + * `azuread_service_principal` - export the `oauth2_permissions` property [GH-103] ## 0.4.0 (June 06, 2019) From 59b095b5e7b5120a859a11b55e4f65ea759c3e0f Mon Sep 17 00:00:00 2001 From: Joakim Bakke Hellum Date: Thu, 13 Jun 2019 04:43:20 +0200 Subject: [PATCH 27/45] azuread_application & azuread_service_principal: export the `app_roles` property (#110) --- azuread/data_application.go | 6 ++ azuread/data_application_test.go | 1 + azuread/data_service_principal.go | 6 ++ azuread/data_service_principal_test.go | 1 + azuread/helpers/graph/application.go | 76 +++++++++++++++++++ azuread/resource_application.go | 34 +-------- website/docs/d/application.html.markdown | 18 +++++ .../docs/d/service_principal.html.markdown | 18 +++++ 8 files changed, 127 insertions(+), 33 deletions(-) diff --git a/azuread/data_application.go b/azuread/data_application.go index fa31a49e2a..c5b15fd359 100644 --- a/azuread/data_application.go +++ b/azuread/data_application.go @@ -79,6 +79,8 @@ func dataApplication() *schema.Resource { Computed: true, }, + "app_roles": graph.SchemaAppRoles(), + "required_resource_access": { Type: schema.TypeList, Computed: true, @@ -193,6 +195,10 @@ func dataApplicationRead(d *schema.ResourceData, meta interface{}) error { d.Set("type", "webapp/api") } + if err := d.Set("app_roles", graph.FlattenAppRoles(app.AppRoles)); err != nil { + return fmt.Errorf("Error setting `app_roles`: %+v", err) + } + if err := d.Set("group_membership_claims", app.GroupMembershipClaims); err != nil { return fmt.Errorf("Error setting `group_membership_claims`: %+v", err) } diff --git a/azuread/data_application_test.go b/azuread/data_application_test.go index bef04cd035..8baf38a103 100644 --- a/azuread/data_application_test.go +++ b/azuread/data_application_test.go @@ -30,6 +30,7 @@ func TestAccAzureADApplicationDataSource_byObjectId(t *testing.T) { resource.TestCheckResourceAttr(dataSourceName, "reply_urls.#", "0"), resource.TestCheckResourceAttr(dataSourceName, "required_resource_access.#", "0"), resource.TestCheckResourceAttr(dataSourceName, "type", "webapp/api"), + resource.TestCheckResourceAttr(dataSourceName, "app_roles.#", "0"), resource.TestCheckResourceAttr(dataSourceName, "oauth2_allow_implicit_flow", "false"), resource.TestCheckResourceAttr(dataSourceName, "oauth2_permissions.#", "1"), resource.TestCheckResourceAttr(dataSourceName, "oauth2_permissions.0.admin_consent_description", fmt.Sprintf("Allow the application to access %s on behalf of the signed-in user.", fmt.Sprintf("acctest%s", id))), diff --git a/azuread/data_service_principal.go b/azuread/data_service_principal.go index 3c1261c806..2586562da1 100644 --- a/azuread/data_service_principal.go +++ b/azuread/data_service_principal.go @@ -40,6 +40,8 @@ func dataServicePrincipal() *schema.Resource { ConflictsWith: []string{"object_id", "display_name"}, }, + "app_roles": graph.SchemaAppRoles(), + "oauth2_permissions": graph.SchemaOauth2Permissions(), }, } @@ -129,6 +131,10 @@ func dataSourceActiveDirectoryServicePrincipalRead(d *schema.ResourceData, meta d.Set("display_name", sp.DisplayName) d.Set("object_id", sp.ObjectID) + if err := d.Set("app_roles", graph.FlattenAppRoles(sp.AppRoles)); err != nil { + return fmt.Errorf("Error setting `app_roles`: %+v", err) + } + if err := d.Set("oauth2_permissions", graph.FlattenOauth2Permissions(sp.Oauth2Permissions)); err != nil { return fmt.Errorf("Error setting `oauth2_permissions`: %+v", err) } diff --git a/azuread/data_service_principal_test.go b/azuread/data_service_principal_test.go index bf2a31afd8..8990b957bb 100644 --- a/azuread/data_service_principal_test.go +++ b/azuread/data_service_principal_test.go @@ -24,6 +24,7 @@ func TestAccAzureADServicePrincipalDataSource_byApplicationId(t *testing.T) { resource.TestCheckResourceAttrSet(dataSourceName, "application_id"), resource.TestCheckResourceAttrSet(dataSourceName, "object_id"), resource.TestCheckResourceAttrSet(dataSourceName, "display_name"), + resource.TestCheckResourceAttr(dataSourceName, "app_roles.#", "0"), resource.TestCheckResourceAttr(dataSourceName, "oauth2_permissions.#", "1"), resource.TestCheckResourceAttr(dataSourceName, "oauth2_permissions.0.admin_consent_description", fmt.Sprintf("Allow the application to access %s on behalf of the signed-in user.", fmt.Sprintf("acctestspa%s", id))), ), diff --git a/azuread/helpers/graph/application.go b/azuread/helpers/graph/application.go index 12f5f2dff5..ce6b9c36fd 100644 --- a/azuread/helpers/graph/application.go +++ b/azuread/helpers/graph/application.go @@ -5,6 +5,50 @@ import ( "github.com/hashicorp/terraform/helper/schema" ) +func SchemaAppRoles() *schema.Schema { + return &schema.Schema{ + Type: schema.TypeList, + Optional: true, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "id": { + Type: schema.TypeString, + Computed: true, + }, + + "allowed_member_types": { + Type: schema.TypeSet, + Computed: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + + "description": { + Type: schema.TypeString, + Computed: true, + }, + + "display_name": { + Type: schema.TypeString, + Computed: true, + }, + + "is_enabled": { + Type: schema.TypeBool, + Computed: true, + }, + + "value": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + } +} + func SchemaOauth2Permissions() *schema.Schema { return &schema.Schema{ Type: schema.TypeList, @@ -56,6 +100,38 @@ func SchemaOauth2Permissions() *schema.Schema { } } +func FlattenAppRoles(in *[]graphrbac.AppRole) []interface{} { + if in == nil { + return []interface{}{} + } + + appRoles := make([]interface{}, 0) + for _, role := range *in { + appRole := make(map[string]interface{}) + if role.ID != nil { + appRole["id"] = *role.ID + } + if role.AllowedMemberTypes != nil { + appRole["allowed_member_types"] = *role.AllowedMemberTypes + } + if role.Description != nil { + appRole["description"] = *role.Description + } + if role.DisplayName != nil { + appRole["display_name"] = *role.DisplayName + } + if role.IsEnabled != nil { + appRole["is_enabled"] = *role.IsEnabled + } + if role.Value != nil { + appRole["value"] = *role.Value + } + appRoles = append(appRoles, appRole) + } + + return appRoles +} + func FlattenOauth2Permissions(in *[]graphrbac.OAuth2Permission) []map[string]interface{} { if in == nil { return []map[string]interface{}{} diff --git a/azuread/resource_application.go b/azuread/resource_application.go index ec147af9f0..58d4b961bb 100644 --- a/azuread/resource_application.go +++ b/azuread/resource_application.go @@ -396,7 +396,7 @@ func resourceApplicationRead(d *schema.ResourceData, meta interface{}) error { return fmt.Errorf("Error setting `required_resource_access`: %+v", err) } - if err := d.Set("app_role", flattenADApplicationAppRoles(app.AppRoles)); err != nil { + if err := d.Set("app_role", graph.FlattenAppRoles(app.AppRoles)); err != nil { return fmt.Errorf("Error setting `app_role`: %+v", err) } @@ -553,35 +553,3 @@ func expandADApplicationAppRoles(i interface{}) *[]graphrbac.AppRole { return &output } - -func flattenADApplicationAppRoles(in *[]graphrbac.AppRole) []interface{} { - if in == nil { - return []interface{}{} - } - - appRoles := make([]interface{}, 0) - for _, role := range *in { - appRole := make(map[string]interface{}) - if role.ID != nil { - appRole["id"] = *role.ID - } - if role.AllowedMemberTypes != nil { - appRole["allowed_member_types"] = *role.AllowedMemberTypes - } - if role.Description != nil { - appRole["description"] = *role.Description - } - if role.DisplayName != nil { - appRole["display_name"] = *role.DisplayName - } - if role.IsEnabled != nil { - appRole["is_enabled"] = *role.IsEnabled - } - if role.Value != nil { - appRole["value"] = *role.Value - } - appRoles = append(appRoles, appRole) - } - - return appRoles -} diff --git a/website/docs/d/application.html.markdown b/website/docs/d/application.html.markdown index 02dd5724e4..2704640ef8 100644 --- a/website/docs/d/application.html.markdown +++ b/website/docs/d/application.html.markdown @@ -54,6 +54,8 @@ output "azure_ad_object_id" { * `oauth2_permissions` - A collection of OAuth 2.0 permission scopes that the web API (resource) app exposes to client apps. Each permission is covered by a `oauth2_permission` block as documented below. +* `app_roles` - A collection of `app_role` blocks as documented below. For more information https://docs.microsoft.com/en-us/azure/architecture/multitenant-identity/app-roles + --- `required_resource_access` block exports the following: @@ -89,3 +91,19 @@ output "azure_ad_object_id" { * `user_consent_display_name` - The display name of the user consent * `value` - The name of this permission + +--- + +`app_role` block exports the following: + +* `id` - The unique identifier of the `app_role`. + +* `allowed_member_types` - Specifies whether this app role definition can be assigned to users and groups, or to other applications (that are accessing this application in daemon service scenarios). Possible values are: `User` and `Application`, or both. + +* `description` - Permission help text that appears in the admin app assignment and consent experiences. + +* `display_name` - Display name for the permission that appears in the admin consent and app assignment experiences. + +* `is_enabled` - Determines if the app role is enabled. + +* `value` - Specifies the value of the roles claim that the application should expect in the authentication and access tokens. diff --git a/website/docs/d/service_principal.html.markdown b/website/docs/d/service_principal.html.markdown index 0311777ab1..863cba746d 100644 --- a/website/docs/d/service_principal.html.markdown +++ b/website/docs/d/service_principal.html.markdown @@ -49,6 +49,8 @@ The following arguments are supported: -> **NOTE:** At least one of `application_id`, `display_name` or `object_id` must be specified. +* `app_roles` - A collection of `app_role` blocks as documented below. For more information https://docs.microsoft.com/en-us/azure/architecture/multitenant-identity/app-roles + * `oauth2_permissions` - A collection of OAuth 2.0 permissions exposed by the associated application. Each permission is covered by a `oauth2_permission` block as documented below. ## Attributes Reference @@ -76,3 +78,19 @@ The following attributes are exported: * `user_consent_display_name` - The display name of the user consent * `value` - The name of this permission + +--- + +`app_role` block exports the following: + +* `id` - The unique identifier of the `app_role`. + +* `allowed_member_types` - Specifies whether this app role definition can be assigned to users and groups, or to other applications (that are accessing this application in daemon service scenarios). Possible values are: `User` and `Application`, or both. + +* `description` - Permission help text that appears in the admin app assignment and consent experiences. + +* `display_name` - Display name for the permission that appears in the admin consent and app assignment experiences. + +* `is_enabled` - Determines if the app role is enabled. + +* `value` - Specifies the value of the roles claim that the application should expect in the authentication and access tokens. From 03e89e1b52eff32fb17e3a04105e7772ee081338 Mon Sep 17 00:00:00 2001 From: kt Date: Wed, 12 Jun 2019 19:44:53 -0700 Subject: [PATCH 28/45] Update CHANGELOG.md to include #110 --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 46ec6bcb4d..c0f6009ef5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,9 +2,10 @@ IMPROVEMENTS: +* Data Source `azuread_application` - support for the `app_roles` property [GH-110] +* Data Source `azuread_service_principal` - export the `app_roles` property [GH-110] * `azuread_application` - support for the `app_roles` property [GH-98] * `azuread_application_password` - deprecation of `application_id` in favour of `application_object_id` [GH-107] - * `azuread_service_principal` - export the `oauth2_permissions` property [GH-103] ## 0.4.0 (June 06, 2019) From 480d487c2442d000d16e1303fae1f36b75e61cfd Mon Sep 17 00:00:00 2001 From: kt Date: Wed, 12 Jun 2019 19:47:40 -0700 Subject: [PATCH 29/45] New Data Source: azuread_users (#109) --- azuread/data_user.go | 38 ++-------- azuread/data_user_test.go | 38 +++++----- azuread/data_users.go | 105 ++++++++++++++++++++++++++ azuread/data_users_test.go | 71 +++++++++++++++++ azuread/helpers/graph/user.go | 38 ++++++++++ azuread/helpers/tf/acctest.go | 27 +++++++ azuread/helpers/tf/acctest_test.go | 18 +++++ azuread/provider.go | 1 + azuread/resource_application.go | 50 ++++++------ azuread/resource_user_test.go | 117 +++++++++++++++-------------- website/azuread.erb | 3 + website/docs/d/user.html.markdown | 2 +- website/docs/d/users.html.markdown | 39 ++++++++++ 13 files changed, 412 insertions(+), 135 deletions(-) create mode 100644 azuread/data_users.go create mode 100644 azuread/data_users_test.go create mode 100644 azuread/helpers/graph/user.go create mode 100644 azuread/helpers/tf/acctest.go create mode 100644 azuread/helpers/tf/acctest_test.go create mode 100644 website/docs/d/users.html.markdown diff --git a/azuread/data_user.go b/azuread/data_user.go index d34c0dfcca..6e7444b32b 100644 --- a/azuread/data_user.go +++ b/azuread/data_user.go @@ -6,7 +6,7 @@ import ( "github.com/Azure/azure-sdk-for-go/services/graphrbac/1.6/graphrbac" "github.com/hashicorp/terraform/helper/schema" - "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/ar" + "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/graph" "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/validate" ) @@ -64,54 +64,26 @@ func dataSourceUserRead(d *schema.ResourceData, meta interface{}) error { var user graphrbac.User if upn, ok := d.Get("user_principal_name").(string); ok && upn != "" { - - // use the object_id to find the Azure AD application resp, err := client.Get(ctx, upn) if err != nil { - if ar.ResponseWasNotFound(resp.Response) { - return fmt.Errorf("Error: AzureAD User with ID %q was not found", upn) - } - return fmt.Errorf("Error making Read request on AzureAD User with ID %q: %+v", upn, err) } - user = resp } else if oId, ok := d.Get("object_id").(string); ok && oId != "" { - filter := fmt.Sprintf("objectId eq '%s'", oId) - - resp, err := client.ListComplete(ctx, filter) + u, err := graph.UserGetByObjectId(&client, ctx, oId) if err != nil { - return fmt.Errorf("Error listing Azure AD Users for filter %q: %+v", filter, err) - } - - values := resp.Response().Value - if values == nil { - return fmt.Errorf("nil values for AD Users matching %q", filter) - } - if len(*values) == 0 { - return fmt.Errorf("Found no AD Users matching %q", filter) - } - if len(*values) > 2 { - return fmt.Errorf("Found multiple AD Users matching %q", filter) - } - - user = (*values)[0] - if user.DisplayName == nil { - return fmt.Errorf("nil DisplayName for AD Users matching %q", filter) - } - if *user.ObjectID != oId { - return fmt.Errorf("objectID for AD Users matching %q does is does not match(%q!=%q)", filter, *user.ObjectID, oId) + return fmt.Errorf("Error finding Azure AD User with object ID %q: %+v", oId, err) } + user = *u } else { return fmt.Errorf("one of `object_id` or `user_principal_name` must be supplied") } if user.ObjectID == nil { - return fmt.Errorf("Group objectId is nil") + return fmt.Errorf("Azure AD User objectId is nil") } d.SetId(*user.ObjectID) - d.SetId(*user.ObjectID) d.Set("object_id", user.ObjectID) d.Set("user_principal_name", user.UserPrincipalName) d.Set("account_enabled", user.AccountEnabled) diff --git a/azuread/data_user_test.go b/azuread/data_user_test.go index b48bf3a247..a9359ae205 100644 --- a/azuread/data_user_test.go +++ b/azuread/data_user_test.go @@ -6,12 +6,14 @@ import ( "github.com/hashicorp/terraform/helper/acctest" "github.com/hashicorp/terraform/helper/resource" + + "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/tf" ) -func TestAccDataSourceAzureADUser_byUserPrincipalName(t *testing.T) { - dataSourceName := "data.azuread_user.test" - id := acctest.RandStringFromCharSet(7, acctest.CharSetAlphaNum) - password := id + "p@$$wR2" +func TestAccAzureADUserDataSource_byUserPrincipalName(t *testing.T) { + dsn := "data.azuread_user.test" + id := tf.AccRandTimeInt() + password := "p@$$wR2" + acctest.RandStringFromCharSet(7, acctest.CharSetAlphaNum) resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -20,20 +22,20 @@ func TestAccDataSourceAzureADUser_byUserPrincipalName(t *testing.T) { { Config: testAccAzureADUserDataSource_byUserPrincipalName(id, password), Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttrSet(dataSourceName, "user_principal_name"), - resource.TestCheckResourceAttrSet(dataSourceName, "account_enabled"), - resource.TestCheckResourceAttrSet(dataSourceName, "display_name"), - resource.TestCheckResourceAttrSet(dataSourceName, "mail_nickname"), + resource.TestCheckResourceAttrSet(dsn, "user_principal_name"), + resource.TestCheckResourceAttrSet(dsn, "account_enabled"), + resource.TestCheckResourceAttrSet(dsn, "display_name"), + resource.TestCheckResourceAttrSet(dsn, "mail_nickname"), ), }, }, }) } -func TestAccDataSourceAzureADUser_byObjectId(t *testing.T) { - dataSourceName := "data.azuread_user.test" - id := acctest.RandStringFromCharSet(7, acctest.CharSetAlphaNum) - password := id + "p@$$wR2" +func TestAccAzureADUserDataSource_byObjectId(t *testing.T) { + dsn := "data.azuread_user.test" + id := tf.AccRandTimeInt() + password := "p@$$wR2" + acctest.RandStringFromCharSet(7, acctest.CharSetAlphaNum) resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -42,17 +44,17 @@ func TestAccDataSourceAzureADUser_byObjectId(t *testing.T) { { Config: testAccAzureADUserDataSource_byObjectId(id, password), Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttrSet(dataSourceName, "user_principal_name"), - resource.TestCheckResourceAttrSet(dataSourceName, "account_enabled"), - resource.TestCheckResourceAttrSet(dataSourceName, "display_name"), - resource.TestCheckResourceAttrSet(dataSourceName, "mail_nickname"), + resource.TestCheckResourceAttrSet(dsn, "user_principal_name"), + resource.TestCheckResourceAttrSet(dsn, "account_enabled"), + resource.TestCheckResourceAttrSet(dsn, "display_name"), + resource.TestCheckResourceAttrSet(dsn, "mail_nickname"), ), }, }, }) } -func testAccAzureADUserDataSource_byUserPrincipalName(id, password string) string { +func testAccAzureADUserDataSource_byUserPrincipalName(id int, password string) string { return fmt.Sprintf(` %s @@ -62,7 +64,7 @@ data "azuread_user" "test" { `, testAccADUser_basic(id, password)) } -func testAccAzureADUserDataSource_byObjectId(id, password string) string { +func testAccAzureADUserDataSource_byObjectId(id int, password string) string { return fmt.Sprintf(` %s diff --git a/azuread/data_users.go b/azuread/data_users.go new file mode 100644 index 0000000000..f7b64e2d5b --- /dev/null +++ b/azuread/data_users.go @@ -0,0 +1,105 @@ +package azuread + +import ( + "crypto/sha1" + "encoding/base64" + "fmt" + "strings" + + "github.com/Azure/azure-sdk-for-go/services/graphrbac/1.6/graphrbac" + "github.com/hashicorp/terraform/helper/schema" + + "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/graph" + "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/validate" +) + +func dataSourceUsers() *schema.Resource { + return &schema.Resource{ + Read: dataSourceUsersRead, + + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Schema: map[string]*schema.Schema{ + "object_ids": { + Type: schema.TypeList, + Optional: true, + Computed: true, + MinItems: 1, + ConflictsWith: []string{"user_principal_names"}, + Elem: &schema.Schema{ + Type: schema.TypeString, + ValidateFunc: validate.UUID, + }, + }, + + "user_principal_names": { + Type: schema.TypeList, + Optional: true, + Computed: true, + MinItems: 1, + ConflictsWith: []string{"object_ids"}, + Elem: &schema.Schema{ + Type: schema.TypeString, + ValidateFunc: validate.NoEmptyStrings, + }, + }, + }, + } +} + +func dataSourceUsersRead(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).usersClient + ctx := meta.(*ArmClient).StopContext + + var users []graphrbac.User + expectedCount := 0 + + if upns, ok := d.Get("user_principal_names").([]interface{}); ok && len(upns) > 0 { + expectedCount = len(upns) + for _, v := range upns { + resp, err := client.Get(ctx, v.(string)) + if err != nil { + return fmt.Errorf("Error making Read request on AzureAD User with ID %q: %+v", v.(string), err) + } + + users = append(users, resp) + } + } else if oids, ok := d.Get("object_ids").([]interface{}); ok && len(oids) > 0 { + expectedCount = len(oids) + for _, v := range oids { + u, err := graph.UserGetByObjectId(&client, ctx, v.(string)) + if err != nil { + return fmt.Errorf("Error finding Azure AD User with object ID %q: %+v", v.(string), err) + } + users = append(users, *u) + } + } else { + return fmt.Errorf("one of `object_ids` or `user_principal_names` must be supplied") + } + + if len(users) != expectedCount { + return fmt.Errorf("Unexpected number of users returns (%d != %d)", len(users), expectedCount) + } + + var upns, oids []string + for _, u := range users { + if u.ObjectID == nil || u.UserPrincipalName == nil { + return fmt.Errorf("User with nil ObjectId or UPN was found: %v", u) + } + + oids = append(oids, *u.ObjectID) + upns = append(upns, *u.UserPrincipalName) + } + + h := sha1.New() + if _, err := h.Write([]byte(strings.Join(upns, "-"))); err != nil { + return fmt.Errorf("Unable to compute hash for upns: %v", err) + } + + d.SetId("users#" + base64.URLEncoding.EncodeToString(h.Sum(nil))) + d.Set("object_ids", oids) + d.Set("user_principal_names", upns) + return nil +} diff --git a/azuread/data_users_test.go b/azuread/data_users_test.go new file mode 100644 index 0000000000..e8867e0394 --- /dev/null +++ b/azuread/data_users_test.go @@ -0,0 +1,71 @@ +package azuread + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" + + "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/tf" +) + +func TestAccAzureADUsersDataSource_byUserPrincipalNames(t *testing.T) { + dsn := "data.azuread_users.test" + id := tf.AccRandTimeInt() + password := "p@$$wR2" + acctest.RandStringFromCharSet(7, acctest.CharSetAlphaNum) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccAzureADUsersDataSource_byUserPrincipalNames(id, password), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(dsn, "user_principal_names.#", "2"), + resource.TestCheckResourceAttr(dsn, "object_ids.#", "2"), + ), + }, + }, + }) +} + +func TestAccAzureADUsersDataSource_byObjectIds(t *testing.T) { + dsn := "data.azuread_users.test" + id := tf.AccRandTimeInt() + password := "p@$$wR2" + acctest.RandStringFromCharSet(7, acctest.CharSetAlphaNum) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccAzureADUsersDataSource_byObjectIds(id, password), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(dsn, "user_principal_names.#", "2"), + resource.TestCheckResourceAttr(dsn, "object_ids.#", "2"), + ), + }, + }, + }) +} + +func testAccAzureADUsersDataSource_byUserPrincipalNames(id int, password string) string { + return fmt.Sprintf(` +%s + +data "azuread_users" "test" { + user_principal_names = ["${azuread_user.testA.user_principal_name}", "${azuread_user.testB.user_principal_name}"] +} +`, testAccADUser_multiple(id, password)) +} + +func testAccAzureADUsersDataSource_byObjectIds(id int, password string) string { + return fmt.Sprintf(` +%s + +data "azuread_users" "test" { + object_ids = ["${azuread_user.testA.object_id}", "${azuread_user.testB.object_id}"] +} +`, testAccADUser_multiple(id, password)) +} diff --git a/azuread/helpers/graph/user.go b/azuread/helpers/graph/user.go new file mode 100644 index 0000000000..1bc79564de --- /dev/null +++ b/azuread/helpers/graph/user.go @@ -0,0 +1,38 @@ +package graph + +import ( + "context" + "fmt" + + "github.com/Azure/azure-sdk-for-go/services/graphrbac/1.6/graphrbac" +) + +func UserGetByObjectId(client *graphrbac.UsersClient, ctx context.Context, objectId string) (*graphrbac.User, error) { + + filter := fmt.Sprintf("objectId eq '%s'", objectId) + resp, err := client.ListComplete(ctx, filter) + if err != nil { + return nil, fmt.Errorf("Error listing Azure AD Users for filter %q: %+v", filter, err) + } + + values := resp.Response().Value + if values == nil { + return nil, fmt.Errorf("nil values for AD Users matching %q", filter) + } + if len(*values) == 0 { + return nil, fmt.Errorf("Found no AD Users matching %q", filter) + } + if len(*values) > 2 { + return nil, fmt.Errorf("Found multiple AD Users matching %q", filter) + } + + user := (*values)[0] + if user.DisplayName == nil { + return nil, fmt.Errorf("nil DisplayName for AD Users matching %q", filter) + } + if *user.ObjectID != objectId { + return nil, fmt.Errorf("objectID for AD Users matching %q does is does not match(%q!=%q)", filter, *user.ObjectID, objectId) + } + + return &user, nil +} diff --git a/azuread/helpers/tf/acctest.go b/azuread/helpers/tf/acctest.go new file mode 100644 index 0000000000..d9e96433be --- /dev/null +++ b/azuread/helpers/tf/acctest.go @@ -0,0 +1,27 @@ +package tf + +import ( + "strconv" + "strings" + "time" + + "github.com/hashicorp/terraform/helper/acctest" +) + +func AccRandTimeInt() int { + // acctest.RantInt() returns a value of size: + // 000000000000000000 + // YYMMddHHmmsshhRRRR + + //go format: 2006-01-02 15:04:05.00 + + timeStr := strings.Replace(time.Now().Local().Format("060102150405.00"), ".", "", 1) //no way to not have a .? + postfix := acctest.RandStringFromCharSet(4, "0123456789") + + i, err := strconv.Atoi(timeStr + postfix) + if err != nil { + panic(err) + } + + return i +} diff --git a/azuread/helpers/tf/acctest_test.go b/azuread/helpers/tf/acctest_test.go new file mode 100644 index 0000000000..bcc98e0281 --- /dev/null +++ b/azuread/helpers/tf/acctest_test.go @@ -0,0 +1,18 @@ +package tf + +import "testing" + +func TestAccRandTimeInt(t *testing.T) { + t.Run("Rand Date int", func(t *testing.T) { + ri := AccRandTimeInt() + + if ri < 100000000000000000 { + t.Fatalf("AccRandTimeInt returned a value (%d) shorter then expected", ri) + } + + if ri > 999999999999999999 { + t.Fatalf("AccRandTimeInt returned a value (%d) longer then expected", ri) + } + + }) +} diff --git a/azuread/provider.go b/azuread/provider.go index 1daa673133..dc5c2488f5 100644 --- a/azuread/provider.go +++ b/azuread/provider.go @@ -79,6 +79,7 @@ func Provider() terraform.ResourceProvider { "azuread_group": dataGroup(), "azuread_service_principal": dataServicePrincipal(), "azuread_user": dataSourceUser(), + "azuread_users": dataSourceUsers(), }, ResourcesMap: map[string]*schema.Resource{ diff --git a/azuread/resource_application.go b/azuread/resource_application.go index 58d4b961bb..1c9efd0d35 100644 --- a/azuread/resource_application.go +++ b/azuread/resource_application.go @@ -36,6 +36,20 @@ func resourceApplication() *schema.Resource { ValidateFunc: validation.NoZeroValues, }, + "available_to_other_tenants": { + Type: schema.TypeBool, + Optional: true, + }, + + "group_membership_claims": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice( + []string{"All", "None", "SecurityGroup", "DirectoryRole", "DistributionGroup"}, + false, + ), + }, + "homepage": { Type: schema.TypeString, Optional: true, @@ -53,6 +67,11 @@ func resourceApplication() *schema.Resource { }, }, + "oauth2_allow_implicit_flow": { + Type: schema.TypeBool, + Optional: true, + }, + "reply_urls": { Type: schema.TypeSet, Optional: true, @@ -63,30 +82,6 @@ func resourceApplication() *schema.Resource { }, }, - "available_to_other_tenants": { - Type: schema.TypeBool, - Optional: true, - }, - - "oauth2_allow_implicit_flow": { - Type: schema.TypeBool, - Optional: true, - }, - - "application_id": { - Type: schema.TypeString, - Computed: true, - }, - - "group_membership_claims": { - Type: schema.TypeString, - Optional: true, - ValidateFunc: validation.StringInSlice( - []string{"All", "None", "SecurityGroup", "DirectoryRole", "DistributionGroup"}, - false, - ), - }, - "type": { Type: schema.TypeString, Optional: true, @@ -180,12 +175,17 @@ func resourceApplication() *schema.Resource { }, }, - "oauth2_permissions": graph.SchemaOauth2Permissions(), + "application_id": { + Type: schema.TypeString, + Computed: true, + }, "object_id": { Type: schema.TypeString, Computed: true, }, + + "oauth2_permissions": graph.SchemaOauth2Permissions(), }, } } diff --git a/azuread/resource_user_test.go b/azuread/resource_user_test.go index 3776a08347..202330351e 100644 --- a/azuread/resource_user_test.go +++ b/azuread/resource_user_test.go @@ -9,12 +9,13 @@ import ( "github.com/hashicorp/terraform/terraform" "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/ar" + "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/tf" ) func TestAccAzureADUser_basic(t *testing.T) { - resourceName := "azuread_user.test" - id := acctest.RandStringFromCharSet(7, acctest.CharSetAlphaNum) - password := id + "p@$$wR2" + rn := "azuread_user.test" + id := tf.AccRandTimeInt() + pw := "p@$$wRd" + acctest.RandStringFromCharSet(7, acctest.CharSetAlphaNum) resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -22,18 +23,18 @@ func TestAccAzureADUser_basic(t *testing.T) { CheckDestroy: testCheckADApplicationDestroy, Steps: []resource.TestStep{ { - Config: testAccADUser_basic(id, password), + Config: testAccADUser_basic(id, pw), Check: resource.ComposeTestCheckFunc( - testCheckADUserExists(resourceName), - resource.TestCheckResourceAttrSet(resourceName, "user_principal_name"), - resource.TestCheckResourceAttrSet(resourceName, "object_id"), - resource.TestCheckResourceAttr(resourceName, "display_name", fmt.Sprintf("acctest%s", id)), - resource.TestCheckResourceAttr(resourceName, "mail_nickname", fmt.Sprintf("acctest%s", id)), - resource.TestCheckResourceAttr(resourceName, "account_enabled", "true"), + testCheckADUserExists(rn), + resource.TestCheckResourceAttrSet(rn, "user_principal_name"), + resource.TestCheckResourceAttrSet(rn, "object_id"), + resource.TestCheckResourceAttr(rn, "display_name", fmt.Sprintf("acctest%d", id)), + resource.TestCheckResourceAttr(rn, "mail_nickname", fmt.Sprintf("acctest%d", id)), + resource.TestCheckResourceAttr(rn, "account_enabled", "true"), ), }, { - ResourceName: resourceName, + ResourceName: rn, ImportState: true, ImportStateVerify: true, ImportStateVerifyIgnore: []string{ @@ -46,9 +47,9 @@ func TestAccAzureADUser_basic(t *testing.T) { } func TestAccAzureADUser_complete(t *testing.T) { - resourceName := "azuread_user.test" - id := acctest.RandStringFromCharSet(7, acctest.CharSetAlphaNum) - password := id + "p@$$wR2" + rn := "azuread_user.test" + id := tf.AccRandTimeInt() + pw := "p@$$wRd" + acctest.RandStringFromCharSet(7, acctest.CharSetAlphaNum) resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -56,18 +57,18 @@ func TestAccAzureADUser_complete(t *testing.T) { CheckDestroy: testCheckADApplicationDestroy, Steps: []resource.TestStep{ { - Config: testAccADUser_complete(id, password), + Config: testAccADUser_complete(id, pw), Check: resource.ComposeTestCheckFunc( - testCheckADUserExists(resourceName), - resource.TestCheckResourceAttrSet(resourceName, "user_principal_name"), - resource.TestCheckResourceAttrSet(resourceName, "object_id"), - resource.TestCheckResourceAttr(resourceName, "display_name", fmt.Sprintf("acctestupdate%s", id)), - resource.TestCheckResourceAttr(resourceName, "mail_nickname", fmt.Sprintf("acctestupdate%s", id)), - resource.TestCheckResourceAttr(resourceName, "account_enabled", "false"), + testCheckADUserExists(rn), + resource.TestCheckResourceAttrSet(rn, "user_principal_name"), + resource.TestCheckResourceAttrSet(rn, "object_id"), + resource.TestCheckResourceAttr(rn, "display_name", fmt.Sprintf("acctestupdate%d", id)), + resource.TestCheckResourceAttr(rn, "mail_nickname", fmt.Sprintf("acctestupdate%d", id)), + resource.TestCheckResourceAttr(rn, "account_enabled", "false"), ), }, { - ResourceName: resourceName, + ResourceName: rn, ImportState: true, ImportStateVerify: true, ImportStateVerifyIgnore: []string{ @@ -80,10 +81,10 @@ func TestAccAzureADUser_complete(t *testing.T) { } func TestAccAzureADUser_update(t *testing.T) { - resourceName := "azuread_user.test" - id := acctest.RandStringFromCharSet(7, acctest.CharSetAlphaNum) - password := id + "p@$$wRd" - updatedPassword := id + "p@$$wRd2" + rn := "azuread_user.test" + id := tf.AccRandTimeInt() + pw1 := "p@$$wRd" + acctest.RandStringFromCharSet(7, acctest.CharSetAlphaNum) + pw2 := "p@$$wRd2" + acctest.RandStringFromCharSet(7, acctest.CharSetAlphaNum) resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -91,38 +92,38 @@ func TestAccAzureADUser_update(t *testing.T) { CheckDestroy: testCheckADUserDestroy, Steps: []resource.TestStep{ { - Config: testAccADUser_basic(id, password), + Config: testAccADUser_basic(id, pw1), Check: resource.ComposeTestCheckFunc( - testCheckADUserExists(resourceName), - resource.TestCheckResourceAttrSet(resourceName, "user_principal_name"), - resource.TestCheckResourceAttrSet(resourceName, "object_id"), - resource.TestCheckResourceAttr(resourceName, "display_name", fmt.Sprintf("acctest%s", id)), - resource.TestCheckResourceAttr(resourceName, "mail_nickname", fmt.Sprintf("acctest%s", id)), - resource.TestCheckResourceAttr(resourceName, "account_enabled", "true"), + testCheckADUserExists(rn), + resource.TestCheckResourceAttrSet(rn, "user_principal_name"), + resource.TestCheckResourceAttrSet(rn, "object_id"), + resource.TestCheckResourceAttr(rn, "display_name", fmt.Sprintf("acctest%d", id)), + resource.TestCheckResourceAttr(rn, "mail_nickname", fmt.Sprintf("acctest%d", id)), + resource.TestCheckResourceAttr(rn, "account_enabled", "true"), ), }, { - Config: testAccADUser_complete(id, updatedPassword), + Config: testAccADUser_complete(id, pw2), Check: resource.ComposeTestCheckFunc( - testCheckADUserExists(resourceName), - resource.TestCheckResourceAttrSet(resourceName, "user_principal_name"), - resource.TestCheckResourceAttrSet(resourceName, "object_id"), - resource.TestCheckResourceAttr(resourceName, "display_name", fmt.Sprintf("acctestupdate%s", id)), - resource.TestCheckResourceAttr(resourceName, "mail_nickname", fmt.Sprintf("acctestupdate%s", id)), - resource.TestCheckResourceAttr(resourceName, "account_enabled", "false"), + testCheckADUserExists(rn), + resource.TestCheckResourceAttrSet(rn, "user_principal_name"), + resource.TestCheckResourceAttrSet(rn, "object_id"), + resource.TestCheckResourceAttr(rn, "display_name", fmt.Sprintf("acctestupdate%d", id)), + resource.TestCheckResourceAttr(rn, "mail_nickname", fmt.Sprintf("acctestupdate%d", id)), + resource.TestCheckResourceAttr(rn, "account_enabled", "false"), ), }, { - Config: testAccADUser_multiple(id, password), + Config: testAccADUser_multiple(id, pw1), Check: resource.ComposeTestCheckFunc( testCheckADUserExists("azuread_user.testA"), testCheckADUserExists("azuread_user.testB"), resource.TestCheckResourceAttrSet("azuread_user.testA", "user_principal_name"), - resource.TestCheckResourceAttr("azuread_user.testA", "display_name", fmt.Sprintf("acctestA%s", id)), - resource.TestCheckResourceAttr("azuread_user.testA", "mail_nickname", fmt.Sprintf("acctestA%s", id)), + resource.TestCheckResourceAttr("azuread_user.testA", "display_name", fmt.Sprintf("acctestA%d", id)), + resource.TestCheckResourceAttr("azuread_user.testA", "mail_nickname", fmt.Sprintf("acctestA%d", id)), resource.TestCheckResourceAttrSet("azuread_user.testB", "user_principal_name"), - resource.TestCheckResourceAttr("azuread_user.testB", "display_name", fmt.Sprintf("acctest_display%s", id)), - resource.TestCheckResourceAttr("azuread_user.testB", "mail_nickname", fmt.Sprintf("acctest_mail%s", id)), + resource.TestCheckResourceAttr("azuread_user.testB", "display_name", fmt.Sprintf("acctest_display%d", id)), + resource.TestCheckResourceAttr("azuread_user.testB", "mail_nickname", fmt.Sprintf("acctest_mail%d", id)), ), }, }, @@ -175,7 +176,7 @@ func testCheckADUserDestroy(s *terraform.State) error { return nil } -func testAccADUser_basic(id string, password string) string { +func testAccADUser_basic(id int, password string) string { return fmt.Sprintf(` data "azuread_domains" "tenant_domain" { @@ -183,14 +184,14 @@ data "azuread_domains" "tenant_domain" { } resource "azuread_user" "test" { - user_principal_name = "acctest%[1]s@${data.azuread_domains.tenant_domain.domains.0.domain_name}" - display_name = "acctest%[1]s" + user_principal_name = "acctest%[1]d@${data.azuread_domains.tenant_domain.domains.0.domain_name}" + display_name = "acctest%[1]d" password = "%[2]s" } `, id, password) } -func testAccADUser_complete(id string, password string) string { +func testAccADUser_complete(id int, password string) string { return fmt.Sprintf(` data "azuread_domains" "tenant_domain" { @@ -198,9 +199,9 @@ data "azuread_domains" "tenant_domain" { } resource "azuread_user" "test" { - user_principal_name = "acctest%[1]s@${data.azuread_domains.tenant_domain.domains.0.domain_name}" - display_name = "acctestupdate%[1]s" - mail_nickname = "acctestupdate%[1]s" + user_principal_name = "acctest%[1]d@${data.azuread_domains.tenant_domain.domains.0.domain_name}" + display_name = "acctestupdate%[1]d" + mail_nickname = "acctestupdate%[1]d" account_enabled = false password = "%[2]s" force_password_change = true @@ -208,7 +209,7 @@ resource "azuread_user" "test" { `, id, password) } -func testAccADUser_multiple(id string, password string) string { +func testAccADUser_multiple(id int, password string) string { return fmt.Sprintf(` data "azuread_domains" "tenant_domain" { @@ -216,15 +217,15 @@ data "azuread_domains" "tenant_domain" { } resource "azuread_user" "testA" { - user_principal_name = "acctestA%[1]s@${data.azuread_domains.tenant_domain.domains.0.domain_name}" - display_name = "acctestA%[1]s" + user_principal_name = "acctestA%[1]d@${data.azuread_domains.tenant_domain.domains.0.domain_name}" + display_name = "acctestA%[1]d" password = "%[2]s" } resource "azuread_user" "testB" { - user_principal_name = "acctestB%[1]s@${data.azuread_domains.tenant_domain.domains.0.domain_name}" - display_name = "acctest_display%[1]s" - mail_nickname = "acctest_mail%[1]s" + user_principal_name = "acctestB%[1]d@${data.azuread_domains.tenant_domain.domains.0.domain_name}" + display_name = "acctest_display%[1]d" + mail_nickname = "acctest_mail%[1]d" password = "%[2]s" } `, id, password) diff --git a/website/azuread.erb b/website/azuread.erb index 0fc53de4d5..5357e7d384 100644 --- a/website/azuread.erb +++ b/website/azuread.erb @@ -70,6 +70,9 @@ azuread_user + > + azuread_users + diff --git a/website/docs/d/user.html.markdown b/website/docs/d/user.html.markdown index f93b29549a..961879d0c3 100644 --- a/website/docs/d/user.html.markdown +++ b/website/docs/d/user.html.markdown @@ -17,7 +17,7 @@ Gets information about an Azure Active Directory user. ```hcl data "azuread_user" "test_user" { - user_principal_name = "john@hashicorp.com" + user_principal_name = "user@hashicorp.com" } ``` diff --git a/website/docs/d/users.html.markdown b/website/docs/d/users.html.markdown new file mode 100644 index 0000000000..64599047d9 --- /dev/null +++ b/website/docs/d/users.html.markdown @@ -0,0 +1,39 @@ +--- +layout: "azuread" +page_title: "Azure Active Directory: azuread_users" +sidebar_current: "docs-azuread-datasource-azuread-users" +description: |- + Gets information about Azure Active Directory users. + +--- + +# Data Source: azuread_user + +Gets Object IDs or UPNs for multiple Azure Active Directory users. + +-> **NOTE:** If you're authenticating using a Service Principal then it must have permissions to `Read directory data` within the `Windows Azure Active Directory` API. + +## Example Usage + +```hcl +data "azuread_users" "users" { + user_principal_name = ["kat@hashicorp.com", "byte@hashicorp.com"] +} +``` + +## Argument Reference + +The following arguments are supported: + +* `user_principal_names` - (optional) The User Principal Names of the Azure AD Users. + +* `object_ids` - (Optional) The Object IDs of the Azure AD Users. + +-> **NOTE:** Either a `user_principal_names` or an `object_ids` must be specified. + +## Attributes Reference + +The following attributes are exported: + +* `object_ids` - The Object IDs of the Azure AD Users. +* `user_principal_names` - The User Principal Names of the Azure AD Users. \ No newline at end of file From e027f7ac4c431c44c51876459ff0cb9b9983d0b0 Mon Sep 17 00:00:00 2001 From: kt Date: Wed, 12 Jun 2019 19:48:40 -0700 Subject: [PATCH 30/45] Update CHANGELOG.md to include #109 --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c0f6009ef5..a38196be74 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ ## 0.5.0 (Unreleased) +FEATURES: + +* **New Data Source:** `azuread_users` [GH-109] + IMPROVEMENTS: * Data Source `azuread_application` - support for the `app_roles` property [GH-110] From 6a58164d52482739a33652e5fb5f7af7326f8628 Mon Sep 17 00:00:00 2001 From: Alex Pilon Date: Fri, 14 Jun 2019 17:58:48 -0400 Subject: [PATCH 31/45] capture current hashibot config --- .hashibot.hcl | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 .hashibot.hcl diff --git a/.hashibot.hcl b/.hashibot.hcl new file mode 100644 index 0000000000..377611dabf --- /dev/null +++ b/.hashibot.hcl @@ -0,0 +1,24 @@ +queued_behavior "release_commenter" "releases" { + repo_prefix = "terraform-provider-" + message = <<-EOF + This has been released in [version ${var.release_version} of the provider](${var.changelog_link}). Please see the [Terraform documentation on provider versioning](https://www.terraform.io/docs/configuration/providers.html#provider-versions) or reach out if you need any assistance upgrading. As an example: + ```hcl + provider "${var.project_name}" { + version = "~> ${var.release_version}" + } + # ... other configuration ... + ``` + EOF +} + +poll "closed_issue_locker" "locker" { + schedule = "0 50 14 * * *" + closed_for = "720h" # 30 days + max_issues = 500 + sleep_between_issues = "5s" + message = <<-EOF + I'm going to lock this issue because it has been closed for _30 days_ ⏳. This helps our maintainers find and focus on the active issues. + + If you feel this issue should be reopened, we encourage creating a new issue linking back to this one for added context. If you feel I made an error 🤖 🙉 , please reach out to my human friends 👉 hashibot-feedback@hashicorp.com. Thanks! + EOF +} From 250aabea462351d5520d0dbeadb8513b07948bc8 Mon Sep 17 00:00:00 2001 From: Alex Pilon Date: Thu, 20 Jun 2019 14:31:08 -0400 Subject: [PATCH 32/45] terraform fmt --- .hashibot.hcl | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.hashibot.hcl b/.hashibot.hcl index 377611dabf..48f48cd61e 100644 --- a/.hashibot.hcl +++ b/.hashibot.hcl @@ -1,6 +1,6 @@ queued_behavior "release_commenter" "releases" { - repo_prefix = "terraform-provider-" - message = <<-EOF + repo_prefix = "terraform-provider-" + message = <<-EOF This has been released in [version ${var.release_version} of the provider](${var.changelog_link}). Please see the [Terraform documentation on provider versioning](https://www.terraform.io/docs/configuration/providers.html#provider-versions) or reach out if you need any assistance upgrading. As an example: ```hcl provider "${var.project_name}" { @@ -12,11 +12,11 @@ queued_behavior "release_commenter" "releases" { } poll "closed_issue_locker" "locker" { - schedule = "0 50 14 * * *" - closed_for = "720h" # 30 days - max_issues = 500 - sleep_between_issues = "5s" - message = <<-EOF + schedule = "0 50 14 * * *" + closed_for = "720h" # 30 days + max_issues = 500 + sleep_between_issues = "5s" + message = <<-EOF I'm going to lock this issue because it has been closed for _30 days_ ⏳. This helps our maintainers find and focus on the active issues. If you feel this issue should be reopened, we encourage creating a new issue linking back to this one for added context. If you feel I made an error 🤖 🙉 , please reach out to my human friends 👉 hashibot-feedback@hashicorp.com. Thanks! From 031cee1202c1ebdfbd2fd76acfeef5e6e3f0eb46 Mon Sep 17 00:00:00 2001 From: Alex Pilon Date: Thu, 20 Jun 2019 16:47:38 -0400 Subject: [PATCH 33/45] fix formatting --- .hashibot.hcl | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/.hashibot.hcl b/.hashibot.hcl index 48f48cd61e..7a58e42aa9 100644 --- a/.hashibot.hcl +++ b/.hashibot.hcl @@ -1,6 +1,7 @@ queued_behavior "release_commenter" "releases" { repo_prefix = "terraform-provider-" - message = <<-EOF + + message = <<-EOF This has been released in [version ${var.release_version} of the provider](${var.changelog_link}). Please see the [Terraform documentation on provider versioning](https://www.terraform.io/docs/configuration/providers.html#provider-versions) or reach out if you need any assistance upgrading. As an example: ```hcl provider "${var.project_name}" { @@ -8,17 +9,18 @@ queued_behavior "release_commenter" "releases" { } # ... other configuration ... ``` - EOF + EOF } poll "closed_issue_locker" "locker" { - schedule = "0 50 14 * * *" - closed_for = "720h" # 30 days - max_issues = 500 + schedule = "0 50 16 * * *" + closed_for = "720h" # 30 days + max_issues = 500 sleep_between_issues = "5s" + message = <<-EOF I'm going to lock this issue because it has been closed for _30 days_ ⏳. This helps our maintainers find and focus on the active issues. - If you feel this issue should be reopened, we encourage creating a new issue linking back to this one for added context. If you feel I made an error 🤖 🙉 , please reach out to my human friends 👉 hashibot-feedback@hashicorp.com. Thanks! - EOF -} + If you feel this issue should be reopened, we encourage creating a new issue linking back to this one for added context. If you feel I made an error 🤖 🙉 , please reach out to my human friends 👉 hashibot-feedback@hashicorp.com. Thanks! + EOF +} \ No newline at end of file From eeaab4b15fa547dd7b619a8156679f75235c4ad3 Mon Sep 17 00:00:00 2001 From: Tom Bamford Date: Fri, 5 Jul 2019 21:43:04 +0100 Subject: [PATCH 34/45] azuread_application: Allow all valid app identifier URI schemes (#115) --- azuread/helpers/validate/url.go | 4 +++ azuread/helpers/validate/url_test.go | 54 ++++++++++++++++++++++++++++ azuread/resource_application.go | 2 +- 3 files changed, 59 insertions(+), 1 deletion(-) diff --git a/azuread/helpers/validate/url.go b/azuread/helpers/validate/url.go index e850c1be0a..a2c3a251bb 100644 --- a/azuread/helpers/validate/url.go +++ b/azuread/helpers/validate/url.go @@ -16,6 +16,10 @@ func URLIsHTTPOrHTTPS(i interface{}, k string) (_ []string, errors []error) { return URLWithScheme([]string{"http", "https"})(i, k) } +func URLIsAppURI(i interface{}, k string) (_ []string, errors []error) { + return URLWithScheme([]string{"http", "https", "api", "urn", "ms-appx"})(i, k) +} + func URLWithScheme(validSchemes []string) schema.SchemaValidateFunc { return func(i interface{}, k string) (_ []string, errors []error) { v, ok := i.(string) diff --git a/azuread/helpers/validate/url_test.go b/azuread/helpers/validate/url_test.go index ed740aecc7..8abae5933c 100644 --- a/azuread/helpers/validate/url_test.go +++ b/azuread/helpers/validate/url_test.go @@ -87,3 +87,57 @@ func TestURLIsHTTPOrHTTPS(t *testing.T) { }) } } + +func TestURLIsAppURI(t *testing.T) { + cases := []struct { + Url string + Errors int + }{ + { + Url: "", + Errors: 1, + }, + { + Url: "this is not a url", + Errors: 1, + }, + { + Url: "www.example.com", + Errors: 1, + }, + { + Url: "ftp://www.example.com", + Errors: 1, + }, + { + Url: "http://www.example.com", + Errors: 0, + }, + { + Url: "https://www.example.com", + Errors: 0, + }, + { + Url: "api://www.example.com", + Errors: 0, + }, + { + Url: "urn://www.example.com", + Errors: 0, + }, + { + Url: "ms-appx://www.example.com", + Errors: 0, + }, + } + + for _, tc := range cases { + t.Run(tc.Url, func(t *testing.T) { + _, errors := URLIsAppURI(tc.Url, "test") + + if len(errors) != tc.Errors { + t.Fatalf("Expected URLIsAppURI to have %d not %d errors for %q", tc.Errors, len(errors), tc.Url) + } + }) + } +} diff --git a/azuread/resource_application.go b/azuread/resource_application.go index 1c9efd0d35..cee73d9b30 100644 --- a/azuread/resource_application.go +++ b/azuread/resource_application.go @@ -63,7 +63,7 @@ func resourceApplication() *schema.Resource { Computed: true, Elem: &schema.Schema{ Type: schema.TypeString, - ValidateFunc: validate.URLIsHTTPOrHTTPS, + ValidateFunc: validate.URLIsAppURI, }, }, From 695924520d7de761fe10bc601d1500562880861e Mon Sep 17 00:00:00 2001 From: kt Date: Fri, 5 Jul 2019 13:44:53 -0700 Subject: [PATCH 35/45] Update CHANGELOG.md to include #115 --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a38196be74..e161230505 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -## 0.5.0 (Unreleased) +*## 0.5.0 (Unreleased) FEATURES: @@ -9,6 +9,7 @@ IMPROVEMENTS: * Data Source `azuread_application` - support for the `app_roles` property [GH-110] * Data Source `azuread_service_principal` - export the `app_roles` property [GH-110] * `azuread_application` - support for the `app_roles` property [GH-98] +* `azuread_application` - the `identifier_uris` property now allows `api`,`urn`, and `ms-appx` URI schemas [GH-115] * `azuread_application_password` - deprecation of `application_id` in favour of `application_object_id` [GH-107] * `azuread_service_principal` - export the `oauth2_permissions` property [GH-103] From 688574c9b9e0a55434ffe91f7262df1d70659751 Mon Sep 17 00:00:00 2001 From: Robert Knochenhauer Date: Sat, 6 Jul 2019 08:54:00 +0200 Subject: [PATCH 36/45] azuread user - import example (#114) --- website/docs/r/user.html.markdown | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/website/docs/r/user.html.markdown b/website/docs/r/user.html.markdown index 2afce8393b..792c0f2422 100644 --- a/website/docs/r/user.html.markdown +++ b/website/docs/r/user.html.markdown @@ -42,3 +42,11 @@ The following attributes are exported: * `object_id` - The Object ID of the Azure AD User. * `id` - The Object ID of the Azure AD User. * `mail` - The primary email address of the Azure AD User. + +## Import + +Azure Active Directory Users can be imported using the `object id`, e.g. + +```shell +terraform import azuread_user.my_user 00000000-0000-0000-0000-000000000000 +``` From 6e312139b3013868bffcd09f107e9b39b8012efa Mon Sep 17 00:00:00 2001 From: kt Date: Tue, 9 Jul 2019 01:06:11 -0700 Subject: [PATCH 37/45] run terrafmt on website & acctest tf blocks (#113) --- azuread/data_user_test.go | 4 +- azuread/data_users_test.go | 4 +- azuread/resource_application_test.go | 34 +++--- azuread/resource_user_test.go | 41 ++++--- ...principal_client_certificate.html.markdown | 2 +- ...vice_principal_client_secret.html.markdown | 2 +- website/docs/d/user.html.markdown | 86 +++++++-------- website/docs/d/users.html.markdown | 78 ++++++------- website/docs/r/application.html.markdown | 17 +-- .../docs/r/application_password.html.markdown | 6 +- .../docs/r/service_principal.html.markdown | 2 +- website/docs/r/user.html.markdown | 104 +++++++++--------- 12 files changed, 190 insertions(+), 190 deletions(-) diff --git a/azuread/data_user_test.go b/azuread/data_user_test.go index a9359ae205..99a4c2c107 100644 --- a/azuread/data_user_test.go +++ b/azuread/data_user_test.go @@ -59,7 +59,7 @@ func testAccAzureADUserDataSource_byUserPrincipalName(id int, password string) s %s data "azuread_user" "test" { - user_principal_name = "${azuread_user.test.user_principal_name}" + user_principal_name = "${azuread_user.test.user_principal_name}" } `, testAccADUser_basic(id, password)) } @@ -69,7 +69,7 @@ func testAccAzureADUserDataSource_byObjectId(id int, password string) string { %s data "azuread_user" "test" { - object_id = "${azuread_user.test.object_id}" + object_id = "${azuread_user.test.object_id}" } `, testAccADUser_basic(id, password)) } diff --git a/azuread/data_users_test.go b/azuread/data_users_test.go index e8867e0394..b24f12c71d 100644 --- a/azuread/data_users_test.go +++ b/azuread/data_users_test.go @@ -55,7 +55,7 @@ func testAccAzureADUsersDataSource_byUserPrincipalNames(id int, password string) %s data "azuread_users" "test" { - user_principal_names = ["${azuread_user.testA.user_principal_name}", "${azuread_user.testB.user_principal_name}"] + user_principal_names = ["${azuread_user.testA.user_principal_name}", "${azuread_user.testB.user_principal_name}"] } `, testAccADUser_multiple(id, password)) } @@ -65,7 +65,7 @@ func testAccAzureADUsersDataSource_byObjectIds(id int, password string) string { %s data "azuread_users" "test" { - object_ids = ["${azuread_user.testA.object_id}", "${azuread_user.testB.object_id}"] + object_ids = ["${azuread_user.testA.object_id}", "${azuread_user.testB.object_id}"] } `, testAccADUser_multiple(id, password)) } diff --git a/azuread/resource_application_test.go b/azuread/resource_application_test.go index 77acd7448d..2fd53a23c7 100644 --- a/azuread/resource_application_test.go +++ b/azuread/resource_application_test.go @@ -469,9 +469,8 @@ resource "azuread_application" "test" { func testAccADApplication_availableToOtherTenants(id string) string { return fmt.Sprintf(` - data "azuread_domains" "tenant_domain" { - only_initial = true + only_initial = true } resource "azuread_application" "test" { @@ -485,8 +484,8 @@ resource "azuread_application" "test" { func testAccADApplication_withGroupMembershipClaimsDirectoryRole(id string) string { return fmt.Sprintf(` resource "azuread_application" "test" { - name = "acctest%s" - group_membership_claims = "DirectoryRole" + name = "acctest%s" + group_membership_claims = "DirectoryRole" } `, id) } @@ -494,8 +493,8 @@ resource "azuread_application" "test" { func testAccADApplication_withGroupMembershipClaimsSecurityGroup(id string) string { return fmt.Sprintf(` resource "azuread_application" "test" { - name = "acctest%s" - group_membership_claims = "SecurityGroup" + name = "acctest%s" + group_membership_claims = "SecurityGroup" } `, id) } @@ -514,17 +513,17 @@ resource "azuread_application" "test" { resource_app_id = "00000003-0000-0000-c000-000000000000" resource_access { - id = "7ab1d382-f21e-4acd-a863-ba3e13f7da61" + id = "7ab1d382-f21e-4acd-a863-ba3e13f7da61" type = "Role" } resource_access { - id = "e1fe6dd8-ba31-4d61-89e7-88639da4683d" + id = "e1fe6dd8-ba31-4d61-89e7-88639da4683d" type = "Scope" } resource_access { - id = "06da0dbc-49e2-44d2-8312-53f166ab848a" + id = "06da0dbc-49e2-44d2-8312-53f166ab848a" type = "Scope" } } @@ -533,7 +532,7 @@ resource "azuread_application" "test" { resource_app_id = "00000002-0000-0000-c000-000000000000" resource_access { - id = "311a71cc-e848-46a1-bdf8-97ff7156d8e6" + id = "311a71cc-e848-46a1-bdf8-97ff7156d8e6" type = "Scope" } } @@ -544,16 +543,18 @@ resource "azuread_application" "test" { func testAccADApplication_appRoles(id string) string { return fmt.Sprintf(` resource "azuread_application" "test" { - name = "acctest%s" + name = "acctest%s" + app_role { allowed_member_types = [ "User", "Application", ] - description = "Admins can manage roles and perform all task actions" - display_name = "Admin" - is_enabled = true - value = "Admin" + + description = "Admins can manage roles and perform all task actions" + display_name = "Admin" + is_enabled = true + value = "Admin" } } `, id) @@ -562,7 +563,8 @@ resource "azuread_application" "test" { func testAccADApplication_appRolesUpdate(id string) string { return fmt.Sprintf(` resource "azuread_application" "test" { - name = "acctest%s" + name = "acctest%s" + app_role { allowed_member_types = ["User"] description = "Admins can manage roles and perform all task actions" diff --git a/azuread/resource_user_test.go b/azuread/resource_user_test.go index 202330351e..948c4343c4 100644 --- a/azuread/resource_user_test.go +++ b/azuread/resource_user_test.go @@ -178,55 +178,52 @@ func testCheckADUserDestroy(s *terraform.State) error { func testAccADUser_basic(id int, password string) string { return fmt.Sprintf(` - data "azuread_domains" "tenant_domain" { - only_initial = true + only_initial = true } resource "azuread_user" "test" { - user_principal_name = "acctest%[1]d@${data.azuread_domains.tenant_domain.domains.0.domain_name}" - display_name = "acctest%[1]d" - password = "%[2]s" + user_principal_name = "acctest%[1]d@${data.azuread_domains.tenant_domain.domains.0.domain_name}" + display_name = "acctest%[1]d" + password = "%[2]s" } `, id, password) } func testAccADUser_complete(id int, password string) string { return fmt.Sprintf(` - data "azuread_domains" "tenant_domain" { - only_initial = true + only_initial = true } resource "azuread_user" "test" { - user_principal_name = "acctest%[1]d@${data.azuread_domains.tenant_domain.domains.0.domain_name}" - display_name = "acctestupdate%[1]d" - mail_nickname = "acctestupdate%[1]d" - account_enabled = false - password = "%[2]s" - force_password_change = true + user_principal_name = "acctest%[1]d@${data.azuread_domains.tenant_domain.domains.0.domain_name}" + display_name = "acctestupdate%[1]d" + mail_nickname = "acctestupdate%[1]d" + account_enabled = false + password = "%[2]s" + force_password_change = true } `, id, password) } func testAccADUser_multiple(id int, password string) string { return fmt.Sprintf(` - data "azuread_domains" "tenant_domain" { - only_initial = true + only_initial = true } resource "azuread_user" "testA" { - user_principal_name = "acctestA%[1]d@${data.azuread_domains.tenant_domain.domains.0.domain_name}" - display_name = "acctestA%[1]d" - password = "%[2]s" + user_principal_name = "acctestA%[1]d@${data.azuread_domains.tenant_domain.domains.0.domain_name}" + display_name = "acctestA%[1]d" + password = "%[2]s" } resource "azuread_user" "testB" { - user_principal_name = "acctestB%[1]d@${data.azuread_domains.tenant_domain.domains.0.domain_name}" - display_name = "acctest_display%[1]d" - mail_nickname = "acctest_mail%[1]d" - password = "%[2]s" + user_principal_name = "acctestB%[1]d@${data.azuread_domains.tenant_domain.domains.0.domain_name}" + display_name = "acctest_display%[1]d" + mail_nickname = "acctest_mail%[1]d" + password = "%[2]s" } `, id, password) } diff --git a/website/docs/auth/service_principal_client_certificate.html.markdown b/website/docs/auth/service_principal_client_certificate.html.markdown index 93651fa08b..e2b4cc1401 100644 --- a/website/docs/auth/service_principal_client_certificate.html.markdown +++ b/website/docs/auth/service_principal_client_certificate.html.markdown @@ -145,4 +145,4 @@ More information on [the fields supported in the Provider block can be found her At this point running either `terraform plan` or `terraform apply` should allow Terraform to run using the Service Principal to authenticate. -Next you may want to follow the [Granting a Service Principal permission to manage AAD](service_principal_configuration.html) guide to grant the Service Ability permission to create and modify Azure Active Directory objects such as users and groups. \ No newline at end of file +Next you may want to follow the [Granting a Service Principal permission to manage AAD](service_principal_configuration.html) guide to grant the Service Ability permission to create and modify Azure Active Directory objects such as users and groups. diff --git a/website/docs/auth/service_principal_client_secret.html.markdown b/website/docs/auth/service_principal_client_secret.html.markdown index cfed9a200c..1b6ab5cf06 100644 --- a/website/docs/auth/service_principal_client_secret.html.markdown +++ b/website/docs/auth/service_principal_client_secret.html.markdown @@ -219,4 +219,4 @@ More information on [the fields supported in the Provider block can be found her At this point running either `terraform plan` or `terraform apply` should allow Terraform to run using the Service Principal to authenticate. -Next you may want to follow the [Granting a Service Principal permission to manage AAD](service_principal_configuration.html) guide to grant the Service Ability permission to create and modify Azure Active Directory objects such as users and groups. \ No newline at end of file +Next you may want to follow the [Granting a Service Principal permission to manage AAD](service_principal_configuration.html) guide to grant the Service Ability permission to create and modify Azure Active Directory objects such as users and groups. diff --git a/website/docs/d/user.html.markdown b/website/docs/d/user.html.markdown index 961879d0c3..f11ae1df0d 100644 --- a/website/docs/d/user.html.markdown +++ b/website/docs/d/user.html.markdown @@ -1,43 +1,43 @@ ---- -layout: "azuread" -page_title: "Azure Active Directory: azuread_user" -sidebar_current: "docs-azuread-datasource-azuread-user" -description: |- - Gets information about an Azure Active Directory user. - ---- - -# Data Source: azuread_user - -Gets information about an Azure Active Directory user. - --> **NOTE:** If you're authenticating using a Service Principal then it must have permissions to `Read directory data` within the `Windows Azure Active Directory` API. - -## Example Usage - -```hcl -data "azuread_user" "test_user" { - user_principal_name = "user@hashicorp.com" -} -``` - -## Argument Reference - -The following arguments are supported: - -* `user_principal_name` - (Required) The User Principal Name of the Azure AD User. - -* `object_id` - (Optional) Specifies the Object ID of the Application within Azure Active Directory. - --> **NOTE:** Either a `user_principal_name` or an `object_id` must be specified. - -## Attributes Reference - -The following attributes are exported: - -* `id` - The Object ID of the Azure AD User. -* `user_principal_name` - The User Principal Name of the Azure AD User. -* `account_enabled` - `True` if the account is enabled; otherwise `False`. -* `display_name` - The Display Name of the Azure AD User. -* `mail` - The primary email address of the Azure AD User. -* `mail_nickname` - The email alias of the Azure AD User. +--- +layout: "azuread" +page_title: "Azure Active Directory: azuread_user" +sidebar_current: "docs-azuread-datasource-azuread-user" +description: |- + Gets information about an Azure Active Directory user. + +--- + +# Data Source: azuread_user + +Gets information about an Azure Active Directory user. + +-> **NOTE:** If you're authenticating using a Service Principal then it must have permissions to `Read directory data` within the `Windows Azure Active Directory` API. + +## Example Usage + +```hcl +data "azuread_user" "test_user" { + user_principal_name = "user@hashicorp.com" +} +``` + +## Argument Reference + +The following arguments are supported: + +* `user_principal_name` - (Required) The User Principal Name of the Azure AD User. + +* `object_id` - (Optional) Specifies the Object ID of the Application within Azure Active Directory. + +-> **NOTE:** Either a `user_principal_name` or an `object_id` must be specified. + +## Attributes Reference + +The following attributes are exported: + +* `id` - The Object ID of the Azure AD User. +* `user_principal_name` - The User Principal Name of the Azure AD User. +* `account_enabled` - `True` if the account is enabled; otherwise `False`. +* `display_name` - The Display Name of the Azure AD User. +* `mail` - The primary email address of the Azure AD User. +* `mail_nickname` - The email alias of the Azure AD User. diff --git a/website/docs/d/users.html.markdown b/website/docs/d/users.html.markdown index 64599047d9..da248c6066 100644 --- a/website/docs/d/users.html.markdown +++ b/website/docs/d/users.html.markdown @@ -1,39 +1,39 @@ ---- -layout: "azuread" -page_title: "Azure Active Directory: azuread_users" -sidebar_current: "docs-azuread-datasource-azuread-users" -description: |- - Gets information about Azure Active Directory users. - ---- - -# Data Source: azuread_user - -Gets Object IDs or UPNs for multiple Azure Active Directory users. - --> **NOTE:** If you're authenticating using a Service Principal then it must have permissions to `Read directory data` within the `Windows Azure Active Directory` API. - -## Example Usage - -```hcl -data "azuread_users" "users" { - user_principal_name = ["kat@hashicorp.com", "byte@hashicorp.com"] -} -``` - -## Argument Reference - -The following arguments are supported: - -* `user_principal_names` - (optional) The User Principal Names of the Azure AD Users. - -* `object_ids` - (Optional) The Object IDs of the Azure AD Users. - --> **NOTE:** Either a `user_principal_names` or an `object_ids` must be specified. - -## Attributes Reference - -The following attributes are exported: - -* `object_ids` - The Object IDs of the Azure AD Users. -* `user_principal_names` - The User Principal Names of the Azure AD Users. \ No newline at end of file +--- +layout: "azuread" +page_title: "Azure Active Directory: azuread_users" +sidebar_current: "docs-azuread-datasource-azuread-users" +description: |- + Gets information about Azure Active Directory users. + +--- + +# Data Source: azuread_user + +Gets Object IDs or UPNs for multiple Azure Active Directory users. + +-> **NOTE:** If you're authenticating using a Service Principal then it must have permissions to `Read directory data` within the `Windows Azure Active Directory` API. + +## Example Usage + +```hcl +data "azuread_users" "users" { + user_principal_name = ["kat@hashicorp.com", "byte@hashicorp.com"] +} +``` + +## Argument Reference + +The following arguments are supported: + +* `user_principal_names` - (optional) The User Principal Names of the Azure AD Users. + +* `object_ids` - (Optional) The Object IDs of the Azure AD Users. + +-> **NOTE:** Either a `user_principal_names` or an `object_ids` must be specified. + +## Attributes Reference + +The following attributes are exported: + +* `object_ids` - The Object IDs of the Azure AD Users. +* `user_principal_names` - The User Principal Names of the Azure AD Users. diff --git a/website/docs/r/application.html.markdown b/website/docs/r/application.html.markdown index 2ce2f62b23..e1829aa19f 100644 --- a/website/docs/r/application.html.markdown +++ b/website/docs/r/application.html.markdown @@ -27,27 +27,28 @@ resource "azuread_application" "test" { required_resource_access { resource_app_id = "00000003-0000-0000-c000-000000000000" - + resource_access { - id = "..." + id = "..." type = "Role" } + resource_access { - id = "..." + id = "..." type = "Scope" } - + resource_access { - id = "..." + id = "..." type = "Scope" } } - + required_resource_access { resource_app_id = "00000002-0000-0000-c000-000000000000" - + resource_access { - id = "..." + id = "..." type = "Scope" } } diff --git a/website/docs/r/application_password.html.markdown b/website/docs/r/application_password.html.markdown index 36fe1a4c9c..80f89561db 100644 --- a/website/docs/r/application_password.html.markdown +++ b/website/docs/r/application_password.html.markdown @@ -26,9 +26,9 @@ resource "azuread_application" "example" { } resource "azuread_application_password" "example" { - application_id = "${azuread_application.example.id}" - value = "VT=uSgbTanZhyz@%nL9Hpd+Tfay_MRV#" - end_date = "2020-01-01T01:02:03Z" + application_id = "${azuread_application.example.id}" + value = "VT=uSgbTanZhyz@%nL9Hpd+Tfay_MRV#" + end_date = "2020-01-01T01:02:03Z" } ``` diff --git a/website/docs/r/service_principal.html.markdown b/website/docs/r/service_principal.html.markdown index 3f1dcefd3a..50bd5bb7fd 100644 --- a/website/docs/r/service_principal.html.markdown +++ b/website/docs/r/service_principal.html.markdown @@ -27,7 +27,7 @@ resource "azuread_application" "test" { resource "azuread_service_principal" "test" { application_id = "${azuread_application.test.application_id}" - + tags = ["example", "tags", "here"] } ``` diff --git a/website/docs/r/user.html.markdown b/website/docs/r/user.html.markdown index 792c0f2422..77f24d27eb 100644 --- a/website/docs/r/user.html.markdown +++ b/website/docs/r/user.html.markdown @@ -1,52 +1,52 @@ ---- -layout: "azuread" -page_title: "Azure Active Directory: azuread_user" -sidebar_current: "docs-azuread-resource-azuread-user" -description: |- - Manages a User within Azure Active Directory. - ---- - -# azuread_user - -Manages a User within Azure Active Directory. - --> **NOTE:** If you're authenticating using a Service Principal then it must have permissions to `Directory.ReadWrite.All` within the `Windows Azure Active Directory` API. - -## Example Usage - -```hcl -resource "azuread_user" "test_user" { - user_principal_name = "john@hashicorp.com" - display_name = "John Doe" - mail_nickname = "johnd" - password = "SecretP@sswd99!" -} -``` - -## Argument Reference - -The following arguments are supported: - -* `user_principal_name` - (Required) The User Principal Name of the Azure AD User. -* `display_name` - (Required) The name to display in the address book for the user. -* `account_enabled` - (Optional) `true` if the account should be enabled, otherwise `false`. Defaults to `true`. -* `mail_nickname`- (Optional) The mail alias for the user. Defaults to the user name part of the User Principal Name. -* `password` - (Required) The password for the User. The password must satisfy minimum requirements as specified by the password policy. The maximum length is 256 characters. -* `force_password_change` - (Optional) `true` if the User is forced to change the password during the next sign-in. Defaults to `false`. - -## Attributes Reference - -The following attributes are exported: - -* `object_id` - The Object ID of the Azure AD User. -* `id` - The Object ID of the Azure AD User. -* `mail` - The primary email address of the Azure AD User. - -## Import - -Azure Active Directory Users can be imported using the `object id`, e.g. - -```shell -terraform import azuread_user.my_user 00000000-0000-0000-0000-000000000000 -``` +--- +layout: "azuread" +page_title: "Azure Active Directory: azuread_user" +sidebar_current: "docs-azuread-resource-azuread-user" +description: |- + Manages a User within Azure Active Directory. + +--- + +# azuread_user + +Manages a User within Azure Active Directory. + +-> **NOTE:** If you're authenticating using a Service Principal then it must have permissions to `Directory.ReadWrite.All` within the `Windows Azure Active Directory` API. + +## Example Usage + +```hcl +resource "azuread_user" "test_user" { + user_principal_name = "john@hashicorp.com" + display_name = "John Doe" + mail_nickname = "johnd" + password = "SecretP@sswd99!" +} +``` + +## Argument Reference + +The following arguments are supported: + +* `user_principal_name` - (Required) The User Principal Name of the Azure AD User. +* `display_name` - (Required) The name to display in the address book for the user. +* `account_enabled` - (Optional) `true` if the account should be enabled, otherwise `false`. Defaults to `true`. +* `mail_nickname`- (Optional) The mail alias for the user. Defaults to the user name part of the User Principal Name. +* `password` - (Required) The password for the User. The password must satisfy minimum requirements as specified by the password policy. The maximum length is 256 characters. +* `force_password_change` - (Optional) `true` if the User is forced to change the password during the next sign-in. Defaults to `false`. + +## Attributes Reference + +The following attributes are exported: + +* `object_id` - The Object ID of the Azure AD User. +* `id` - The Object ID of the Azure AD User. +* `mail` - The primary email address of the Azure AD User. + +## Import + +Azure Active Directory Users can be imported using the `object id`, e.g. + +```shell +terraform import azuread_user.my_user 00000000-0000-0000-0000-000000000000 +``` From e9db6a8ae78ea542439af24cb75180d987d04ae7 Mon Sep 17 00:00:00 2001 From: Even Holthe Date: Fri, 12 Jul 2019 03:05:50 +0200 Subject: [PATCH 38/45] Resource: 'azuread_group_member' (#100) continues #63 fixed 1/2 of #36 --- azuread/helpers/graph/group.go | 76 +++++++++ azuread/helpers/slices/slices.go | 16 ++ azuread/provider.go | 1 + azuread/resource_group.go | 65 +++++++ azuread/resource_group_member.go | 113 ++++++++++++ azuread/resource_group_member_test.go | 224 ++++++++++++++++++++++++ azuread/resource_group_test.go | 237 ++++++++++++++++++++++++-- website/azuread.erb | 4 + website/docs/r/group.markdown | 24 ++- website/docs/r/group_member.markdown | 57 +++++++ 10 files changed, 806 insertions(+), 11 deletions(-) create mode 100644 azuread/helpers/graph/group.go create mode 100644 azuread/helpers/slices/slices.go create mode 100644 azuread/resource_group_member.go create mode 100644 azuread/resource_group_member_test.go create mode 100644 website/docs/r/group_member.markdown diff --git a/azuread/helpers/graph/group.go b/azuread/helpers/graph/group.go new file mode 100644 index 0000000000..68308e4159 --- /dev/null +++ b/azuread/helpers/graph/group.go @@ -0,0 +1,76 @@ +package graph + +import ( + "context" + "fmt" + "log" + + "github.com/Azure/azure-sdk-for-go/services/graphrbac/1.6/graphrbac" +) + +func GroupAllMembers(client graphrbac.GroupsClient, ctx context.Context, groupId string) ([]string, error) { + it, err := client.GetGroupMembersComplete(ctx, groupId) + + if err != nil { + return nil, fmt.Errorf("Error listing existing group members from Azure AD Group with ID %q: %+v", groupId, err) + } + + existingMembers := make([]string, 0) + + var memberObjectID string + for it.NotDone() { + // possible members are users, groups or service principals + // we try to 'cast' each result as the corresponding type and diff + // if we found the object we're looking for + user, _ := it.Value().AsUser() + if user != nil { + memberObjectID = *user.ObjectID + } + + group, _ := it.Value().AsADGroup() + if group != nil { + memberObjectID = *group.ObjectID + } + + servicePrincipal, _ := it.Value().AsServicePrincipal() + if servicePrincipal != nil { + memberObjectID = *servicePrincipal.ObjectID + } + + existingMembers = append(existingMembers, memberObjectID) + if err := it.NextWithContext(ctx); err != nil { + return nil, fmt.Errorf("Error during pagination of group members from Azure AD Group with ID %q: %+v", groupId, err) + } + } + + log.Printf("[DEBUG] %d members in Azure AD group with ID: %q", len(existingMembers), groupId) + + return existingMembers, nil +} + +func GroupAddMember(client graphrbac.GroupsClient, ctx context.Context, groupId string, member string) error { + memberGraphURL := fmt.Sprintf("https://graph.windows.net/%s/directoryObjects/%s", client.TenantID, member) + + properties := graphrbac.GroupAddMemberParameters{ + URL: &memberGraphURL, + } + + log.Printf("[DEBUG] Adding member with id %q to Azure AD group with id %q", member, groupId) + if _, err := client.AddMember(ctx, groupId, properties); err != nil { + return fmt.Errorf("Error adding group member %q to Azure AD Group with ID %q: %+v", member, groupId, err) + } + + return nil +} + +func GroupAddMembers(client graphrbac.GroupsClient, ctx context.Context, groupId string, members []string) error { + for _, memberUuid := range members { + err := GroupAddMember(client, ctx, groupId, memberUuid) + + if err != nil { + return fmt.Errorf("Error while adding members to Azure AD Group with ID %q: %+v", groupId, err) + } + } + + return nil +} diff --git a/azuread/helpers/slices/slices.go b/azuread/helpers/slices/slices.go new file mode 100644 index 0000000000..f0b7ca972c --- /dev/null +++ b/azuread/helpers/slices/slices.go @@ -0,0 +1,16 @@ +package slices + +// difference returns the elements in `a` that aren't in `b`. +func Difference(a, b []string) []string { + mb := make(map[string]struct{}, len(b)) + for _, x := range b { + mb[x] = struct{}{} + } + var diff []string + for _, x := range a { + if _, found := mb[x]; !found { + diff = append(diff, x) + } + } + return diff +} diff --git a/azuread/provider.go b/azuread/provider.go index dc5c2488f5..0998c752f8 100644 --- a/azuread/provider.go +++ b/azuread/provider.go @@ -86,6 +86,7 @@ func Provider() terraform.ResourceProvider { "azuread_application": resourceApplication(), "azuread_application_password": resourceApplicationPassword(), "azuread_group": resourceGroup(), + "azuread_group_member": resourceGroupMember(), "azuread_service_principal": resourceServicePrincipal(), "azuread_service_principal_password": resourceServicePrincipalPassword(), "azuread_user": resourceUser(), diff --git a/azuread/resource_group.go b/azuread/resource_group.go index 82c686d3fb..980d355dd1 100644 --- a/azuread/resource_group.go +++ b/azuread/resource_group.go @@ -4,6 +4,10 @@ import ( "fmt" "log" + "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/slices" + "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/tf" + "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/validate" + "github.com/Azure/azure-sdk-for-go/services/graphrbac/1.6/graphrbac" "github.com/google/uuid" "github.com/hashicorp/terraform/helper/schema" @@ -18,6 +22,7 @@ func resourceGroup() *schema.Resource { return &schema.Resource{ Create: resourceGroupCreate, Read: resourceGroupRead, + Update: resourceGroupUpdate, Delete: resourceGroupDelete, Importer: &schema.ResourceImporter{ @@ -36,6 +41,18 @@ func resourceGroup() *schema.Resource { Type: schema.TypeString, Computed: true, }, + + "members": { + Type: schema.TypeSet, + Optional: true, + Computed: true, + Set: schema.HashString, + ForceNew: false, + Elem: &schema.Schema{ + Type: schema.TypeString, + ValidateFunc: validate.UUID, + }, + }, }, } } @@ -62,6 +79,15 @@ func resourceGroupCreate(d *schema.ResourceData, meta interface{}) error { } d.SetId(*group.ObjectID) + // Add members if specified + if v, ok := d.GetOk("members"); ok { + members := tf.ExpandStringSlicePtr(v.(*schema.Set).List()) + + if err := graph.GroupAddMembers(client, ctx, *group.ObjectID, *members); err != nil { + return err + } + } + _, err = graph.WaitForReplication(func() (interface{}, error) { return client.Get(ctx, *group.ObjectID) }) @@ -89,9 +115,48 @@ func resourceGroupRead(d *schema.ResourceData, meta interface{}) error { d.Set("name", resp.DisplayName) d.Set("object_id", resp.ObjectID) + + members, err := graph.GroupAllMembers(client, ctx, d.Id()) + if err != nil { + return err + } + + d.Set("members", members) + return nil } +func resourceGroupUpdate(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).groupsClient + ctx := meta.(*ArmClient).StopContext + + if v, ok := d.GetOkExists("members"); ok && d.HasChange("members") { + existingMembers, err := graph.GroupAllMembers(client, ctx, d.Id()) + if err != nil { + return err + } + + desiredMembers := *tf.ExpandStringSlicePtr(v.(*schema.Set).List()) + membersForRemoval := slices.Difference(existingMembers, desiredMembers) + membersToAdd := slices.Difference(desiredMembers, existingMembers) + + for _, existingMember := range membersForRemoval { + log.Printf("[DEBUG] Removing member with id %q from Azure AD group with id %q", existingMember, d.Id()) + if resp, err := client.RemoveMember(ctx, d.Id(), existingMember); err != nil { + if !ar.ResponseWasNotFound(resp) { + return fmt.Errorf("Error Deleting group member %q from Azure AD Group with ID %q: %+v", existingMember, d.Id(), err) + } + } + } + + if err := graph.GroupAddMembers(client, ctx, d.Id(), membersToAdd); err != nil { + return err + } + } + + return resourceGroupRead(d, meta) +} + func resourceGroupDelete(d *schema.ResourceData, meta interface{}) error { client := meta.(*ArmClient).groupsClient ctx := meta.(*ArmClient).StopContext diff --git a/azuread/resource_group_member.go b/azuread/resource_group_member.go new file mode 100644 index 0000000000..0afc87a2c9 --- /dev/null +++ b/azuread/resource_group_member.go @@ -0,0 +1,113 @@ +package azuread + +import ( + "fmt" + "strings" + + "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/graph" + + "github.com/hashicorp/terraform/helper/schema" + "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/ar" + "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/validate" +) + +func resourceGroupMember() *schema.Resource { + return &schema.Resource{ + Create: resourceGroupMemberCreate, + Read: resourceGroupMemberRead, + Delete: resourceGroupMemberDelete, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Schema: map[string]*schema.Schema{ + "group_object_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validate.UUID, + }, + "member_object_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validate.UUID, + }, + }, + } +} + +func resourceGroupMemberCreate(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).groupsClient + ctx := meta.(*ArmClient).StopContext + + groupID := d.Get("group_object_id").(string) + memberID := d.Get("member_object_id").(string) + + if err := graph.GroupAddMember(client, ctx, groupID, memberID); err != nil { + return err + } + + id := fmt.Sprintf("%s/member/%s", groupID, memberID) + d.SetId(id) + + return resourceGroupMemberRead(d, meta) +} + +func resourceGroupMemberRead(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).groupsClient + ctx := meta.(*ArmClient).StopContext + + id := strings.Split(d.Id(), "/member/") + if len(id) != 2 { + return fmt.Errorf("ID should be in the format {groupObjectId}/member/{memberObjectId} - but got %q", d.Id()) + } + + groupID := id[0] + memberID := id[1] + + members, err := graph.GroupAllMembers(client, ctx, groupID) + if err != nil { + return fmt.Errorf("Error retrieving Azure AD Group members (groupObjectId: %q): %+v", groupID, err) + } + + var memberObjectID string + + for _, objectID := range members { + if objectID == memberID { + memberObjectID = objectID + } + } + + if memberObjectID == "" { + d.SetId("") + return fmt.Errorf("Azure AD Group Member not found - groupObjectId:%q / memberObjectId:%q", groupID, memberID) + } + + d.Set("group_object_id", groupID) + d.Set("member_object_id", memberObjectID) + + return nil +} + +func resourceGroupMemberDelete(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).groupsClient + ctx := meta.(*ArmClient).StopContext + + id := strings.Split(d.Id(), "/member/") + if len(id) != 2 { + return fmt.Errorf("ID should be in the format {groupObjectId}/member/{memberObjectId} - but got %q", d.Id()) + } + + groupID := id[0] + memberID := id[1] + + resp, err := client.RemoveMember(ctx, groupID, memberID) + if err != nil { + if !ar.ResponseWasNotFound(resp) { + return fmt.Errorf("Error removing Member (memberObjectId: %q) from Azure AD Group (groupObjectId: %q): %+v", memberID, groupID, err) + } + } + + return nil +} diff --git a/azuread/resource_group_member_test.go b/azuread/resource_group_member_test.go new file mode 100644 index 0000000000..8b8ac21fa6 --- /dev/null +++ b/azuread/resource_group_member_test.go @@ -0,0 +1,224 @@ +package azuread + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/ar" +) + +func TestAccAzureADGroupMember_User(t *testing.T) { + resourceName := "azuread_group_member.test" + id := acctest.RandStringFromCharSet(7, acctest.CharSetAlphaNum) + password := id + "p@$$wR2" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureADGroupMemberDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureADGroupMember_User(id, password), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet(resourceName, "group_object_id"), + resource.TestCheckResourceAttrSet(resourceName, "member_object_id"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAzureADGroupMember_Group(t *testing.T) { + resourceName := "azuread_group_member.test" + id := acctest.RandStringFromCharSet(7, acctest.CharSetAlphaNum) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureADGroupMemberDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureADGroupMember_Group(id), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet(resourceName, "group_object_id"), + resource.TestCheckResourceAttrSet(resourceName, "member_object_id"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAzureADGroupMember_ServicePrincipal(t *testing.T) { + resourceName := "azuread_group_member.test" + id := acctest.RandStringFromCharSet(7, acctest.CharSetAlphaNum) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureADGroupMemberDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureADGroupMember_ServicePrincipal(id), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet(resourceName, "group_object_id"), + resource.TestCheckResourceAttrSet(resourceName, "member_object_id"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testCheckAzureADGroupMemberDestroy(s *terraform.State) error { + for _, rs := range s.RootModule().Resources { + if rs.Type != "azuread_group_member" { + continue + } + + client := testAccProvider.Meta().(*ArmClient).groupsClient + ctx := testAccProvider.Meta().(*ArmClient).StopContext + + groupID := rs.Primary.Attributes["group_object_id"] + memberID := rs.Primary.Attributes["member_object_id"] + + members, err := client.GetGroupMembersComplete(ctx, groupID) + if err != nil { + if ar.ResponseWasNotFound(members.Response().Response) { + return nil + } + + return err + } + + var memberObjectID string + for members.NotDone() { + // possible members are users, groups or service principals + // we try to 'cast' each result as the corresponding type and diff + // if we found the object we're looking for + user, _ := members.Value().AsUser() + if user != nil { + if *user.ObjectID == memberID { + memberObjectID = *user.ObjectID + // we successfully found the directory object we're looking for, we can stop looping + // through the results + break + } + } + + group, _ := members.Value().AsADGroup() + if group != nil { + if *group.ObjectID == memberID { + memberObjectID = *group.ObjectID + // we successfully found the directory object we're looking for, we can stop looping + // through the results + break + } + } + + servicePrincipal, _ := members.Value().AsServicePrincipal() + if servicePrincipal != nil { + if *servicePrincipal.ObjectID == memberID { + memberObjectID = *servicePrincipal.ObjectID + // we successfully found the directory object we're looking for, we can stop looping + // through the results + break + } + } + + err = members.NextWithContext(ctx) + if err != nil { + return fmt.Errorf("Error listing Azure AD Group Members: %s", err) + } + } + + if memberObjectID != "" { + return fmt.Errorf("Azure AD group member still exists:\n%#v", memberObjectID) + } + } + + return nil +} + +func testAccAzureADGroupMember_User(id string, password string) string { + return fmt.Sprintf(` + +data "azuread_domains" "tenant_domain" { + only_initial = true +} + +resource "azuread_user" "test" { + user_principal_name = "acctestA%[1]s@${data.azuread_domains.tenant_domain.domains.0.domain_name}" + display_name = "acctestA%[1]s" + password = "%[2]s" +} + +resource "azuread_group" "test" { + name = "acctest%[1]s" +} + +resource "azuread_group_member" "test" { + group_object_id = "${azuread_group.test.object_id}" + member_object_id = "${azuread_user.test.object_id}" +} + +`, id, password) +} + +func testAccAzureADGroupMember_Group(id string) string { + return fmt.Sprintf(` + +resource "azuread_group" "testA" { + name = "acctestA%[1]s" +} + +resource "azuread_group" "testB" { + name = "acctestB%[1]s" +} + +resource "azuread_group_member" "test" { + group_object_id = "${azuread_group.testA.object_id}" + member_object_id = "${azuread_group.testB.object_id}" +} + +`, id) +} + +func testAccAzureADGroupMember_ServicePrincipal(id string) string { + return fmt.Sprintf(` + +resource "azuread_application" "test" { + name = "acctest%[1]s" +} + +resource "azuread_service_principal" "test" { + application_id = "${azuread_application.test.application_id}" +} + +resource "azuread_group" "test" { + name = "acctestA%[1]s" +} + +resource "azuread_group_member" "test" { + group_object_id = "${azuread_group.test.object_id}" + member_object_id = "${azuread_service_principal.test.object_id}" +} + +`, id) +} diff --git a/azuread/resource_group_test.go b/azuread/resource_group_test.go index 75e923e514..b46b20e73e 100644 --- a/azuread/resource_group_test.go +++ b/azuread/resource_group_test.go @@ -26,11 +26,35 @@ func TestAccAzureADGroup_basic(t *testing.T) { Steps: []resource.TestStep{ { Config: config, - Check: resource.ComposeTestCheckFunc( - testCheckAzureADGroupExists(resourceName), - resource.TestCheckResourceAttr(resourceName, "name", fmt.Sprintf("acctest%s", id)), - resource.TestCheckResourceAttrSet(resourceName, "object_id"), - ), + Check: assertResourceWithMemberCount(id, "0"), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAzureADGroup_members(t *testing.T) { + resourceName := "azuread_group.test" + + id, err := uuid.GenerateUUID() + if err != nil { + t.Fatal(err) + } + + config := testAccAzureADGroupWithThreeMembers(id) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureADGroupDestroy, + Steps: []resource.TestStep{ + { + Config: config, + Check: assertResourceWithMemberCount(id, "3"), }, { ResourceName: resourceName, @@ -56,17 +80,100 @@ func TestAccAzureADGroup_complete(t *testing.T) { Steps: []resource.TestStep{ { Config: config, - Check: resource.ComposeTestCheckFunc( - testCheckAzureADGroupExists(resourceName), - resource.TestCheckResourceAttr(resourceName, "name", fmt.Sprintf("acctest%s", id)), - resource.TestCheckResourceAttrSet(resourceName, "object_id"), - ), + Check: assertResourceWithMemberCount(id, "0"), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAzureADGroup_diverse(t *testing.T) { + resourceName := "azuread_group.test" + id, err := uuid.GenerateUUID() + if err != nil { + t.Fatal(err) + } + config := testAccAzureADGroupWithDiverseMembers(id) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureADGroupDestroy, + Steps: []resource.TestStep{ + { + Config: config, + Check: assertResourceWithMemberCount(id, "3"), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAzureADGroup_progression(t *testing.T) { + resourceName := "azuread_group.test" + id, err := uuid.GenerateUUID() + if err != nil { + t.Fatal(err) + } + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureADGroupDestroy, + Steps: []resource.TestStep{ + // Empty group with 0 members + { + Config: testAccAzureADGroup(id), + Check: assertResourceWithMemberCount(id, "0"), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + // Group with 1 member + { + Config: testAccAzureADGroupWithOneMember(id), + Check: assertResourceWithMemberCount(id, "1"), }, { ResourceName: resourceName, ImportState: true, ImportStateVerify: true, }, + // Group with multiple members + { + Config: testAccAzureADGroupWithThreeMembers(id), + Check: assertResourceWithMemberCount(id, "3"), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + // Group with a different member + { + Config: testAccAzureADGroupWithServicePrincipal(id), + Check: assertResourceWithMemberCount(id, "1"), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + // Empty group with 0 members + { + Config: testAccAzureADGroup(id), + Check: assertResourceWithMemberCount(id, "0"), + }, }, }) } @@ -117,10 +224,120 @@ func testCheckAzureADGroupDestroy(s *terraform.State) error { return nil } +func assertResourceWithMemberCount(id string, memberCount string) resource.TestCheckFunc { + resourceName := "azuread_group.test" + + return resource.ComposeTestCheckFunc( + testCheckAzureADGroupExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "name", fmt.Sprintf("acctest%s", id)), + resource.TestCheckResourceAttrSet(resourceName, "object_id"), + resource.TestCheckResourceAttr(resourceName, "members.#", memberCount), + ) +} + func testAccAzureADGroup(id string) string { return fmt.Sprintf(` resource "azuread_group" "test" { name = "acctest%s" + members = [] +} +`, id) +} + +func testAccAzureADGroupWithDiverseMembers(id string) string { + return fmt.Sprintf(` +data "azuread_domains" "tenant_domain" { + only_initial = true +} + +resource "azuread_application" "test_app_%[1]s" { + name = "app%[1]s" +} + +resource "azuread_service_principal" "test_sp_%[1]s" { + application_id = azuread_application.test_app_%[1]s.application_id +} + +resource "azuread_group" "test_g_%[1]s" { + name = "acctest%[1]s" +} + +resource "azuread_user" "acctest_user_%[1]s" { + user_principal_name = "acctest.%[1]s@${data.azuread_domains.tenant_domain.domains.0.domain_name}" + display_name = "acctest-%[1]s" + password = "%[1]s" +} + +resource "azuread_group" "test" { + name = "acctest%[1]s" + members = [ azuread_user.acctest_user_%[1]s.object_id, azuread_group.test_g_%[1]s.object_id, azuread_service_principal.test_sp_%[1]s.object_id ] +} +`, id) +} + +func testAccAzureADGroupWithOneMember(id string) string { + return fmt.Sprintf(` +data "azuread_domains" "tenant_domain" { + only_initial = true +} + +resource "azuread_user" "acctest_user_%[1]s" { + user_principal_name = "acctest.%[1]s@${data.azuread_domains.tenant_domain.domains.0.domain_name}" + display_name = "acctest-%[1]s" + password = "%[1]s" +} + +resource "azuread_group" "test" { + name = "acctest%[1]s" + members = [ azuread_user.acctest_user_%[1]s.object_id ] +} +`, id) +} + +func testAccAzureADGroupWithThreeMembers(id string) string { + return fmt.Sprintf(` +data "azuread_domains" "tenant_domain" { + only_initial = true +} + +resource "azuread_user" "acctest_user_%[2]s" { + user_principal_name = "acctest.%[2]s@${data.azuread_domains.tenant_domain.domains.0.domain_name}" + display_name = "acctest-%[2]s" + password = "%[2]s" +} + +resource "azuread_user" "acctest_user_%[3]s" { + user_principal_name = "acctest.%[3]s@${data.azuread_domains.tenant_domain.domains.0.domain_name}" + display_name = "acctest-%[3]s" + password = "%[3]s" +} + +resource "azuread_user" "acctest_user_%[4]s" { + user_principal_name = "acctest.%[4]s@${data.azuread_domains.tenant_domain.domains.0.domain_name}" + display_name = "acctest-%[4]s" + password = "%[4]s" +} + +resource "azuread_group" "test" { + name = "acctest%[1]s" + members = [ azuread_user.acctest_user_%[2]s.object_id, azuread_user.acctest_user_%[3]s.object_id, azuread_user.acctest_user_%[4]s.object_id ] +} +`, id, id+"a", id+"b", id+"c") +} + +func testAccAzureADGroupWithServicePrincipal(id string) string { + return fmt.Sprintf(` +resource "azuread_application" "test_app_%[1]s" { + name = "app%[1]s" +} + +resource "azuread_service_principal" "test_sp_%[1]s" { + application_id = azuread_application.test_app_%[1]s.application_id +} + +resource "azuread_group" "test" { + name = "acctest%[1]s" + members = [ azuread_service_principal.test_sp_%[1]s.object_id ] } `, id) } diff --git a/website/azuread.erb b/website/azuread.erb index 5357e7d384..6fdfbd9f22 100644 --- a/website/azuread.erb +++ b/website/azuread.erb @@ -92,6 +92,10 @@ azuread_group + > + azuread_group_member + + > azuread_service_principal diff --git a/website/docs/r/group.markdown b/website/docs/r/group.markdown index 6a55abaa87..f178629c93 100644 --- a/website/docs/r/group.markdown +++ b/website/docs/r/group.markdown @@ -15,9 +15,26 @@ Manages a Group within Azure Active Directory. ## Example Usage +*Basic example* + +```hcl +resource "azuread_group" "my_group" { + name = "MyGroup" +} +``` + +*A group with members* + ```hcl +resource "azuread_user" "my_user" { + display_name = "John Doe" + password = "notSecure123" + user_principal_name = "john.doe@terraform.onmicrosoft.com" +} + resource "azuread_group" "my_group" { name = "MyGroup" + members = [ azuread_user.my_user.object_id /*, more users */ ] } ``` @@ -25,10 +42,13 @@ resource "azuread_group" "my_group" { The following arguments are supported: -* `name` - (Required) The display name for the Group. +* `name` - (Required) The display name 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. -> **NOTE:** Group names are not unique within Azure Active Directory. +-> **NOTE:** Do not use `azuread_group_member` at the same time as the `members` argument. + ## Attributes Reference The following attributes are exported: @@ -37,6 +57,8 @@ The following attributes are exported: * `name` - The Display Name of the Group. +* `members` - The Members of the Group. + ## Import Azure Active Directory Groups can be imported using the `object id`, e.g. diff --git a/website/docs/r/group_member.markdown b/website/docs/r/group_member.markdown new file mode 100644 index 0000000000..48ddd11f98 --- /dev/null +++ b/website/docs/r/group_member.markdown @@ -0,0 +1,57 @@ +--- +layout: "azuread" +page_title: "Azure Active Directory: azuread_group_member" +sidebar_current: "docs-azuread-resource-azuread-group-member" +description: |- + Manages a single Group Membership within Azure Active Directory. + +--- + +# azuread_group_member + +Manages a single Group Membership within Azure Active Directory. + +-> **NOTE:** Do not use this resource at the same time as `azuread_group.members`. + +## Example Usage + +```hcl + +data "azuread_user" "my_user" { + user_principal_name = "johndoe@hashicorp.com" +} + +resource "azuread_group" "my_group" { + name = "my_group" +} + +resource "azuread_group_member" "test" { + group_object_id = "${azuread_group.my_group.id}" + member_object_id = "${data.azuread_user.my_user.id}" +} +``` + +## Argument Reference + +The following arguments are supported: + +* `group_object_id` - (Required) The Object ID of the Azure AD Group you want to add the Member to. Changing this forces a new resource to be created. +* `member_object_id` - (Required) The Object ID of the Azure AD Object you want to add as a Member to the Group. Supported Object types are Users, Groups or Service Principals. Changing this forces a new resource to be created. + +-> **NOTE:** The Member object has to be present in your Azure Active Directory, either as a Member or a Guest. + +## Attributes Reference + +The following attributes are exported: + +* `id` - The ID of the Azure AD Group Member. + +## Import + +Azure Active Directory Group Members can be imported using the `object id`, e.g. + +```shell +terraform import azuread_group_member.test 00000000-0000-0000-0000-000000000000/11111111-1111-1111-1111-111111111111 +``` + +-> **NOTE:** This ID format is unique to Terraform and is composed of the Azure AD Group Object ID and the target Member Object ID in the format `{GroupObjectID}/{MemberObjectID}`. From 6f5629c75e976745d5d501a2256a8033dba216ab Mon Sep 17 00:00:00 2001 From: kt Date: Thu, 11 Jul 2019 18:07:14 -0700 Subject: [PATCH 39/45] Update CHANGELOG.md to include #100 --- CHANGELOG.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e161230505..3467783759 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,9 @@ -*## 0.5.0 (Unreleased) +## 0.5.0 (Unreleased) FEATURES: * **New Data Source:** `azuread_users` [GH-109] +* **New Resource:** `azuread_group_member` [GH-100] IMPROVEMENTS: @@ -11,6 +12,7 @@ IMPROVEMENTS: * `azuread_application` - support for the `app_roles` property [GH-98] * `azuread_application` - the `identifier_uris` property now allows `api`,`urn`, and `ms-appx` URI schemas [GH-115] * `azuread_application_password` - deprecation of `application_id` in favour of `application_object_id` [GH-107] +* `azuread_group` - support for the `members` property [GH-100] * `azuread_service_principal` - export the `oauth2_permissions` property [GH-103] ## 0.4.0 (June 06, 2019) From c29badf56e1939986b849b22adccd4ec44a77de4 Mon Sep 17 00:00:00 2001 From: kt Date: Thu, 11 Jul 2019 19:05:41 -0700 Subject: [PATCH 40/45] azuread_application_password: add replication wait (#118) --- azuread/helpers/graph/credentials.go | 28 ++++++++++++++++++++++++ azuread/helpers/graph/replication.go | 20 ++++++++--------- azuread/resource_application_password.go | 10 +++++++-- 3 files changed, 46 insertions(+), 12 deletions(-) diff --git a/azuread/helpers/graph/credentials.go b/azuread/helpers/graph/credentials.go index 2b47835c40..ca7a744568 100644 --- a/azuread/helpers/graph/credentials.go +++ b/azuread/helpers/graph/credentials.go @@ -8,8 +8,10 @@ import ( "github.com/Azure/azure-sdk-for-go/services/graphrbac/1.6/graphrbac" "github.com/Azure/go-autorest/autorest/date" "github.com/hashicorp/go-uuid" + "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/helper/schema" "github.com/hashicorp/terraform/helper/validation" + "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/ar" "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/p" "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/validate" @@ -210,3 +212,29 @@ func PasswordCredentialResultRemoveByKeyId(existing graphrbac.PasswordCredential return &newCreds } + +func WaitForPasswordCredentialReplication(keyId string, f func() (graphrbac.PasswordCredentialListResult, error)) (interface{}, error) { + return (&resource.StateChangeConf{ + Pending: []string{"404", "BadCast", "NotFound"}, + Target: []string{"Found"}, + Timeout: 5 * time.Minute, + MinTimeout: 1 * time.Second, + ContinuousTargetOccurence: 10, + Refresh: func() (interface{}, string, error) { + creds, err := f() + if err != nil { + if ar.ResponseWasNotFound(creds.Response) { + return creds, "404", nil + } + return creds, "Error", fmt.Errorf("Error calling f, response was not 404 (%d): %v", creds.Response.StatusCode, err) + } + + credential := PasswordCredentialResultFindByKeyId(creds, keyId) + if credential == nil { + return creds, "NotFound", nil + } + + return creds, "Found", nil + }, + }).WaitForState() +} diff --git a/azuread/helpers/graph/replication.go b/azuread/helpers/graph/replication.go index fb29e8e170..aee6b771c2 100644 --- a/azuread/helpers/graph/replication.go +++ b/azuread/helpers/graph/replication.go @@ -19,18 +19,18 @@ func WaitForReplication(f func() (interface{}, error)) (interface{}, error) { ContinuousTargetOccurence: 10, Refresh: func() (interface{}, string, error) { i, err := f() - if err != nil { - r, ok := i.(autorest.Response) - if !ok { - return i, "BadCast", nil // sometimes the SDK bubbles up an entirely empty object - } - if ar.ResponseWasNotFound(r) { - return i, "404", nil - } - return i, "Error", fmt.Errorf("Error calling f, response was not 404 (%d): %v", r.StatusCode, err) + if err == nil { + return i, "Found", nil } - return i, "Found", nil + r, ok := i.(autorest.Response) + if !ok { + return i, "BadCast", nil // sometimes the SDK bubbles up an entirely empty object + } + if ar.ResponseWasNotFound(r) { + return i, "404", nil + } + return i, "Error", fmt.Errorf("Error calling f, response was not 404 (%d): %v", r.StatusCode, err) }, }).WaitForState() } diff --git a/azuread/resource_application_password.go b/azuread/resource_application_password.go index 1387cf735d..5ec884e6ab 100644 --- a/azuread/resource_application_password.go +++ b/azuread/resource_application_password.go @@ -122,7 +122,14 @@ func resourceApplicationPasswordCreate(d *schema.ResourceData, meta interface{}) } if _, err = client.UpdatePasswordCredentials(ctx, id.ObjectId, graphrbac.PasswordCredentialsUpdateParameters{Value: newCreds}); err != nil { - return fmt.Errorf("Error creating Application Credentials %q for Object ID %q: %+v", *cred.KeyID, id.ObjectId, err) + return fmt.Errorf("Error creating Application Credentials %q for Object ID %q: %+v", id.KeyId, id.ObjectId, err) + } + + _, err = graph.WaitForPasswordCredentialReplication(id.KeyId, func() (graphrbac.PasswordCredentialListResult, error) { + return client.ListPasswordCredentials(ctx, id.ObjectId) + }) + if err != nil { + return fmt.Errorf("Error waiting for Application password (AppID %q, KeyID %q: %+v", id.ObjectId, id.KeyId, err) } d.SetId(id.String()) @@ -138,7 +145,6 @@ func resourceApplicationPasswordRead(d *schema.ResourceData, meta interface{}) e if err != nil { return fmt.Errorf("Error parsing Application Password ID: %v", err) } - // ensure the Application Object exists app, err := client.Get(ctx, id.ObjectId) if err != nil { From c80dd0654a16517c847dd8527536ea5c45aed042 Mon Sep 17 00:00:00 2001 From: Jose Luis Pedrosa Date: Fri, 12 Jul 2019 04:22:35 +0200 Subject: [PATCH 41/45] Wait on password replication (#117) --- azuread/resource_application_password.go | 2 +- azuread/resource_service_principal_password.go | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/azuread/resource_application_password.go b/azuread/resource_application_password.go index 5ec884e6ab..bd20ab2e39 100644 --- a/azuread/resource_application_password.go +++ b/azuread/resource_application_password.go @@ -129,7 +129,7 @@ func resourceApplicationPasswordCreate(d *schema.ResourceData, meta interface{}) return client.ListPasswordCredentials(ctx, id.ObjectId) }) if err != nil { - return fmt.Errorf("Error waiting for Application password (AppID %q, KeyID %q: %+v", id.ObjectId, id.KeyId, err) + return fmt.Errorf("Error waiting for Application Password replication (AppID %q, KeyID %q: %+v", id.ObjectId, id.KeyId, err) } d.SetId(id.String()) diff --git a/azuread/resource_service_principal_password.go b/azuread/resource_service_principal_password.go index c94a918d99..9c7d57a204 100644 --- a/azuread/resource_service_principal_password.go +++ b/azuread/resource_service_principal_password.go @@ -58,6 +58,13 @@ func resourceServicePrincipalPasswordCreate(d *schema.ResourceData, meta interfa d.SetId(id.String()) + _, err = graph.WaitForPasswordCredentialReplication(id.KeyId, func() (graphrbac.PasswordCredentialListResult, error) { + return client.ListPasswordCredentials(ctx, id.ObjectId) + }) + if err != nil { + return fmt.Errorf("Error waiting for Service Principal Password replication (SP %q, KeyID %q: %+v", id.ObjectId, id.KeyId, err) + } + return resourceServicePrincipalPasswordRead(d, meta) } From 0272169c0f66c5f525e3bdf09904935ec0f1d08d Mon Sep 17 00:00:00 2001 From: kt Date: Thu, 11 Jul 2019 19:24:03 -0700 Subject: [PATCH 42/45] Update CHANGELOG.md to include #117 & #118 --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3467783759..4bbfb8bf4e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,11 @@ IMPROVEMENTS: * `azuread_group` - support for the `members` property [GH-100] * `azuread_service_principal` - export the `oauth2_permissions` property [GH-103] +BUG FIXES: + +* `azuread_application_password` - will now wait for replication on resource creation [GH-118] +* `azuread_service_principal_password` - will now wait for replication on resource creation [GH-117] + ## 0.4.0 (June 06, 2019) NOTES: From b00c1efdac3656956e5a2af479a40d5d8897c314 Mon Sep 17 00:00:00 2001 From: kt Date: Thu, 18 Jul 2019 13:35:42 -0700 Subject: [PATCH 43/45] New Data Source: azuread_groups (#120) * New Data Source: azuread_groups --- azuread/data_group.go | 27 +--- azuread/data_group_test.go | 34 ++--- azuread/data_groups.go | 105 +++++++++++++ azuread/data_groups_test.go | 81 ++++++++++ azuread/data_user.go | 2 +- azuread/data_users.go | 4 +- azuread/helpers/graph/group.go | 31 ++++ azuread/provider.go | 5 +- azuread/resource_group_member_test.go | 67 ++++----- azuread/resource_group_test.go | 203 ++++++++++++-------------- website/docs/d/groups.html.markdown | 39 +++++ website/docs/d/users.html.markdown | 2 +- 12 files changed, 411 insertions(+), 189 deletions(-) create mode 100644 azuread/data_groups.go create mode 100644 azuread/data_groups_test.go create mode 100644 website/docs/d/groups.html.markdown diff --git a/azuread/data_group.go b/azuread/data_group.go index af2557ab88..5e3ccdccb5 100644 --- a/azuread/data_group.go +++ b/azuread/data_group.go @@ -5,6 +5,7 @@ import ( "github.com/Azure/azure-sdk-for-go/services/graphrbac/1.6/graphrbac" "github.com/hashicorp/terraform/helper/schema" + "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/graph" "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/ar" "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/validate" @@ -56,31 +57,11 @@ func dataSourceActiveDirectoryGroupRead(d *schema.ResourceData, meta interface{} group = resp } else if name, ok := d.Get("name").(string); ok && name != "" { - filter := fmt.Sprintf("displayName eq '%s'", name) - - resp, err := client.ListComplete(ctx, filter) + g, err := graph.GroupGetByDisplayName(&client, ctx, name) if err != nil { - return fmt.Errorf("Error listing Azure AD Groups for filter %q: %+v", filter, err) - } - - values := resp.Response().Value - if values == nil { - return fmt.Errorf("nil values for AD Groups matching %q", filter) - } - if len(*values) == 0 { - return fmt.Errorf("Found no AD Groups matching %q", filter) - } - if len(*values) > 2 { - return fmt.Errorf("Found multiple AD Groups matching %q", filter) - } - - group = (*values)[0] - if group.DisplayName == nil { - return fmt.Errorf("nil DisplayName for AD Groups matching %q", filter) - } - if *group.DisplayName != name { - return fmt.Errorf("displayname for AD Groups matching %q does is does not match(%q!=%q)", filter, *group.DisplayName, name) + return fmt.Errorf("Error finding Azure AD Group with display name %q: %+v", name, err) } + group = *g } else { return fmt.Errorf("one of `object_id` or `name` must be supplied") } diff --git a/azuread/data_group_test.go b/azuread/data_group_test.go index eaa077d146..3953ab0b80 100644 --- a/azuread/data_group_test.go +++ b/azuread/data_group_test.go @@ -4,27 +4,27 @@ import ( "fmt" "testing" - "github.com/google/uuid" "github.com/hashicorp/terraform/helper/resource" + "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/tf" ) func TestAccDataSourceAzureADGroup_byName(t *testing.T) { - dataSourceName := "data.azuread_group.test" - id := uuid.New().String() + dsn := "data.azuread_group.test" + id := tf.AccRandTimeInt() - resource.Test(t, resource.TestCase{ + resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, Providers: testAccProviders, CheckDestroy: testCheckAzureADGroupDestroy, Steps: []resource.TestStep{ { - Config: testAccAzureADGroup(id), + Config: testAccAzureADGroup_basic(id), }, { Config: testAccDataSourceAzureADGroup_name(id), Check: resource.ComposeTestCheckFunc( - testCheckAzureADGroupExists(dataSourceName), - resource.TestCheckResourceAttr(dataSourceName, "name", fmt.Sprintf("acctest%s", id)), + testCheckAzureADGroupExists(dsn), + resource.TestCheckResourceAttr(dsn, "name", fmt.Sprintf("acctestGroup-%d", id)), ), }, }, @@ -32,44 +32,44 @@ func TestAccDataSourceAzureADGroup_byName(t *testing.T) { } func TestAccDataSourceAzureADGroup_byObjectId(t *testing.T) { - dataSourceName := "data.azuread_group.test" - id := uuid.New().String() + dsn := "data.azuread_group.test" + id := tf.AccRandTimeInt() - resource.Test(t, resource.TestCase{ + resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, Providers: testAccProviders, CheckDestroy: testCheckAzureADGroupDestroy, Steps: []resource.TestStep{ { - Config: testAccAzureADGroup(id), + Config: testAccAzureADGroup_basic(id), }, { Config: testAccDataSourceAzureADGroup_objectId(id), Check: resource.ComposeTestCheckFunc( - testCheckAzureADGroupExists(dataSourceName), - resource.TestCheckResourceAttr(dataSourceName, "name", fmt.Sprintf("acctest%s", id)), + testCheckAzureADGroupExists(dsn), + resource.TestCheckResourceAttr(dsn, "name", fmt.Sprintf("acctestGroup-%d", id)), ), }, }, }) } -func testAccDataSourceAzureADGroup_name(id string) string { +func testAccDataSourceAzureADGroup_name(id int) string { return fmt.Sprintf(` %s data "azuread_group" "test" { name = "${azuread_group.test.name}" } -`, testAccAzureADGroup(id)) +`, testAccAzureADGroup_basic(id)) } -func testAccDataSourceAzureADGroup_objectId(id string) string { +func testAccDataSourceAzureADGroup_objectId(id int) string { return fmt.Sprintf(` %s data "azuread_group" "test" { object_id = "${azuread_group.test.object_id}" } -`, testAccAzureADGroup(id)) +`, testAccAzureADGroup_basic(id)) } diff --git a/azuread/data_groups.go b/azuread/data_groups.go new file mode 100644 index 0000000000..dd35bab602 --- /dev/null +++ b/azuread/data_groups.go @@ -0,0 +1,105 @@ +package azuread + +import ( + "crypto/sha1" + "encoding/base64" + "fmt" + "strings" + + "github.com/Azure/azure-sdk-for-go/services/graphrbac/1.6/graphrbac" + "github.com/hashicorp/terraform/helper/schema" + "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/graph" + + "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/validate" +) + +func dataGroups() *schema.Resource { + return &schema.Resource{ + Read: dataSourceGroupsRead, + + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Schema: map[string]*schema.Schema{ + "object_ids": { + Type: schema.TypeList, + Optional: true, + Computed: true, + MinItems: 1, + ConflictsWith: []string{"names"}, + Elem: &schema.Schema{ + Type: schema.TypeString, + ValidateFunc: validate.UUID, + }, + }, + + "names": { + Type: schema.TypeList, + Optional: true, + Computed: true, + MinItems: 1, + ConflictsWith: []string{"object_ids"}, + Elem: &schema.Schema{ + Type: schema.TypeString, + ValidateFunc: validate.NoEmptyStrings, + }, + }, + }, + } +} + +func dataSourceGroupsRead(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).groupsClient + ctx := meta.(*ArmClient).StopContext + + var groups []graphrbac.ADGroup + expectedCount := 0 + + if names, ok := d.Get("names").([]interface{}); ok && len(names) > 0 { + expectedCount = len(names) + for _, v := range names { + g, err := graph.GroupGetByDisplayName(&client, ctx, v.(string)) + if err != nil { + return fmt.Errorf("Error finding Azure AD Group with display name %q: %+v", v.(string), err) + } + groups = append(groups, *g) + } + } else if oids, ok := d.Get("object_ids").([]interface{}); ok && len(oids) > 0 { + expectedCount = len(oids) + for _, v := range oids { + resp, err := client.Get(ctx, v.(string)) + if err != nil { + return fmt.Errorf("Error making Read request on AzureAD Group with ID %q: %+v", v.(string), err) + } + + groups = append(groups, resp) + } + } else { + return fmt.Errorf("one of `object_ids` or `names` must be supplied") + } + + if len(groups) != expectedCount { + return fmt.Errorf("Unexpected number of groups returned (%d != %d)", len(groups), expectedCount) + } + + var names, oids []string + for _, u := range groups { + if u.ObjectID == nil || u.DisplayName == nil { + return fmt.Errorf("User with nil ObjectId or UPN was found: %v", u) + } + + oids = append(oids, *u.ObjectID) + names = append(names, *u.DisplayName) + } + + h := sha1.New() + if _, err := h.Write([]byte(strings.Join(names, "-"))); err != nil { + return fmt.Errorf("Unable to compute hash for names: %v", err) + } + + d.SetId("groups#" + base64.URLEncoding.EncodeToString(h.Sum(nil))) + d.Set("object_ids", oids) + d.Set("names", names) + return nil +} diff --git a/azuread/data_groups_test.go b/azuread/data_groups_test.go new file mode 100644 index 0000000000..df83fb13e6 --- /dev/null +++ b/azuread/data_groups_test.go @@ -0,0 +1,81 @@ +package azuread + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform/helper/resource" + "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/tf" +) + +func TestAccAzureADGroupsDataSource_byUserPrincipalNames(t *testing.T) { + dsn := "data.azuread_groups.test" + id := tf.AccRandTimeInt() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccAzureADGroupsDataSource_byDisplayNames(id), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(dsn, "names.#", "2"), + resource.TestCheckResourceAttr(dsn, "object_ids.#", "2"), + ), + }, + }, + }) +} + +func TestAccAzureADGroupsDataSource_byObjectIds(t *testing.T) { + dsn := "data.azuread_groups.test" + id := tf.AccRandTimeInt() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccAzureADGroupsDataSource_byObjectIds(id), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(dsn, "names.#", "2"), + resource.TestCheckResourceAttr(dsn, "object_ids.#", "2"), + ), + }, + }, + }) +} + +func testAccAzureADGroup_multiple(id int) string { + return fmt.Sprintf(` +resource "azuread_group" "testA" { + name = "acctestGroup-%[1]d" + members = [] +} + +resource "azuread_group" "testB" { + name = "acctestGroup-%[1]d" + members = [] +} +`, id) +} + +func testAccAzureADGroupsDataSource_byDisplayNames(id int) string { + return fmt.Sprintf(` +%s + +data "azuread_groups" "test" { + names = ["${azuread_group.testA.name}", "${azuread_group.testB.name}"] +} +`, testAccAzureADGroup_multiple(id)) +} + +func testAccAzureADGroupsDataSource_byObjectIds(id int) string { + return fmt.Sprintf(` +%s + +data "azuread_groups" "test" { + object_ids = ["${azuread_group.testA.object_id}", "${azuread_group.testB.object_id}"] +} +`, testAccAzureADGroup_multiple(id)) +} diff --git a/azuread/data_user.go b/azuread/data_user.go index 6e7444b32b..07de2263ee 100644 --- a/azuread/data_user.go +++ b/azuread/data_user.go @@ -10,7 +10,7 @@ import ( "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/validate" ) -func dataSourceUser() *schema.Resource { +func dataUser() *schema.Resource { return &schema.Resource{ Read: dataSourceUserRead, Importer: &schema.ResourceImporter{ diff --git a/azuread/data_users.go b/azuread/data_users.go index f7b64e2d5b..89401ac696 100644 --- a/azuread/data_users.go +++ b/azuread/data_users.go @@ -13,7 +13,7 @@ import ( "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/validate" ) -func dataSourceUsers() *schema.Resource { +func dataUsers() *schema.Resource { return &schema.Resource{ Read: dataSourceUsersRead, @@ -80,7 +80,7 @@ func dataSourceUsersRead(d *schema.ResourceData, meta interface{}) error { } if len(users) != expectedCount { - return fmt.Errorf("Unexpected number of users returns (%d != %d)", len(users), expectedCount) + return fmt.Errorf("Unexpected number of users returned (%d != %d)", len(users), expectedCount) } var upns, oids []string diff --git a/azuread/helpers/graph/group.go b/azuread/helpers/graph/group.go index 68308e4159..222373f903 100644 --- a/azuread/helpers/graph/group.go +++ b/azuread/helpers/graph/group.go @@ -8,6 +8,37 @@ import ( "github.com/Azure/azure-sdk-for-go/services/graphrbac/1.6/graphrbac" ) +func GroupGetByDisplayName(client *graphrbac.GroupsClient, ctx context.Context, displayName string) (*graphrbac.ADGroup, error) { + + filter := fmt.Sprintf("displayName eq '%s'", displayName) + + resp, err := client.ListComplete(ctx, filter) + if err != nil { + return nil, fmt.Errorf("Error listing Azure AD Groups for filter %q: %+v", filter, err) + } + + values := resp.Response().Value + if values == nil { + return nil, fmt.Errorf("nil values for AD Groups matching %q", filter) + } + if len(*values) == 0 { + return nil, fmt.Errorf("Found no AD Groups matching %q", filter) + } + if len(*values) > 2 { + return nil, fmt.Errorf("Found multiple AD Groups matching %q", filter) + } + + group := (*values)[0] + if group.DisplayName == nil { + return nil, fmt.Errorf("nil DisplayName for AD Groups matching %q", filter) + } + if *group.DisplayName != displayName { + return nil, fmt.Errorf("displayname for AD Groups matching %q does is does not match(%q!=%q)", filter, *group.DisplayName, displayName) + } + + return &group, nil +} + func GroupAllMembers(client graphrbac.GroupsClient, ctx context.Context, groupId string) ([]string, error) { it, err := client.GetGroupMembersComplete(ctx, groupId) diff --git a/azuread/provider.go b/azuread/provider.go index 0998c752f8..04f6298144 100644 --- a/azuread/provider.go +++ b/azuread/provider.go @@ -77,9 +77,10 @@ func Provider() terraform.ResourceProvider { "azuread_application": dataApplication(), "azuread_domains": dataDomains(), "azuread_group": dataGroup(), + "azuread_groups": dataGroups(), "azuread_service_principal": dataServicePrincipal(), - "azuread_user": dataSourceUser(), - "azuread_users": dataSourceUsers(), + "azuread_user": dataUser(), + "azuread_users": dataUsers(), }, ResourcesMap: map[string]*schema.Resource{ diff --git a/azuread/resource_group_member_test.go b/azuread/resource_group_member_test.go index 8b8ac21fa6..4cc76762b2 100644 --- a/azuread/resource_group_member_test.go +++ b/azuread/resource_group_member_test.go @@ -8,12 +8,13 @@ import ( "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/ar" + "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/tf" ) func TestAccAzureADGroupMember_User(t *testing.T) { - resourceName := "azuread_group_member.test" - id := acctest.RandStringFromCharSet(7, acctest.CharSetAlphaNum) - password := id + "p@$$wR2" + rn := "azuread_group_member.test" + id := tf.AccRandTimeInt() + pw := "p@$$wR2" + acctest.RandStringFromCharSet(7, acctest.CharSetAlphaNum) resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -21,14 +22,14 @@ func TestAccAzureADGroupMember_User(t *testing.T) { CheckDestroy: testCheckAzureADGroupMemberDestroy, Steps: []resource.TestStep{ { - Config: testAccAzureADGroupMember_User(id, password), + Config: testAccAzureADGroupMember_User(id, pw), Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttrSet(resourceName, "group_object_id"), - resource.TestCheckResourceAttrSet(resourceName, "member_object_id"), + resource.TestCheckResourceAttrSet(rn, "group_object_id"), + resource.TestCheckResourceAttrSet(rn, "member_object_id"), ), }, { - ResourceName: resourceName, + ResourceName: rn, ImportState: true, ImportStateVerify: true, }, @@ -37,8 +38,8 @@ func TestAccAzureADGroupMember_User(t *testing.T) { } func TestAccAzureADGroupMember_Group(t *testing.T) { - resourceName := "azuread_group_member.test" - id := acctest.RandStringFromCharSet(7, acctest.CharSetAlphaNum) + rn := "azuread_group_member.test" + id := tf.AccRandTimeInt() resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -48,12 +49,12 @@ func TestAccAzureADGroupMember_Group(t *testing.T) { { Config: testAccAzureADGroupMember_Group(id), Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttrSet(resourceName, "group_object_id"), - resource.TestCheckResourceAttrSet(resourceName, "member_object_id"), + resource.TestCheckResourceAttrSet(rn, "group_object_id"), + resource.TestCheckResourceAttrSet(rn, "member_object_id"), ), }, { - ResourceName: resourceName, + ResourceName: rn, ImportState: true, ImportStateVerify: true, }, @@ -62,8 +63,8 @@ func TestAccAzureADGroupMember_Group(t *testing.T) { } func TestAccAzureADGroupMember_ServicePrincipal(t *testing.T) { - resourceName := "azuread_group_member.test" - id := acctest.RandStringFromCharSet(7, acctest.CharSetAlphaNum) + rn := "azuread_group_member.test" + id := tf.AccRandTimeInt() resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -73,12 +74,12 @@ func TestAccAzureADGroupMember_ServicePrincipal(t *testing.T) { { Config: testAccAzureADGroupMember_ServicePrincipal(id), Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttrSet(resourceName, "group_object_id"), - resource.TestCheckResourceAttrSet(resourceName, "member_object_id"), + resource.TestCheckResourceAttrSet(rn, "group_object_id"), + resource.TestCheckResourceAttrSet(rn, "member_object_id"), ), }, { - ResourceName: resourceName, + ResourceName: rn, ImportState: true, ImportStateVerify: true, }, @@ -156,7 +157,7 @@ func testCheckAzureADGroupMemberDestroy(s *terraform.State) error { return nil } -func testAccAzureADGroupMember_User(id string, password string) string { +func testAccAzureADGroupMember_User(id int, password string) string { return fmt.Sprintf(` data "azuread_domains" "tenant_domain" { @@ -164,13 +165,13 @@ data "azuread_domains" "tenant_domain" { } resource "azuread_user" "test" { - user_principal_name = "acctestA%[1]s@${data.azuread_domains.tenant_domain.domains.0.domain_name}" - display_name = "acctestA%[1]s" + user_principal_name = "acctestUser.%[1]d.A@${data.azuread_domains.tenant_domain.domains.0.domain_name}" + display_name = "acctestUser-%[1]d-A" password = "%[2]s" } resource "azuread_group" "test" { - name = "acctest%[1]s" + name = "acctestGroup-%[1]d" } resource "azuread_group_member" "test" { @@ -181,30 +182,30 @@ resource "azuread_group_member" "test" { `, id, password) } -func testAccAzureADGroupMember_Group(id string) string { +func testAccAzureADGroupMember_Group(id int) string { return fmt.Sprintf(` -resource "azuread_group" "testA" { - name = "acctestA%[1]s" +resource "azuread_group" "test" { + name = "acctestGroup-%[1]d" } -resource "azuread_group" "testB" { - name = "acctestB%[1]s" +resource "azuread_group" "member" { + name = "acctestGroup-%[1]d-Member" } resource "azuread_group_member" "test" { - group_object_id = "${azuread_group.testA.object_id}" - member_object_id = "${azuread_group.testB.object_id}" + group_object_id = "${azuread_group.test.object_id}" + member_object_id = "${azuread_group.member.object_id}" } `, id) } -func testAccAzureADGroupMember_ServicePrincipal(id string) string { +func testAccAzureADGroupMember_ServicePrincipal(id int) string { return fmt.Sprintf(` resource "azuread_application" "test" { - name = "acctest%[1]s" + name = "acctestApp-%[1]d" } resource "azuread_service_principal" "test" { @@ -212,12 +213,12 @@ resource "azuread_service_principal" "test" { } resource "azuread_group" "test" { - name = "acctestA%[1]s" + name = "acctestGroup-%[1]d" } resource "azuread_group_member" "test" { - group_object_id = "${azuread_group.test.object_id}" - member_object_id = "${azuread_service_principal.test.object_id}" + group_object_id = "${azuread_group.test.object_id}" + member_object_id = "${azuread_service_principal.test.object_id}" } `, id) diff --git a/azuread/resource_group_test.go b/azuread/resource_group_test.go index b46b20e73e..34a121b723 100644 --- a/azuread/resource_group_test.go +++ b/azuread/resource_group_test.go @@ -4,32 +4,29 @@ import ( "fmt" "testing" - "github.com/hashicorp/go-uuid" + "github.com/hashicorp/terraform/helper/acctest" "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" + "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/tf" "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/ar" ) func TestAccAzureADGroup_basic(t *testing.T) { - resourceName := "azuread_group.test" - id, err := uuid.GenerateUUID() - if err != nil { - t.Fatal(err) - } - config := testAccAzureADGroup(id) + rn := "azuread_group.test" + id := tf.AccRandTimeInt() - resource.Test(t, resource.TestCase{ + resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, Providers: testAccProviders, CheckDestroy: testCheckAzureADGroupDestroy, Steps: []resource.TestStep{ { - Config: config, - Check: assertResourceWithMemberCount(id, "0"), + Config: testAccAzureADGroup_basic(id), + Check: testCheckAzureAdGroupBasic(id, "0"), }, { - ResourceName: resourceName, + ResourceName: rn, ImportState: true, ImportStateVerify: true, }, @@ -38,26 +35,21 @@ func TestAccAzureADGroup_basic(t *testing.T) { } func TestAccAzureADGroup_members(t *testing.T) { - resourceName := "azuread_group.test" - - id, err := uuid.GenerateUUID() - if err != nil { - t.Fatal(err) - } + rn := "azuread_group.test" + id := tf.AccRandTimeInt() + pw := "p@$$wR2" + acctest.RandStringFromCharSet(7, acctest.CharSetAlphaNum) - config := testAccAzureADGroupWithThreeMembers(id) - - resource.Test(t, resource.TestCase{ + resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, Providers: testAccProviders, CheckDestroy: testCheckAzureADGroupDestroy, Steps: []resource.TestStep{ { - Config: config, - Check: assertResourceWithMemberCount(id, "3"), + Config: testAccAzureADGroupWithThreeMembers(id, pw), + Check: testCheckAzureAdGroupBasic(id, "3"), }, { - ResourceName: resourceName, + ResourceName: rn, ImportState: true, ImportStateVerify: true, }, @@ -66,24 +58,20 @@ func TestAccAzureADGroup_members(t *testing.T) { } func TestAccAzureADGroup_complete(t *testing.T) { - resourceName := "azuread_group.test" - id, err := uuid.GenerateUUID() - if err != nil { - t.Fatal(err) - } - config := testAccAzureADGroup(id) + rn := "azuread_group.test" + id := tf.AccRandTimeInt() - resource.Test(t, resource.TestCase{ + resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, Providers: testAccProviders, CheckDestroy: testCheckAzureADGroupDestroy, Steps: []resource.TestStep{ { - Config: config, - Check: assertResourceWithMemberCount(id, "0"), + Config: testAccAzureADGroup_basic(id), + Check: testCheckAzureAdGroupBasic(id, "0"), }, { - ResourceName: resourceName, + ResourceName: rn, ImportState: true, ImportStateVerify: true, }, @@ -92,24 +80,21 @@ func TestAccAzureADGroup_complete(t *testing.T) { } func TestAccAzureADGroup_diverse(t *testing.T) { - resourceName := "azuread_group.test" - id, err := uuid.GenerateUUID() - if err != nil { - t.Fatal(err) - } - config := testAccAzureADGroupWithDiverseMembers(id) + rn := "azuread_group.test" + id := tf.AccRandTimeInt() + pw := "p@$$wR2" + acctest.RandStringFromCharSet(7, acctest.CharSetAlphaNum) - resource.Test(t, resource.TestCase{ + resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, Providers: testAccProviders, CheckDestroy: testCheckAzureADGroupDestroy, Steps: []resource.TestStep{ { - Config: config, - Check: assertResourceWithMemberCount(id, "3"), + Config: testAccAzureADGroupWithDiverseMembers(id, pw), + Check: testCheckAzureAdGroupBasic(id, "3"), }, { - ResourceName: resourceName, + ResourceName: rn, ImportState: true, ImportStateVerify: true, }, @@ -118,61 +103,59 @@ func TestAccAzureADGroup_diverse(t *testing.T) { } func TestAccAzureADGroup_progression(t *testing.T) { - resourceName := "azuread_group.test" - id, err := uuid.GenerateUUID() - if err != nil { - t.Fatal(err) - } + rn := "azuread_group.test" + id := tf.AccRandTimeInt() + pw := "p@$$wR2" + acctest.RandStringFromCharSet(7, acctest.CharSetAlphaNum) - resource.Test(t, resource.TestCase{ + resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, Providers: testAccProviders, CheckDestroy: testCheckAzureADGroupDestroy, Steps: []resource.TestStep{ // Empty group with 0 members { - Config: testAccAzureADGroup(id), - Check: assertResourceWithMemberCount(id, "0"), + Config: testAccAzureADGroup_basic(id), + Check: testCheckAzureAdGroupBasic(id, "0"), }, { - ResourceName: resourceName, + ResourceName: rn, ImportState: true, ImportStateVerify: true, }, // Group with 1 member { - Config: testAccAzureADGroupWithOneMember(id), - Check: assertResourceWithMemberCount(id, "1"), + Config: testAccAzureADGroupWithOneMember(id, pw), + Check: testCheckAzureAdGroupBasic(id, "1"), }, { - ResourceName: resourceName, + ResourceName: rn, ImportState: true, ImportStateVerify: true, }, // Group with multiple members { - Config: testAccAzureADGroupWithThreeMembers(id), - Check: assertResourceWithMemberCount(id, "3"), + Config: testAccAzureADGroupWithThreeMembers(id, pw), + Check: testCheckAzureAdGroupBasic(id, "3"), }, { - ResourceName: resourceName, + ResourceName: rn, ImportState: true, ImportStateVerify: true, }, // Group with a different member { Config: testAccAzureADGroupWithServicePrincipal(id), - Check: assertResourceWithMemberCount(id, "1"), + Check: testCheckAzureAdGroupBasic(id, "1"), }, { - ResourceName: resourceName, + ResourceName: rn, ImportState: true, ImportStateVerify: true, }, // Empty group with 0 members { - Config: testAccAzureADGroup(id), - Check: assertResourceWithMemberCount(id, "0"), + Config: testAccAzureADGroup_basic(id), + Check: testCheckAzureAdGroupBasic(id, "0"), }, }, }) @@ -224,120 +207,120 @@ func testCheckAzureADGroupDestroy(s *terraform.State) error { return nil } -func assertResourceWithMemberCount(id string, memberCount string) resource.TestCheckFunc { +func testCheckAzureAdGroupBasic(id int, memberCount string) resource.TestCheckFunc { resourceName := "azuread_group.test" return resource.ComposeTestCheckFunc( testCheckAzureADGroupExists(resourceName), - resource.TestCheckResourceAttr(resourceName, "name", fmt.Sprintf("acctest%s", id)), + resource.TestCheckResourceAttr(resourceName, "name", fmt.Sprintf("acctestGroup-%d", id)), resource.TestCheckResourceAttrSet(resourceName, "object_id"), resource.TestCheckResourceAttr(resourceName, "members.#", memberCount), ) } -func testAccAzureADGroup(id string) string { +func testAccAzureADGroup_basic(id int) string { return fmt.Sprintf(` resource "azuread_group" "test" { - name = "acctest%s" + name = "acctestGroup-%d" members = [] } `, id) } -func testAccAzureADGroupWithDiverseMembers(id string) string { +func testAccAzureADGroupWithDiverseMembers(id int, password string) string { return fmt.Sprintf(` data "azuread_domains" "tenant_domain" { only_initial = true } -resource "azuread_application" "test_app_%[1]s" { - name = "app%[1]s" +resource "azuread_application" "test" { + name = "acctestApp-%[1]d" } -resource "azuread_service_principal" "test_sp_%[1]s" { - application_id = azuread_application.test_app_%[1]s.application_id +resource "azuread_service_principal" "test" { + application_id = azuread_application.test.application_id } -resource "azuread_group" "test_g_%[1]s" { - name = "acctest%[1]s" +resource "azuread_group" "member" { + name = "acctestGroup-%[1]d-Member" } -resource "azuread_user" "acctest_user_%[1]s" { - user_principal_name = "acctest.%[1]s@${data.azuread_domains.tenant_domain.domains.0.domain_name}" - display_name = "acctest-%[1]s" - password = "%[1]s" +resource "azuread_user" "test" { + user_principal_name = "acctestUser.%[1]d@${data.azuread_domains.tenant_domain.domains.0.domain_name}" + display_name = "acctestUser-%[1]d" + password = "%[2]s" } resource "azuread_group" "test" { - name = "acctest%[1]s" - members = [ azuread_user.acctest_user_%[1]s.object_id, azuread_group.test_g_%[1]s.object_id, azuread_service_principal.test_sp_%[1]s.object_id ] + name = "acctestGroup-%[1]d" + members = [ azuread_user.test.object_id, azuread_group.member.object_id, azuread_service_principal.test.object_id ] } -`, id) +`, id, password) } -func testAccAzureADGroupWithOneMember(id string) string { +func testAccAzureADGroupWithOneMember(id int, password string) string { return fmt.Sprintf(` data "azuread_domains" "tenant_domain" { only_initial = true } -resource "azuread_user" "acctest_user_%[1]s" { - user_principal_name = "acctest.%[1]s@${data.azuread_domains.tenant_domain.domains.0.domain_name}" - display_name = "acctest-%[1]s" - password = "%[1]s" +resource "azuread_user" "test" { + user_principal_name = "acctestUser.%[1]d@${data.azuread_domains.tenant_domain.domains.0.domain_name}" + display_name = "acctestUser-%[1]d" + password = "%[2]s" } resource "azuread_group" "test" { - name = "acctest%[1]s" - members = [ azuread_user.acctest_user_%[1]s.object_id ] + name = "acctestGroup-%[1]d" + members = [ azuread_user.test.object_id ] } -`, id) +`, id, password) } -func testAccAzureADGroupWithThreeMembers(id string) string { +func testAccAzureADGroupWithThreeMembers(id int, password string) string { return fmt.Sprintf(` data "azuread_domains" "tenant_domain" { only_initial = true } -resource "azuread_user" "acctest_user_%[2]s" { - user_principal_name = "acctest.%[2]s@${data.azuread_domains.tenant_domain.domains.0.domain_name}" - display_name = "acctest-%[2]s" +resource "azuread_user" "testA" { + user_principal_name = "acctestUser.%[1]d.A@${data.azuread_domains.tenant_domain.domains.0.domain_name}" + display_name = "acctestUser-%[1]dA" password = "%[2]s" } -resource "azuread_user" "acctest_user_%[3]s" { - user_principal_name = "acctest.%[3]s@${data.azuread_domains.tenant_domain.domains.0.domain_name}" - display_name = "acctest-%[3]s" - password = "%[3]s" +resource "azuread_user" "testB" { + user_principal_name = "acctestUser.%[1]d.B@${data.azuread_domains.tenant_domain.domains.0.domain_name}" + display_name = "acctestUser-%[1]d-B" + password = "%[2]s" } -resource "azuread_user" "acctest_user_%[4]s" { - user_principal_name = "acctest.%[4]s@${data.azuread_domains.tenant_domain.domains.0.domain_name}" - display_name = "acctest-%[4]s" - password = "%[4]s" +resource "azuread_user" "testC" { + user_principal_name = "acctestUser.%[1]dC@${data.azuread_domains.tenant_domain.domains.0.domain_name}" + display_name = "acctestUser-%[1]dC" + password = "%[2]s" } resource "azuread_group" "test" { - name = "acctest%[1]s" - members = [ azuread_user.acctest_user_%[2]s.object_id, azuread_user.acctest_user_%[3]s.object_id, azuread_user.acctest_user_%[4]s.object_id ] + name = "acctestGroup-%[1]d" + members = [ azuread_user.testA.object_id, azuread_user.testB.object_id, azuread_user.testC.object_id ] } -`, id, id+"a", id+"b", id+"c") +`, id, password) } -func testAccAzureADGroupWithServicePrincipal(id string) string { +func testAccAzureADGroupWithServicePrincipal(id int) string { return fmt.Sprintf(` -resource "azuread_application" "test_app_%[1]s" { - name = "app%[1]s" +resource "azuread_application" "test" { + name = "acctestApp-%[1]d" } -resource "azuread_service_principal" "test_sp_%[1]s" { - application_id = azuread_application.test_app_%[1]s.application_id +resource "azuread_service_principal" "test" { + application_id = azuread_application.test.application_id } resource "azuread_group" "test" { - name = "acctest%[1]s" - members = [ azuread_service_principal.test_sp_%[1]s.object_id ] + name = "acctestGroup-%[1]d" + members = [ azuread_service_principal.test.object_id ] } `, id) } diff --git a/website/docs/d/groups.html.markdown b/website/docs/d/groups.html.markdown new file mode 100644 index 0000000000..0c253c30d1 --- /dev/null +++ b/website/docs/d/groups.html.markdown @@ -0,0 +1,39 @@ +--- +layout: "azuread" +page_title: "Azure Active Directory: azuread_groups" +sidebar_current: "docs-azuread-datasource-azuread-groups" +description: |- + Gets information about Azure Active Directory groups. + +--- + +# Data Source: azuread_user + +Gets Object IDs or Display Names for multiple Azure Active Directory groups. + +-> **NOTE:** If you're authenticating using a Service Principal then it must have permissions to `Read directory data` within the `Windows Azure Active Directory` API. + +## Example Usage + +```hcl +data "azuread_groups" "groups" { + names = ["groupa", "groupb"] +} +``` + +## Argument Reference + +The following arguments are supported: + +* `names` - (optional) The Display Names of the Azure AD Groups. + +* `object_ids` - (Optional) The Object IDs of the Azure AD Groups. + +-> **NOTE:** Either `names` or `object_ids` must be specified. + +## Attributes Reference + +The following attributes are exported: + +* `object_ids` - The Object IDs of the Azure AD Groups. +* `names` - The Display Names of the Azure AD Groups. diff --git a/website/docs/d/users.html.markdown b/website/docs/d/users.html.markdown index da248c6066..6518b5d34b 100644 --- a/website/docs/d/users.html.markdown +++ b/website/docs/d/users.html.markdown @@ -29,7 +29,7 @@ The following arguments are supported: * `object_ids` - (Optional) The Object IDs of the Azure AD Users. --> **NOTE:** Either a `user_principal_names` or an `object_ids` must be specified. +-> **NOTE:** Either `user_principal_names` or `object_ids` must be specified. ## Attributes Reference From 4fdb3ae9dcf6a730a4fddc88b1f5cb0415ca1dc0 Mon Sep 17 00:00:00 2001 From: kt Date: Thu, 18 Jul 2019 14:30:43 -0700 Subject: [PATCH 44/45] azuread_application - support for the public_client property (#121) fixes #119 --- azuread/resource_application.go | 21 ++++++++++--- azuread/resource_application_test.go | 38 ++++++++++++++++++++++++ website/docs/r/application.html.markdown | 2 ++ 3 files changed, 57 insertions(+), 4 deletions(-) diff --git a/azuread/resource_application.go b/azuread/resource_application.go index cee73d9b30..961b830f76 100644 --- a/azuread/resource_application.go +++ b/azuread/resource_application.go @@ -72,6 +72,12 @@ func resourceApplication() *schema.Resource { Optional: true, }, + "public_client": { + Type: schema.TypeBool, + Optional: true, + Computed: true, + }, + "reply_urls": { Type: schema.TypeSet, Optional: true, @@ -226,6 +232,10 @@ func resourceApplicationCreate(d *schema.ResourceData, meta interface{}) error { properties.Oauth2AllowImplicitFlow = p.Bool(v.(bool)) } + if v, ok := d.GetOk("public_client"); ok { + properties.PublicClient = p.Bool(v.(bool)) + } + if v, ok := d.GetOk("group_membership_claims"); ok { properties.GroupMembershipClaims = v } @@ -289,13 +299,15 @@ func resourceApplicationUpdate(d *schema.ResourceData, meta interface{}) error { } if d.HasChange("available_to_other_tenants") { - availableToOtherTenants := d.Get("available_to_other_tenants").(bool) - properties.AvailableToOtherTenants = p.Bool(availableToOtherTenants) + properties.AvailableToOtherTenants = p.Bool(d.Get("available_to_other_tenants").(bool)) } if d.HasChange("oauth2_allow_implicit_flow") { - oauth := d.Get("oauth2_allow_implicit_flow").(bool) - properties.Oauth2AllowImplicitFlow = p.Bool(oauth) + properties.Oauth2AllowImplicitFlow = p.Bool(d.Get("oauth2_allow_implicit_flow").(bool)) + } + + if d.HasChange("public_client") { + properties.PublicClient = p.Bool(d.Get("public_client").(bool)) } if d.HasChange("required_resource_access") { @@ -372,6 +384,7 @@ func resourceApplicationRead(d *schema.ResourceData, meta interface{}) error { d.Set("homepage", app.Homepage) d.Set("available_to_other_tenants", app.AvailableToOtherTenants) d.Set("oauth2_allow_implicit_flow", app.Oauth2AllowImplicitFlow) + d.Set("public_client", app.PublicClient) d.Set("object_id", app.ObjectID) if v := app.PublicClient; v != nil && *v { diff --git a/azuread/resource_application_test.go b/azuread/resource_application_test.go index 2fd53a23c7..37287ba53b 100644 --- a/azuread/resource_application_test.go +++ b/azuread/resource_application_test.go @@ -27,6 +27,7 @@ func TestAccAzureADApplication_basic(t *testing.T) { testCheckADApplicationExists(resourceName), resource.TestCheckResourceAttr(resourceName, "name", fmt.Sprintf("acctest%s", id)), resource.TestCheckResourceAttr(resourceName, "homepage", fmt.Sprintf("https://acctest%s", id)), + resource.TestCheckResourceAttr(resourceName, "oauth2_allow_implicit_flow", "false"), resource.TestCheckResourceAttr(resourceName, "type", "webapp/api"), resource.TestCheckResourceAttr(resourceName, "oauth2_permissions.#", "1"), resource.TestCheckResourceAttr(resourceName, "oauth2_permissions.0.admin_consent_description", fmt.Sprintf("Allow the application to access %s on behalf of the signed-in user.", fmt.Sprintf("acctest%s", id))), @@ -58,6 +59,7 @@ func TestAccAzureADApplication_complete(t *testing.T) { testCheckADApplicationExists(resourceName), resource.TestCheckResourceAttr(resourceName, "name", fmt.Sprintf("acctest%s", id)), resource.TestCheckResourceAttr(resourceName, "homepage", fmt.Sprintf("https://homepage-%s", id)), + resource.TestCheckResourceAttr(resourceName, "oauth2_allow_implicit_flow", "true"), resource.TestCheckResourceAttr(resourceName, "identifier_uris.#", "1"), resource.TestCheckResourceAttr(resourceName, "identifier_uris.0", fmt.Sprintf("http://%s.hashicorptest.com/00000000-0000-0000-0000-00000000", id)), resource.TestCheckResourceAttr(resourceName, "reply_urls.#", "1"), @@ -76,6 +78,31 @@ func TestAccAzureADApplication_complete(t *testing.T) { }) } +func TestAccAzureADApplication_publicClient(t *testing.T) { + resourceName := "azuread_application.test" + id := uuid.New().String() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckADApplicationDestroy, + Steps: []resource.TestStep{ + { + Config: testAccADApplication_publicClient(id), + Check: resource.ComposeTestCheckFunc( + testCheckADApplicationExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "public_client", "true"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + func TestAccAzureADApplication_update(t *testing.T) { resourceName := "azuread_application.test" id := uuid.New().String() @@ -467,6 +494,16 @@ resource "azuread_application" "test" { `, id) } +func testAccADApplication_publicClient(id string) string { + return fmt.Sprintf(` +resource "azuread_application" "test" { + name = "acctest%s" + type = "native" + public_client = true +} +`, id) +} + func testAccADApplication_availableToOtherTenants(id string) string { return fmt.Sprintf(` data "azuread_domains" "tenant_domain" { @@ -507,6 +544,7 @@ resource "azuread_application" "test" { identifier_uris = ["http://%s.hashicorptest.com/00000000-0000-0000-0000-00000000"] reply_urls = ["http://unittest.hashicorptest.com"] oauth2_allow_implicit_flow = true + group_membership_claims = "All" required_resource_access { diff --git a/website/docs/r/application.html.markdown b/website/docs/r/application.html.markdown index e1829aa19f..e672b3f0a8 100644 --- a/website/docs/r/application.html.markdown +++ b/website/docs/r/application.html.markdown @@ -81,6 +81,8 @@ The following arguments are supported: * `available_to_other_tenants` - (Optional) Is this Azure AD Application available to other tenants? Defaults to `false`. +* `public_client` - (Optional) Is this Azure AD Application a public client? Defaults to `false`. + * `oauth2_allow_implicit_flow` - (Optional) Does this Azure AD Application allow OAuth2.0 implicit flow tokens? Defaults to `false`. * `group_membership_claims` - (Optional) Configures the `groups` claim issued in a user or OAuth 2.0 access token that the app expects. Defaults to `SecurityGroup`. Possible values are `None`, `SecurityGroup` or `All`. From 0926a94de8579424ca9c85e38350d034ccc57310 Mon Sep 17 00:00:00 2001 From: kt Date: Thu, 18 Jul 2019 18:11:23 -0700 Subject: [PATCH 45/45] Update tests to be consistent (#124) --- azuread/data_application_test.go | 12 ++--- azuread/data_service_principal_test.go | 2 +- azuread/resource_application_password_test.go | 2 +- azuread/resource_application_test.go | 50 +++++++++---------- ...esource_service_principal_password_test.go | 2 +- azuread/resource_service_principal_test.go | 6 +-- azuread/resource_user_test.go | 44 ++++++++-------- 7 files changed, 59 insertions(+), 59 deletions(-) diff --git a/azuread/data_application_test.go b/azuread/data_application_test.go index 8baf38a103..18f032b78e 100644 --- a/azuread/data_application_test.go +++ b/azuread/data_application_test.go @@ -24,8 +24,8 @@ func TestAccAzureADApplicationDataSource_byObjectId(t *testing.T) { Config: testAccAzureADApplicationDataSource_objectId(id), Check: resource.ComposeTestCheckFunc( testCheckADApplicationExists(dataSourceName), - resource.TestCheckResourceAttr(dataSourceName, "name", fmt.Sprintf("acctest%s", id)), - resource.TestCheckResourceAttr(dataSourceName, "homepage", fmt.Sprintf("https://acctest%s", id)), + resource.TestCheckResourceAttr(dataSourceName, "name", fmt.Sprintf("acctestApp-%s", id)), + resource.TestCheckResourceAttr(dataSourceName, "homepage", fmt.Sprintf("https://acctestApp-%s", id)), resource.TestCheckResourceAttr(dataSourceName, "identifier_uris.#", "0"), resource.TestCheckResourceAttr(dataSourceName, "reply_urls.#", "0"), resource.TestCheckResourceAttr(dataSourceName, "required_resource_access.#", "0"), @@ -33,7 +33,7 @@ func TestAccAzureADApplicationDataSource_byObjectId(t *testing.T) { resource.TestCheckResourceAttr(dataSourceName, "app_roles.#", "0"), resource.TestCheckResourceAttr(dataSourceName, "oauth2_allow_implicit_flow", "false"), resource.TestCheckResourceAttr(dataSourceName, "oauth2_permissions.#", "1"), - resource.TestCheckResourceAttr(dataSourceName, "oauth2_permissions.0.admin_consent_description", fmt.Sprintf("Allow the application to access %s on behalf of the signed-in user.", fmt.Sprintf("acctest%s", id))), + resource.TestCheckResourceAttr(dataSourceName, "oauth2_permissions.0.admin_consent_description", fmt.Sprintf("Allow the application to access %s on behalf of the signed-in user.", fmt.Sprintf("acctestApp-%s", id))), resource.TestCheckResourceAttrSet(dataSourceName, "application_id"), ), }, @@ -57,7 +57,7 @@ func TestAccAzureADApplicationDataSource_byObjectIdComplete(t *testing.T) { Config: testAccAzureADApplicationDataSource_objectIdComplete(id), Check: resource.ComposeTestCheckFunc( testCheckADApplicationExists(dataSourceName), - resource.TestCheckResourceAttr(dataSourceName, "name", fmt.Sprintf("acctest%s", id)), + resource.TestCheckResourceAttr(dataSourceName, "name", fmt.Sprintf("acctestApp-%s", id)), resource.TestCheckResourceAttr(dataSourceName, "homepage", fmt.Sprintf("https://homepage-%s", id)), resource.TestCheckResourceAttr(dataSourceName, "identifier_uris.#", "1"), resource.TestCheckResourceAttr(dataSourceName, "reply_urls.#", "1"), @@ -87,8 +87,8 @@ func TestAccAzureADApplicationDataSource_byName(t *testing.T) { Config: testAccAzureADApplicationDataSource_name(id), Check: resource.ComposeTestCheckFunc( testCheckADApplicationExists(dataSourceName), - resource.TestCheckResourceAttr(dataSourceName, "name", fmt.Sprintf("acctest%s", id)), - resource.TestCheckResourceAttr(dataSourceName, "homepage", fmt.Sprintf("https://acctest%s", id)), + resource.TestCheckResourceAttr(dataSourceName, "name", fmt.Sprintf("acctestApp-%s", id)), + resource.TestCheckResourceAttr(dataSourceName, "homepage", fmt.Sprintf("https://acctestApp-%s", id)), resource.TestCheckResourceAttr(dataSourceName, "identifier_uris.#", "0"), resource.TestCheckResourceAttr(dataSourceName, "reply_urls.#", "0"), resource.TestCheckResourceAttr(dataSourceName, "required_resource_access.#", "0"), diff --git a/azuread/data_service_principal_test.go b/azuread/data_service_principal_test.go index 8990b957bb..7fd95c4605 100644 --- a/azuread/data_service_principal_test.go +++ b/azuread/data_service_principal_test.go @@ -26,7 +26,7 @@ func TestAccAzureADServicePrincipalDataSource_byApplicationId(t *testing.T) { resource.TestCheckResourceAttrSet(dataSourceName, "display_name"), resource.TestCheckResourceAttr(dataSourceName, "app_roles.#", "0"), resource.TestCheckResourceAttr(dataSourceName, "oauth2_permissions.#", "1"), - resource.TestCheckResourceAttr(dataSourceName, "oauth2_permissions.0.admin_consent_description", fmt.Sprintf("Allow the application to access %s on behalf of the signed-in user.", fmt.Sprintf("acctestspa%s", id))), + resource.TestCheckResourceAttr(dataSourceName, "oauth2_permissions.0.admin_consent_description", fmt.Sprintf("Allow the application to access %s on behalf of the signed-in user.", fmt.Sprintf("acctestApp-%s", id))), ), }, }, diff --git a/azuread/resource_application_password_test.go b/azuread/resource_application_password_test.go index feb6391b87..7c49a1ab28 100644 --- a/azuread/resource_application_password_test.go +++ b/azuread/resource_application_password_test.go @@ -205,7 +205,7 @@ func TestAccAzureADApplicationPassword_relativeEndDate(t *testing.T) { func testAccADApplicationPassword_template(applicationId string) string { return fmt.Sprintf(` resource "azuread_application" "test" { - name = "acctestspa%s" + name = "acctestApp-%s" } `, applicationId) } diff --git a/azuread/resource_application_test.go b/azuread/resource_application_test.go index 37287ba53b..47622b0da2 100644 --- a/azuread/resource_application_test.go +++ b/azuread/resource_application_test.go @@ -25,12 +25,12 @@ func TestAccAzureADApplication_basic(t *testing.T) { Config: testAccADApplication_basic(id), Check: resource.ComposeTestCheckFunc( testCheckADApplicationExists(resourceName), - resource.TestCheckResourceAttr(resourceName, "name", fmt.Sprintf("acctest%s", id)), - resource.TestCheckResourceAttr(resourceName, "homepage", fmt.Sprintf("https://acctest%s", id)), + resource.TestCheckResourceAttr(resourceName, "name", fmt.Sprintf("acctestApp-%s", id)), + resource.TestCheckResourceAttr(resourceName, "homepage", fmt.Sprintf("https://acctestApp-%s", id)), resource.TestCheckResourceAttr(resourceName, "oauth2_allow_implicit_flow", "false"), resource.TestCheckResourceAttr(resourceName, "type", "webapp/api"), resource.TestCheckResourceAttr(resourceName, "oauth2_permissions.#", "1"), - resource.TestCheckResourceAttr(resourceName, "oauth2_permissions.0.admin_consent_description", fmt.Sprintf("Allow the application to access %s on behalf of the signed-in user.", fmt.Sprintf("acctest%s", id))), + resource.TestCheckResourceAttr(resourceName, "oauth2_permissions.0.admin_consent_description", fmt.Sprintf("Allow the application to access %s on behalf of the signed-in user.", fmt.Sprintf("acctestApp-%s", id))), resource.TestCheckResourceAttrSet(resourceName, "application_id"), resource.TestCheckResourceAttrSet(resourceName, "object_id"), ), @@ -57,7 +57,7 @@ func TestAccAzureADApplication_complete(t *testing.T) { Config: testAccADApplication_complete(id), Check: resource.ComposeTestCheckFunc( testCheckADApplicationExists(resourceName), - resource.TestCheckResourceAttr(resourceName, "name", fmt.Sprintf("acctest%s", id)), + resource.TestCheckResourceAttr(resourceName, "name", fmt.Sprintf("acctestApp-%s", id)), resource.TestCheckResourceAttr(resourceName, "homepage", fmt.Sprintf("https://homepage-%s", id)), resource.TestCheckResourceAttr(resourceName, "oauth2_allow_implicit_flow", "true"), resource.TestCheckResourceAttr(resourceName, "identifier_uris.#", "1"), @@ -117,8 +117,8 @@ func TestAccAzureADApplication_update(t *testing.T) { Config: testAccADApplication_basic(id), Check: resource.ComposeTestCheckFunc( testCheckADApplicationExists(resourceName), - resource.TestCheckResourceAttr(resourceName, "name", fmt.Sprintf("acctest%s", id)), - resource.TestCheckResourceAttr(resourceName, "homepage", fmt.Sprintf("https://acctest%s", id)), + resource.TestCheckResourceAttr(resourceName, "name", fmt.Sprintf("acctestApp-%s", id)), + resource.TestCheckResourceAttr(resourceName, "homepage", fmt.Sprintf("https://acctestApp-%s", id)), resource.TestCheckResourceAttr(resourceName, "identifier_uris.#", "0"), resource.TestCheckResourceAttr(resourceName, "reply_urls.#", "0"), ), @@ -127,7 +127,7 @@ func TestAccAzureADApplication_update(t *testing.T) { Config: testAccADApplication_complete(updatedId), Check: resource.ComposeTestCheckFunc( testCheckADApplicationExists(resourceName), - resource.TestCheckResourceAttr(resourceName, "name", fmt.Sprintf("acctest%s", updatedId)), + resource.TestCheckResourceAttr(resourceName, "name", fmt.Sprintf("acctestApp-%s", updatedId)), resource.TestCheckResourceAttr(resourceName, "homepage", fmt.Sprintf("https://homepage-%s", updatedId)), resource.TestCheckResourceAttr(resourceName, "identifier_uris.#", "1"), resource.TestCheckResourceAttr(resourceName, "identifier_uris.0", fmt.Sprintf("http://%s.hashicorptest.com/00000000-0000-0000-0000-00000000", updatedId)), @@ -321,7 +321,7 @@ func TestAccAzureADApplication_native(t *testing.T) { Config: testAccADApplication_native(id), Check: resource.ComposeTestCheckFunc( testCheckADApplicationExists(resourceName), - resource.TestCheckResourceAttr(resourceName, "name", fmt.Sprintf("acctest%s", id)), + resource.TestCheckResourceAttr(resourceName, "name", fmt.Sprintf("acctestApp-%s", id)), resource.TestCheckResourceAttr(resourceName, "homepage", ""), resource.TestCheckResourceAttr(resourceName, "type", "native"), resource.TestCheckResourceAttr(resourceName, "identifier_uris.#", "0"), @@ -350,7 +350,7 @@ func TestAccAzureADApplication_nativeReplyUrls(t *testing.T) { Config: testAccADApplication_nativeReplyUrls(id), Check: resource.ComposeTestCheckFunc( testCheckADApplicationExists(resourceName), - resource.TestCheckResourceAttr(resourceName, "name", fmt.Sprintf("acctest%s", id)), + resource.TestCheckResourceAttr(resourceName, "name", fmt.Sprintf("acctestApp-%s", id)), resource.TestCheckResourceAttr(resourceName, "type", "native"), resource.TestCheckResourceAttr(resourceName, "reply_urls.#", "1"), resource.TestCheckResourceAttr(resourceName, "reply_urls.3637476042", "urn:ietf:wg:oauth:2.0:oob"), @@ -379,8 +379,8 @@ func TestAccAzureADApplication_nativeUpdate(t *testing.T) { Config: testAccADApplication_basic(id), Check: resource.ComposeTestCheckFunc( testCheckADApplicationExists(resourceName), - resource.TestCheckResourceAttr(resourceName, "name", fmt.Sprintf("acctest%s", id)), - resource.TestCheckResourceAttr(resourceName, "homepage", fmt.Sprintf("https://acctest%s", id)), + resource.TestCheckResourceAttr(resourceName, "name", fmt.Sprintf("acctestApp-%s", id)), + resource.TestCheckResourceAttr(resourceName, "homepage", fmt.Sprintf("https://acctestApp-%s", id)), resource.TestCheckResourceAttr(resourceName, "type", "webapp/api"), resource.TestCheckResourceAttr(resourceName, "identifier_uris.#", "0"), resource.TestCheckResourceAttrSet(resourceName, "application_id"), @@ -390,8 +390,8 @@ func TestAccAzureADApplication_nativeUpdate(t *testing.T) { Config: testAccADApplication_native(id), Check: resource.ComposeTestCheckFunc( testCheckADApplicationExists(resourceName), - resource.TestCheckResourceAttr(resourceName, "name", fmt.Sprintf("acctest%s", id)), - resource.TestCheckResourceAttr(resourceName, "homepage", fmt.Sprintf("https://acctest%s", id)), + resource.TestCheckResourceAttr(resourceName, "name", fmt.Sprintf("acctestApp-%s", id)), + resource.TestCheckResourceAttr(resourceName, "homepage", fmt.Sprintf("https://acctestApp-%s", id)), resource.TestCheckResourceAttr(resourceName, "type", "native"), resource.TestCheckResourceAttr(resourceName, "identifier_uris.#", "0"), resource.TestCheckResourceAttrSet(resourceName, "application_id"), @@ -406,7 +406,7 @@ func TestAccAzureADApplication_nativeUpdate(t *testing.T) { Config: testAccADApplication_complete(id), Check: resource.ComposeTestCheckFunc( testCheckADApplicationExists(resourceName), - resource.TestCheckResourceAttr(resourceName, "name", fmt.Sprintf("acctest%s", id)), + resource.TestCheckResourceAttr(resourceName, "name", fmt.Sprintf("acctestApp-%s", id)), resource.TestCheckResourceAttr(resourceName, "homepage", fmt.Sprintf("https://homepage-%s", id)), resource.TestCheckResourceAttr(resourceName, "identifier_uris.#", "1"), resource.TestCheckResourceAttr(resourceName, "identifier_uris.0", fmt.Sprintf("http://%s.hashicorptest.com/00000000-0000-0000-0000-00000000", id)), @@ -489,7 +489,7 @@ func testCheckADApplicationDestroy(s *terraform.State) error { func testAccADApplication_basic(id string) string { return fmt.Sprintf(` resource "azuread_application" "test" { - name = "acctest%s" + name = "acctestApp-%s" } `, id) } @@ -497,7 +497,7 @@ resource "azuread_application" "test" { func testAccADApplication_publicClient(id string) string { return fmt.Sprintf(` resource "azuread_application" "test" { - name = "acctest%s" + name = "acctestApp-%s" type = "native" public_client = true } @@ -511,7 +511,7 @@ data "azuread_domains" "tenant_domain" { } resource "azuread_application" "test" { - name = "acctest%s" + name = "acctestApp-%s" identifier_uris = ["https://%s.${data.azuread_domains.tenant_domain.domains.0.domain_name}"] available_to_other_tenants = true } @@ -521,7 +521,7 @@ resource "azuread_application" "test" { func testAccADApplication_withGroupMembershipClaimsDirectoryRole(id string) string { return fmt.Sprintf(` resource "azuread_application" "test" { - name = "acctest%s" + name = "acctestApp-%s" group_membership_claims = "DirectoryRole" } `, id) @@ -530,7 +530,7 @@ resource "azuread_application" "test" { func testAccADApplication_withGroupMembershipClaimsSecurityGroup(id string) string { return fmt.Sprintf(` resource "azuread_application" "test" { - name = "acctest%s" + name = "acctestApp-%s" group_membership_claims = "SecurityGroup" } `, id) @@ -539,7 +539,7 @@ resource "azuread_application" "test" { func testAccADApplication_complete(id string) string { return fmt.Sprintf(` resource "azuread_application" "test" { - name = "acctest%s" + name = "acctestApp-%s" homepage = "https://homepage-%s" identifier_uris = ["http://%s.hashicorptest.com/00000000-0000-0000-0000-00000000"] reply_urls = ["http://unittest.hashicorptest.com"] @@ -581,7 +581,7 @@ resource "azuread_application" "test" { func testAccADApplication_appRoles(id string) string { return fmt.Sprintf(` resource "azuread_application" "test" { - name = "acctest%s" + name = "acctestApp-%s" app_role { allowed_member_types = [ @@ -601,7 +601,7 @@ resource "azuread_application" "test" { func testAccADApplication_appRolesUpdate(id string) string { return fmt.Sprintf(` resource "azuread_application" "test" { - name = "acctest%s" + name = "acctestApp-%s" app_role { allowed_member_types = ["User"] @@ -625,7 +625,7 @@ resource "azuread_application" "test" { func testAccADApplication_native(id string) string { return fmt.Sprintf(` resource "azuread_application" "test" { - name = "acctest%s" + name = "acctestApp-%s" type = "native" } `, id) @@ -634,7 +634,7 @@ resource "azuread_application" "test" { func testAccADApplication_nativeReplyUrls(id string) string { return fmt.Sprintf(` resource "azuread_application" "test" { - name = "acctest%s" + name = "acctestApp-%s" type = "native" reply_urls = ["urn:ietf:wg:oauth:2.0:oob"] } @@ -644,7 +644,7 @@ resource "azuread_application" "test" { func testAccADApplication_native_app_does_not_allow_identifier_uris(id string) string { return fmt.Sprintf(` resource "azuread_application" "test" { - name = "acctest%s" + name = "acctestApp-%s" identifier_uris = ["http://%s.hashicorptest.com"] type = "native" } diff --git a/azuread/resource_service_principal_password_test.go b/azuread/resource_service_principal_password_test.go index 6a18e2182e..0b8ee51616 100644 --- a/azuread/resource_service_principal_password_test.go +++ b/azuread/resource_service_principal_password_test.go @@ -183,7 +183,7 @@ func TestAccAzureADServicePrincipalPassword_relativeEndDate(t *testing.T) { func testAccADServicePrincipalPassword_template(applicationId string) string { return fmt.Sprintf(` resource "azuread_application" "test" { - name = "acctestspa%s" + name = "acctestApp-%s" } resource "azuread_service_principal" "test" { diff --git a/azuread/resource_service_principal_test.go b/azuread/resource_service_principal_test.go index 52bdc3acde..db618f770a 100644 --- a/azuread/resource_service_principal_test.go +++ b/azuread/resource_service_principal_test.go @@ -27,7 +27,7 @@ func TestAccAzureADServicePrincipal_basic(t *testing.T) { resource.TestCheckResourceAttrSet(resourceName, "display_name"), resource.TestCheckResourceAttrSet(resourceName, "application_id"), resource.TestCheckResourceAttr(resourceName, "oauth2_permissions.#", "1"), - resource.TestCheckResourceAttr(resourceName, "oauth2_permissions.0.admin_consent_description", fmt.Sprintf("Allow the application to access %s on behalf of the signed-in user.", fmt.Sprintf("acctestspa%s", id))), + resource.TestCheckResourceAttr(resourceName, "oauth2_permissions.0.admin_consent_description", fmt.Sprintf("Allow the application to access %s on behalf of the signed-in user.", fmt.Sprintf("acctestApp-%s", id))), resource.TestCheckResourceAttrSet(resourceName, "object_id"), ), }, @@ -115,7 +115,7 @@ func testCheckADServicePrincipalDestroy(s *terraform.State) error { func testAccADServicePrincipal_basic(id string) string { return fmt.Sprintf(` resource "azuread_application" "test" { - name = "acctestspa%s" + name = "acctestApp-%s" } resource "azuread_service_principal" "test" { @@ -127,7 +127,7 @@ resource "azuread_service_principal" "test" { func testAccADServicePrincipal_complete(id string) string { return fmt.Sprintf(` resource "azuread_application" "test" { - name = "acctestspa%s" + name = "acctesttApp-%s" } resource "azuread_service_principal" "test" { diff --git a/azuread/resource_user_test.go b/azuread/resource_user_test.go index 948c4343c4..34f6470938 100644 --- a/azuread/resource_user_test.go +++ b/azuread/resource_user_test.go @@ -28,8 +28,8 @@ func TestAccAzureADUser_basic(t *testing.T) { testCheckADUserExists(rn), resource.TestCheckResourceAttrSet(rn, "user_principal_name"), resource.TestCheckResourceAttrSet(rn, "object_id"), - resource.TestCheckResourceAttr(rn, "display_name", fmt.Sprintf("acctest%d", id)), - resource.TestCheckResourceAttr(rn, "mail_nickname", fmt.Sprintf("acctest%d", id)), + resource.TestCheckResourceAttr(rn, "display_name", fmt.Sprintf("acctestUser-%d", id)), + resource.TestCheckResourceAttr(rn, "mail_nickname", fmt.Sprintf("acctestUser.%d", id)), resource.TestCheckResourceAttr(rn, "account_enabled", "true"), ), }, @@ -62,8 +62,8 @@ func TestAccAzureADUser_complete(t *testing.T) { testCheckADUserExists(rn), resource.TestCheckResourceAttrSet(rn, "user_principal_name"), resource.TestCheckResourceAttrSet(rn, "object_id"), - resource.TestCheckResourceAttr(rn, "display_name", fmt.Sprintf("acctestupdate%d", id)), - resource.TestCheckResourceAttr(rn, "mail_nickname", fmt.Sprintf("acctestupdate%d", id)), + resource.TestCheckResourceAttr(rn, "display_name", fmt.Sprintf("acctestUser-%d-Updated", id)), + resource.TestCheckResourceAttr(rn, "mail_nickname", fmt.Sprintf("acctestUser-%d-Updated", id)), resource.TestCheckResourceAttr(rn, "account_enabled", "false"), ), }, @@ -97,8 +97,8 @@ func TestAccAzureADUser_update(t *testing.T) { testCheckADUserExists(rn), resource.TestCheckResourceAttrSet(rn, "user_principal_name"), resource.TestCheckResourceAttrSet(rn, "object_id"), - resource.TestCheckResourceAttr(rn, "display_name", fmt.Sprintf("acctest%d", id)), - resource.TestCheckResourceAttr(rn, "mail_nickname", fmt.Sprintf("acctest%d", id)), + resource.TestCheckResourceAttr(rn, "display_name", fmt.Sprintf("acctestUser-%d", id)), + resource.TestCheckResourceAttr(rn, "mail_nickname", fmt.Sprintf("acctestUser.%d", id)), resource.TestCheckResourceAttr(rn, "account_enabled", "true"), ), }, @@ -108,8 +108,8 @@ func TestAccAzureADUser_update(t *testing.T) { testCheckADUserExists(rn), resource.TestCheckResourceAttrSet(rn, "user_principal_name"), resource.TestCheckResourceAttrSet(rn, "object_id"), - resource.TestCheckResourceAttr(rn, "display_name", fmt.Sprintf("acctestupdate%d", id)), - resource.TestCheckResourceAttr(rn, "mail_nickname", fmt.Sprintf("acctestupdate%d", id)), + resource.TestCheckResourceAttr(rn, "display_name", fmt.Sprintf("acctestUser-%d-Updated", id)), + resource.TestCheckResourceAttr(rn, "mail_nickname", fmt.Sprintf("acctestUser-%d-Updated", id)), resource.TestCheckResourceAttr(rn, "account_enabled", "false"), ), }, @@ -119,11 +119,11 @@ func TestAccAzureADUser_update(t *testing.T) { testCheckADUserExists("azuread_user.testA"), testCheckADUserExists("azuread_user.testB"), resource.TestCheckResourceAttrSet("azuread_user.testA", "user_principal_name"), - resource.TestCheckResourceAttr("azuread_user.testA", "display_name", fmt.Sprintf("acctestA%d", id)), - resource.TestCheckResourceAttr("azuread_user.testA", "mail_nickname", fmt.Sprintf("acctestA%d", id)), + resource.TestCheckResourceAttr("azuread_user.testA", "display_name", fmt.Sprintf("acctestUser-%d-A", id)), + resource.TestCheckResourceAttr("azuread_user.testA", "mail_nickname", fmt.Sprintf("acctestUser.%d.A", id)), resource.TestCheckResourceAttrSet("azuread_user.testB", "user_principal_name"), - resource.TestCheckResourceAttr("azuread_user.testB", "display_name", fmt.Sprintf("acctest_display%d", id)), - resource.TestCheckResourceAttr("azuread_user.testB", "mail_nickname", fmt.Sprintf("acctest_mail%d", id)), + resource.TestCheckResourceAttr("azuread_user.testB", "display_name", fmt.Sprintf("acctestUser-%d-B", id)), + resource.TestCheckResourceAttr("azuread_user.testB", "mail_nickname", fmt.Sprintf("acctestUser-%d-B", id)), ), }, }, @@ -183,8 +183,8 @@ data "azuread_domains" "tenant_domain" { } resource "azuread_user" "test" { - user_principal_name = "acctest%[1]d@${data.azuread_domains.tenant_domain.domains.0.domain_name}" - display_name = "acctest%[1]d" + user_principal_name = "acctestUser.%[1]d@${data.azuread_domains.tenant_domain.domains.0.domain_name}" + display_name = "acctestUser-%[1]d" password = "%[2]s" } `, id, password) @@ -197,9 +197,9 @@ data "azuread_domains" "tenant_domain" { } resource "azuread_user" "test" { - user_principal_name = "acctest%[1]d@${data.azuread_domains.tenant_domain.domains.0.domain_name}" - display_name = "acctestupdate%[1]d" - mail_nickname = "acctestupdate%[1]d" + user_principal_name = "acctestUser.%[1]d@${data.azuread_domains.tenant_domain.domains.0.domain_name}" + display_name = "acctestUser-%[1]d-Updated" + mail_nickname = "acctestUser-%[1]d-Updated" account_enabled = false password = "%[2]s" force_password_change = true @@ -214,15 +214,15 @@ data "azuread_domains" "tenant_domain" { } resource "azuread_user" "testA" { - user_principal_name = "acctestA%[1]d@${data.azuread_domains.tenant_domain.domains.0.domain_name}" - display_name = "acctestA%[1]d" + user_principal_name = "acctestUser.%[1]d.A@${data.azuread_domains.tenant_domain.domains.0.domain_name}" + display_name = "acctestUser-%[1]d-A" password = "%[2]s" } resource "azuread_user" "testB" { - user_principal_name = "acctestB%[1]d@${data.azuread_domains.tenant_domain.domains.0.domain_name}" - display_name = "acctest_display%[1]d" - mail_nickname = "acctest_mail%[1]d" + user_principal_name = "acctestUser.%[1]d.B@${data.azuread_domains.tenant_domain.domains.0.domain_name}" + display_name = "acctestUser-%[1]d-B" + mail_nickname = "acctestUser-%[1]d-B" password = "%[2]s" } `, id, password)