Skip to content

Commit

Permalink
SDK Migration: migrate synchronization to go-azure-sdk
Browse files Browse the repository at this point in the history
  • Loading branch information
manicminer committed Sep 23, 2024
1 parent 712aa61 commit eb72b8d
Show file tree
Hide file tree
Showing 9 changed files with 369 additions and 371 deletions.
39 changes: 26 additions & 13 deletions internal/services/synchronization/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,27 +4,40 @@
package client

import (
"github.com/hashicorp/go-azure-sdk/microsoft-graph/serviceprincipals/stable/serviceprincipal"
"github.com/hashicorp/go-azure-sdk/microsoft-graph/serviceprincipals/stable/synchronizationjob"
"github.com/hashicorp/go-azure-sdk/microsoft-graph/serviceprincipals/stable/synchronizationsecret"
"github.com/hashicorp/terraform-provider-azuread/internal/common"
"github.com/manicminer/hamilton/msgraph"
)

type Client struct {
ServicePrincipalsClient *msgraph.ServicePrincipalsClient
SynchronizationJobClient *msgraph.SynchronizationJobClient
ServicePrincipalClient *serviceprincipal.ServicePrincipalClient
SynchronizationJobClient *synchronizationjob.SynchronizationJobClient
SynchronizationSecretClient *synchronizationsecret.SynchronizationSecretClient
}

func NewClient(o *common.ClientOptions) *Client {
servicePrincipalsClient := msgraph.NewServicePrincipalsClient()
o.ConfigureClient(&servicePrincipalsClient.BaseClient)
func NewClient(o *common.ClientOptions) (*Client, error) {
servicePrincipalClient, err := serviceprincipal.NewServicePrincipalClientWithBaseURI(o.Environment.MicrosoftGraph)
if err != nil {
return nil, err
}
o.Configure(servicePrincipalClient.Client)

synchronizationJobClient := msgraph.NewSynchronizationJobClient()
o.ConfigureClient(&synchronizationJobClient.BaseClient)
synchronizationJobClient, err := synchronizationjob.NewSynchronizationJobClientWithBaseURI(o.Environment.MicrosoftGraph)
if err != nil {
return nil, err
}
o.Configure(synchronizationJobClient.Client)

// Synchronization doesn't yet exist in v1.0
synchronizationJobClient.BaseClient.ApiVersion = msgraph.VersionBeta
synchronizationSecretClient, err := synchronizationsecret.NewSynchronizationSecretClientWithBaseURI(o.Environment.MicrosoftGraph)
if err != nil {
return nil, err
}
o.Configure(synchronizationSecretClient.Client)

return &Client{
ServicePrincipalsClient: servicePrincipalsClient,
SynchronizationJobClient: synchronizationJobClient,
}
ServicePrincipalClient: servicePrincipalClient,
SynchronizationJobClient: synchronizationJobClient,
SynchronizationSecretClient: synchronizationSecretClient,
}, nil
}
2 changes: 1 addition & 1 deletion internal/services/synchronization/registration.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
package synchronization

import (
"github.com/hashicorp/terraform-provider-azuread/internal/helpers/tf/pluginsdk"
"github.com/hashicorp/terraform-provider-azuread/internal/sdk"
"github.com/hashicorp/terraform-provider-azuread/internal/tf/pluginsdk"
)

type Registration struct{}
Expand Down
81 changes: 44 additions & 37 deletions internal/services/synchronization/synchronization.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,123 +4,129 @@
package synchronization

import (
"time"
"net/http"

"github.com/hashicorp/go-azure-helpers/lang/pointer"
"github.com/manicminer/hamilton/msgraph"
"github.com/hashicorp/go-azure-helpers/lang/response"
"github.com/hashicorp/go-azure-sdk/microsoft-graph/common-types/stable"
"github.com/hashicorp/go-azure-sdk/sdk/client"
"github.com/hashicorp/go-azure-sdk/sdk/nullable"
"github.com/hashicorp/go-azure-sdk/sdk/odata"
)

const servicePrincipalResourceName = "azuread_service_principal"

