Skip to content

Commit

Permalink
Merge pull request #6713 from njuCZ/maintenance_assignment
Browse files Browse the repository at this point in the history
  • Loading branch information
jackofallops committed Jun 18, 2020
2 parents 826b39c + 11f0305 commit 7589dba
Show file tree
Hide file tree
Showing 14 changed files with 1,245 additions and 3 deletions.
9 changes: 7 additions & 2 deletions azurerm/internal/services/maintenance/client/client.go
Expand Up @@ -6,14 +6,19 @@ import (
)

type Client struct {
ConfigurationsClient *maintenance.ConfigurationsClient
ConfigurationsClient *maintenance.ConfigurationsClient
ConfigurationAssignmentsClient *maintenance.ConfigurationAssignmentsClient
}

func NewClient(o *common.ClientOptions) *Client {
configurationsClient := maintenance.NewConfigurationsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId)
o.ConfigureClient(&configurationsClient.Client, o.ResourceManagerAuthorizer)

configurationAssignmentsClient := maintenance.NewConfigurationAssignmentsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId)
o.ConfigureClient(&configurationAssignmentsClient.Client, o.ResourceManagerAuthorizer)

return &Client{
ConfigurationsClient: &configurationsClient,
ConfigurationsClient: &configurationsClient,
ConfigurationAssignmentsClient: &configurationAssignmentsClient,
}
}
@@ -0,0 +1,180 @@
package maintenance

import (
"context"
"fmt"
"strings"
"time"

"github.com/Azure/azure-sdk-for-go/services/preview/maintenance/mgmt/2018-06-01-preview/maintenance"
"github.com/hashicorp/terraform-plugin-sdk/helper/resource"
"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/suppress"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/clients"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/location"
parseCompute "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/compute/parse"
validateCompute "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/compute/validate"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/maintenance/parse"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/maintenance/validate"
azSchema "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tf/schema"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/timeouts"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils"
)

func resourceArmMaintenanceAssignmentDedicatedHost() *schema.Resource {
return &schema.Resource{
Create: resourceArmMaintenanceAssignmentDedicatedHostCreate,
Read: resourceArmMaintenanceAssignmentDedicatedHostRead,
Delete: resourceArmMaintenanceAssignmentDedicatedHostDelete,

Timeouts: &schema.ResourceTimeout{
Create: schema.DefaultTimeout(30 * time.Minute),
Read: schema.DefaultTimeout(5 * time.Minute),
Delete: schema.DefaultTimeout(30 * time.Minute),
},

Importer: azSchema.ValidateResourceIDPriorToImport(func(id string) error {
_, err := parse.MaintenanceAssignmentDedicatedHostID(id)
return err
}),

Schema: map[string]*schema.Schema{
"location": azure.SchemaLocation(),

"maintenance_configuration_id": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
ValidateFunc: validate.MaintenanceConfigurationID,
},

"dedicated_host_id": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
ValidateFunc: validateCompute.DedicatedHostID,
DiffSuppressFunc: suppress.CaseDifference,
},
},
}
}

func resourceArmMaintenanceAssignmentDedicatedHostCreate(d *schema.ResourceData, meta interface{}) error {
client := meta.(*clients.Client).Maintenance.ConfigurationAssignmentsClient
ctx, cancel := timeouts.ForCreate(meta.(*clients.Client).StopContext, d)
defer cancel()

dedicatedHostIdRaw := d.Get("dedicated_host_id").(string)
dedicatedHostId, _ := parseCompute.DedicatedHostID(dedicatedHostIdRaw)

existing, err := getMaintenanceAssignmentDedicatedHost(ctx, client, dedicatedHostId, dedicatedHostIdRaw)
if err != nil {
return err
}
if existing.ID != nil && *existing.ID != "" {
return tf.ImportAsExistsError("azurerm_maintenance_assignment_dedicated_host", *existing.ID)
}

maintenanceConfigurationID := d.Get("maintenance_configuration_id").(string)
configurationId, _ := parse.MaintenanceConfigurationID(maintenanceConfigurationID)

// set assignment name to configuration name
assignmentName := configurationId.Name
assignment := maintenance.ConfigurationAssignment{
Name: utils.String(assignmentName),
Location: utils.String(location.Normalize(d.Get("location").(string))),
ConfigurationAssignmentProperties: &maintenance.ConfigurationAssignmentProperties{
MaintenanceConfigurationID: utils.String(maintenanceConfigurationID),
ResourceID: utils.String(dedicatedHostIdRaw),
},
}

// It may take a few minutes after starting a VM for it to become available to assign to a configuration
err = resource.Retry(d.Timeout(schema.TimeoutCreate), func() *resource.RetryError {
if _, err := client.CreateOrUpdateParent(ctx, dedicatedHostId.ResourceGroup, "Microsoft.Compute", "hostGroups", dedicatedHostId.HostGroup, "hosts", dedicatedHostId.Name, assignmentName, assignment); err != nil {
if strings.Contains(err.Error(), "It may take a few minutes after starting a VM for it to become available to assign to a configuration") {
return resource.RetryableError(fmt.Errorf("expected VM is available to assign to a configuration but was in pending state, retrying"))
}
return resource.NonRetryableError(fmt.Errorf("issuing creating request for Maintenance Assignment (Dedicated Host ID %q): %+v", dedicatedHostIdRaw, err))
}

return nil
})
if err != nil {
return err
}

resp, err := getMaintenanceAssignmentDedicatedHost(ctx, client, dedicatedHostId, dedicatedHostIdRaw)
if err != nil {
return err
}
if resp.ID == nil || *resp.ID == "" {
return fmt.Errorf("empty or nil ID of Maintenance Assignment (Dedicated Host ID %q)", dedicatedHostIdRaw)
}

d.SetId(*resp.ID)
return resourceArmMaintenanceAssignmentDedicatedHostRead(d, meta)
}

