Skip to content

Commit

Permalink
New resource 'azuread_application_password' (#71)
Browse files Browse the repository at this point in the history
  • Loading branch information
twendt authored and katbyte committed May 26, 2019
1 parent f8bd108 commit 237d8b8
Show file tree
Hide file tree
Showing 16 changed files with 812 additions and 293 deletions.
4 changes: 2 additions & 2 deletions azuread/data_application.go
Original file line number Diff line number Diff line change
Expand Up @@ -220,11 +220,11 @@ func dataApplicationRead(d *schema.ResourceData, meta interface{}) error {
d.Set("available_to_other_tenants", app.AvailableToOtherTenants)
d.Set("oauth2_allow_implicit_flow", app.Oauth2AllowImplicitFlow)

if err := d.Set("identifier_uris", tf.FlattenStringArrayPtr(app.IdentifierUris)); err != nil {
if err := d.Set("identifier_uris", tf.FlattenStringSlicePtr(app.IdentifierUris)); err != nil {
return fmt.Errorf("Error setting `identifier_uris`: %+v", err)
}

if err := d.Set("reply_urls", tf.FlattenStringArrayPtr(app.ReplyUrls)); err != nil {
if err := d.Set("reply_urls", tf.FlattenStringSlicePtr(app.ReplyUrls)); err != nil {
return fmt.Errorf("Error setting `reply_urls`: %+v", err)
}

Expand Down
211 changes: 211 additions & 0 deletions azuread/helpers/graph/credentials.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,211 @@
package graph

import (
"fmt"
"strings"
"time"

"github.com/Azure/azure-sdk-for-go/services/graphrbac/1.6/graphrbac"
"github.com/Azure/go-autorest/autorest/date"
"github.com/hashicorp/go-uuid"
"github.com/hashicorp/terraform/helper/schema"
"github.com/hashicorp/terraform/helper/validation"
"github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/p"
"github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/validate"
)

// valid types are `application` and `service_principal`
func PasswordResourceSchema(object_type string) map[string]*schema.Schema {
return map[string]*schema.Schema{
object_type + "_id": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
ValidateFunc: validate.UUID,
},

"key_id": {
Type: schema.TypeString,
Optional: true,
Computed: true,
ForceNew: true,
ValidateFunc: validate.UUID,
},

"value": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
Sensitive: true,
ValidateFunc: validate.NoEmptyStrings,
},

"start_date": {
Type: schema.TypeString,
Optional: true,
Computed: true,
ForceNew: true,
ValidateFunc: validation.ValidateRFC3339TimeString,
},

"end_date": {
Type: schema.TypeString,
Optional: true,
Computed: true,
ForceNew: true,
ConflictsWith: []string{"end_date_relative"},
ValidateFunc: validation.ValidateRFC3339TimeString,
},

"end_date_relative": {
Type: schema.TypeString,
Optional: true,
ForceNew: true,
ConflictsWith: []string{"end_date"},
ValidateFunc: validate.NoEmptyStrings,
},
}
}

type PasswordCredentialId struct {
ObjectId string
KeyId string
}

func (id PasswordCredentialId) String() string {
return id.ObjectId + "/" + id.KeyId
}

func ParsePasswordCredentialId(id string) (PasswordCredentialId, error) {
parts := strings.Split(id, "/")
if len(parts) != 2 {
return PasswordCredentialId{}, fmt.Errorf("Password Credential ID should be in the format {objectId}/{keyId} - but got %q", id)
}

if _, err := uuid.ParseUUID(parts[0]); err != nil {
return PasswordCredentialId{}, fmt.Errorf("Object ID isn't a valid UUID (%q): %+v", id[0], err)
}

if _, err := uuid.ParseUUID(parts[1]); err != nil {
return PasswordCredentialId{}, fmt.Errorf("Object ID isn't a valid UUID (%q): %+v", id[1], err)
}

return PasswordCredentialId{
ObjectId: parts[0],
KeyId: parts[1],
}, nil

}

func PasswordCredentialIdFrom(objectId, keyId string) PasswordCredentialId {
return PasswordCredentialId{
ObjectId: objectId,
KeyId: keyId,
}
}

func PasswordCredentialForResource(d *schema.ResourceData) (*graphrbac.PasswordCredential, error) {
value := d.Get("value").(string)

// errors should be handled by the validation
var keyId string
if v, ok := d.GetOk("key_id"); ok {
keyId = v.(string)
} else {
kid, err := uuid.GenerateUUID()
if err != nil {
return nil, err
}

keyId = kid
}

var endDate time.Time
if v := d.Get("end_date").(string); v != "" {
endDate, _ = time.Parse(time.RFC3339, v)
} else if v := d.Get("end_date_relative").(string); v != "" {
d, err := time.ParseDuration(v)
if err != nil {
return nil, fmt.Errorf("unable to parse `end_date_relative` (%s) as a duration", v)
}
endDate = time.Now().Add(d)
} else {
return nil, fmt.Errorf("one of `end_date` or `end_date_relative` must be specified")
}

credential := graphrbac.PasswordCredential{
KeyID: p.String(keyId),
Value: p.String(value),
EndDate: &date.Time{Time: endDate},
}

if v, ok := d.GetOk("start_date"); ok {
// errors will be handled by the validation
startDate, _ := time.Parse(time.RFC3339, v.(string))
credential.StartDate = &date.Time{Time: startDate}
}

return &credential, nil
}

func PasswordCredentialResultFindByKeyId(creds graphrbac.PasswordCredentialListResult, keyId string) *graphrbac.PasswordCredential {
var cred *graphrbac.PasswordCredential

if creds.Value != nil {
for _, c := range *creds.Value {
if c.KeyID == nil {
continue
}

if *c.KeyID == keyId {
cred = &c
break
}
}
}

return cred
}

func PasswordCredentialResultAdd(existing graphrbac.PasswordCredentialListResult, cred *graphrbac.PasswordCredential, errorOnDuplicate bool) (*[]graphrbac.PasswordCredential, error) {
newCreds := make([]graphrbac.PasswordCredential, 0)

if existing.Value != nil {
if errorOnDuplicate {
for _, v := range *existing.Value {
if v.KeyID == nil {
continue
}

if *v.KeyID == *cred.KeyID {
return nil, fmt.Errorf("credential already exists found")
}
}
}

newCreds = *existing.Value
}
newCreds = append(newCreds, *cred)

return &newCreds, nil
}

func PasswordCredentialResultRemoveByKeyId(existing graphrbac.PasswordCredentialListResult, keyId string) *[]graphrbac.PasswordCredential {
newCreds := make([]graphrbac.PasswordCredential, 0)

if existing.Value != nil {
for _, v := range *existing.Value {
if v.KeyID == nil {
continue
}

if *v.KeyID == keyId {
continue
}

newCreds = append(newCreds, v)
}
}

return &newCreds
}
13 changes: 0 additions & 13 deletions azuread/helpers/guid/guid.go

This file was deleted.

5 changes: 2 additions & 3 deletions azuread/helpers/p/p.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package p //or maybe ptr?

//helper functions to convert to a pointer
package p // or maybe ptr?

// helper functions to convert to a pointer
func Bool(input bool) *bool {
return &input
}
Expand Down
4 changes: 2 additions & 2 deletions azuread/helpers/tf/marshall.go
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
package tf

func ExpandStringArrayPtr(input []interface{}) *[]string {
func ExpandStringSlicePtr(input []interface{}) *[]string {
result := make([]string, 0)
for _, item := range input {
result = append(result, item.(string))
}
return &result
}

func FlattenStringArrayPtr(input *[]string) []interface{} {
func FlattenStringSlicePtr(input *[]string) []interface{} {
result := make([]interface{}, 0)
if input != nil {
for _, item := range *input {
Expand Down
6 changes: 3 additions & 3 deletions azuread/locks.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
package azuread

// handle the case of using the same name for different kinds of resources
func azureADLockByName(name string, resourceType string) {
// handles the case of using the same name for different kinds of resources
func azureADLockByName(resourceType string, name string) {
armMutexKV.Lock(resourceType + "." + name)
}

func azureADUnlockByName(name string, resourceType string) {
func azureADUnlockByName(resourceType string, name string) {
armMutexKV.Unlock(resourceType + "." + name)
}
1 change: 1 addition & 0 deletions azuread/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ func Provider() terraform.ResourceProvider {

ResourcesMap: map[string]*schema.Resource{
"azuread_application": resourceApplication(),
"azuread_application_password": resourceApplicationPassword(),
"azuread_group": resourceGroup(),
"azuread_service_principal": resourceServicePrincipal(),
"azuread_service_principal_password": resourceServicePrincipalPassword(),
Expand Down
16 changes: 9 additions & 7 deletions azuread/resource_application.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import (
"github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/validate"
)

const resourceApplicationName = "azuread_application"

func resourceApplication() *schema.Resource {
return &schema.Resource{
Create: resourceApplicationCreate,
Expand Down Expand Up @@ -192,8 +194,8 @@ func resourceApplicationCreate(d *schema.ResourceData, meta interface{}) error {
properties := graphrbac.ApplicationCreateParameters{
AdditionalProperties: make(map[string]interface{}),
DisplayName: &name,
IdentifierUris: tf.ExpandStringArrayPtr(identUrls.([]interface{})),
ReplyUrls: tf.ExpandStringArrayPtr(d.Get("reply_urls").(*schema.Set).List()),
IdentifierUris: tf.ExpandStringSlicePtr(identUrls.([]interface{})),
ReplyUrls: tf.ExpandStringSlicePtr(d.Get("reply_urls").(*schema.Set).List()),
AvailableToOtherTenants: p.Bool(d.Get("available_to_other_tenants").(bool)),
RequiredResourceAccess: expandADApplicationRequiredResourceAccess(d),
}
Expand Down Expand Up @@ -265,11 +267,11 @@ func resourceApplicationUpdate(d *schema.ResourceData, meta interface{}) error {
}

if d.HasChange("identifier_uris") {
properties.IdentifierUris = tf.ExpandStringArrayPtr(d.Get("identifier_uris").([]interface{}))
properties.IdentifierUris = tf.ExpandStringSlicePtr(d.Get("identifier_uris").([]interface{}))
}

if d.HasChange("reply_urls") {
properties.ReplyUrls = tf.ExpandStringArrayPtr(d.Get("reply_urls").(*schema.Set).List())
properties.ReplyUrls = tf.ExpandStringSlicePtr(d.Get("reply_urls").(*schema.Set).List())
}

if d.HasChange("available_to_other_tenants") {
Expand Down Expand Up @@ -300,7 +302,7 @@ func resourceApplicationUpdate(d *schema.ResourceData, meta interface{}) error {
switch appType := d.Get("type"); appType {
case "webapp/api":
properties.AdditionalProperties["publicClient"] = false
properties.IdentifierUris = tf.ExpandStringArrayPtr(d.Get("identifier_uris").([]interface{}))
properties.IdentifierUris = tf.ExpandStringSlicePtr(d.Get("identifier_uris").([]interface{}))
case "native":
properties.AdditionalProperties["publicClient"] = true
properties.IdentifierUris = &[]string{}
Expand Down Expand Up @@ -349,11 +351,11 @@ func resourceApplicationRead(d *schema.ResourceData, meta interface{}) error {
d.Set("type", "webapp/api")
}

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

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

Expand Down
Loading

0 comments on commit 237d8b8

Please sign in to comment.