Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New Resource - azurerm_virtual_machine_scale_set_standby_pool #28441

Merged
merged 8 commits into from
Feb 11, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/labeler-issue-triage.yml
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ service/cognitive-services:
- '### (|New or )Affected Resource\(s\)\/Data Source\(s\)((.|\n)*)azurerm_(ai_services|cognitive_)((.|\n)*)###'

service/communication:
- '### (|New or )Affected Resource\(s\)\/Data Source\(s\)((.|\n)*)azurerm_(communication_service|email_communication_service|gallery_application|orchestrated_virtual_machine_scale_set\W+|restore_point_collection|virtual_machine_gallery_application_assignment\W+|virtual_machine_implicit_data_disk_from_source\W+|virtual_machine_restore_point\W+|virtual_machine_restore_point_collection\W+|virtual_machine_run_command\W+)((.|\n)*)###'
- '### (|New or )Affected Resource\(s\)\/Data Source\(s\)((.|\n)*)azurerm_(communication_service|email_communication_service|gallery_application|orchestrated_virtual_machine_scale_set\W+|restore_point_collection|virtual_machine_gallery_application_assignment\W+|virtual_machine_implicit_data_disk_from_source\W+|virtual_machine_restore_point\W+|virtual_machine_restore_point_collection\W+|virtual_machine_run_command\W+|virtual_machine_scale_set_standby_pool\W+)((.|\n)*)###'

service/connections:
- '### (|New or )Affected Resource\(s\)\/Data Source\(s\)((.|\n)*)azurerm_(api_connection|managed_api)((.|\n)*)###'
Expand Down
9 changes: 9 additions & 0 deletions internal/services/compute/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ import (
"github.com/hashicorp/go-azure-sdk/resource-manager/compute/2024-03-01/virtualmachinescalesetvms"
"github.com/hashicorp/go-azure-sdk/resource-manager/compute/2024-07-01/virtualmachinescalesets"
"github.com/hashicorp/go-azure-sdk/resource-manager/marketplaceordering/2015-06-01/agreements"
"github.com/hashicorp/go-azure-sdk/resource-manager/standbypool/2024-03-01/standbyvirtualmachinepools"
"github.com/hashicorp/terraform-provider-azurerm/internal/common"
)

