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/.hashibot.hcl b/.hashibot.hcl new file mode 100644 index 0000000000..7a58e42aa9 --- /dev/null +++ b/.hashibot.hcl @@ -0,0 +1,26 @@ +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 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 +} \ No newline at end of file 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 diff --git a/CHANGELOG.md b/CHANGELOG.md index 29cc0878ca..7a5738406f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,34 @@ -## 0.4.0 (Unreleased) +## 0.5.0 (Unreleased) + +FEATURES: + +* **New Data Source:** `azuread_users` [GH-109] +* **New Resource:** `azuread_group_member` [GH-100] + +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_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: + +* 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: + +* **New Resource:** `azuread_application_password` ([#71](https://github.com/terraform-providers/terraform-provider-azuread/issues/71)) FEATURES: @@ -7,13 +37,21 @@ FEATURES: 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` - support for the `type` property enabling the creation of `native` applications [GH-74] -* `azuread_user` - increase the maximum allowed lengh of `password` to 256 [GH-81] +* 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) 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 d5d9d70e70..6a7a30d01c 100644 --- a/azuread/config.go +++ b/azuread/config.go @@ -16,6 +16,8 @@ import ( "github.com/hashicorp/go-azure-helpers/sender" "github.com/hashicorp/terraform/httpclient" "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. @@ -50,6 +52,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 +66,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 1ce102f633..c5b15fd359 100644 --- a/azuread/data_application.go +++ b/azuread/data_application.go @@ -3,12 +3,13 @@ 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/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 { @@ -78,6 +79,8 @@ func dataApplication() *schema.Resource { Computed: true, }, + "app_roles": graph.SchemaAppRoles(), + "required_resource_access": { Type: schema.TypeList, Computed: true, @@ -109,54 +112,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(), }, } } @@ -167,45 +123,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 { @@ -232,19 +189,22 @@ 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("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) } - if oauth2Permissions, ok := app.AdditionalProperties["oauth2Permissions"].([]interface{}); ok { - d.Set("oauth2_permissions", flattenADApplicationOauth2Permissions(oauth2Permissions)) + 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/data_application_test.go b/azuread/data_application_test.go index 97766ae1ad..18f032b78e 100644 --- a/azuread/data_application_test.go +++ b/azuread/data_application_test.go @@ -24,15 +24,16 @@ 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"), 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("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("acctestApp-%s", id))), resource.TestCheckResourceAttrSet(dataSourceName, "application_id"), ), }, @@ -56,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"), @@ -86,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"), @@ -105,7 +106,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 +117,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_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 c7bfbabccc..50b3a8c83a 100644 --- a/azuread/data_group.go +++ b/azuread/data_group.go @@ -2,10 +2,12 @@ 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/graph" + + "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/ar" "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/validate" ) @@ -18,10 +20,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"}, }, //todo abiliy to lookup by object ID? @@ -35,40 +47,36 @@ 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) - } - - for _, v := range *resp.Response().Value { - if v.DisplayName == nil { - continue + return fmt.Errorf("Error making Read request on AzureAD Group with ID %q: %+v", oId, 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) + group = resp + } else if name, ok := d.Get("name").(string); ok && name != "" { + g, err := graph.GroupGetByDisplayName(&client, ctx, name) + if err != nil { + 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") } - if groupObj == nil { - return fmt.Errorf("Couldn't locate a Azure AD group with a name of %q", name) - } - - 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..3953ab0b80 100644 --- a/azuread/data_group_test.go +++ b/azuread/data_group_test.go @@ -4,44 +4,72 @@ import ( "fmt" "testing" - "github.com/hashicorp/go-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, err := uuid.GenerateUUID() - if err != nil { - t.Fatal(err) - } - config := testAccDataSourceAzureADGroup_name(id) - - resource.Test(t, resource.TestCase{ + dsn := "data.azuread_group.test" + id := tf.AccRandTimeInt() + + resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, Providers: testAccProviders, CheckDestroy: testCheckAzureADGroupDestroy, Steps: []resource.TestStep{ { - Config: testAccAzureADGroup(id), + Config: testAccAzureADGroup_basic(id), }, { - Config: config, + 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)), ), }, }, }) } -func testAccDataSourceAzureADGroup_name(id string) string { - template := testAccAzureADGroup(id) +func TestAccDataSourceAzureADGroup_byObjectId(t *testing.T) { + dsn := "data.azuread_group.test" + id := tf.AccRandTimeInt() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureADGroupDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureADGroup_basic(id), + }, + { + Config: testAccDataSourceAzureADGroup_objectId(id), + Check: resource.ComposeTestCheckFunc( + testCheckAzureADGroupExists(dsn), + resource.TestCheckResourceAttr(dsn, "name", fmt.Sprintf("acctestGroup-%d", id)), + ), + }, + }, + }) +} + +func testAccDataSourceAzureADGroup_name(id int) string { return fmt.Sprintf(` %s data "azuread_group" "test" { name = "${azuread_group.test.name}" } -`, template) +`, testAccAzureADGroup_basic(id)) +} + +func testAccDataSourceAzureADGroup_objectId(id int) string { + return fmt.Sprintf(` +%s + +data "azuread_group" "test" { + object_id = "${azuread_group.test.object_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_service_principal.go b/azuread/data_service_principal.go index c9982f4679..cb8e06532f 100644 --- a/azuread/data_service_principal.go +++ b/azuread/data_service_principal.go @@ -3,11 +3,12 @@ package azuread import ( "fmt" - "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/ar" - "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" + + "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" ) func dataServicePrincipal() *schema.Resource { @@ -38,6 +39,10 @@ func dataServicePrincipal() *schema.Resource { ValidateFunc: validate.UUID, ConflictsWith: []string{"object_id", "display_name"}, }, + + "app_roles": graph.SchemaAppRoles(), + + "oauth2_permissions": graph.SchemaOauth2Permissions(), }, } } @@ -128,5 +133,13 @@ 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) + } + return nil } diff --git a/azuread/data_service_principal_test.go b/azuread/data_service_principal_test.go index 1328f2bcd2..7fd95c4605 100644 --- a/azuread/data_service_principal_test.go +++ b/azuread/data_service_principal_test.go @@ -24,6 +24,9 @@ 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("acctestApp-%s", id))), ), }, }, @@ -102,7 +105,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.go b/azuread/data_user.go index f711ea0804..07de2263ee 100644 --- a/azuread/data_user.go +++ b/azuread/data_user.go @@ -2,15 +2,15 @@ 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/graph" "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{ @@ -18,10 +18,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 +63,28 @@ func dataSourceUserRead(d *schema.ResourceData, meta interface{}) error { var user graphrbac.User - queryString := d.Get("user_principal_name").(string) - - 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) + if upn, ok := d.Get("user_principal_name").(string); ok && upn != "" { + resp, err := client.Get(ctx, upn) + if err != nil { + return fmt.Errorf("Error making Read request on AzureAD User with ID %q: %+v", upn, err) } - return fmt.Errorf("Error making Read request on AzureAD User the following query string: %q: %+v", queryString, err) + user = resp + } else if oId, ok := d.Get("object_id").(string); ok && oId != "" { + u, err := graph.UserGetByObjectId(&client, ctx, oId) + if err != nil { + 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("User objectId is nil") + return fmt.Errorf("Azure AD User objectId is nil") } - 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..99a4c2c107 100644 --- a/azuread/data_user_test.go +++ b/azuread/data_user_test.go @@ -5,14 +5,15 @@ import ( "testing" "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) }, @@ -21,24 +22,54 @@ 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 testAccAzureADUserDataSource_byUserPrincipalName(id, password string) string { - template := testAccADUser_basic(id, password) +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) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccAzureADUserDataSource_byObjectId(id, password), + Check: resource.ComposeTestCheckFunc( + 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 int, password string) string { return fmt.Sprintf(` +%s +data "azuread_user" "test" { + user_principal_name = "${azuread_user.test.user_principal_name}" +} +`, testAccADUser_basic(id, password)) +} + +func testAccAzureADUserDataSource_byObjectId(id int, password string) string { + return fmt.Sprintf(` %s data "azuread_user" "test" { - user_principal_name = "${azuread_user.test.user_principal_name}" + object_id = "${azuread_user.test.object_id}" } -`, template) +`, testAccADUser_basic(id, password)) } diff --git a/azuread/data_users.go b/azuread/data_users.go new file mode 100644 index 0000000000..89401ac696 --- /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 dataUsers() *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 returned (%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..b24f12c71d --- /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/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/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/helpers/graph/application.go b/azuread/helpers/graph/application.go new file mode 100644 index 0000000000..ce6b9c36fd --- /dev/null +++ b/azuread/helpers/graph/application.go @@ -0,0 +1,172 @@ +package graph + +import ( + "github.com/Azure/azure-sdk-for-go/services/graphrbac/1.6/graphrbac" + "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, + 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 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{}{} + } + + 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/helpers/graph/credentials.go b/azuread/helpers/graph/credentials.go index 66bda5bab2..ca7a744568 100644 --- a/azuread/helpers/graph/credentials.go +++ b/azuread/helpers/graph/credentials.go @@ -2,13 +2,17 @@ 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/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" ) @@ -67,31 +71,37 @@ func PasswordResourceSchema(object_type string) map[string]*schema.Schema { } type PasswordCredentialId struct { - objectSubResourceId ObjectId string KeyId string } -func ParsePasswordCredentialId(input string) (PasswordCredentialId, error) { - id, err := objectSubResourceIdParse(input) - if err != nil { - return PasswordCredentialId{}, fmt.Errorf("Password Credential ID invalid: %v", err) +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{ - objectSubResourceId: id, - ObjectId: id.guid1, - KeyId: id.guid1, + ObjectId: parts[0], + KeyId: parts[1], }, nil } func PasswordCredentialIdFrom(objectId, keyId string) PasswordCredentialId { return PasswordCredentialId{ - objectSubResourceId: objectSubResourceId{ - guid1: objectId, - guid2: keyId, - }, ObjectId: objectId, KeyId: keyId, } @@ -202,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/group.go b/azuread/helpers/graph/group.go new file mode 100644 index 0000000000..222373f903 --- /dev/null +++ b/azuread/helpers/graph/group.go @@ -0,0 +1,107 @@ +package graph + +import ( + "context" + "fmt" + "log" + + "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) + + 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/graph/replication.go b/azuread/helpers/graph/replication.go new file mode 100644 index 0000000000..aee6b771c2 --- /dev/null +++ b/azuread/helpers/graph/replication.go @@ -0,0 +1,36 @@ +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 { + 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/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/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/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/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/helpers/tf/marshall.go b/azuread/helpers/tf/marshall.go index 69c03ada09..f86d844191 100644 --- a/azuread/helpers/tf/marshall.go +++ b/azuread/helpers/tf/marshall.go @@ -1,6 +1,6 @@ package tf -func ExpandStringSlice(input []interface{}) []string { +func ExpandStringSlicePtr(input []interface{}) *[]string { result := make([]string, 0) for _, item := range input { result = append(result, item.(string)) @@ -8,11 +8,6 @@ func ExpandStringSlice(input []interface{}) []string { return result } -func ExpandStringSlicePtr(input []interface{}) *[]string { - r := ExpandStringSlice(input) - return &r -} - func FlattenStringSlicePtr(input *[]string) []interface{} { result := make([]interface{}, 0) if input != nil { 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/provider.go b/azuread/provider.go index 437946327b..04f6298144 100644 --- a/azuread/provider.go +++ b/azuread/provider.go @@ -77,15 +77,17 @@ func Provider() terraform.ResourceProvider { "azuread_application": dataApplication(), "azuread_domains": dataDomains(), "azuread_group": dataGroup(), + "azuread_groups": dataGroups(), "azuread_service_principal": dataServicePrincipal(), - "azuread_user": dataSourceUser(), + "azuread_user": dataUser(), + "azuread_users": dataUsers(), }, ResourcesMap: map[string]*schema.Resource{ "azuread_application": resourceApplication(), "azuread_application_password": resourceApplicationPassword(), "azuread_group": resourceGroup(), - "azuread_group_owner": resourceGroupOwner(), + "azuread_group_member": resourceGroupMember(), "azuread_service_principal": resourceServicePrincipal(), "azuread_service_principal_password": resourceServicePrincipalPassword(), "azuread_user": resourceUser(), diff --git a/azuread/resource_application.go b/azuread/resource_application.go index ad5361ffa6..87b5df738d 100644 --- a/azuread/resource_application.go +++ b/azuread/resource_application.go @@ -5,9 +5,12 @@ 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" "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/tf" "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/validate" @@ -33,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, @@ -46,43 +63,29 @@ func resourceApplication() *schema.Resource { Computed: true, Elem: &schema.Schema{ Type: schema.TypeString, - ValidateFunc: validate.URLIsHTTPOrHTTPS, - }, - }, - - "reply_urls": { - Type: schema.TypeSet, - Set: schema.HashString, - Optional: true, - Computed: true, - Elem: &schema.Schema{ - Type: schema.TypeString, - ValidateFunc: validate.URLIsHTTPOrHTTPS, + ValidateFunc: validate.URLIsAppURI, }, }, - "available_to_other_tenants": { + "oauth2_allow_implicit_flow": { Type: schema.TypeBool, Optional: true, }, - "oauth2_allow_implicit_flow": { + "public_client": { Type: schema.TypeBool, Optional: true, - }, - - "application_id": { - Type: schema.TypeString, Computed: true, }, - "group_membership_claims": { - Type: schema.TypeString, + "reply_urls": { + Type: schema.TypeSet, Optional: true, - ValidateFunc: validation.StringInSlice( - []string{"None", "SecurityGroup", "All"}, - false, - ), + Computed: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + ValidateFunc: validate.NoEmptyStrings, + }, }, "type": { @@ -92,6 +95,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, @@ -128,53 +181,17 @@ func resourceApplication() *schema.Resource { }, }, - "oauth2_permissions": { - Type: schema.TypeList, + "application_id": { + Type: schema.TypeString, 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, - }, - }, - }, + "object_id": { + Type: schema.TypeString, + Computed: true, }, + + "oauth2_permissions": graph.SchemaOauth2Permissions(), }, } } @@ -193,12 +210,12 @@ 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()), 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 { @@ -215,8 +232,12 @@ 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.AdditionalProperties["groupMembershipClaims"] = v + properties.GroupMembershipClaims = v } // todo require resources to be imported @@ -227,6 +248,14 @@ func resourceApplicationCreate(d *schema.ResourceData, meta interface{}) error { if app.ObjectID == nil { return fmt.Errorf("objectID is nil for Application %q", name) } + d.SetId(*app.ObjectID) + + _, err = graph.WaitForReplication(func() (interface{}, error) { + return client.Get(ctx, *app.ObjectID) + }) + if err != nil { + return fmt.Errorf("Error waiting for Application with ObjectId %q: %+v", *app.ObjectID, 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 @@ -236,17 +265,13 @@ 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 } } - d.SetId(*app.ObjectID) - return resourceApplicationRead(d, meta) } @@ -257,7 +282,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 @@ -276,40 +300,61 @@ 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") { properties.RequiredResourceAccess = expandADApplicationRequiredResourceAccess(d) } - if d.HasChange("group_membership_claims") { - groupMembershipClaims := d.Get("group_membership_claims").(string) + 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()) + } - if len(groupMembershipClaims) == 0 { - properties.AdditionalProperties["groupMembershipClaims"] = nil - } else { - properties.AdditionalProperties["groupMembershipClaims"] = groupMembershipClaims + 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") } 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) - } } @@ -324,9 +369,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 @@ -335,37 +380,42 @@ 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) - - 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("public_client", app.PublicClient) + 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("app_role", graph.FlattenAppRoles(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) } return nil @@ -477,42 +527,43 @@ func flattenADApplicationResourceAccess(in *[]graphrbac.ResourceAccess) []interf return accesses } -func flattenADApplicationOauth2Permissions(in []interface{}) []map[string]interface{} { - if in == nil { - return []map[string]interface{}{} +func expandADApplicationAppRoles(i interface{}) *[]graphrbac.AppRole { + input := i.(*schema.Set).List() + if len(input) == 0 { + return nil } - result := make([]map[string]interface{}, 0, len(in)) - for _, oauth2Permissions := range in { - rawPermission := oauth2Permissions.(map[string]interface{}) - permission := make(map[string]interface{}) - if v := rawPermission["adminConsentDescription"]; v != nil { - permission["admin_consent_description"] = v - } - if v := rawPermission["adminConsentDisplayName"]; v != nil { - permission["admin_consent_description"] = v - } - if v := rawPermission["id"]; v != nil { - permission["id"] = v - } - if v := rawPermission["isEnabled"]; v != nil { - permission["is_enabled"] = v.(bool) - } - if v := rawPermission["type"]; v != nil { - permission["type"] = v - } - if v := rawPermission["userConsentDescription"]; v != nil { - permission["user_consent_description"] = v - } - if v := rawPermission["userConsentDisplayName"]; v != nil { - permission["user_consent_display_name"] = v + var output []graphrbac.AppRole + + for _, appRoleRaw := range input { + appRole := appRoleRaw.(map[string]interface{}) + + appRoleID := appRole["id"].(string) + if appRoleID == "" { + appRoleID = uuid.New().String() } - if v := rawPermission["value"]; v != nil { - permission["value"] = v + + var appRoleAllowedMemberTypes []string + for _, appRoleAllowedMemberType := range appRole["allowed_member_types"].(*schema.Set).List() { + appRoleAllowedMemberTypes = append(appRoleAllowedMemberTypes, appRoleAllowedMemberType.(string)) } - result = append(result, permission) + 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 result + return &output } diff --git a/azuread/resource_application_password.go b/azuread/resource_application_password.go index 7b3ca146b5..bd20ab2e39 100644 --- a/azuread/resource_application_password.go +++ b/azuread/resource_application_password.go @@ -7,9 +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 { @@ -22,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, + }, + }, } } @@ -30,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 { @@ -52,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 replication (AppID %q, KeyID %q: %+v", id.ObjectId, id.KeyId, err) } d.SetId(id.String()) @@ -68,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 { @@ -94,7 +170,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 405e575bad..7c49a1ab28 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 @@ -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) } } @@ -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") @@ -181,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) } @@ -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 b0713cf7a1..47622b0da2 100644 --- a/azuread/resource_application_test.go +++ b/azuread/resource_application_test.go @@ -5,11 +5,11 @@ 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) { @@ -25,12 +25,14 @@ 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("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("acctestApp-%s", id))), resource.TestCheckResourceAttrSet(resourceName, "application_id"), + resource.TestCheckResourceAttrSet(resourceName, "object_id"), ), }, { @@ -55,14 +57,41 @@ 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"), resource.TestCheckResourceAttr(resourceName, "identifier_uris.0", fmt.Sprintf("http://%s.hashicorptest.com/00000000-0000-0000-0000-00000000", id)), resource.TestCheckResourceAttr(resourceName, "reply_urls.#", "1"), resource.TestCheckResourceAttr(resourceName, "group_membership_claims", "All"), resource.TestCheckResourceAttr(resourceName, "required_resource_access.#", "2"), resource.TestCheckResourceAttrSet(resourceName, "application_id"), + resource.TestCheckResourceAttrSet(resourceName, "object_id"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +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"), ), }, { @@ -88,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"), ), @@ -98,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)), @@ -136,6 +165,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() @@ -152,10 +286,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"), ), }, { @@ -187,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"), @@ -203,6 +337,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("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"), + 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() @@ -216,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"), @@ -227,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"), @@ -243,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)), @@ -326,31 +489,40 @@ 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) } -func testAccADApplication_availableToOtherTenants(id string) string { +func testAccADApplication_publicClient(id string) string { return fmt.Sprintf(` +resource "azuread_application" "test" { + name = "acctestApp-%s" + type = "native" + public_client = true +} +`, id) +} +func testAccADApplication_availableToOtherTenants(id string) string { + return fmt.Sprintf(` data "azuread_domains" "tenant_domain" { - only_initial = true + only_initial = true } 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 } `, 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" + name = "acctestApp-%s" + group_membership_claims = "DirectoryRole" } `, id) } @@ -358,8 +530,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 = "acctestApp-%s" + group_membership_claims = "SecurityGroup" } `, id) } @@ -367,28 +539,29 @@ 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"] oauth2_allow_implicit_flow = true + group_membership_claims = "All" required_resource_access { 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" } } @@ -397,7 +570,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" } } @@ -405,19 +578,73 @@ resource "azuread_application" "test" { `, id, id, id) } +func testAccADApplication_appRoles(id string) string { + return fmt.Sprintf(` +resource "azuread_application" "test" { + name = "acctestApp-%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 = "acctestApp-%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" { - name = "acctest%s" + name = "acctestApp-%s" type = "native" } `, id) } +func testAccADApplication_nativeReplyUrls(id string) string { + return fmt.Sprintf(` +resource "azuread_application" "test" { + name = "acctestApp-%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" { - name = "acctest%s" + name = "acctestApp-%s" identifier_uris = ["http://%s.hashicorptest.com"] type = "native" } diff --git a/azuread/resource_group.go b/azuread/resource_group.go index b2b4992d5a..466eb97941 100644 --- a/azuread/resource_group.go +++ b/azuread/resource_group.go @@ -4,11 +4,17 @@ 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" "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/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" @@ -20,6 +26,7 @@ func resourceGroup() *schema.Resource { Read: resourceGroupRead, Update: resourceGroupUpdate, Delete: resourceGroupDelete, + Importer: &schema.ResourceImporter{ State: schema.ImportStatePassthrough, }, @@ -32,6 +39,23 @@ func resourceGroup() *schema.Resource { ValidateFunc: validation.NoZeroValues, }, + "object_id": { + 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, + }, + }, + "owners": { Type: schema.TypeSet, Set: schema.HashString, @@ -59,20 +83,18 @@ 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 behaviour SecurityEnabled: p.Bool(true), // we're defaulting to true, as the API currently only supports the creation of non-mail enabled security groups. } // todo require resources to be imported group, err := client.Create(ctx, properties) if err != nil { - return fmt.Errorf("error creating group %q", name) + return fmt.Errorf("Error creating Group (%q): %+v", name, err) } if group.ObjectID == nil { - return fmt.Errorf("objectID is nil for group %q", name) + return fmt.Errorf("nil Group ID for %q: %+v", name, err) } - - // we have to make a request for each owner we want to add to the group for _, owner := range tf.ExpandStringSlice(d.Get("owners").(*schema.Set).List()) { add := graphrbac.AddOwnerParameters{ URL: p.String(fmt.Sprintf("https://graph.windows.net/%s/directoryObjects/%s", tenantID, owner)), @@ -82,8 +104,24 @@ func resourceGroupCreate(d *schema.ResourceData, meta interface{}) error { return fmt.Errorf("erro adding owner %q to group %q", owner, name) } } - 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) + }) + if err != nil { + return fmt.Errorf("Error waiting for Group (%s) with ObjectId %q: %+v", name, *group.ObjectID, err) + } + return resourceGroupRead(d, meta) } @@ -103,6 +141,14 @@ 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) respOwners, err := client.ListOwnersComplete(ctx, d.Id()) if err != nil { @@ -121,93 +167,27 @@ func resourceGroupUpdate(d *schema.ResourceData, meta interface{}) error { client := meta.(*ArmClient).groupsClient ctx := meta.(*ArmClient).StopContext - objectID := d.Id() - tenantID := client.TenantID - - if d.HasChange("owners") { - - - // get existing -> map - // get desired -> map - - //remove missing - for - - //add miss - - - owners := d.Get("owners").([]interface{}) - ownersExpanded, err := expandGroupOwners(owners) + if v, ok := d.GetOkExists("members"); ok && d.HasChange("members") { + existingMembers, err := graph.GroupAllMembers(client, ctx, d.Id()) if err != nil { - return fmt.Errorf("Error expanding `owners`: %+v", owners) + return err } - currentOwnersListIterator, err := client.ListOwnersComplete(ctx, objectID) - if err != nil { - return fmt.Errorf("Error retrieving Azure AD Group owners (groupObjectId: %q): %+v", objectID, err) - } + desiredMembers := *tf.ExpandStringSlicePtr(v.(*schema.Set).List()) + membersForRemoval := slices.Difference(existingMembers, desiredMembers) + membersToAdd := slices.Difference(desiredMembers, existingMembers) - currentOwners := make([]string, 0) - for currentOwnersListIterator.NotDone() { - user, _ := currentOwnersListIterator.Value().AsUser() - currentOwners = append(currentOwners, *user.ObjectID) - if err := currentOwnersListIterator.NextWithContext(ctx); err != nil { - return fmt.Errorf("Error listing Azure AD Group Owners: %s", err) - } - } - - //first we loop through all expanded owners and add them if necessary. We do the add/remove in separate loops as - //we want to prevent that we're removing the last owner from the group which will cause an error in the Azure API. - if ownersExpanded != nil { - for _, expandedOwner := range *ownersExpanded { - - //check if the user is already in the list of current owners - var alreadyOwner = false - for _, currentOwner := range currentOwners { - if expandedOwner == currentOwner { - alreadyOwner = true - } - } - - if !alreadyOwner { - log.Printf("[DEBUG] Adding %q as owner of group %q.", expandedOwner, objectID) - ownerGraphURL := fmt.Sprintf("https://graph.windows.net/%s/directoryObjects/%s", tenantID, expandedOwner) - addOwnerProperties := graphrbac.AddOwnerParameters{ - URL: &ownerGraphURL, - } - - if _, err := client.AddOwner(ctx, objectID, addOwnerProperties); err != nil { - return err - } + 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) } } } - //loop through all current owners of the group - for _, currentOwner := range currentOwners { - - currentOwnerGraphURL := fmt.Sprintf("https://graph.windows.net/%s/directoryObjects/%s", tenantID, currentOwner) - - //check if the current owner should be kept or removed - var keep = false - for _, expandedOwner := range *ownersExpanded { - - ownerGraphURL := fmt.Sprintf("https://graph.windows.net/%s/directoryObjects/%s", tenantID, expandedOwner) - if ownerGraphURL == currentOwnerGraphURL { - keep = true - } - - if !keep { - //the owner should be removed from the list of group owners - log.Printf("[DEBUG] Removing owner %q from group %q.", currentOwner, objectID) - resp, err := client.RemoveOwner(ctx, objectID, currentOwner) - if err != nil { - if !ar.ResponseWasNotFound(resp) { - return fmt.Errorf("Error removing Owner (ownerObjectId: %q) from Azure AD Group (groupObjectId: %q): %+v", expandedOwner, objectID, err) - } - } - } - } + if err := graph.GroupAddMembers(client, ctx, d.Id(), membersToAdd); err != nil { + return err } } 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..4cc76762b2 --- /dev/null +++ b/azuread/resource_group_member_test.go @@ -0,0 +1,225 @@ +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" + "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/tf" +) + +func TestAccAzureADGroupMember_User(t *testing.T) { + 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) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureADGroupMemberDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureADGroupMember_User(id, pw), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet(rn, "group_object_id"), + resource.TestCheckResourceAttrSet(rn, "member_object_id"), + ), + }, + { + ResourceName: rn, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAzureADGroupMember_Group(t *testing.T) { + rn := "azuread_group_member.test" + id := tf.AccRandTimeInt() + + 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(rn, "group_object_id"), + resource.TestCheckResourceAttrSet(rn, "member_object_id"), + ), + }, + { + ResourceName: rn, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAzureADGroupMember_ServicePrincipal(t *testing.T) { + rn := "azuread_group_member.test" + id := tf.AccRandTimeInt() + + 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(rn, "group_object_id"), + resource.TestCheckResourceAttrSet(rn, "member_object_id"), + ), + }, + { + ResourceName: rn, + 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 int, password string) string { + return fmt.Sprintf(` + +data "azuread_domains" "tenant_domain" { + only_initial = true +} + +resource "azuread_user" "test" { + 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 = "acctestGroup-%[1]d" +} + +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 int) string { + return fmt.Sprintf(` + +resource "azuread_group" "test" { + name = "acctestGroup-%[1]d" +} + +resource "azuread_group" "member" { + name = "acctestGroup-%[1]d-Member" +} + +resource "azuread_group_member" "test" { + group_object_id = "${azuread_group.test.object_id}" + member_object_id = "${azuread_group.member.object_id}" +} + +`, id) +} + +func testAccAzureADGroupMember_ServicePrincipal(id int) string { + return fmt.Sprintf(` + +resource "azuread_application" "test" { + name = "acctestApp-%[1]d" +} + +resource "azuread_service_principal" "test" { + application_id = "${azuread_application.test.application_id}" +} + +resource "azuread_group" "test" { + 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}" +} + +`, id) +} diff --git a/azuread/resource_group_test.go b/azuread/resource_group_test.go index 68478efa2a..27c9b5fbe0 100644 --- a/azuread/resource_group_test.go +++ b/azuread/resource_group_test.go @@ -4,35 +4,30 @@ 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" ) //todo with SP/group/ application? func TestAccAzureADGroup_basic(t *testing.T) { - resourceName := "azuread_group.test" - id, err := uuid.GenerateUUID() - if err != nil { - t.Fatal(err) - } + 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: testAccAzureADGroup(id), - Check: resource.ComposeTestCheckFunc( - testCheckAzureADGroupExists(resourceName), - resource.TestCheckResourceAttr(resourceName, "name", fmt.Sprintf("acctest%s", id)), - ), + Config: testAccAzureADGroup_basic(id), + Check: testCheckAzureAdGroupBasic(id, "0"), }, { - ResourceName: resourceName, + ResourceName: rn, ImportState: true, ImportStateVerify: true, }, @@ -40,26 +35,22 @@ func TestAccAzureADGroup_basic(t *testing.T) { }) } -func TestAccAzureADGroup_withOwner(t *testing.T) { - resourceName := "azuread_group.test" - id := acctest.RandStringFromCharSet(7, acctest.CharSetAlphaNum) - password := id + "p@$$wR2" +func TestAccAzureADGroup_members(t *testing.T) { + 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: testAccAzureADGroupWithOwner(id, password), - Check: resource.ComposeTestCheckFunc( - testCheckAzureADGroupExists(resourceName), - resource.TestCheckResourceAttr(resourceName, "name", fmt.Sprintf("acctest%s", id)), - resource.TestCheckResourceAttr(resourceName, "owners.#", "1"), - ), + Config: testAccAzureADGroupWithThreeMembers(id, pw), + Check: testCheckAzureAdGroupBasic(id, "3"), }, { - ResourceName: resourceName, + ResourceName: rn, ImportState: true, ImportStateVerify: true, }, @@ -67,29 +58,106 @@ func TestAccAzureADGroup_withOwner(t *testing.T) { }) } -func TestAccAzureADGroup_withMultipleOwners(t *testing.T) { - resourceName := "azuread_group.test" - id := acctest.RandStringFromCharSet(7, acctest.CharSetAlphaNum) - password := id + "p@$$wR2" +func TestAccAzureADGroup_complete(t *testing.T) { + rn := "azuread_group.test" + id := tf.AccRandTimeInt() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureADGroupDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureADGroup_basic(id), + Check: testCheckAzureAdGroupBasic(id, "0"), + }, + { + ResourceName: rn, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAzureADGroup_diverse(t *testing.T) { + 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: testAccAzureADGroupWithMultipleOwners(id, password), - Check: resource.ComposeTestCheckFunc( - testCheckAzureADGroupExists(resourceName), - resource.TestCheckResourceAttr(resourceName, "name", fmt.Sprintf("acctest%s", id)), - resource.TestCheckResourceAttr(resourceName, "owners.#", "2"), - ), + Config: testAccAzureADGroupWithDiverseMembers(id, pw), + Check: testCheckAzureAdGroupBasic(id, "3"), + }, + { + ResourceName: rn, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAzureADGroup_progression(t *testing.T) { + rn := "azuread_group.test" + id := tf.AccRandTimeInt() + pw := "p@$$wR2" + acctest.RandStringFromCharSet(7, acctest.CharSetAlphaNum) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureADGroupDestroy, + Steps: []resource.TestStep{ + // Empty group with 0 members + { + Config: testAccAzureADGroup_basic(id), + Check: testCheckAzureAdGroupBasic(id, "0"), + }, + { + ResourceName: rn, + ImportState: true, + ImportStateVerify: true, + }, + // Group with 1 member + { + Config: testAccAzureADGroupWithOneMember(id, pw), + Check: testCheckAzureAdGroupBasic(id, "1"), + }, + { + ResourceName: rn, + ImportState: true, + ImportStateVerify: true, + }, + // Group with multiple members + { + 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: testCheckAzureAdGroupBasic(id, "1"), + }, + { + ResourceName: rn, + ImportState: true, + ImportStateVerify: true, + }, + // Empty group with 0 members + { + Config: testAccAzureADGroup_basic(id), + Check: testCheckAzureAdGroupBasic(id, "0"), + }, }, }) } @@ -140,61 +208,120 @@ func testCheckAzureADGroupDestroy(s *terraform.State) error { return nil } -func testAccAzureADGroup(id string) string { +func testCheckAzureAdGroupBasic(id int, memberCount string) resource.TestCheckFunc { + resourceName := "azuread_group.test" + + return resource.ComposeTestCheckFunc( + testCheckAzureADGroupExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "name", fmt.Sprintf("acctestGroup-%d", id)), + resource.TestCheckResourceAttrSet(resourceName, "object_id"), + resource.TestCheckResourceAttr(resourceName, "members.#", memberCount), + ) +} + +func testAccAzureADGroup_basic(id int) string { return fmt.Sprintf(` resource "azuread_group" "test" { - name = "acctest%s" + name = "acctestGroup-%d" + members = [] } `, id) } -func testAccAzureADGroupWithOwner(id string, password string) string { +func testAccAzureADGroupWithDiverseMembers(id int, password string) string { return fmt.Sprintf(` - data "azuread_domains" "tenant_domain" { only_initial = true } - -resource "azuread_user" "testA" { - user_principal_name = "acctestA%[1]s@${data.azuread_domains.tenant_domain.domains.0.domain_name}" - display_name = "acctestA%[1]s" + +resource "azuread_application" "test" { + name = "acctestApp-%[1]d" +} + +resource "azuread_service_principal" "test" { + application_id = azuread_application.test.application_id +} + +resource "azuread_group" "member" { + name = "acctestGroup-%[1]d-Member" +} + +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" - owners = [ - "${azuread_user.testA.id}" - ] + name = "acctestGroup-%[1]d" + members = [ azuread_user.test.object_id, azuread_group.member.object_id, azuread_service_principal.test.object_id ] } `, id, password) } -func testAccAzureADGroupWithMultipleOwners(id string, password string) string { +func testAccAzureADGroupWithOneMember(id int, password string) string { return fmt.Sprintf(` +data "azuread_domains" "tenant_domain" { + only_initial = true +} +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 = "acctestGroup-%[1]d" + members = [ azuread_user.test.object_id ] +} +`, id, password) +} + +func testAccAzureADGroupWithThreeMembers(id int, password string) string { + return fmt.Sprintf(` data "azuread_domains" "tenant_domain" { only_initial = true } - + 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 = "acctestUser.%[1]d.A@${data.azuread_domains.tenant_domain.domains.0.domain_name}" + display_name = "acctestUser-%[1]dA" password = "%[2]s" } resource "azuread_user" "testB" { - user_principal_name = "acctestB%[1]s@${data.azuread_domains.tenant_domain.domains.0.domain_name}" - display_name = "acctestB%[1]s" + 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" "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" - owners = [ - "${azuread_user.testB.id}", - "${azuread_user.testA.id}", - ] + name = "acctestGroup-%[1]d" + members = [ azuread_user.testA.object_id, azuread_user.testB.object_id, azuread_user.testC.object_id ] } `, id, password) } + +func testAccAzureADGroupWithServicePrincipal(id int) string { + return fmt.Sprintf(` +resource "azuread_application" "test" { + name = "acctestApp-%[1]d" +} + +resource "azuread_service_principal" "test" { + application_id = azuread_application.test.application_id +} + +resource "azuread_group" "test" { + name = "acctestGroup-%[1]d" + members = [ azuread_service_principal.test.object_id ] +} +`, id) +} diff --git a/azuread/resource_service_principal.go b/azuread/resource_service_principal.go index 1960edf24a..f1a8b9cb90 100644 --- a/azuread/resource_service_principal.go +++ b/azuread/resource_service_principal.go @@ -4,16 +4,15 @@ import ( "fmt" "log" - "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" @@ -35,6 +34,18 @@ func resourceServicePrincipal() *schema.Resource { ValidateFunc: validate.UUID, }, + "display_name": { + Type: schema.TypeString, + Computed: true, + }, + + "object_id": { + Type: schema.TypeString, + Computed: true, + }, + + "oauth2_permissions": graph.SchemaOauth2Permissions(), + "tags": { Type: schema.TypeSet, Optional: true, @@ -44,11 +55,6 @@ func resourceServicePrincipal() *schema.Resource { Type: schema.TypeString, }, }, - - "display_name": { - Type: schema.TypeString, - Computed: true, - }, }, } } @@ -74,21 +80,17 @@ 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) + _, err = graph.WaitForReplication(func() (interface{}, error) { + return client.Get(ctx, *sp.ObjectID) + }) if err != nil { - return fmt.Errorf("Error retrieving Service Principal with ID %q: %+v", objectId, err) - } - - if resp.ObjectID == nil { - return fmt.Errorf("Get returned a nil object ID for %q", objectId) + return fmt.Errorf("Error waiting for Service Principal with ObjectId %q: %+v", *sp.ObjectID, err) } - d.SetId(*resp.ObjectID) return resourceServicePrincipalRead(d, meta) } @@ -111,14 +113,15 @@ 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) + } + + 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_password.go b/azuread/resource_service_principal_password.go index b52b8238e2..9c7d57a204 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" @@ -57,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) } diff --git a/azuread/resource_service_principal_password_test.go b/azuread/resource_service_principal_password_test.go index ccdfad41d5..0b8ee51616 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 @@ -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 18e31db61f..db618f770a 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) { @@ -26,6 +26,9 @@ 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("acctestApp-%s", id))), + resource.TestCheckResourceAttrSet(resourceName, "object_id"), ), }, { @@ -51,6 +54,7 @@ func TestAccAzureADServicePrincipal_complete(t *testing.T) { Check: resource.ComposeTestCheckFunc( testCheckADServicePrincipalExists(resourceName), resource.TestCheckResourceAttr(resourceName, "tags.#", "3"), + resource.TestCheckResourceAttrSet(resourceName, "object_id"), ), }, { @@ -111,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" { @@ -123,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.go b/azuread/resource_user.go index 1e64448476..943989596a 100644 --- a/azuread/resource_user.go +++ b/azuread/resource_user.go @@ -8,7 +8,9 @@ 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" "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/validate" ) @@ -19,6 +21,7 @@ func resourceUser() *schema.Resource { Read: resourceUserRead, Update: resourceUserUpdate, Delete: resourceUserDelete, + Importer: &schema.ResourceImporter{ State: schema.ImportStatePassthrough, }, @@ -66,6 +69,11 @@ func resourceUser() *schema.Resource { Type: schema.TypeString, Computed: true, }, + + "object_id": { + Type: schema.TypeString, + Computed: true, + }, }, } } @@ -74,7 +82,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 +91,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,28 +102,26 @@ func resourceUserCreate(d *schema.ResourceData, meta interface{}) error { ForceChangePasswordNextLogin: &forcePasswordChange, Password: &password, }, - UserPrincipalName: &userPrincipalName, + UserPrincipalName: &upn, } // todo require resources to be imported 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) } - - objectId := user.ObjectID - - resp, err := client.Get(ctx, *objectId) - if err != nil { - return fmt.Errorf("Error retrieving User (%q) with ObjectID %q: %+v", userPrincipalName, *objectId, err) + if user.ObjectID == nil { + return fmt.Errorf("nil User ID for %q: %+v", upn, err) } + d.SetId(*user.ObjectID) - if resp.ObjectID == nil { - return fmt.Errorf("objectID is nil for user %s", userPrincipalName) + _, err = graph.WaitForReplication(func() (interface{}, error) { + return client.Get(ctx, *user.ObjectID) + }) + if err != nil { + return fmt.Errorf("Error waiting for User (%s) with ObjectId %q: %+v", upn, *user.ObjectID, err) } - d.SetId(*resp.ObjectID) - return resourceUserRead(d, meta) } @@ -140,7 +146,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..34f6470938 100644 --- a/azuread/resource_user_test.go +++ b/azuread/resource_user_test.go @@ -5,16 +5,17 @@ import ( "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" + "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,17 +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.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("acctestUser-%d", id)), + resource.TestCheckResourceAttr(rn, "mail_nickname", fmt.Sprintf("acctestUser.%d", id)), + resource.TestCheckResourceAttr(rn, "account_enabled", "true"), ), }, { - ResourceName: resourceName, + ResourceName: rn, ImportState: true, ImportStateVerify: true, ImportStateVerifyIgnore: []string{ @@ -45,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) }, @@ -55,17 +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.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("acctestUser-%d-Updated", id)), + resource.TestCheckResourceAttr(rn, "mail_nickname", fmt.Sprintf("acctestUser-%d-Updated", id)), + resource.TestCheckResourceAttr(rn, "account_enabled", "false"), ), }, { - ResourceName: resourceName, + ResourceName: rn, ImportState: true, ImportStateVerify: true, ImportStateVerifyIgnore: []string{ @@ -78,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) }, @@ -89,36 +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.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("acctestUser-%d", id)), + resource.TestCheckResourceAttr(rn, "mail_nickname", fmt.Sprintf("acctestUser.%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.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("acctestUser-%d-Updated", id)), + resource.TestCheckResourceAttr(rn, "mail_nickname", fmt.Sprintf("acctestUser-%d-Updated", 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("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%s", id)), - resource.TestCheckResourceAttr("azuread_user.testB", "mail_nickname", fmt.Sprintf("acctest_mail%s", 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)), ), }, }, @@ -171,57 +176,54 @@ 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" { - only_initial = true + only_initial = true } resource "azuread_user" "test" { - user_principal_name = "acctest%[1]s@${data.azuread_domains.tenant_domain.domains.0.domain_name}" - display_name = "acctest%[1]s" - password = "%[2]s" + 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) } -func testAccADUser_complete(id string, password string) string { +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]s@${data.azuread_domains.tenant_domain.domains.0.domain_name}" - display_name = "acctestupdate%[1]s" - mail_nickname = "acctestupdate%[1]s" - account_enabled = false - password = "%[2]s" - force_password_change = true + 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 } `, 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" { - only_initial = true + only_initial = true } resource "azuread_user" "testA" { - user_principal_name = "acctestA%[1]s@${data.azuread_domains.tenant_domain.domains.0.domain_name}" - display_name = "acctestA%[1]s" - password = "%[2]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_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" - password = "%[2]s" + 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) } 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/go.mod b/go.mod index 3b2adfcc4d..a3ba06eff6 100644 --- a/go.mod +++ b/go.mod @@ -2,11 +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/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/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 18a6d79e90..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= @@ -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= @@ -124,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= @@ -152,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/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/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 11524798ac..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 @@ -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 @@ -102,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 @@ -112,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 @@ -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 diff --git a/website/azuread.erb b/website/azuread.erb index 3ff6ba95a1..6fdfbd9f22 100644 --- a/website/azuread.erb +++ b/website/azuread.erb @@ -70,6 +70,9 @@ azuread_user + > + azuread_users + @@ -89,8 +92,8 @@ azuread_group - > - azuread_group_owner + > + azuread_group_member > 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/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 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/group.html.markdown b/website/docs/d/group.html.markdown index d263789951..6ee37b08a4 100644 --- a/website/docs/d/group.html.markdown +++ b/website/docs/d/group.html.markdown @@ -25,12 +25,15 @@ 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 The following attributes are exported: * `id` - The Object ID of the Azure AD Group. + 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/service_principal.html.markdown b/website/docs/d/service_principal.html.markdown index fe8af7fee5..863cba746d 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. @@ -49,8 +49,48 @@ 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 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 + +--- + +`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/user.html.markdown b/website/docs/d/user.html.markdown index 93109ac072..f11ae1df0d 100644 --- a/website/docs/d/user.html.markdown +++ b/website/docs/d/user.html.markdown @@ -1,39 +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 = "john@hashicorp.com" -} -``` - -## Argument Reference - -The following arguments are supported: - -* `user_principal_name` - (Required) The User Principal Name of the Azure AD User. - -## 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 new file mode 100644 index 0000000000..6518b5d34b --- /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 `user_principal_names` or `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 e33b57b038..e672b3f0a8 100644 --- a/website/docs/r/application.html.markdown +++ b/website/docs/r/application.html.markdown @@ -27,30 +27,43 @@ 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" } } + + 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" + } } ``` @@ -68,13 +81,17 @@ 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`. * `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,16 +105,34 @@ 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: * `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/application_password.html.markdown b/website/docs/r/application_password.html.markdown index bcc383d894..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" } ``` @@ -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 . diff --git a/website/docs/r/group.markdown b/website/docs/r/group.markdown index 4c13ace73f..f178629c93 100644 --- a/website/docs/r/group.markdown +++ b/website/docs/r/group.markdown @@ -15,7 +15,7 @@ Manages a Group within Azure Active Directory. ## Example Usage -### Creating a Group +*Basic example* ```hcl resource "azuread_group" "my_group" { @@ -23,22 +23,18 @@ resource "azuread_group" "my_group" { } ``` -### Creating a Group with Owners +*A group with members* ```hcl -data "azuread_domains" "tenant_domain" { - only_initial = true +resource "azuread_user" "my_user" { + display_name = "John Doe" + password = "notSecure123" + user_principal_name = "john.doe@terraform.onmicrosoft.com" } -resource "azuread_user" "test_user" { - user_principal_name = "my_user@${data.azuread_domains.tenant_domain.domains.0.domain_name}" - display_name = "my_user" - password = "my&P@ssw0Rd" -} - -resource "azuread_group" "my_group_with_owners" { - name = "MyGroup" - owners = ["${azuread_user.test_user.id}"] +resource "azuread_group" "my_group" { + name = "MyGroup" + members = [ azuread_user.my_user.object_id /*, more users */ ] } ``` @@ -47,12 +43,11 @@ resource "azuread_group" "my_group_with_owners" { The following arguments are supported: * `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. -* `owners` - (Optional) A list of User Object IDs which should be owner of this Group. - --> **NOTE:** Once set, Azure requires at least one Group Owner. Therefore it may be required to delete the Group manually. +-> **NOTE:** Do not use `azuread_group_member` at the same time as the `members` argument. ## Attributes Reference @@ -62,7 +57,7 @@ The following attributes are exported: * `name` - The Display Name of the Group. -* `owners` - A list of Object IDs which represent the Group owners. +* `members` - The Members of the Group. ## Import 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}`. diff --git a/website/docs/r/service_principal.html.markdown b/website/docs/r/service_principal.html.markdown index 7dd5e40092..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"] } ``` @@ -44,10 +44,36 @@ 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. + +* `object_id` - The Service Principal's Object ID. * `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. diff --git a/website/docs/r/user.html.markdown b/website/docs/r/user.html.markdown index f1111d645e..77f24d27eb 100644 --- a/website/docs/r/user.html.markdown +++ b/website/docs/r/user.html.markdown @@ -1,43 +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: - -* `id` - The Object ID of the Azure AD User. -* `mail` - The primary email address of the Azure AD User. +--- +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 +```