Skip to content

Commit

Permalink
Work around inconsistency for password and certificate credentials
Browse files Browse the repository at this point in the history
  • Loading branch information
manicminer committed Oct 6, 2021
1 parent d736a0c commit d1c0ff5
Show file tree
Hide file tree
Showing 4 changed files with 138 additions and 1 deletion.
34 changes: 34 additions & 0 deletions internal/services/applications/application_certificate_resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import (
"strings"
"time"

"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"

"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
Expand Down Expand Up @@ -172,6 +174,38 @@ func applicationCertificateResourceCreate(ctx context.Context, d *schema.Resourc
return tf.ErrorDiagF(err, "Adding certificate for application with object ID %q", id.ObjectId)
}

// Wait for the credential to appear in the application manifest, this can take several minutes
timeout, _ := ctx.Deadline()
polledForCredential, err := (&resource.StateChangeConf{
Pending: []string{"Waiting"},
Target: []string{"Done"},
Timeout: time.Until(timeout),
MinTimeout: 1 * time.Second,
ContinuousTargetOccurence: 5,
Refresh: func() (interface{}, string, error) {
app, _, err := client.Get(ctx, id.ObjectId, odata.Query{})
if err != nil {
return nil, "Error", err
}

if app.KeyCredentials != nil {
for _, cred := range *app.KeyCredentials {
if cred.KeyId != nil && strings.EqualFold(*cred.KeyId, id.KeyId) {
return &cred, "Done", nil
}
}
}

return nil, "Waiting", nil
},
}).WaitForStateContext(ctx)

if err != nil {
return tf.ErrorDiagF(err, "Waiting for certificate credential for application with object ID %q", id.ObjectId)
} else if polledForCredential == nil {
return tf.ErrorDiagF(errors.New("certificate credential not found in application manifest"), "Waiting for certificate credential for application with object ID %q", id.ObjectId)
}

d.SetId(id.String())

return applicationCertificateResourceRead(ctx, d, meta)
Expand Down
36 changes: 35 additions & 1 deletion internal/services/applications/application_password_resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"time"

"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
"github.com/manicminer/hamilton/msgraph"
Expand All @@ -30,7 +31,7 @@ func applicationPasswordResource() *schema.Resource {
DeleteContext: applicationPasswordResourceDelete,

Timeouts: &schema.ResourceTimeout{
Create: schema.DefaultTimeout(5 * time.Minute),
Create: schema.DefaultTimeout(15 * time.Minute),
Read: schema.DefaultTimeout(5 * time.Minute),
Update: schema.DefaultTimeout(5 * time.Minute),
Delete: schema.DefaultTimeout(5 * time.Minute),
Expand Down Expand Up @@ -161,6 +162,39 @@ func applicationPasswordResourceCreate(ctx context.Context, d *schema.ResourceDa
}

id := parse.NewCredentialID(*app.ID, "password", *newCredential.KeyId)

// Wait for the credential to appear in the application manifest, this can take several minutes
timeout, _ := ctx.Deadline()
polledForCredential, err := (&resource.StateChangeConf{
Pending: []string{"Waiting"},
Target: []string{"Done"},
Timeout: time.Until(timeout),
MinTimeout: 1 * time.Second,
ContinuousTargetOccurence: 5,
Refresh: func() (interface{}, string, error) {
app, _, err := client.Get(ctx, id.ObjectId, odata.Query{})
if err != nil {
return nil, "Error", err
}

if app.PasswordCredentials != nil {
for _, cred := range *app.PasswordCredentials {
if cred.KeyId != nil && strings.EqualFold(*cred.KeyId, id.KeyId) {
return &cred, "Done", nil
}
}
}

return nil, "Waiting", nil
},
}).WaitForStateContext(ctx)

if err != nil {
return tf.ErrorDiagF(err, "Waiting for password credential for application with object ID %q", id.ObjectId)
} else if polledForCredential == nil {
return tf.ErrorDiagF(errors.New("password credential not found in application manifest"), "Waiting for password credential for application with object ID %q", id.ObjectId)
}

d.SetId(id.String())
d.Set("value", newCredential.SecretText)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import (
"strings"
"time"

"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"

"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
Expand Down Expand Up @@ -172,6 +174,38 @@ func servicePrincipalCertificateResourceCreate(ctx context.Context, d *schema.Re
return tf.ErrorDiagF(err, "Adding certificate for service principal with object ID %q", id.ObjectId)
}

// Wait for the credential to appear in the service principal manifest, this can take several minutes
timeout, _ := ctx.Deadline()
polledForCredential, err := (&resource.StateChangeConf{
Pending: []string{"Waiting"},
Target: []string{"Done"},
Timeout: time.Until(timeout),
MinTimeout: 1 * time.Second,
ContinuousTargetOccurence: 5,
Refresh: func() (interface{}, string, error) {
servicePrincipal, _, err := client.Get(ctx, id.ObjectId, odata.Query{})
if err != nil {
return nil, "Error", err
}

if servicePrincipal.KeyCredentials != nil {
for _, cred := range *servicePrincipal.KeyCredentials {
if cred.KeyId != nil && strings.EqualFold(*cred.KeyId, id.KeyId) {
return &cred, "Done", nil
}
}
}

return nil, "Waiting", nil
},
}).WaitForStateContext(ctx)

if err != nil {
return tf.ErrorDiagF(err, "Waiting for certificate credential for service principal with object ID %q", id.ObjectId)
} else if polledForCredential == nil {
return tf.ErrorDiagF(errors.New("certificate credential not found in service principal manifest"), "Waiting for certificate credential for service principal with object ID %q", id.ObjectId)
}

d.SetId(id.String())

return servicePrincipalCertificateResourceRead(ctx, d, meta)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import (
"strings"
"time"

"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"

"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/manicminer/hamilton/msgraph"
Expand Down Expand Up @@ -142,6 +144,39 @@ func servicePrincipalPasswordResourceCreate(ctx context.Context, d *schema.Resou
}

id := parse.NewCredentialID(*sp.ID, "password", *newCredential.KeyId)

// Wait for the credential to appear in the service principal manifest, this can take several minutes
timeout, _ := ctx.Deadline()
polledForCredential, err := (&resource.StateChangeConf{
Pending: []string{"Waiting"},
Target: []string{"Done"},
Timeout: time.Until(timeout),
MinTimeout: 1 * time.Second,
ContinuousTargetOccurence: 5,
Refresh: func() (interface{}, string, error) {
servicePrincipal, _, err := client.Get(ctx, id.ObjectId, odata.Query{})
if err != nil {
return nil, "Error", err
}

if servicePrincipal.PasswordCredentials != nil {
for _, cred := range *servicePrincipal.PasswordCredentials {
if cred.KeyId != nil && strings.EqualFold(*cred.KeyId, id.KeyId) {
return &cred, "Done", nil
}
}
}

return nil, "Waiting", nil
},
}).WaitForStateContext(ctx)

if err != nil {
return tf.ErrorDiagF(err, "Waiting for password credential for service principal with object ID %q", id.ObjectId)
} else if polledForCredential == nil {
return tf.ErrorDiagF(errors.New("password credential not found in service principal manifest"), "Waiting for password credential for service principal with object ID %q", id.ObjectId)
}

d.SetId(id.String())
d.Set("value", newCredential.SecretText)

Expand Down

0 comments on commit d1c0ff5

Please sign in to comment.