Expand Down Expand Up @@ -68,6 +69,7 @@ type Client struct {
SkusClient *skus.SkusClient
SSHPublicKeysClient *sshpublickeys.SshPublicKeysClient
SnapshotsClient *snapshots.SnapshotsClient
StandbyVirtualMachinePoolsClient *standbyvirtualmachinepools.StandbyVirtualMachinePoolsClient
VirtualMachinesClient *virtualmachines.VirtualMachinesClient
VirtualMachineExtensionsClient *virtualmachineextensions.VirtualMachineExtensionsClient
VirtualMachineRunCommandsClient *virtualmachineruncommands.VirtualMachineRunCommandsClient
Expand Down Expand Up @@ -211,6 +213,12 @@ func NewClient(o *common.ClientOptions) (*Client, error) {
}
o.Configure(sshPublicKeysClient.Client, o.Authorizers.ResourceManager)

standbyVirtualMachinePoolsClient, err := standbyvirtualmachinepools.NewStandbyVirtualMachinePoolsClientWithBaseURI(o.Environment.ResourceManager)
if err != nil {
return nil, fmt.Errorf("building Standby Virtual Machine Pools client: %+v", err)
}
o.Configure(standbyVirtualMachinePoolsClient.Client, o.Authorizers.ResourceManager)

virtualMachinesClient, err := virtualmachines.NewVirtualMachinesClientWithBaseURI(o.Environment.ResourceManager)
if err != nil {
return nil, fmt.Errorf("building VirtualMachines client: %+v", err)
Expand Down Expand Up @@ -282,6 +290,7 @@ func NewClient(o *common.ClientOptions) (*Client, error) {
SkusClient: skusClient,
SSHPublicKeysClient: sshPublicKeysClient,
SnapshotsClient: snapshotsClient,
StandbyVirtualMachinePoolsClient: standbyVirtualMachinePoolsClient,
VirtualMachinesClient: virtualMachinesClient,
VirtualMachineExtensionsClient: virtualMachineExtensionsClient,
VirtualMachineRunCommandsClient: virtualMachineRunCommandsClient,
Expand Down
1 change: 1 addition & 0 deletions internal/services/compute/registration.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,5 +96,6 @@ func (r Registration) Resources() []sdk.Resource {
VirtualMachineRestorePointCollectionResource{},
VirtualMachineRestorePointResource{},
VirtualMachineGalleryApplicationAssignmentResource{},
VirtualMachineScaleSetStandbyPoolResource{},
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,314 @@
package compute

import (
"context"
"fmt"
"regexp"
"time"

"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/location"
"github.com/hashicorp/go-azure-sdk/resource-manager/compute/2024-07-01/virtualmachinescalesets"
"github.com/hashicorp/go-azure-sdk/resource-manager/standbypool/2024-03-01/standbyvirtualmachinepools"
"github.com/hashicorp/terraform-provider-azurerm/internal/sdk"
"github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk"
"github.com/hashicorp/terraform-provider-azurerm/internal/tf/validation"
)

type VirtualMachineScaleSetStandbyPoolModel struct {
Name string `tfschema:"name"`
ResourceGroupName string `tfschema:"resource_group_name"`
Location string `tfschema:"location"`
AttachedVirtualMachineScaleSetId string `tfschema:"attached_virtual_machine_scale_set_id"`
ElasticityProfile []VirtualMachineScaleSetStandbyPoolElasticityProfileModel `tfschema:"elasticity_profile"`
VirtualMachineState standbyvirtualmachinepools.VirtualMachineState `tfschema:"virtual_machine_state"`
Tags map[string]string `tfschema:"tags"`
}

type VirtualMachineScaleSetStandbyPoolElasticityProfileModel struct {
MaxReadyCapacity int64 `tfschema:"max_ready_capacity"`
MinReadyCapacity int64 `tfschema:"min_ready_capacity"`
}

type VirtualMachineScaleSetStandbyPoolResource struct{}

var (
_ sdk.ResourceWithUpdate = VirtualMachineScaleSetStandbyPoolResource{}
_ sdk.ResourceWithCustomizeDiff = VirtualMachineScaleSetStandbyPoolResource{}
)

func (r VirtualMachineScaleSetStandbyPoolResource) ResourceType() string {
return "azurerm_virtual_machine_scale_set_standby_pool"
}

func (r VirtualMachineScaleSetStandbyPoolResource) ModelObject() interface{} {
return &VirtualMachineScaleSetStandbyPoolModel{}
}

func (r VirtualMachineScaleSetStandbyPoolResource) IDValidationFunc() pluginsdk.SchemaValidateFunc {
return standbyvirtualmachinepools.ValidateStandbyVirtualMachinePoolID
}

func (r VirtualMachineScaleSetStandbyPoolResource) Arguments() map[string]*pluginsdk.Schema {
return map[string]*pluginsdk.Schema{
"name": {
Type: pluginsdk.TypeString,
Required: true,
ForceNew: true,
ValidateFunc: validation.StringMatch(
regexp.MustCompile("^[a-zA-Z0-9-]{3,24}$"),
"name must be between 3 and 24 characters in length and may contain only letters, numbers and hyphens (-).",
),
},

"resource_group_name": commonschema.ResourceGroupName(),

"location": commonschema.Location(),

"attached_virtual_machine_scale_set_id": {
Type: pluginsdk.TypeString,
Required: true,
ValidateFunc: virtualmachinescalesets.ValidateVirtualMachineScaleSetID,
},

"elasticity_profile": {
Type: pluginsdk.TypeList,
Required: true,
MaxItems: 1,
Elem: &pluginsdk.Resource{
Schema: map[string]*pluginsdk.Schema{
"max_ready_capacity": {
Type: pluginsdk.TypeInt,
Required: true,
ValidateFunc: validation.IntBetween(0, 2000),
},

"min_ready_capacity": {
Type: pluginsdk.TypeInt,
Required: true,
ValidateFunc: validation.IntBetween(0, 2000),
},
},
},
},

"virtual_machine_state": {
Type: pluginsdk.TypeString,
Required: true,
ValidateFunc: validation.StringInSlice(standbyvirtualmachinepools.PossibleValuesForVirtualMachineState(), false),
},

"tags": commonschema.Tags(),
}
}

func (r VirtualMachineScaleSetStandbyPoolResource) Attributes() map[string]*pluginsdk.Schema {
return map[string]*pluginsdk.Schema{}
}

func (r VirtualMachineScaleSetStandbyPoolResource) CustomizeDiff() sdk.ResourceFunc {
return sdk.ResourceFunc{
Timeout: 5 * time.Minute,
Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error {
var config VirtualMachineScaleSetStandbyPoolModel
if err := metadata.DecodeDiff(&config); err != nil {
return fmt.Errorf("decoding: %+v", err)
}

if len(config.ElasticityProfile) > 0 && config.ElasticityProfile[0].MaxReadyCapacity < config.ElasticityProfile[0].MinReadyCapacity {
return fmt.Errorf("`min_ready_capacity` cannot exceed `max_ready_capacity`")
}

return nil
},
}
}

func (r VirtualMachineScaleSetStandbyPoolResource) Create() sdk.ResourceFunc {
return sdk.ResourceFunc{
Timeout: 30 * time.Minute,
Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error {
client := metadata.Client.Compute.StandbyVirtualMachinePoolsClient
subscriptionId := metadata.Client.Account.SubscriptionId

var model VirtualMachineScaleSetStandbyPoolModel
if err := metadata.Decode(&model); err != nil {
return fmt.Errorf("decoding: %+v", err)
}

id := standbyvirtualmachinepools.NewStandbyVirtualMachinePoolID(subscriptionId, model.ResourceGroupName, model.Name)
existing, err := client.Get(ctx, id)
if err != nil && !response.WasNotFound(existing.HttpResponse) {
return fmt.Errorf("checking for existing %s: %+v", id, err)
}

if !response.WasNotFound(existing.HttpResponse) {
return metadata.ResourceRequiresImport(r.ResourceType(), id)
}

properties := &standbyvirtualmachinepools.StandbyVirtualMachinePoolResource{
Location: location.Normalize(model.Location),
Properties: &standbyvirtualmachinepools.StandbyVirtualMachinePoolResourceProperties{
AttachedVirtualMachineScaleSetId: pointer.To(model.AttachedVirtualMachineScaleSetId),
ElasticityProfile: expandStandbyVirtualMachinePoolElasticityProfileModel(model.ElasticityProfile),
VirtualMachineState: model.VirtualMachineState,
},
Tags: &model.Tags,
}

if err := client.CreateOrUpdateThenPoll(ctx, id, *properties); err != nil {
return fmt.Errorf("creating %s: %+v", id, err)
}

metadata.SetID(id)
return nil
},
}
}

func (r VirtualMachineScaleSetStandbyPoolResource) Update() sdk.ResourceFunc {
return sdk.ResourceFunc{
Timeout: 30 * time.Minute,
Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error {
client := metadata.Client.Compute.StandbyVirtualMachinePoolsClient

id, err := standbyvirtualmachinepools.ParseStandbyVirtualMachinePoolID(metadata.ResourceData.Id())
if err != nil {
return err
}

var model VirtualMachineScaleSetStandbyPoolModel
if err := metadata.Decode(&model); err != nil {
return fmt.Errorf("decoding: %+v", err)
}

resp, err := client.Get(ctx, *id)
if err != nil {
return fmt.Errorf("retrieving %s: %+v", *id, err)
}

properties := resp.Model
if properties == nil {
return fmt.Errorf("retrieving %s: properties was nil", id)
}

if metadata.ResourceData.HasChange("attached_virtual_machine_scale_set_id") {
properties.Properties.AttachedVirtualMachineScaleSetId = pointer.To(model.AttachedVirtualMachineScaleSetId)
}

if metadata.ResourceData.HasChange("elasticity_profile") {
properties.Properties.ElasticityProfile = expandStandbyVirtualMachinePoolElasticityProfileModel(model.ElasticityProfile)
}

if metadata.ResourceData.HasChange("virtual_machine_state") {
properties.Properties.VirtualMachineState = model.VirtualMachineState
}

if metadata.ResourceData.HasChange("tags") {
properties.Tags = &model.Tags
}

if err := client.CreateOrUpdateThenPoll(ctx, *id, *properties); err != nil {
return fmt.Errorf("updating %s: %+v", *id, err)
}

return nil
},
}
}

func (r VirtualMachineScaleSetStandbyPoolResource) Read() sdk.ResourceFunc {
return sdk.ResourceFunc{
Timeout: 5 * time.Minute,
Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error {
client := metadata.Client.Compute.StandbyVirtualMachinePoolsClient

id, err := standbyvirtualmachinepools.ParseStandbyVirtualMachinePoolID(metadata.ResourceData.Id())
if err != nil {
return err
}

resp, err := client.Get(ctx, *id)
if err != nil {
if response.WasNotFound(resp.HttpResponse) {
return metadata.MarkAsGone(id)
}

return fmt.Errorf("retrieving %s: %+v", *id, err)
}

state := VirtualMachineScaleSetStandbyPoolModel{
Name: id.StandbyVirtualMachinePoolName,
ResourceGroupName: id.ResourceGroupName,
}

if model := resp.Model; model != nil {
state.Location = location.Normalize(model.Location)
if properties := model.Properties; properties != nil {
parsedAttachedVirtualMachineScaleSetId, err := virtualmachinescalesets.ParseVirtualMachineScaleSetIDInsensitively(pointer.From(properties.AttachedVirtualMachineScaleSetId))
if err != nil {
return fmt.Errorf("parsing `attached_virtual_machine_scale_set_id` for %s: %+v", *id, err)
}

state.AttachedVirtualMachineScaleSetId = parsedAttachedVirtualMachineScaleSetId.ID()
state.ElasticityProfile = flattenStandbyVirtualMachinePoolElasticityProfileModel(properties.ElasticityProfile)
state.VirtualMachineState = properties.VirtualMachineState
}

state.Tags = pointer.From(model.Tags)
}

return metadata.Encode(&state)
},
}
}

func (r VirtualMachineScaleSetStandbyPoolResource) Delete() sdk.ResourceFunc {
return sdk.ResourceFunc{
Timeout: 30 * time.Minute,
Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error {
client := metadata.Client.Compute.StandbyVirtualMachinePoolsClient

id, err := standbyvirtualmachinepools.ParseStandbyVirtualMachinePoolID(metadata.ResourceData.Id())
if err != nil {
return err
}

if err := client.DeleteThenPoll(ctx, *id); err != nil {
return fmt.Errorf("deleting %s: %+v", id, err)
}

return nil
},
}
}

func expandStandbyVirtualMachinePoolElasticityProfileModel(inputList []VirtualMachineScaleSetStandbyPoolElasticityProfileModel) *standbyvirtualmachinepools.StandbyVirtualMachinePoolElasticityProfile {
if len(inputList) == 0 {
return nil
}

input := &inputList[0]
output := standbyvirtualmachinepools.StandbyVirtualMachinePoolElasticityProfile{
MaxReadyCapacity: input.MaxReadyCapacity,
MinReadyCapacity: pointer.To(input.MinReadyCapacity),
}

return &output
}

func flattenStandbyVirtualMachinePoolElasticityProfileModel(input *standbyvirtualmachinepools.StandbyVirtualMachinePoolElasticityProfile) []VirtualMachineScaleSetStandbyPoolElasticityProfileModel {
outputList := make([]VirtualMachineScaleSetStandbyPoolElasticityProfileModel, 0)
if input == nil {
return outputList
}

output := VirtualMachineScaleSetStandbyPoolElasticityProfileModel{
MaxReadyCapacity: input.MaxReadyCapacity,
MinReadyCapacity: pointer.From(input.MinReadyCapacity),
}

return append(outputList, output)
}
Loading
Loading