1// +build go1.7
2
3// Package virtualmachine provides a client for Virtual Machines.
4package virtualmachine
5
6// Copyright (c) Microsoft Corporation. All rights reserved.
7// Licensed under the MIT License. See License.txt in the project root for license information.
8
9import (
10	"encoding/xml"
11	"fmt"
12
13	"github.com/Azure/azure-sdk-for-go/services/classic/management"
14)
15
16const (
17	azureDeploymentListURL                    = "services/hostedservices/%s/deployments"
18	azureDeploymentURL                        = "services/hostedservices/%s/deployments/%s"
19	azureUpdateDeploymentURL                  = "services/hostedservices/%s/deployments/%s?comp=%s"
20	azureDeploymentSlotSwapURL                = "services/hostedservices/%s"
21	azureDeploymentSlotURL                    = "services/hostedservices/%s/deploymentslots/%s"
22	azureUpdateDeploymentSlotConfigurationURL = "services/hostedservices/%s/deploymentslots/%s?comp=%s"
23	deleteAzureDeploymentURL                  = "services/hostedservices/%s/deployments/%s?comp=media"
24	azureDeleteDeploymentBySlotURL            = "services/hostedservices/%s/deploymentslots/%s"
25	azureAddRoleURL                           = "services/hostedservices/%s/deployments/%s/roles"
26	azureRoleURL                              = "services/hostedservices/%s/deployments/%s/roles/%s"
27	azureOperationsURL                        = "services/hostedservices/%s/deployments/%s/roleinstances/%s/Operations"
28	azureRoleSizeListURL                      = "rolesizes"
29
30	errParamNotSpecified = "Parameter %s is not specified."
31)
32
33//NewClient is used to instantiate a new VirtualMachineClient from an Azure client
34func NewClient(client management.Client) VirtualMachineClient {
35	return VirtualMachineClient{client: client}
36}
37
38// CreateDeploymentOptions can be used to create a customized deployement request
39type CreateDeploymentOptions struct {
40	DNSServers         []DNSServer
41	LoadBalancers      []LoadBalancer
42	ReservedIPName     string
43	VirtualNetworkName string
44}
45
46// CreateDeployment creates a deployment and then creates a virtual machine
47// in the deployment based on the specified configuration.
48//
49// https://msdn.microsoft.com/en-us/library/azure/jj157194.aspx
50func (vm VirtualMachineClient) CreateDeployment(
51	role Role,
52	cloudServiceName string,
53	options CreateDeploymentOptions) (management.OperationID, error) {
54
55	req := DeploymentRequest{
56		Name:               role.RoleName,
57		DeploymentSlot:     "Production",
58		Label:              role.RoleName,
59		RoleList:           []Role{role},
60		DNSServers:         options.DNSServers,
61		LoadBalancers:      options.LoadBalancers,
62		ReservedIPName:     options.ReservedIPName,
63		VirtualNetworkName: options.VirtualNetworkName,
64	}
65
66	data, err := xml.Marshal(req)
67	if err != nil {
68		return "", err
69	}
70
71	requestURL := fmt.Sprintf(azureDeploymentListURL, cloudServiceName)
72	return vm.client.SendAzurePostRequest(requestURL, data)
73}
74
75// CreateDeploymentFromPackageOptions can be used to create a customized deployement request
76type CreateDeploymentFromPackageOptions struct {
77	Name                   string
78	PackageURL             string
79	Label                  string
80	Configuration          string
81	StartDeployment        bool
82	TreatWarningsAsError   bool
83	ExtendedProperties     []ExtendedProperty
84	ExtensionConfiguration ExtensionConfiguration
85}
86
87// CreateDeploymentRequest is the type for creating a deployment of a cloud service package
88// in the deployment based on the specified configuration. See
89// https://docs.microsoft.com/en-us/rest/api/compute/cloudservices/rest-create-deployment
90type CreateDeploymentRequest struct {
91	XMLName xml.Name `xml:"http://schemas.microsoft.com/windowsazure CreateDeployment"`
92	// Required parameters:
93	Name          string ``                 // Specifies the name of the deployment.
94	PackageURL    string `xml:"PackageUrl"` // Specifies a URL that refers to the location of the service package in the Blob service. The service package can be located either in a storage account beneath the same subscription or a Shared Access Signature (SAS) URI from any storage account.
95	Label         string ``                 // Specifies an identifier for the deployment that is base-64 encoded. The identifier can be up to 100 characters in length. It is recommended that the label be unique within the subscription. The label can be used for your tracking purposes.
96	Configuration string ``                 // Specifies the base-64 encoded service configuration file for the deployment.
97	// Optional parameters:
98	StartDeployment        bool                   ``                                  // Indicates whether to start the deployment immediately after it is created. The default value is false
99	TreatWarningsAsError   bool                   ``                                  // Indicates whether to treat package validation warnings as errors. The default value is false. If set to true, the Created Deployment operation fails if there are validation warnings on the service package.
100	ExtendedProperties     []ExtendedProperty     `xml:">ExtendedProperty,omitempty"` // Array of ExtendedProprties. Each extended property must have both a defined name and value. You can have a maximum of 25 extended property name and value pairs.
101	ExtensionConfiguration ExtensionConfiguration `xml:",omitempty"`
102}
103
104// CreateDeploymentFromPackage creates a deployment from a cloud services package (.cspkg) and configuration file (.cscfg)
105func (vm VirtualMachineClient) CreateDeploymentFromPackage(
106	cloudServiceName string,
107	deploymentSlot DeploymentSlot,
108	options CreateDeploymentFromPackageOptions) (management.OperationID, error) {
109
110	if cloudServiceName == "" {
111		return "", fmt.Errorf(errParamNotSpecified, "cloudServiceName")
112	}
113
114	req := CreateDeploymentRequest{
115		Name:                   options.Name,
116		Label:                  options.Label,
117		Configuration:          options.Configuration,
118		PackageURL:             options.PackageURL,
119		StartDeployment:        options.StartDeployment,
120		TreatWarningsAsError:   options.TreatWarningsAsError,
121		ExtendedProperties:     options.ExtendedProperties,
122		ExtensionConfiguration: options.ExtensionConfiguration,
123	}
124
125	data, err := xml.Marshal(req)
126	if err != nil {
127		return "", err
128	}
129
130	requestURL := fmt.Sprintf(azureDeploymentSlotURL, cloudServiceName, deploymentSlot)
131	return vm.client.SendAzurePostRequest(requestURL, data)
132}
133
134// SwapDeploymentRequest is the type used for specifying information to swap the deployments in
135// a cloud service
136// https://docs.microsoft.com/en-us/rest/api/compute/cloudservices/rest-swap-deployment
137type SwapDeploymentRequest struct {
138	XMLName xml.Name `xml:"http://schemas.microsoft.com/windowsazure Swap"`
139	// Required parameters:
140	Production       string
141	SourceDeployment string
142}
143
144// SwapDeployment initiates a virtual IP address swap between the staging and production deployment environments for a service.
145// If the service is currently running in the staging environment, it will be swapped to the production environment.
146// If it is running in the production environment, it will be swapped to staging.
147func (vm VirtualMachineClient) SwapDeployment(
148	cloudServiceName string) (management.OperationID, error) {
149
150	if cloudServiceName == "" {
151		return "", fmt.Errorf(errParamNotSpecified, "cloudServiceName")
152	}
153
154	productionDeploymentName, err := vm.GetDeploymentNameForSlot(cloudServiceName, DeploymentSlotProduction)
155	if err != nil {
156		return "", err
157	}
158
159	stagingDeploymentName, err := vm.GetDeploymentNameForSlot(cloudServiceName, DeploymentSlotStaging)
160	if err != nil {
161		return "", err
162	}
163
164	req := SwapDeploymentRequest{
165		Production:       productionDeploymentName,
166		SourceDeployment: stagingDeploymentName,
167	}
168
169	data, err := xml.Marshal(req)
170	if err != nil {
171		return "", err
172	}
173
174	requestURL := fmt.Sprintf(azureDeploymentSlotSwapURL, cloudServiceName)
175	return vm.client.SendAzurePostRequest(requestURL, data)
176}
177
178// ChangeDeploymentConfigurationRequestOptions can be used to update configuration of a deployment
179type ChangeDeploymentConfigurationRequestOptions struct {
180	Mode                   UpgradeType
181	Configuration          string
182	TreatWarningsAsError   bool
183	ExtendedProperties     []ExtendedProperty
184	ExtensionConfiguration ExtensionConfiguration
185}
186
187// ChangeDeploymentConfigurationRequest is the type for changing the configuration of a deployment of a cloud service p
188// https://docs.microsoft.com/en-us/rest/api/compute/cloudservices/rest-change-deployment-configuration
189type ChangeDeploymentConfigurationRequest struct {
190	XMLName xml.Name `xml:"http://schemas.microsoft.com/windowsazure ChangeConfiguration"`
191	// Required parameters:
192	Configuration string `` // Specifies the base-64 encoded service configuration file for the deployment.
193	// Optional parameters:
194	Mode                   UpgradeType            ``                                  // Specifies the type of Upgrade (Auto | Manual | Simultaneous) .
195	TreatWarningsAsError   bool                   ``                                  // Indicates whether to treat package validation warnings as errors. The default value is false. If set to true, the Created Deployment operation fails if there are validation warnings on the service package.
196	ExtendedProperties     []ExtendedProperty     `xml:">ExtendedProperty,omitempty"` // Array of ExtendedProprties. Each extended property must have both a defined name and value. You can have a maximum of 25 extended property name and value pairs.
197	ExtensionConfiguration ExtensionConfiguration `xml:",omitempty"`
198}
199
200// ChangeDeploymentConfiguration updates the configuration for a deployment from a configuration file (.cscfg)
201func (vm VirtualMachineClient) ChangeDeploymentConfiguration(
202	cloudServiceName string,
203	deploymentSlot DeploymentSlot,
204	options ChangeDeploymentConfigurationRequestOptions) (management.OperationID, error) {
205
206	if cloudServiceName == "" {
207		return "", fmt.Errorf(errParamNotSpecified, "cloudServiceName")
208	}
209
210	req := ChangeDeploymentConfigurationRequest{
211		Mode:                   options.Mode,
212		Configuration:          options.Configuration,
213		TreatWarningsAsError:   options.TreatWarningsAsError,
214		ExtendedProperties:     options.ExtendedProperties,
215		ExtensionConfiguration: options.ExtensionConfiguration,
216	}
217	if req.Mode == "" {
218		req.Mode = UpgradeTypeAuto
219	}
220
221	data, err := xml.Marshal(req)
222	if err != nil {
223		return "", err
224	}
225
226	requestURL := fmt.Sprintf(azureUpdateDeploymentSlotConfigurationURL, cloudServiceName, deploymentSlot, "config")
227	return vm.client.SendAzurePostRequest(requestURL, data)
228}
229
230// UpdateDeploymentStatusRequest is the type used to make UpdateDeploymentStatus requests
231type UpdateDeploymentStatusRequest struct {
232	XMLName xml.Name `xml:"http://schemas.microsoft.com/windowsazure UpdateDeploymentStatus"`
233	// Required parameters:
234	Status string
235}
236
237// UpdateDeploymentStatus changes the running status of a deployment. The status of a deployment can be running or suspended.
238// https://docs.microsoft.com/en-us/rest/api/compute/cloudservices/rest-update-deployment-status
239func (vm VirtualMachineClient) UpdateDeploymentStatus(
240	cloudServiceName string,
241	deploymentSlot DeploymentSlot,
242	status string) (management.OperationID, error) {
243
244	if cloudServiceName == "" {
245		return "", fmt.Errorf(errParamNotSpecified, "cloudServiceName")
246	}
247
248	if status != "Running" && status != "Suspended" {
249		return "", fmt.Errorf("Invalid status provided")
250	}
251
252	req := UpdateDeploymentStatusRequest{
253		Status: status,
254	}
255
256	data, err := xml.Marshal(req)
257	if err != nil {
258		return "", err
259	}
260
261	requestURL := fmt.Sprintf(azureUpdateDeploymentSlotConfigurationURL, cloudServiceName, deploymentSlot, "status")
262	return vm.client.SendAzurePostRequest(requestURL, data)
263}
264
265// UpdateDeploymentStatusByName changes the running status of a deployment. The status of a deployment can be running or suspended.
266// https://docs.microsoft.com/en-us/rest/api/compute/cloudservices/rest-update-deployment-status
267func (vm VirtualMachineClient) UpdateDeploymentStatusByName(
268	cloudServiceName string,
269	deploymentName string,
270	status string) (management.OperationID, error) {
271
272	if cloudServiceName == "" {
273		return "", fmt.Errorf(errParamNotSpecified, "cloudServiceName")
274	}
275
276	if status != "Running" && status != "Suspended" {
277		return "", fmt.Errorf("Invalid status provided")
278	}
279
280	req := UpdateDeploymentStatusRequest{
281		Status: status,
282	}
283
284	data, err := xml.Marshal(req)
285	if err != nil {
286		return "", err
287	}
288
289	requestURL := fmt.Sprintf(azureUpdateDeploymentURL, cloudServiceName, deploymentName, "status")
290	return vm.client.SendAzurePostRequest(requestURL, data)
291}
292
293// GetDeploymentName queries an existing Azure cloud service for the name of the Deployment,
294// if any, in its 'Production' slot (the only slot possible). If none exists, it returns empty
295// string but no error
296//
297//https://msdn.microsoft.com/en-us/library/azure/ee460804.aspx
298func (vm VirtualMachineClient) GetDeploymentName(cloudServiceName string) (string, error) {
299	return vm.GetDeploymentNameForSlot(cloudServiceName, DeploymentSlotProduction)
300}
301
302// GetDeploymentNameForSlot queries an existing Azure cloud service for the name of the Deployment,
303// in a given slot. If none exists, it returns empty
304// string but no error
305//
306//https://msdn.microsoft.com/en-us/library/azure/ee460804.aspx
307func (vm VirtualMachineClient) GetDeploymentNameForSlot(cloudServiceName string, deploymentSlot DeploymentSlot) (string, error) {
308	var deployment DeploymentResponse
309	if cloudServiceName == "" {
310		return "", fmt.Errorf(errParamNotSpecified, "cloudServiceName")
311	}
312	requestURL := fmt.Sprintf(azureDeploymentSlotURL, cloudServiceName, deploymentSlot)
313	response, err := vm.client.SendAzureGetRequest(requestURL)
314	if err != nil {
315		if management.IsResourceNotFoundError(err) {
316			return "", nil
317		}
318		return "", err
319	}
320	err = xml.Unmarshal(response, &deployment)
321	if err != nil {
322		return "", err
323	}
324
325	return deployment.Name, nil
326}
327
328func (vm VirtualMachineClient) GetDeployment(cloudServiceName, deploymentName string) (DeploymentResponse, error) {
329	var deployment DeploymentResponse
330	if cloudServiceName == "" {
331		return deployment, fmt.Errorf(errParamNotSpecified, "cloudServiceName")
332	}
333	if deploymentName == "" {
334		return deployment, fmt.Errorf(errParamNotSpecified, "deploymentName")
335	}
336	requestURL := fmt.Sprintf(azureDeploymentURL, cloudServiceName, deploymentName)
337	response, azureErr := vm.client.SendAzureGetRequest(requestURL)
338	if azureErr != nil {
339		return deployment, azureErr
340	}
341
342	err := xml.Unmarshal(response, &deployment)
343	return deployment, err
344}
345
346// GetDeploymentBySlot used to retrieve deployment events for a single deployment slot (staging or production)
347func (vm VirtualMachineClient) GetDeploymentBySlot(cloudServiceName string, deploymentSlot DeploymentSlot) (DeploymentResponse, error) {
348	var deployment DeploymentResponse
349	if cloudServiceName == "" {
350		return deployment, fmt.Errorf(errParamNotSpecified, "cloudServiceName")
351	}
352	if deploymentSlot == "" {
353		return deployment, fmt.Errorf(errParamNotSpecified, "deploymentSlot")
354	}
355	requestURL := fmt.Sprintf(azureDeploymentSlotURL, cloudServiceName, deploymentSlot)
356	response, azureErr := vm.client.SendAzureGetRequest(requestURL)
357	if azureErr != nil {
358		return deployment, azureErr
359	}
360
361	err := xml.Unmarshal(response, &deployment)
362	return deployment, err
363}
364
365func (vm VirtualMachineClient) DeleteDeployment(cloudServiceName, deploymentName string) (management.OperationID, error) {
366	if cloudServiceName == "" {
367		return "", fmt.Errorf(errParamNotSpecified, "cloudServiceName")
368	}
369	if deploymentName == "" {
370		return "", fmt.Errorf(errParamNotSpecified, "deploymentName")
371	}
372
373	requestURL := fmt.Sprintf(deleteAzureDeploymentURL, cloudServiceName, deploymentName)
374	return vm.client.SendAzureDeleteRequest(requestURL)
375}
376
377func (vm VirtualMachineClient) DeleteDeploymentBySlot(cloudServiceName string, deploymentSlot DeploymentSlot) (management.OperationID, error) {
378	if cloudServiceName == "" {
379		return "", fmt.Errorf(errParamNotSpecified, "cloudServiceName")
380	}
381	if deploymentSlot == "" {
382		return "", fmt.Errorf(errParamNotSpecified, "deploymentSlot")
383	}
384
385	requestURL := fmt.Sprintf(azureDeleteDeploymentBySlotURL, cloudServiceName, deploymentSlot)
386	return vm.client.SendAzureDeleteRequest(requestURL)
387}
388
389func (vm VirtualMachineClient) GetRole(cloudServiceName, deploymentName, roleName string) (*Role, error) {
390	if cloudServiceName == "" {
391		return nil, fmt.Errorf(errParamNotSpecified, "cloudServiceName")
392	}
393	if deploymentName == "" {
394		return nil, fmt.Errorf(errParamNotSpecified, "deploymentName")
395	}
396	if roleName == "" {
397		return nil, fmt.Errorf(errParamNotSpecified, "roleName")
398	}
399
400	role := new(Role)
401
402	requestURL := fmt.Sprintf(azureRoleURL, cloudServiceName, deploymentName, roleName)
403	response, azureErr := vm.client.SendAzureGetRequest(requestURL)
404	if azureErr != nil {
405		return nil, azureErr
406	}
407
408	err := xml.Unmarshal(response, role)
409	if err != nil {
410		return nil, err
411	}
412
413	return role, nil
414}
415
416// AddRole adds a Virtual Machine to a deployment of Virtual Machines, where role name = VM name
417// See https://msdn.microsoft.com/en-us/library/azure/jj157186.aspx
418func (vm VirtualMachineClient) AddRole(cloudServiceName string, deploymentName string, role Role) (management.OperationID, error) {
419	if cloudServiceName == "" {
420		return "", fmt.Errorf(errParamNotSpecified, "cloudServiceName")
421	}
422	if deploymentName == "" {
423		return "", fmt.Errorf(errParamNotSpecified, "deploymentName")
424	}
425
426	data, err := xml.Marshal(PersistentVMRole{Role: role})
427	if err != nil {
428		return "", err
429	}
430
431	requestURL := fmt.Sprintf(azureAddRoleURL, cloudServiceName, deploymentName)
432	return vm.client.SendAzurePostRequest(requestURL, data)
433}
434
435// UpdateRole updates the configuration of the specified virtual machine
436// See https://msdn.microsoft.com/en-us/library/azure/jj157187.aspx
437func (vm VirtualMachineClient) UpdateRole(cloudServiceName, deploymentName, roleName string, role Role) (management.OperationID, error) {
438	if cloudServiceName == "" {
439		return "", fmt.Errorf(errParamNotSpecified, "cloudServiceName")
440	}
441	if deploymentName == "" {
442		return "", fmt.Errorf(errParamNotSpecified, "deploymentName")
443	}
444	if roleName == "" {
445		return "", fmt.Errorf(errParamNotSpecified, "roleName")
446	}
447
448	data, err := xml.Marshal(PersistentVMRole{Role: role})
449	if err != nil {
450		return "", err
451	}
452
453	requestURL := fmt.Sprintf(azureRoleURL, cloudServiceName, deploymentName, roleName)
454	return vm.client.SendAzurePutRequest(requestURL, "text/xml", data)
455}
456
457func (vm VirtualMachineClient) StartRole(cloudServiceName, deploymentName, roleName string) (management.OperationID, error) {
458	if cloudServiceName == "" {
459		return "", fmt.Errorf(errParamNotSpecified, "cloudServiceName")
460	}
461	if deploymentName == "" {
462		return "", fmt.Errorf(errParamNotSpecified, "deploymentName")
463	}
464	if roleName == "" {
465		return "", fmt.Errorf(errParamNotSpecified, "roleName")
466	}
467
468	startRoleOperationBytes, err := xml.Marshal(StartRoleOperation{
469		OperationType: "StartRoleOperation",
470	})
471	if err != nil {
472		return "", err
473	}
474
475	requestURL := fmt.Sprintf(azureOperationsURL, cloudServiceName, deploymentName, roleName)
476	return vm.client.SendAzurePostRequest(requestURL, startRoleOperationBytes)
477}
478
479func (vm VirtualMachineClient) ShutdownRole(cloudServiceName, deploymentName, roleName string, postaction PostShutdownAction) (management.OperationID, error) {
480	if cloudServiceName == "" {
481		return "", fmt.Errorf(errParamNotSpecified, "cloudServiceName")
482	}
483	if deploymentName == "" {
484		return "", fmt.Errorf(errParamNotSpecified, "deploymentName")
485	}
486	if roleName == "" {
487		return "", fmt.Errorf(errParamNotSpecified, "roleName")
488	}
489
490	shutdownRoleOperationBytes, err := xml.Marshal(ShutdownRoleOperation{
491		OperationType:      "ShutdownRoleOperation",
492		PostShutdownAction: postaction,
493	})
494	if err != nil {
495		return "", err
496	}
497
498	requestURL := fmt.Sprintf(azureOperationsURL, cloudServiceName, deploymentName, roleName)
499	return vm.client.SendAzurePostRequest(requestURL, shutdownRoleOperationBytes)
500}
501
502func (vm VirtualMachineClient) RestartRole(cloudServiceName, deploymentName, roleName string) (management.OperationID, error) {
503	if cloudServiceName == "" {
504		return "", fmt.Errorf(errParamNotSpecified, "cloudServiceName")
505	}
506	if deploymentName == "" {
507		return "", fmt.Errorf(errParamNotSpecified, "deploymentName")
508	}
509	if roleName == "" {
510		return "", fmt.Errorf(errParamNotSpecified, "roleName")
511	}
512
513	restartRoleOperationBytes, err := xml.Marshal(RestartRoleOperation{
514		OperationType: "RestartRoleOperation",
515	})
516	if err != nil {
517		return "", err
518	}
519
520	requestURL := fmt.Sprintf(azureOperationsURL, cloudServiceName, deploymentName, roleName)
521	return vm.client.SendAzurePostRequest(requestURL, restartRoleOperationBytes)
522}
523
524func (vm VirtualMachineClient) DeleteRole(cloudServiceName, deploymentName, roleName string, deleteVHD bool) (management.OperationID, error) {
525	if cloudServiceName == "" {
526		return "", fmt.Errorf(errParamNotSpecified, "cloudServiceName")
527	}
528	if deploymentName == "" {
529		return "", fmt.Errorf(errParamNotSpecified, "deploymentName")
530	}
531	if roleName == "" {
532		return "", fmt.Errorf(errParamNotSpecified, "roleName")
533	}
534
535	requestURL := fmt.Sprintf(azureRoleURL, cloudServiceName, deploymentName, roleName)
536	if deleteVHD {
537		requestURL += "?comp=media"
538	}
539	return vm.client.SendAzureDeleteRequest(requestURL)
540}
541
542func (vm VirtualMachineClient) GetRoleSizeList() (RoleSizeList, error) {
543	roleSizeList := RoleSizeList{}
544
545	response, err := vm.client.SendAzureGetRequest(azureRoleSizeListURL)
546	if err != nil {
547		return roleSizeList, err
548	}
549
550	err = xml.Unmarshal(response, &roleSizeList)
551	return roleSizeList, err
552}
553
554// CaptureRole captures a VM role. If reprovisioningConfigurationSet is non-nil,
555// the VM role is redeployed after capturing the image, otherwise, the original
556// VM role is deleted.
557//
558// NOTE: an image resulting from this operation shows up in
559// osimage.GetImageList() as images with Category "User".
560func (vm VirtualMachineClient) CaptureRole(cloudServiceName, deploymentName, roleName, imageName, imageLabel string,
561	reprovisioningConfigurationSet *ConfigurationSet) (management.OperationID, error) {
562	if cloudServiceName == "" {
563		return "", fmt.Errorf(errParamNotSpecified, "cloudServiceName")
564	}
565	if deploymentName == "" {
566		return "", fmt.Errorf(errParamNotSpecified, "deploymentName")
567	}
568	if roleName == "" {
569		return "", fmt.Errorf(errParamNotSpecified, "roleName")
570	}
571
572	if reprovisioningConfigurationSet != nil &&
573		!(reprovisioningConfigurationSet.ConfigurationSetType == ConfigurationSetTypeLinuxProvisioning ||
574			reprovisioningConfigurationSet.ConfigurationSetType == ConfigurationSetTypeWindowsProvisioning) {
575		return "", fmt.Errorf("ConfigurationSet type can only be WindowsProvisioningConfiguration or LinuxProvisioningConfiguration")
576	}
577
578	operation := CaptureRoleOperation{
579		OperationType:             "CaptureRoleOperation",
580		PostCaptureAction:         PostCaptureActionReprovision,
581		ProvisioningConfiguration: reprovisioningConfigurationSet,
582		TargetImageLabel:          imageLabel,
583		TargetImageName:           imageName,
584	}
585	if reprovisioningConfigurationSet == nil {
586		operation.PostCaptureAction = PostCaptureActionDelete
587	}
588
589	data, err := xml.Marshal(operation)
590	if err != nil {
591		return "", err
592	}
593
594	return vm.client.SendAzurePostRequest(fmt.Sprintf(azureOperationsURL, cloudServiceName, deploymentName, roleName), data)
595}
596