Skip to content

Commit

Permalink
New Resource: azurerm_azuread_application (#1269)
Browse files Browse the repository at this point in the history
* Add an AD Application provider.

Split from PR #961.

* Rename resource to azurerm_azuread_application and update function names to match.

* Address code review comments.

* Remove Password/Key Credentials to turn them into resources.

* Refactoring:

- updating the acceptance tests to prefix the name with `acctest` so it's easily identifyable
- wrapping the error messages to display more useful information
- ensuring we disable apps or other tenants before deleting them
- making available_to_other_tenants / oauth2_allow_implicit_flow not computed
- updating the docs

```
 $ acctests azurerm TestAccAzureRMActiveDirectoryApplication_
=== RUN   TestAccAzureRMActiveDirectoryApplication_importBasic
--- PASS: TestAccAzureRMActiveDirectoryApplication_importBasic (38.38s)
=== RUN   TestAccAzureRMActiveDirectoryApplication_importComplete
--- PASS: TestAccAzureRMActiveDirectoryApplication_importComplete (25.42s)
=== RUN   TestAccAzureRMActiveDirectoryApplication_basic
--- PASS: TestAccAzureRMActiveDirectoryApplication_basic (20.18s)
=== RUN   TestAccAzureRMActiveDirectoryApplication_availableToOtherTenants
--- PASS: TestAccAzureRMActiveDirectoryApplication_availableToOtherTenants (21.60s)
=== RUN   TestAccAzureRMActiveDirectoryApplication_complete
--- PASS: TestAccAzureRMActiveDirectoryApplication_complete (20.19s)
=== RUN   TestAccAzureRMActiveDirectoryApplication_update
--- PASS: TestAccAzureRMActiveDirectoryApplication_update (39.92s)
PASS
ok  	github.com/terraform-providers/terraform-provider-azurerm/azurerm	165.735s
```
  • Loading branch information
Stavrus authored and tombuildsstuff committed Jul 11, 2018
1 parent 4c760cb commit 30f0dfa
Show file tree
Hide file tree
Showing 7 changed files with 577 additions and 0 deletions.
8 changes: 8 additions & 0 deletions azurerm/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ type ArmClient struct {
// Authentication
roleAssignmentsClient authorization.RoleAssignmentsClient
roleDefinitionsClient authorization.RoleDefinitionsClient
applicationsClient graphrbac.ApplicationsClient
servicePrincipalsClient graphrbac.ServicePrincipalsClient

// CDN
Expand Down Expand Up @@ -462,6 +463,13 @@ func (c *ArmClient) registerAuthentication(endpoint, graphEndpoint, subscription
definitionsClient.SkipResourceProviderRegistration = c.skipProviderRegistration
c.roleDefinitionsClient = definitionsClient

applicationsClient := graphrbac.NewApplicationsClientWithBaseURI(graphEndpoint, tenantId)
setUserAgent(&applicationsClient.Client)
applicationsClient.Authorizer = graphAuth
applicationsClient.Sender = sender
applicationsClient.SkipResourceProviderRegistration = c.skipProviderRegistration
c.applicationsClient = applicationsClient

servicePrincipalsClient := graphrbac.NewServicePrincipalsClientWithBaseURI(graphEndpoint, tenantId)
setUserAgent(&servicePrincipalsClient.Client)
servicePrincipalsClient.Authorizer = graphAuth
Expand Down
54 changes: 54 additions & 0 deletions azurerm/import_arm_azuread_application_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package azurerm

import (
"testing"

"github.com/google/uuid"
"github.com/hashicorp/terraform/helper/resource"
)

func TestAccAzureRMActiveDirectoryApplication_importBasic(t *testing.T) {
resourceName := "azurerm_azuread_application.test"

id := uuid.New().String()
config := testAccAzureRMActiveDirectoryApplication_basic(id)

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testCheckAzureRMActiveDirectoryApplicationDestroy,
Steps: []resource.TestStep{
{
Config: config,
},
{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
},
},
})
}

func TestAccAzureRMActiveDirectoryApplication_importComplete(t *testing.T) {
resourceName := "azurerm_azuread_application.test"

id := uuid.New().String()
config := testAccAzureRMActiveDirectoryApplication_complete(id)

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testCheckAzureRMActiveDirectoryApplicationDestroy,
Steps: []resource.TestStep{
{
Config: config,
},
{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
},
},
})
}
1 change: 1 addition & 0 deletions azurerm/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ func Provider() terraform.ResourceProvider {
},