func resourceArmMaintenanceAssignmentDedicatedHostRead(d *schema.ResourceData, meta interface{}) error {
client := meta.(*clients.Client).Maintenance.ConfigurationAssignmentsClient
ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d)
defer cancel()

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

assignment, err := getMaintenanceAssignmentDedicatedHost(ctx, client, id.DedicatedHostId, id.DedicatedHostIdRaw)
if err != nil {
return err
}
if assignment.ID == nil || *assignment.ID == "" {
return fmt.Errorf("empty or nil ID of Maintenance Assignment (Dedicated Host ID: %q", id.DedicatedHostIdRaw)
}

// in list api, `ResourceID` returned is always nil
d.Set("dedicated_host_id", id.DedicatedHostIdRaw)
if props := assignment.ConfigurationAssignmentProperties; props != nil {
d.Set("maintenance_configuration_id", props.MaintenanceConfigurationID)
}
return nil
}

func resourceArmMaintenanceAssignmentDedicatedHostDelete(d *schema.ResourceData, meta interface{}) error {
client := meta.(*clients.Client).Maintenance.ConfigurationAssignmentsClient
ctx, cancel := timeouts.ForDelete(meta.(*clients.Client).StopContext, d)
defer cancel()

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

if _, err := client.DeleteParent(ctx, id.DedicatedHostId.ResourceGroup, "Microsoft.Compute", "hostGroups", id.DedicatedHostId.HostGroup, "hosts", id.DedicatedHostId.Name, id.Name); err != nil {
return fmt.Errorf("deleting Maintenance Assignment to resource %q: %+v", id.DedicatedHostIdRaw, err)
}

return nil
}

func getMaintenanceAssignmentDedicatedHost(ctx context.Context, client *maintenance.ConfigurationAssignmentsClient, id *parseCompute.DedicatedHostId, dedicatedHostId string) (result maintenance.ConfigurationAssignment, err error) {
resp, err := client.ListParent(ctx, id.ResourceGroup, "Microsoft.Compute", "hostGroups", id.HostGroup, "hosts", id.Name)

if err != nil {
if !utils.ResponseWasNotFound(resp.Response) {
err = fmt.Errorf("checking for presence of existing Maintenance assignment (Dedicated Host ID %q): %+v", dedicatedHostId, err)
return
}
return result, nil
}
if resp.Value == nil || len(*resp.Value) == 0 {
err = fmt.Errorf("could not find Maintenance assignment (Dedicated Host ID %q)", dedicatedHostId)
return
}

return (*resp.Value)[0], nil
}
@@ -0,0 +1,180 @@
package maintenance

import (
"context"
"fmt"
"strings"
"time"

"github.com/Azure/azure-sdk-for-go/services/preview/maintenance/mgmt/2018-06-01-preview/maintenance"
"github.com/hashicorp/terraform-plugin-sdk/helper/resource"
"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/suppress"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/clients"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/location"
parseCompute "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/compute/parse"
validateCompute "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/compute/validate"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/maintenance/parse"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/maintenance/validate"
azSchema "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tf/schema"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/timeouts"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils"
)

func resourceArmMaintenanceAssignmentVirtualMachine() *schema.Resource {
return &schema.Resource{
Create: resourceArmMaintenanceAssignmentVirtualMachineCreate,
Read: resourceArmMaintenanceAssignmentVirtualMachineRead,
Delete: resourceArmMaintenanceAssignmentVirtualMachineDelete,

Timeouts: &schema.ResourceTimeout{
Create: schema.DefaultTimeout(30 * time.Minute),
Read: schema.DefaultTimeout(5 * time.Minute),
Delete: schema.DefaultTimeout(30 * time.Minute),
},

Importer: azSchema.ValidateResourceIDPriorToImport(func(id string) error {
_, err := parse.MaintenanceAssignmentVirtualMachineID(id)
return err
}),

Schema: map[string]*schema.Schema{
"location": azure.SchemaLocation(),

"maintenance_configuration_id": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
ValidateFunc: validate.MaintenanceConfigurationID,
},

"virtual_machine_id": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
ValidateFunc: validateCompute.VirtualMachineID,
DiffSuppressFunc: suppress.CaseDifference,
},
},
}
}

