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