ResourcesMap: map[string]*schema.Resource{
"azurerm_azuread_application": resourceArmActiveDirectoryApplication(),
"azurerm_application_gateway": resourceArmApplicationGateway(),
"azurerm_application_insights": resourceArmApplicationInsights(),
"azurerm_application_security_group": resourceArmApplicationSecurityGroup(),
Expand Down
255 changes: 255 additions & 0 deletions azurerm/resource_arm_azuread_application.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,255 @@
package azurerm

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-azurerm/azurerm/utils"
)

func resourceArmActiveDirectoryApplication() *schema.Resource {
return &schema.Resource{
Create: resourceArmActiveDirectoryApplicationCreate,
Read: resourceArmActiveDirectoryApplicationRead,
Update: resourceArmActiveDirectoryApplicationUpdate,
Delete: resourceArmActiveDirectoryApplicationDelete,
Importer: &schema.ResourceImporter{
State: schema.ImportStatePassthrough,
},

Schema: map[string]*schema.Schema{
"name": {
Type: schema.TypeString,
Required: true,
},

"homepage": {
Type: schema.TypeString,
Optional: true,
Computed: true,
},

"identifier_uris": {
Type: schema.TypeList,
Optional: true,
Computed: true,
MinItems: 1,
Elem: &schema.Schema{
Type: schema.TypeString,
},
},

"reply_urls": {
Type: schema.TypeList,
Optional: true,
Computed: true,
Elem: &schema.Schema{
Type: schema.TypeString,
},
},

"available_to_other_tenants": {
Type: schema.TypeBool,
Optional: true,
},

"oauth2_allow_implicit_flow": {
Type: schema.TypeBool,
Optional: true,
},

"application_id": {
Type: schema.TypeString,
Computed: true,
},
},
}
}

func resourceArmActiveDirectoryApplicationCreate(d *schema.ResourceData, meta interface{}) error {
client := meta.(*ArmClient).applicationsClient
ctx := meta.(*ArmClient).StopContext

name := d.Get("name").(string)
availableToOtherTenants := d.Get("available_to_other_tenants").(bool)

properties := graphrbac.ApplicationCreateParameters{
DisplayName: &name,
Homepage: expandAzureRmActiveDirectoryApplicationHomepage(d, name),
IdentifierUris: expandAzureRmActiveDirectoryApplicationIdentifierUris(d),
ReplyUrls: expandAzureRmActiveDirectoryApplicationReplyUrls(d),
AvailableToOtherTenants: utils.Bool(availableToOtherTenants),
}

if v, ok := d.GetOk("oauth2_allow_implicit_flow"); ok {
properties.Oauth2AllowImplicitFlow = utils.Bool(v.(bool))
}

app, err := client.Create(ctx, properties)
if err != nil {
return err
}

d.SetId(*app.ObjectID)

return resourceArmActiveDirectoryApplicationRead(d, meta)
}

func resourceArmActiveDirectoryApplicationUpdate(d *schema.ResourceData, meta interface{}) error {
client := meta.(*ArmClient).applicationsClient
ctx := meta.(*ArmClient).StopContext

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

var properties graphrbac.ApplicationUpdateParameters

if d.HasChange("name") {
properties.DisplayName = &name
}

if d.HasChange("homepage") {
properties.Homepage = expandAzureRmActiveDirectoryApplicationHomepage(d, name)
}

if d.HasChange("identifier_uris") {
properties.IdentifierUris = expandAzureRmActiveDirectoryApplicationIdentifierUris(d)
}

if d.HasChange("reply_urls") {
properties.ReplyUrls = expandAzureRmActiveDirectoryApplicationReplyUrls(d)
}

if d.HasChange("available_to_other_tenants") {
availableToOtherTenants := d.Get("available_to_other_tenants").(bool)
properties.AvailableToOtherTenants = utils.Bool(availableToOtherTenants)
}

if d.HasChange("oauth2_allow_implicit_flow") {
oauth := d.Get("oauth2_allow_implicit_flow").(bool)
properties.Oauth2AllowImplicitFlow = utils.Bool(oauth)
}

_, err := client.Patch(ctx, d.Id(), properties)
if err != nil {
return fmt.Errorf("Error patching Azure AD Application with ID %q: %+v", d.Id(), err)
}

return resourceArmActiveDirectoryApplicationRead(d, meta)
}