func resourceArmMaintenanceAssignmentVirtualMachineCreate(d *schema.ResourceData, meta interface{}) error {
client := meta.(*clients.Client).Maintenance.ConfigurationAssignmentsClient
ctx, cancel := timeouts.ForCreate(meta.(*clients.Client).StopContext, d)
defer cancel()

virtualMachineIdRaw := d.Get("virtual_machine_id").(string)
virtualMachineId, _ := parseCompute.VirtualMachineID(virtualMachineIdRaw)

existing, err := getMaintenanceAssignmentVirtualMachine(ctx, client, virtualMachineId, virtualMachineIdRaw)
if err != nil {
return err
}
if existing.ID != nil && *existing.ID != "" {
return tf.ImportAsExistsError("azurerm_maintenance_assignment_virtual_machine", *existing.ID)
}

maintenanceConfigurationID := d.Get("maintenance_configuration_id").(string)
configurationId, _ := parse.MaintenanceConfigurationID(maintenanceConfigurationID)

// set assignment name to configuration name
assignmentName := configurationId.Name
assignment := maintenance.ConfigurationAssignment{
Name: utils.String(assignmentName),
Location: utils.String(location.Normalize(d.Get("location").(string))),
ConfigurationAssignmentProperties: &maintenance.ConfigurationAssignmentProperties{
MaintenanceConfigurationID: utils.String(maintenanceConfigurationID),
ResourceID: utils.String(virtualMachineIdRaw),
},
}

// It may take a few minutes after starting a VM for it to become available to assign to a configuration
err = resource.Retry(d.Timeout(schema.TimeoutCreate), func() *resource.RetryError {
if _, err := client.CreateOrUpdate(ctx, virtualMachineId.ResourceGroup, "Microsoft.Compute", "virtualMachines", virtualMachineId.Name, assignmentName, assignment); err != nil {
if strings.Contains(err.Error(), "It may take a few minutes after starting a VM for it to become available to assign to a configuration") {
return resource.RetryableError(fmt.Errorf("expected VM is available to assign to a configuration but was in pending state, retrying"))
}
return resource.NonRetryableError(fmt.Errorf("issuing creating request for Maintenance Assignment (virtual machine ID %q): %+v", virtualMachineIdRaw, err))
}

return nil
})
if err != nil {
return err
}

resp, err := getMaintenanceAssignmentVirtualMachine(ctx, client, virtualMachineId, virtualMachineIdRaw)
if err != nil {
return err
}
if resp.ID == nil || *resp.ID == "" {
return fmt.Errorf("empty or nil ID of Maintenance Assignment (virtual machine ID %q)", virtualMachineIdRaw)
}

d.SetId(*resp.ID)
return resourceArmMaintenanceAssignmentVirtualMachineRead(d, meta)
}

func resourceArmMaintenanceAssignmentVirtualMachineRead(d *schema.ResourceData, meta interface{}) error {
client := meta.(*clients.Client).Maintenance.ConfigurationAssignmentsClient
ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d)
defer cancel()

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

assignment, err := getMaintenanceAssignmentVirtualMachine(ctx, client, id.VirtualMachineId, id.VirtualMachineIdRaw)
if err != nil {
return err
}
if assignment.ID == nil || *assignment.ID == "" {
return fmt.Errorf("empty or nil ID of Maintenance Assignment (virtual machine ID id: %q", id.VirtualMachineIdRaw)
}

// in list api, `ResourceID` returned is always nil
d.Set("virtual_machine_id", id.VirtualMachineIdRaw)
if props := assignment.ConfigurationAssignmentProperties; props != nil {
d.Set("maintenance_configuration_id", props.MaintenanceConfigurationID)
}
return nil
}

func resourceArmMaintenanceAssignmentVirtualMachineDelete(d *schema.ResourceData, meta interface{}) error {
client := meta.(*clients.Client).Maintenance.ConfigurationAssignmentsClient
ctx, cancel := timeouts.ForDelete(meta.(*clients.Client).StopContext, d)
defer cancel()

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

if _, err := client.Delete(ctx, id.VirtualMachineId.ResourceGroup, "Microsoft.Compute", "virtualMachines", id.VirtualMachineId.Name, id.Name); err != nil {
return fmt.Errorf("deleting Maintenance Assignment to resource %q: %+v", id.VirtualMachineIdRaw, err)
}

return nil
}

func getMaintenanceAssignmentVirtualMachine(ctx context.Context, client *maintenance.ConfigurationAssignmentsClient, id *parseCompute.VirtualMachineId, virtualMachineId string) (result maintenance.ConfigurationAssignment, err error) {
resp, err := client.List(ctx, id.ResourceGroup, "Microsoft.Compute", "virtualMachines", id.Name)

if err != nil {
if !utils.ResponseWasNotFound(resp.Response) {
err = fmt.Errorf("checking for presence of existing Maintenance assignment (virtual machine ID: %q): %+v", virtualMachineId, err)
return
}
return result, nil
}
if resp.Value == nil || len(*resp.Value) == 0 {
err = fmt.Errorf("could not find Maintenance assignment (virtual machine ID: %q)", virtualMachineId)
return
}

return (*resp.Value)[0], nil
}

0 comments on commit 7589dba

Please sign in to comment.