Skip to content

Commit

Permalink
Merge pull request #21569 from hashicorp/kt/blueprint-for-pandora
Browse files Browse the repository at this point in the history
blueprints: swap to go-azure-sdk
  • Loading branch information
tombuildsstuff authored Aug 4, 2023
2 parents 40844b9 + 8ee73de commit ecffcd6
Show file tree
Hide file tree
Showing 66 changed files with 2,603 additions and 638 deletions.
4 changes: 3 additions & 1 deletion internal/clients/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -325,7 +325,9 @@ func (client *Client) Build(ctx context.Context, o *common.ClientOptions) error
if client.Batch, err = batch.NewClient(o); err != nil {
return fmt.Errorf("building clients for Batch: %+v", err)
}
client.Blueprints = blueprints.NewClient(o)
if client.Blueprints, err = blueprints.NewClient(o); err != nil {
return fmt.Errorf("building clients for BluePrints: %+v", err)
}
client.Bot = bot.NewClient(o)
client.Cdn = cdn.NewClient(o)
if client.Cognitive, err = cognitiveServices.NewClient(o); err != nil {
Expand Down
194 changes: 89 additions & 105 deletions internal/services/blueprints/blueprint_assignment_resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,16 @@ import (
"log"
"time"

"github.com/Azure/azure-sdk-for-go/services/preview/blueprint/mgmt/2018-11-01-preview/blueprint" // nolint: staticcheck
"github.com/hashicorp/go-azure-helpers/lang/pointer"
"github.com/hashicorp/go-azure-helpers/lang/response"
"github.com/hashicorp/go-azure-helpers/resourcemanager/commonschema"
"github.com/hashicorp/go-azure-helpers/resourcemanager/identity"
"github.com/hashicorp/go-azure-helpers/resourcemanager/location"
"github.com/hashicorp/go-azure-sdk/resource-manager/blueprints/2018-11-01-preview/assignment"
"github.com/hashicorp/go-azure-sdk/resource-manager/blueprints/2018-11-01-preview/publishedblueprint"
"github.com/hashicorp/terraform-provider-azurerm/helpers/azure"
"github.com/hashicorp/terraform-provider-azurerm/helpers/tf"
"github.com/hashicorp/terraform-provider-azurerm/internal/clients"
"github.com/hashicorp/terraform-provider-azurerm/internal/services/blueprints/parse"
"github.com/hashicorp/terraform-provider-azurerm/internal/services/blueprints/validate"
"github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk"
"github.com/hashicorp/terraform-provider-azurerm/internal/tf/suppress"
"github.com/hashicorp/terraform-provider-azurerm/internal/tf/validation"
Expand All @@ -30,7 +33,7 @@ func resourceBlueprintAssignment() *pluginsdk.Resource {
Delete: resourceBlueprintAssignmentDelete,

Importer: pluginsdk.ImporterValidatingResourceId(func(id string) error {
_, err := parse.AssignmentID(id)
_, err := assignment.ParseScopedBlueprintAssignmentID(id)
return err
}),

Expand Down Expand Up @@ -58,12 +61,12 @@ func resourceBlueprintAssignment() *pluginsdk.Resource {

"location": commonschema.Location(),

"identity": commonschema.UserAssignedIdentityRequired(),
"identity": commonschema.SystemOrUserAssignedIdentityRequired(),

"version_id": {
Type: pluginsdk.TypeString,
Required: true,
ValidateFunc: validate.VersionID,
ValidateFunc: publishedblueprint.ValidateScopedVersionID,
},

"parameter_values": {
Expand All @@ -85,11 +88,11 @@ func resourceBlueprintAssignment() *pluginsdk.Resource {
"lock_mode": {
Type: pluginsdk.TypeString,
Optional: true,
Default: string(blueprint.None),
Default: string(assignment.AssignmentLockModeNone),
ValidateFunc: validation.StringInSlice([]string{
string(blueprint.AssignmentLockModeNone),
string(blueprint.AssignmentLockModeAllResourcesReadOnly),
string(blueprint.AssignmentLockModeAllResourcesDoNotDelete),
string(assignment.AssignmentLockModeNone),
string(assignment.AssignmentLockModeAllResourcesReadOnly),
string(assignment.AssignmentLockModeAllResourcesDoNotDelete),
}, false),
// The first character of value returned by the service is always in lower case.
DiffSuppressFunc: suppress.CaseDifference,
Expand Down Expand Up @@ -142,34 +145,33 @@ func resourceBlueprintAssignmentCreateUpdate(d *pluginsdk.ResourceData, meta int
ctx, cancel := timeouts.ForCreateUpdate(meta.(*clients.Client).StopContext, d)
defer cancel()

name := d.Get("name").(string)
id := assignment.NewScopedBlueprintAssignmentID(d.Get("target_subscription_id").(string), d.Get("name").(string))
blueprintId := d.Get("version_id").(string)
targetScope := d.Get("target_subscription_id").(string)

if d.IsNewResource() {
resp, err := client.Get(ctx, targetScope, name)
resp, err := client.Get(ctx, id)
if err != nil {
if !utils.ResponseWasNotFound(resp.Response) {
return fmt.Errorf("failure checking for existing Blueprint Assignment %q in scope %q", name, targetScope)
if !response.WasNotFound(resp.HttpResponse) {
return fmt.Errorf("checking for an existing %s: %+v", id, err)
}
}
if !utils.ResponseWasNotFound(resp.Response) {
return tf.ImportAsExistsError("azurerm_blueprint_assignment", *resp.ID)
if !response.WasNotFound(resp.HttpResponse) {
return tf.ImportAsExistsError("azurerm_blueprint_assignment", id.ID())
}
}

assignment := blueprint.Assignment{
AssignmentProperties: &blueprint.AssignmentProperties{
BlueprintID: utils.String(blueprintId), // This is mislabeled - The ID is that of the Published Version, not just the Blueprint
Scope: utils.String(targetScope),
payload := assignment.Assignment{
Properties: assignment.AssignmentProperties{
BlueprintId: pointer.To(blueprintId), // This is mislabeled - The ID is that of the Published Version, not just the Blueprint
Scope: pointer.To(id.ResourceScope),
},
Location: utils.String(azure.NormalizeLocation(d.Get("location"))),
Location: location.Normalize(d.Get("location").(string)),
}

if lockModeRaw, ok := d.GetOk("lock_mode"); ok {
assignmentLockSettings := &blueprint.AssignmentLockSettings{}
assignmentLockSettings := &assignment.AssignmentLockSettings{}
lockMode := lockModeRaw.(string)
assignmentLockSettings.Mode = blueprint.AssignmentLockMode(lockMode)
assignmentLockSettings.Mode = pointer.To(assignment.AssignmentLockMode(lockMode))
if lockMode != "None" {
excludedPrincipalsRaw := d.Get("lock_exclude_principals").([]interface{})
if len(excludedPrincipalsRaw) != 0 {
Expand All @@ -181,54 +183,52 @@ func resourceBlueprintAssignmentCreateUpdate(d *pluginsdk.ResourceData, meta int
assignmentLockSettings.ExcludedActions = utils.ExpandStringSlice(excludedActionsRaw)
}
}
assignment.AssignmentProperties.Locks = assignmentLockSettings
payload.Properties.Locks = assignmentLockSettings
}

identity, err := expandArmBlueprintAssignmentIdentity(d.Get("identity").([]interface{}))
i, err := identity.ExpandSystemOrUserAssignedMap(d.Get("identity").([]interface{}))
if err != nil {
return fmt.Errorf("expanding `identity`: %+v", err)
}
assignment.Identity = identity
payload.Identity = *i

if paramValuesRaw := d.Get("parameter_values"); paramValuesRaw != "" {
assignment.Parameters = expandArmBlueprintAssignmentParameters(paramValuesRaw.(string))
payload.Properties.Parameters = expandArmBlueprintAssignmentParameters(paramValuesRaw.(string))
} else {
assignment.Parameters = expandArmBlueprintAssignmentParameters("{}")
payload.Properties.Parameters = expandArmBlueprintAssignmentParameters("{}")
}

if resourceGroupsRaw := d.Get("resource_groups"); resourceGroupsRaw != "" {
assignment.ResourceGroups = expandArmBlueprintAssignmentResourceGroups(resourceGroupsRaw.(string))
payload.Properties.ResourceGroups = expandArmBlueprintAssignmentResourceGroups(resourceGroupsRaw.(string))
} else {
assignment.ResourceGroups = expandArmBlueprintAssignmentResourceGroups("{}")
payload.Properties.ResourceGroups = expandArmBlueprintAssignmentResourceGroups("{}")
}

resp, err := client.CreateOrUpdate(ctx, targetScope, name, assignment)
if err != nil {
if _, err = client.CreateOrUpdate(ctx, id, payload); err != nil {
return err
}

deadline, ok := ctx.Deadline()
if !ok {
return fmt.Errorf("internal-error: context had no deadline")
}
stateConf := &pluginsdk.StateChangeConf{
Pending: []string{
string(blueprint.Waiting),
string(blueprint.Validating),
string(blueprint.Creating),
string(blueprint.Deploying),
string(blueprint.Locking),
string(assignment.AssignmentProvisioningStateWaiting),
string(assignment.AssignmentProvisioningStateValidating),
string(assignment.AssignmentProvisioningStateCreating),
string(assignment.AssignmentProvisioningStateDeploying),
string(assignment.AssignmentProvisioningStateLocking),
},
Target: []string{string(blueprint.Succeeded)},
Refresh: blueprintAssignmentCreateStateRefreshFunc(ctx, client, targetScope, name),
Timeout: d.Timeout(pluginsdk.TimeoutCreate),
Target: []string{string(assignment.AssignmentProvisioningStateSucceeded)},
Refresh: blueprintAssignmentCreateStateRefreshFunc(ctx, client, id),
Timeout: time.Until(deadline),
}

if _, err := stateConf.WaitForStateContext(ctx); err != nil {
return fmt.Errorf("failed waiting for Blueprint Assignment %q (Scope %q): %+v", name, targetScope, err)
return fmt.Errorf("failed waiting for Blueprint Assignment %s: %+v", id.String(), err)
}

if resp.ID == nil || *resp.ID == "" {
return fmt.Errorf("could not read ID from Blueprint Assignment %q on scope %q", name, targetScope)
}

d.SetId(*resp.ID)
d.SetId(id.ID())

return resourceBlueprintAssignmentRead(d, meta)
}
Expand All @@ -238,81 +238,66 @@ func resourceBlueprintAssignmentRead(d *pluginsdk.ResourceData, meta interface{}
ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d)
defer cancel()

id, err := parse.AssignmentID(d.Id())
id, err := assignment.ParseScopedBlueprintAssignmentID(d.Id())
if err != nil {
return err
}

resp, err := client.Get(ctx, id.Scope, id.Name)
resp, err := client.Get(ctx, *id)
if err != nil {
if utils.ResponseWasNotFound(resp.Response) {
log.Printf("[INFO] the Blueprint Assignment %q does not exist - removing from state", id.Name)
if response.WasNotFound(resp.HttpResponse) {
log.Printf("[INFO] the Blueprint Assignment %q does not exist - removing from state", id.String())
d.SetId("")
return nil
}

return fmt.Errorf("Read failed for Blueprint Assignment (%q): %+v", id.Name, err)
}

if resp.Name != nil {
d.Set("name", resp.Name)
return fmt.Errorf("Read failed for Blueprint Assignment (%q): %+v", id.String(), err)
}

if resp.Scope != nil {
d.Set("target_subscription_id", resp.Scope)
}
d.Set("name", id.BlueprintAssignmentName)
if model := resp.Model; model != nil {
p := model.Properties

if resp.Location != nil {
d.Set("location", azure.NormalizeLocation(*resp.Location))
}
d.Set("location", azure.NormalizeLocation(model.Location))
d.Set("target_subscription_id", pointer.From(p.Scope))
d.Set("version_id", pointer.From(p.BlueprintId))
d.Set("display_name", pointer.From(p.DisplayName))
d.Set("description", pointer.From(p.Description))

identity, err := flattenArmBlueprintAssignmentIdentity(resp.Identity)
if err != nil {
return fmt.Errorf("flattening `identity`: %+v", err)
}
if err := d.Set("identity", identity); err != nil {
return fmt.Errorf("setting `identity`: %+v", err)
}

if resp.AssignmentProperties != nil {
if resp.AssignmentProperties.BlueprintID != nil {
d.Set("version_id", resp.AssignmentProperties.BlueprintID)
}

if resp.AssignmentProperties.Parameters != nil {
params, err := flattenArmBlueprintAssignmentParameters(resp.Parameters)
if p.Parameters != nil {
params, err := flattenArmBlueprintAssignmentParameters(p.Parameters)
if err != nil {
return err
}
d.Set("parameter_values", params)
}

if resp.AssignmentProperties.ResourceGroups != nil {
resourceGroups, err := flattenArmBlueprintAssignmentResourceGroups(resp.ResourceGroups)
if p.ResourceGroups != nil {
resourceGroups, err := flattenArmBlueprintAssignmentResourceGroups(p.ResourceGroups)
if err != nil {
return err
}
d.Set("resource_groups", resourceGroups)
}

// Locks
if locks := resp.Locks; locks != nil {
d.Set("lock_mode", locks.Mode)
if locks := p.Locks; locks != nil {
d.Set("lock_mode", string(pointer.From(locks.Mode)))
if locks.ExcludedPrincipals != nil {
d.Set("lock_exclude_principals", locks.ExcludedPrincipals)
}
if locks.ExcludedActions != nil {
d.Set("lock_exclude_actions", locks.ExcludedActions)
}
}
}

if resp.DisplayName != nil {
d.Set("display_name", resp.DisplayName)
}

if resp.Description != nil {
d.Set("description", resp.Description)
i, err := identity.FlattenSystemOrUserAssignedMap(&model.Identity)
if err != nil {
return err
}
if err := d.Set("identity", i); err != nil {
return fmt.Errorf("setting `identity`: %+v", err)
}
}

return nil
Expand All @@ -323,36 +308,35 @@ func resourceBlueprintAssignmentDelete(d *pluginsdk.ResourceData, meta interface
ctx, cancel := timeouts.ForDelete(meta.(*clients.Client).StopContext, d)
defer cancel()

id, err := parse.AssignmentID(d.Id())
id, err := assignment.ParseScopedBlueprintAssignmentID(d.Id())
if err != nil {
return err
}

// We use none here to align the previous behaviour of the blueprint resource
// TODO: we could add a features flag for the blueprint to empower terraform when deleting the blueprint to delete all the generated resources as well
resp, err := client.Delete(ctx, id.Scope, id.Name, blueprint.None)
if err != nil {
if utils.ResponseWasNotFound(resp.Response) {
return nil
}
return fmt.Errorf("failed to delete Blueprint Assignment %q from scope %q: %+v", id.Name, id.Scope, err)
if _, err := client.Delete(ctx, *id, assignment.DeleteOperationOptions{}); err != nil {
return fmt.Errorf("failed to delete Blueprint Assignment %q from scope %q: %+v", id.BlueprintAssignmentName, id.ResourceScope, err)
}

deadline, ok := ctx.Deadline()
if !ok {
return fmt.Errorf("internal-error: context had no deadline")
}
stateConf := &pluginsdk.StateChangeConf{
Pending: []string{
string(blueprint.Waiting),
string(blueprint.Validating),
string(blueprint.Locking),
string(blueprint.Deleting),
string(blueprint.Failed),
string(assignment.AssignmentProvisioningStateWaiting),
string(assignment.AssignmentProvisioningStateValidating),
string(assignment.AssignmentProvisioningStateLocking),
string(assignment.AssignmentProvisioningStateDeleting),
string(assignment.AssignmentProvisioningStateFailed),
},
Target: []string{"NotFound"},
Refresh: blueprintAssignmentDeleteStateRefreshFunc(ctx, client, id.Scope, id.Name),
Timeout: d.Timeout(pluginsdk.TimeoutDelete),
Refresh: blueprintAssignmentDeleteStateRefreshFunc(ctx, client, *id),
Timeout: time.Until(deadline),
}

if _, err := stateConf.WaitForStateContext(ctx); err != nil {
return fmt.Errorf("Failed waiting for Blueprint Assignment %q (Scope %q): %+v", id.Name, id.Scope, err)
return fmt.Errorf("waiting for Blueprint Assignment %q: %+v", id.String(), err)
}

return nil
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@ import (
"fmt"
"testing"

"github.com/hashicorp/go-azure-helpers/lang/pointer"
"github.com/hashicorp/go-azure-sdk/resource-manager/blueprints/2018-11-01-preview/assignment"
"github.com/hashicorp/terraform-provider-azurerm/internal/acceptance"
"github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/check"
"github.com/hashicorp/terraform-provider-azurerm/internal/clients"
"github.com/hashicorp/terraform-provider-azurerm/internal/services/blueprints/parse"
"github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk"
"github.com/hashicorp/terraform-provider-azurerm/utils"
)

type BlueprintAssignmentResource struct{}
Expand Down Expand Up @@ -107,17 +107,17 @@ func TestAccBlueprintAssignment_managementGroup(t *testing.T) {
}

func (t BlueprintAssignmentResource) Exists(ctx context.Context, clients *clients.Client, state *pluginsdk.InstanceState) (*bool, error) {
id, err := parse.AssignmentID(state.ID)
id, err := assignment.ParseScopedBlueprintAssignmentID(state.ID)
if err != nil {
return nil, err
}

resp, err := clients.Blueprints.AssignmentsClient.Get(ctx, id.Scope, id.Name)
resp, err := clients.Blueprints.AssignmentsClient.Get(ctx, *id)
if err != nil {
return nil, fmt.Errorf("retrieving Blueprint Assignment %q (scope %q) was not found", id.Name, id.Scope)
return nil, fmt.Errorf("retrieving Blueprint Assignment %s was not found", id.String())
}

return utils.Bool(resp.AssignmentProperties != nil), nil
return pointer.To(resp.Model != nil), nil
}

func (BlueprintAssignmentResource) basic(data acceptance.TestData, bpName string, version string) string {
Expand Down
Loading

0 comments on commit ecffcd6

Please sign in to comment.