func resourceArmActiveDirectoryApplicationRead(d *schema.ResourceData, meta interface{}) error {
client := meta.(*ArmClient).applicationsClient
ctx := meta.(*ArmClient).StopContext

resp, err := client.Get(ctx, d.Id())
if err != nil {
if utils.ResponseWasNotFound(resp.Response) {
log.Printf("[DEBUG] Azure AD Application with ID %q was not found - removing from state", d.Id())
d.SetId("")
return nil
}

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)

identifierUris := flattenAzureADApplicationIdentifierUris(resp.IdentifierUris)
if err := d.Set("identifier_uris", identifierUris); err != nil {
return fmt.Errorf("Error setting`identifier_uris`: %+v", err)
}

replyUrls := flattenAzureADApplicationReplyUrls(resp.ReplyUrls)
if err := d.Set("reply_urls", replyUrls); err != nil {
return fmt.Errorf("Error setting`reply_urls`: %+v", err)
}

return nil
}

func resourceArmActiveDirectoryApplicationDelete(d *schema.ResourceData, meta interface{}) error {
client := meta.(*ArmClient).applicationsClient
ctx := meta.(*ArmClient).StopContext

// in order to delete an application which is available to other tenants, we first have to disable this setting
availableToOtherTenants := d.Get("available_to_other_tenants").(bool)
if availableToOtherTenants {
log.Printf("[DEBUG] Azure AD Application is available to other tenants - disabling that feature before deleting.")
properties := graphrbac.ApplicationUpdateParameters{
AvailableToOtherTenants: utils.Bool(false),
}
_, err := client.Patch(ctx, d.Id(), properties)
if err != nil {
return fmt.Errorf("Error patching Azure AD Application with ID %q: %+v", d.Id(), err)
}
}

resp, err := client.Delete(ctx, d.Id())
if err != nil {
if !utils.ResponseWasNotFound(resp) {
return fmt.Errorf("Error Deleting Azure AD Application with ID %q: %+v", d.Id(), err)
}
}

return nil
}

func expandAzureRmActiveDirectoryApplicationHomepage(d *schema.ResourceData, name string) *string {
if v, ok := d.GetOk("homepage"); ok {
return utils.String(v.(string))
}

return utils.String(fmt.Sprintf("http://%s", name))
}

func expandAzureRmActiveDirectoryApplicationIdentifierUris(d *schema.ResourceData) *[]string {
identifierUris := d.Get("identifier_uris").([]interface{})
identifiers := make([]string, 0)

for _, id := range identifierUris {
identifiers = append(identifiers, id.(string))
}

return &identifiers
}

func expandAzureRmActiveDirectoryApplicationReplyUrls(d *schema.ResourceData) *[]string {
replyUrls := d.Get("reply_urls").([]interface{})
urls := make([]string, 0)

for _, url := range replyUrls {
urls = append(urls, url.(string))
}

return &urls
}

func flattenAzureADApplicationIdentifierUris(input *[]string) []string {
output := make([]string, 0)

if input != nil {
for _, v := range *input {
output = append(output, v)
}
}

return output
}

func flattenAzureADApplicationReplyUrls(input *[]string) []string {
output := make([]string, 0)

if input != nil {
for _, v := range *input {
output = append(output, v)
}
}

return output
}
Loading

0 comments on commit 30f0dfa

Please sign in to comment.