func emptySynchronizationSecretKeyStringValuePair(in []interface{}) *[]msgraph.SynchronizationSecretKeyStringValuePair {
result := make([]msgraph.SynchronizationSecretKeyStringValuePair, 0)
func synchronizationRetryFunc() client.RequestRetryFunc {
return func(resp *http.Response, o *odata.OData) (bool, error) {
return response.WasConflict(resp) || response.WasStatusCode(resp, http.StatusForbidden), nil
}
}

func emptySynchronizationSecretKeyStringValuePair(in []interface{}) *[]stable.SynchronizationSecretKeyStringValuePair {
result := make([]stable.SynchronizationSecretKeyStringValuePair, 0)

for _, raw := range in {
if raw == nil {
continue
}
item := raw.(map[string]interface{})

result = append(result, msgraph.SynchronizationSecretKeyStringValuePair{
Key: pointer.To(item["key"].(string)),
Value: pointer.To(""),
result = append(result, stable.SynchronizationSecretKeyStringValuePair{
Key: pointer.To(stable.SynchronizationSecret(item["key"].(string))),
Value: nullable.Value(""),
})
}

return &result
}

func expandSynchronizationSecretKeyStringValuePair(in []interface{}) *[]msgraph.SynchronizationSecretKeyStringValuePair {
result := make([]msgraph.SynchronizationSecretKeyStringValuePair, 0)
func expandSynchronizationSecretKeyStringValuePair(in []interface{}) *[]stable.SynchronizationSecretKeyStringValuePair {
result := make([]stable.SynchronizationSecretKeyStringValuePair, 0)

for _, raw := range in {
if raw == nil {
continue
}
item := raw.(map[string]interface{})

result = append(result, msgraph.SynchronizationSecretKeyStringValuePair{
Key: pointer.To(item["key"].(string)),
Value: pointer.To(item["value"].(string)),
result = append(result, stable.SynchronizationSecretKeyStringValuePair{
Key: pointer.To(stable.SynchronizationSecret(item["key"].(string))),
Value: nullable.Value(item["value"].(string)),
})
}

return &result
}

func expandSynchronizationJobApplicationParameters(in []interface{}) *[]msgraph.SynchronizationJobApplicationParameters {
result := make([]msgraph.SynchronizationJobApplicationParameters, 0)
func expandSynchronizationJobApplicationParameters(in []interface{}) *[]stable.SynchronizationJobApplicationParameters {
result := make([]stable.SynchronizationJobApplicationParameters, 0)

for _, raw := range in {
if raw == nil {
continue
}
item := raw.(map[string]interface{})

result = append(result, msgraph.SynchronizationJobApplicationParameters{
result = append(result, stable.SynchronizationJobApplicationParameters{
Subjects: expandSynchronizationJobSubject(item["subject"].([]interface{})),
RuleId: pointer.To(item["rule_id"].(string)),
RuleId: nullable.Value(item["rule_id"].(string)),
})
}

return &result
}

func expandSynchronizationJobSubject(in []interface{}) *[]msgraph.SynchronizationJobSubject {
result := make([]msgraph.SynchronizationJobSubject, 0)
func expandSynchronizationJobSubject(in []interface{}) *[]stable.SynchronizationJobSubject {
result := make([]stable.SynchronizationJobSubject, 0)
for _, raw := range in {
if raw == nil {
continue
}
item := raw.(map[string]interface{})

result = append(result, msgraph.SynchronizationJobSubject{
ObjectId: pointer.To(item["object_id"].(string)),
ObjectTypeName: pointer.To(item["object_type_name"].(string)),
result = append(result, stable.SynchronizationJobSubject{
ObjectId: nullable.Value(item["object_id"].(string)),
ObjectTypeName: nullable.Value(item["object_type_name"].(string)),
})
}

return &result
}

func flattenSynchronizationSchedule(in *msgraph.SynchronizationSchedule) []map[string]interface{} {
func flattenSynchronizationSchedule(in *stable.SynchronizationSchedule) []map[string]interface{} {
if in == nil {
return []map[string]interface{}{}
}

expiration := ""
if v := in.Expiration; v != nil {
expiration = v.Format(time.RFC3339)
}
return []map[string]interface{}{{
"expiration": expiration,
"interval": in.Interval,
"state": in.State,
"expiration": in.Expiration.GetOrZero(),
"interval": pointer.From(in.Interval),
"state": pointer.From(in.State),
}}
}

func flattenSynchronizationSecretKeyStringValuePair(in *[]msgraph.SynchronizationSecretKeyStringValuePair, current []interface{}) []interface{} {
func flattenSynchronizationSecretKeyStringValuePair(in *[]stable.SynchronizationSecretKeyStringValuePair, current []interface{}) []interface{} {
if in == nil {
return []interface{}{}
}

credentials := make([]interface{}, 0)
for _, item := range *in {
value := item.Value
if *value == "*" && current != nil {
value := item.Value.GetOrZero()
if value == "*" && current != nil {
// Use value from state if API returns * indicating sensitive data
for _, raw := range current {
if raw == nil {
continue
}
currentItem := raw.(map[string]interface{})
if currentItem["key"].(string) == *item.Key {
value = pointer.To(currentItem["value"].(string))
if currentItem["key"].(string) == string(pointer.From(item.Key)) {
value = currentItem["value"].(string)
}
}
}
credential := map[string]interface{}{
"key": item.Key,
"key": pointer.From(item.Key),
"value": value,
}
credentials = append(credentials, credential)
Expand All @@ -129,13 +135,14 @@ func flattenSynchronizationSecretKeyStringValuePair(in *[]msgraph.Synchronizatio
return credentials
}

func allCredentialsRemoved(in []msgraph.SynchronizationSecretKeyStringValuePair, current []msgraph.SynchronizationSecretKeyStringValuePair) bool {
func allCredentialsRemoved(in []stable.SynchronizationSecretKeyStringValuePair, current []stable.SynchronizationSecretKeyStringValuePair) bool {
for _, item := range in {
for _, itemCurrent := range current {
if *item.Key == *itemCurrent.Key {
if pointer.From(item.Key) == pointer.From(itemCurrent.Key) {
return false
}
}
}

return true
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,18 @@ package synchronization
import (
"context"
"errors"
"net/http"
"time"

"github.com/hashicorp/go-azure-sdk/sdk/odata"
"github.com/hashicorp/go-azure-helpers/lang/response"
"github.com/hashicorp/go-azure-sdk/microsoft-graph/common-types/stable"
"github.com/hashicorp/go-azure-sdk/microsoft-graph/serviceprincipals/stable/serviceprincipal"
"github.com/hashicorp/go-azure-sdk/microsoft-graph/serviceprincipals/stable/synchronizationjob"
"github.com/hashicorp/go-uuid"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-provider-azuread/internal/clients"
"github.com/hashicorp/terraform-provider-azuread/internal/tf"
"github.com/hashicorp/terraform-provider-azuread/internal/tf/validation"
"github.com/manicminer/hamilton/msgraph"
"github.com/hashicorp/terraform-provider-azuread/internal/helpers/tf"
"github.com/hashicorp/terraform-provider-azuread/internal/helpers/tf/validation"
)

func synchronizationJobProvisionOnDemandResource() *schema.Resource {
Expand All @@ -34,11 +35,11 @@ func synchronizationJobProvisionOnDemandResource() *schema.Resource {

Schema: map[string]*schema.Schema{
"service_principal_id": {
Description: "The object ID of the service principal for which this synchronization job should be provisioned",
Type: schema.TypeString,
Required: true,
ForceNew: true,
ValidateDiagFunc: validation.ValidateDiag(validation.IsUUID),
Description: "The object ID of the service principal for which this synchronization job should be provisioned",
Type: schema.TypeString,
Required: true,
ForceNew: true,
ValidateFunc: validation.IsUUID,
},

"synchronization_job_id": {
Expand Down Expand Up @@ -102,43 +103,53 @@ func synchronizationJobProvisionOnDemandResource() *schema.Resource {

func synchronizationProvisionOnDemandResourceCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
client := meta.(*clients.Client).ServicePrincipals.SynchronizationJobClient
spClient := meta.(*clients.Client).ServicePrincipals.ServicePrincipalsClient
objectId := d.Get("service_principal_id").(string)
jobId := d.Get("synchronization_job_id").(string)
servicePrincipalClient := meta.(*clients.Client).ServicePrincipals.ServicePrincipalClient

tf.LockByName(servicePrincipalResourceName, objectId)
defer tf.UnlockByName(servicePrincipalResourceName, objectId)
servicePrincipalId := stable.NewServicePrincipalID(d.Get("service_principal_id").(string))

servicePrincipal, status, err := spClient.Get(ctx, objectId, odata.Query{})
tf.LockByName(servicePrincipalResourceName, servicePrincipalId.ServicePrincipalId)
defer tf.UnlockByName(servicePrincipalResourceName, servicePrincipalId.ServicePrincipalId)

servicePrincipalResp, err := servicePrincipalClient.GetServicePrincipal(ctx, servicePrincipalId, serviceprincipal.DefaultGetServicePrincipalOperationOptions())
if err != nil {
if status == http.StatusNotFound {
return tf.ErrorDiagPathF(nil, "service_principal_id", "Service principal with object ID %q was not found", objectId)
if response.WasNotFound(servicePrincipalResp.HttpResponse) {
return tf.ErrorDiagPathF(nil, "service_principal_id", "%s was not found", servicePrincipalId)
}
return tf.ErrorDiagPathF(err, "service_principal_id", "Retrieving service principal with object ID %q", objectId)
return tf.ErrorDiagPathF(err, "service_principal_id", "Retrieving %s", servicePrincipalId)
}

servicePrincipal := servicePrincipalResp.Model
if servicePrincipal == nil {
return tf.ErrorDiagF(errors.New("model was nil"), "Retrieving %s", servicePrincipalId)
}
if servicePrincipal == nil || servicePrincipal.ID() == nil {
return tf.ErrorDiagF(errors.New("nil service principal or service principal with nil ID was returned"), "API error retrieving service principal with object ID %q", objectId)
if servicePrincipal.Id == nil {
return tf.ErrorDiagF(errors.New("model has nil ID"), "Retrieving %s", servicePrincipalId)
}

job, status, err := client.Get(ctx, jobId, objectId)
jobId := stable.NewServicePrincipalIdSynchronizationJobID(servicePrincipalId.ServicePrincipalId, d.Get("synchronization_job_id").(string))

jobResp, err := client.GetSynchronizationJob(ctx, jobId, synchronizationjob.GetSynchronizationJobOperationOptions{RetryFunc: synchronizationRetryFunc()})
if err != nil {
if status == http.StatusNotFound {
return tf.ErrorDiagPathF(nil, "job_id", "Job with object ID %q was not found for service principle %q", jobId, objectId)
if response.WasNotFound(jobResp.HttpResponse) {
return tf.ErrorDiagPathF(nil, "synchronization_job_id", "%s was not found", jobId)
}
return tf.ErrorDiagPathF(err, "job_id", "Retrieving job with object ID %q for service principle %q", jobId, objectId)
return tf.ErrorDiagPathF(err, "job_id", "Retrieving %s", jobId)
}
if job == nil || job.ID == nil {
return tf.ErrorDiagF(errors.New("nil job or job with nil ID was returned"), "API error retrieving job with object ID %q/%s", objectId, jobId)

job := jobResp.Model
if job == nil {
return tf.ErrorDiagF(errors.New("model was nil"), "Retrieving %s", jobId)
}
if job.Id == nil {
return tf.ErrorDiagF(errors.New("model has nil ID"), "Retrieving %s", jobId)
}

// Create a new synchronization job
synchronizationProvisionOnDemand := &msgraph.SynchronizationJobProvisionOnDemand{
properties := synchronizationjob.ProvisionSynchronizationJobOnDemandRequest{
Parameters: expandSynchronizationJobApplicationParameters(d.Get("parameter").([]interface{})),
}

_, err = client.ProvisionOnDemand(ctx, jobId, synchronizationProvisionOnDemand, *servicePrincipal.ID())
if err != nil {
return tf.ErrorDiagF(err, "Creating synchronization job for service principal ID %q", *servicePrincipal.ID())
if _, err = client.ProvisionSynchronizationJobOnDemand(ctx, jobId, properties, synchronizationjob.DefaultProvisionSynchronizationJobOnDemandOperationOptions()); err != nil {
return tf.ErrorDiagF(err, "Provisioning %s", jobId)
}

id, _ := uuid.GenerateUUID()
Expand All @@ -147,10 +158,12 @@ func synchronizationProvisionOnDemandResourceCreate(ctx context.Context, d *sche
return synchronizationProvisionOnDemandResourceRead(ctx, d, meta)
}

func synchronizationProvisionOnDemandResourceRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
func synchronizationProvisionOnDemandResourceRead(_ context.Context, _ *schema.ResourceData, _ interface{}) diag.Diagnostics {
// Nothing to read
return nil
}

func synchronizationProvisionOnDemandResourceDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
func synchronizationProvisionOnDemandResourceDelete(_ context.Context, _ *schema.ResourceData, _ interface{}) diag.Diagnostics {
// Nothing to destroy
return nil
}
Loading

0 comments on commit eb72b8d

Please sign in to comment.