knife-azure 1.6.0.rc.0 → 1.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +304 -8
  3. data/lib/azure/azure_interface.rb +81 -0
  4. data/lib/azure/custom_errors.rb +35 -0
  5. data/lib/azure/helpers.rb +44 -0
  6. data/lib/azure/resource_management/ARM_base.rb +29 -0
  7. data/lib/azure/resource_management/ARM_deployment_template.rb +561 -0
  8. data/lib/azure/resource_management/ARM_interface.rb +795 -0
  9. data/lib/azure/resource_management/windows_credentials.rb +136 -0
  10. data/lib/azure/service_management/ASM_interface.rb +301 -0
  11. data/lib/azure/{ag.rb → service_management/ag.rb} +2 -2
  12. data/lib/azure/{certificate.rb → service_management/certificate.rb} +2 -2
  13. data/lib/azure/service_management/connection.rb +102 -0
  14. data/lib/azure/{deploy.rb → service_management/deploy.rb} +8 -2
  15. data/lib/azure/{disk.rb → service_management/disk.rb} +2 -2
  16. data/lib/azure/{host.rb → service_management/host.rb} +2 -2
  17. data/lib/azure/{image.rb → service_management/image.rb} +2 -2
  18. data/lib/azure/{loadbalancer.rb → service_management/loadbalancer.rb} +4 -18
  19. data/lib/azure/{rest.rb → service_management/rest.rb} +15 -10
  20. data/lib/azure/{role.rb → service_management/role.rb} +174 -6
  21. data/lib/azure/{storageaccount.rb → service_management/storageaccount.rb} +2 -2
  22. data/lib/azure/{utility.rb → service_management/utility.rb} +0 -0
  23. data/lib/azure/{vnet.rb → service_management/vnet.rb} +2 -2
  24. data/lib/chef/knife/azure_ag_create.rb +3 -6
  25. data/lib/chef/knife/azure_ag_list.rb +2 -16
  26. data/lib/chef/knife/azure_base.rb +89 -22
  27. data/lib/chef/knife/azure_image_list.rb +3 -7
  28. data/lib/chef/knife/azure_internal-lb_create.rb +2 -5
  29. data/lib/chef/knife/azure_internal-lb_list.rb +2 -16
  30. data/lib/chef/knife/azure_server_create.rb +122 -501
  31. data/lib/chef/knife/azure_server_delete.rb +15 -38
  32. data/lib/chef/knife/azure_server_list.rb +2 -27
  33. data/lib/chef/knife/azure_server_show.rb +4 -60
  34. data/lib/chef/knife/azure_vnet_create.rb +2 -7
  35. data/lib/chef/knife/azure_vnet_list.rb +2 -17
  36. data/lib/chef/knife/azurerm_base.rb +228 -0
  37. data/lib/chef/knife/azurerm_server_create.rb +393 -0
  38. data/lib/chef/knife/azurerm_server_delete.rb +121 -0
  39. data/lib/chef/knife/azurerm_server_list.rb +18 -0
  40. data/lib/chef/knife/azurerm_server_show.rb +37 -0
  41. data/lib/chef/knife/bootstrap/bootstrap_options.rb +105 -0
  42. data/lib/chef/knife/bootstrap/bootstrapper.rb +343 -0
  43. data/lib/chef/knife/bootstrap/common_bootstrap_options.rb +116 -0
  44. data/lib/chef/knife/bootstrap_azure.rb +110 -0
  45. data/lib/chef/knife/bootstrap_azurerm.rb +116 -0
  46. data/lib/knife-azure/version.rb +1 -2
  47. metadata +132 -16
  48. data/lib/azure/connection.rb +0 -99
@@ -0,0 +1,795 @@
1
+ # License:: Apache License, Version 2.0
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ #
15
+
16
+ require 'azure/azure_interface'
17
+ require 'azure/resource_management/ARM_base'
18
+ require 'azure/resource_management/ARM_deployment_template'
19
+ require 'azure_mgmt_resources'
20
+ require 'azure_mgmt_compute'
21
+ require 'azure_mgmt_storage'
22
+ require 'azure_mgmt_network'
23
+
24
+ module Azure
25
+ class ResourceManagement
26
+ class ARMInterface < AzureInterface
27
+ include Azure::ARM::ARMBase
28
+ include Azure::ARM::ARMDeploymentTemplate
29
+
30
+ include Azure::ARM::Resources
31
+ include Azure::ARM::Resources::Models
32
+
33
+ include Azure::ARM::Compute
34
+ include Azure::ARM::Compute::Models
35
+
36
+ include Azure::ARM::Storage
37
+ include Azure::ARM::Storage::Models
38
+
39
+ include Azure::ARM::Network
40
+ include Azure::ARM::Network::Models
41
+
42
+ attr_accessor :connection
43
+
44
+ def initialize(params = {})
45
+ if params[:azure_client_secret]
46
+ token_provider = MsRestAzure::ApplicationTokenProvider.new(params[:azure_tenant_id], params[:azure_client_id], params[:azure_client_secret])
47
+ else
48
+ token_provider = MsRest::StringTokenProvider.new(params[:token],params[:tokentype])
49
+ end
50
+ @credentials = MsRest::TokenCredentials.new(token_provider)
51
+ @azure_subscription_id = params[:azure_subscription_id]
52
+ super
53
+ end
54
+
55
+ def resource_management_client
56
+ @resource_management_client ||= begin
57
+ resource_management_client = ResourceManagementClient.new(@credentials)
58
+ resource_management_client.subscription_id = @azure_subscription_id
59
+ resource_management_client
60
+ end
61
+ end
62
+
63
+ def compute_management_client
64
+ @compute_management_client ||= begin
65
+ compute_management_client = ComputeManagementClient.new(@credentials)
66
+ compute_management_client.subscription_id = @azure_subscription_id
67
+ compute_management_client
68
+ end
69
+ end
70
+
71
+ def storage_management_client
72
+ @storage_management_client ||= begin
73
+ storage_management_client = StorageManagementClient.new(@credentials)
74
+ storage_management_client.subscription_id = @azure_subscription_id
75
+ storage_management_client
76
+ end
77
+ end
78
+
79
+ def network_resource_client
80
+ @network_resource_client ||= begin
81
+ network_resource_client = NetworkManagementClient.new(@credentials)
82
+ network_resource_client.subscription_id = @azure_subscription_id
83
+ network_resource_client
84
+ end
85
+ end
86
+
87
+ def list_images
88
+ end
89
+
90
+ def list_servers(resource_group_name = nil)
91
+ begin
92
+ if resource_group_name.nil?
93
+ promise = compute_management_client.virtual_machines.list_all
94
+ else
95
+ promise = compute_management_client.virtual_machines.list(resource_group_name)
96
+ end
97
+
98
+ result = promise.value!
99
+ servers = result.body.value
100
+
101
+ cols = ['VM Name', 'Resource Group Name', 'Location', 'Provisioning State', 'OS Type']
102
+ rows = []
103
+
104
+ servers.each do |server|
105
+ rows << server.name.to_s
106
+ rows << server.id.split('/')[4].downcase
107
+ rows << server.location.to_s
108
+ rows << begin
109
+ state = server.properties.provisioning_state.to_s.downcase
110
+ case state
111
+ when 'failed'
112
+ ui.color(state, :red)
113
+ when 'succeeded'
114
+ ui.color(state, :green)
115
+ else
116
+ ui.color(state, :yellow)
117
+ end
118
+ end
119
+ rows << server.properties.storage_profile.os_disk.os_type.to_s
120
+ end
121
+ display_list(ui, cols, rows)
122
+ rescue => error
123
+ if error.class == MsRestAzure::AzureOperationError && error.body
124
+ if error.body['error']['code']
125
+ ui.error("#{error.body['error']['message']}")
126
+ else
127
+ ui.error(error.body)
128
+ end
129
+ else
130
+ ui.error("#{error.message}")
131
+ Chef::Log.debug("#{error.backtrace.join("\n")}")
132
+ end
133
+ exit
134
+ end
135
+ end
136
+
137
+ def delete_server(resource_group_name, vm_name)
138
+ promise = compute_management_client.virtual_machines.get(resource_group_name, vm_name)
139
+ if promise.value! && promise.value!.body.name == vm_name
140
+ puts "\n\n"
141
+ msg_pair(ui, 'VM Name', promise.value!.body.name)
142
+ msg_pair(ui, 'VM Size', promise.value!.body.properties.hardware_profile.vm_size)
143
+ msg_pair(ui, 'VM OS', promise.value!.body.properties.storage_profile.os_disk.os_type)
144
+ puts "\n"
145
+
146
+ begin
147
+ ui.confirm('Do you really want to delete this server')
148
+ rescue SystemExit # Need to handle this as confirming with N/n raises SystemExit exception
149
+ server = nil # Cleanup is implicitly performed in other cloud plugins
150
+ exit!
151
+ end
152
+
153
+ ui.info 'Deleting ..'
154
+
155
+ begin
156
+ print '.'
157
+ promise = compute_management_client.virtual_machines.delete(resource_group_name, vm_name)
158
+ end until promise.value!.body.nil?
159
+
160
+ puts "\n"
161
+ ui.warn "Deleted server #{vm_name}"
162
+ end
163
+ end
164
+
165
+ def show_server(name, resource_group)
166
+ begin
167
+ server = find_server(resource_group, name)
168
+ if server
169
+ network_interface_name = server.properties.network_profile.network_interfaces[0].id.split('/')[-1]
170
+ network_interface_data = network_resource_client.network_interfaces.get(resource_group, network_interface_name).value!.body
171
+ public_ip_id_data = network_interface_data.properties.ip_configurations[0].properties.public_ipaddress
172
+ unless public_ip_id_data.nil?
173
+ public_ip_name = public_ip_id_data.id.split('/')[-1]
174
+ public_ip_data = network_resource_client.public_ipaddresses.get(resource_group, public_ip_name).value!.body
175
+ else
176
+ public_ip_data = nil
177
+ end
178
+
179
+ details = Array.new
180
+ details << ui.color('Server Name', :bold, :cyan)
181
+ details << server.name
182
+
183
+ details << ui.color('Size', :bold, :cyan)
184
+ details << server.properties.hardware_profile.vm_size
185
+
186
+ details << ui.color('Provisioning State', :bold, :cyan)
187
+ details << server.properties.provisioning_state
188
+
189
+ details << ui.color('Location', :bold, :cyan)
190
+ details << server.location
191
+
192
+ details << ui.color('Publisher', :bold, :cyan)
193
+ details << server.properties.storage_profile.image_reference.publisher
194
+
195
+ details << ui.color('Offer', :bold, :cyan)
196
+ details << server.properties.storage_profile.image_reference.offer
197
+
198
+ details << ui.color('Sku', :bold, :cyan)
199
+ details << server.properties.storage_profile.image_reference.sku
200
+
201
+ details << ui.color('Version', :bold, :cyan)
202
+ details << server.properties.storage_profile.image_reference.version
203
+
204
+ details << ui.color('OS Type', :bold, :cyan)
205
+ details << server.properties.storage_profile.os_disk.os_type
206
+
207
+ details << ui.color('Public IP address', :bold, :cyan)
208
+ unless public_ip_data.nil?
209
+ details << public_ip_data.properties.ip_address
210
+ else
211
+ details << ' -- '
212
+ end
213
+
214
+ details << ui.color('FQDN', :bold, :cyan)
215
+ unless public_ip_data.nil? or public_ip_data.properties.dns_settings.nil?
216
+ details << public_ip_data.properties.dns_settings.fqdn
217
+ else
218
+ details << ' -- '
219
+ end
220
+
221
+ puts ui.list(details, :columns_across, 2)
222
+ end
223
+ rescue => error
224
+ puts "#{error.body["error"]["message"]}"
225
+ end
226
+ end
227
+
228
+ def find_server(resource_group, name)
229
+ begin
230
+ promise = compute_management_client.virtual_machines.get(resource_group, name)
231
+ result = promise.value!
232
+
233
+ unless result.nil?
234
+ server = result.body
235
+ else
236
+ ui.error("There is no server with name #{name} or resource_group #{resource_group}. Please provide correct details.")
237
+ end
238
+ rescue => error
239
+ ui.error("#{error.body["error"]["message"]}")
240
+ end
241
+ server
242
+ end
243
+
244
+ def virtual_machine_exist?(resource_group_name, vm_name)
245
+ !compute_management_client.virtual_machines.get(resource_group_name, vm_name).value.nil?
246
+ end
247
+
248
+ def resource_group_exist?(resource_group_name)
249
+ resource_management_client.resource_groups.check_existence(resource_group_name).value!.body
250
+ end
251
+
252
+ def platform(image_reference)
253
+ @platform ||= begin
254
+ if image_reference =~ /WindowsServer.*/
255
+ platform = 'Windows'
256
+ else
257
+ platform = 'Linux'
258
+ end
259
+ platform
260
+ end
261
+ end
262
+
263
+ def create_server(params = {})
264
+ platform(params[:azure_image_reference_offer])
265
+ # resource group creation
266
+ if resource_group_exist?(params[:azure_resource_group_name])
267
+ ui.log("INFO:Resource Group #{params[:azure_resource_group_name]} already exist. Skipping its creation.")
268
+ ui.log("INFO:Adding new VM #{params[:azure_vm_name]} to this resource group.")
269
+ else
270
+ ui.log("Creating ResourceGroup....\n\n")
271
+ resource_group = create_resource_group(params)
272
+ Chef::Log.info("ResourceGroup creation successfull.")
273
+ Chef::Log.info("Resource Group name is: #{resource_group.name}")
274
+ Chef::Log.info("Resource Group ID is: #{resource_group.id}")
275
+ end
276
+
277
+ # virtual machine creation
278
+ if virtual_machine_exist?(params[:azure_resource_group_name], params[:azure_vm_name])
279
+ ui.log("INFO:Virtual Machine #{params[:azure_vm_name]} already exist under the Resource Group #{params[:azure_resource_group_name]}. Exiting for now.")
280
+ else
281
+ params[:chef_extension_version] = params[:chef_extension_version].nil? ? get_latest_chef_extension_version(params) : params[:chef_extension_version]
282
+ params[:vm_size] = get_vm_size(params[:azure_vm_size])
283
+ ui.log("Creating Virtual Machine....")
284
+ deployment = create_virtual_machine_using_template(params)
285
+ ui.log("Virtual Machine creation successfull.") unless deployment.nil?
286
+
287
+ unless deployment.nil?
288
+ ui.log("Deployment name is: #{deployment.name}")
289
+ ui.log("Deployment ID is: #{deployment.id}")
290
+ deployment.properties.dependencies.each do |deploy|
291
+ if deploy.resource_type == "Microsoft.Compute/virtualMachines"
292
+ ui.log("VM Details ...")
293
+ ui.log("-------------------------------")
294
+ ui.log("Virtual Machine name is: #{deploy.resource_name}")
295
+ ui.log("Virtual Machine ID is: #{deploy.id}")
296
+ show_server(deploy.resource_name, params[:azure_resource_group_name])
297
+ end
298
+ end
299
+ end
300
+ end
301
+ end
302
+
303
+ def vm_details(virtual_machine, vm_extension, params)
304
+ vm_details = OpenStruct.new
305
+ vm_details.publicipaddress = vm_public_ip(params)
306
+
307
+ if @platform == 'Windows'
308
+ vm_details.rdpport = vm_default_port(params)
309
+ else
310
+ vm_details.sshport = vm_default_port(params)
311
+ end
312
+
313
+ vm_details.id = virtual_machine.id
314
+ vm_details.name = virtual_machine.name
315
+ vm_details.locationname = params[:azure_service_location].gsub(/[ ]/,'').downcase
316
+ vm_details.ostype = virtual_machine.properties.storage_profile.os_disk.os_type
317
+ vm_details.provisioningstate = virtual_machine.properties.provisioning_state
318
+ vm_details.resources = OpenStruct.new
319
+ vm_details.resources.id = vm_extension.id
320
+ vm_details.resources.name = vm_extension.name
321
+ vm_details.resources.publisher = vm_extension.properties.publisher
322
+ vm_details.resources.type = vm_extension.properties.type
323
+ vm_details.resources.type_handler_version = vm_extension.properties.type_handler_version
324
+ vm_details.resources.provisioning_state = vm_extension.properties.provisioning_state
325
+
326
+ vm_details
327
+ end
328
+
329
+ def vm_public_ip(params = {})
330
+ network_resource_client.public_ipaddresses.get(
331
+ params[:azure_resource_group_name],
332
+ params[:azure_vm_name]
333
+ ).value!.body.properties.ip_address
334
+ end
335
+
336
+ def vm_default_port(params = {})
337
+ network_resource_client.network_security_groups.get(
338
+ params[:azure_resource_group_name],
339
+ params[:azure_vm_name]
340
+ ).value!.body.properties.security_rules[0].properties.destination_port_range
341
+ end
342
+
343
+ def create_resource_group(params = {})
344
+ resource_group = ResourceGroup.new()
345
+ resource_group.name = params[:azure_resource_group_name]
346
+ resource_group.location = params[:azure_service_location]
347
+
348
+ begin
349
+ resource_group = resource_management_client.resource_groups.create_or_update(resource_group.name, resource_group).value!.body
350
+ rescue Exception => e
351
+ Chef::Log.error("Failed to create the Resource Group -- exception being rescued: #{e.to_s}")
352
+ backtrace_message = "#{e.class}: #{e}\n#{e.backtrace.join("\n")}"
353
+ Chef::Log.debug("#{backtrace_message}")
354
+ end
355
+
356
+ resource_group
357
+ end
358
+
359
+ def create_virtual_machine_using_template(params)
360
+ template = create_deployment_template(params)
361
+ parameters = create_deployment_parameters(params, @platform)
362
+
363
+ deploy_prop = DeploymentProperties.new
364
+ deploy_prop.template = template
365
+ deploy_prop.parameters = parameters
366
+ deploy_prop.mode = 'Incremental'
367
+
368
+ deploy_params = Deployment.new
369
+ deploy_params.properties = deploy_prop
370
+
371
+ deployment = resource_management_client.deployments.create_or_update(params[:azure_resource_group_name], "#{params[:azure_vm_name]}_deploy", deploy_params).value!.body
372
+ deployment
373
+ end
374
+
375
+ def create_virtual_machine(params)
376
+ os_profile = OSProfile.new
377
+ os_profile.computer_name = params[:azure_vm_name]
378
+ os_profile.secrets = []
379
+
380
+ if @platform == 'Windows'
381
+ windows_config = WindowsConfiguration.new
382
+ windows_config.provision_vmagent = true
383
+ windows_config.enable_automatic_updates = true
384
+
385
+ os_profile.admin_username = params[:winrm_user]
386
+ os_profile.admin_password = params[:admin_password]
387
+ os_profile.windows_configuration = windows_config
388
+ else
389
+ linux_config = LinuxConfiguration.new
390
+ linux_config.disable_password_authentication = false
391
+
392
+ os_profile.admin_username = params[:ssh_user]
393
+ os_profile.admin_password = params[:ssh_password]
394
+ os_profile.linux_configuration = linux_config
395
+ end
396
+
397
+ hardware_profile = HardwareProfile.new
398
+ hardware_profile.vm_size = get_vm_size(params[:azure_vm_size])
399
+
400
+ vm_props = VirtualMachineProperties.new
401
+ vm_props.os_profile = os_profile
402
+ vm_props.hardware_profile = hardware_profile
403
+ vm_props.storage_profile = create_storage_profile(params)
404
+ vm_props.network_profile = create_network_profile(params)
405
+
406
+ vm_params = VirtualMachine.new
407
+ vm_params.name = params[:azure_vm_name]
408
+ vm_params.type = 'Microsoft.Compute/virtualMachines'
409
+ vm_params.properties = vm_props
410
+ vm_params.location = params[:azure_service_location]
411
+
412
+ begin
413
+ virtual_machine = compute_management_client.virtual_machines.create_or_update(params[:azure_resource_group_name], vm_params.name, vm_params).value!.body
414
+ rescue Exception => e
415
+ ui.log("Failed to create the virtual machine, use verbose mode for more details")
416
+ Chef::Log.error("Failed to create the Virtual Machine -- exception being rescued: #{e.to_s}")
417
+ backtrace_message = "#{e.class}: #{e}\n#{e.backtrace.join("\n")}"
418
+ Chef::Log.debug("#{backtrace_message}")
419
+ end
420
+
421
+ virtual_machine
422
+ end
423
+
424
+ def create_storage_profile(params)
425
+ ui.log("Creating Storage Account.... \n\n ")
426
+ storage_account = create_storage_account(
427
+ params[:azure_storage_account],
428
+ params[:azure_service_location],
429
+ params[:azure_storage_account_type],
430
+ params[:azure_resource_group_name]
431
+ )
432
+
433
+ virtual_hard_disk = get_vhd(
434
+ params[:azure_storage_account],
435
+ params[:azure_os_disk_name]
436
+ )
437
+
438
+ ui.log("StorageAccount creation successfull.")
439
+ storage_profile = StorageProfile.new
440
+ storage_profile.image_reference = get_image_reference(
441
+ params[:azure_image_reference_publisher],
442
+ params[:azure_image_reference_offer],
443
+ params[:azure_image_reference_sku],
444
+ params[:azure_image_reference_version]
445
+ )
446
+ storage_profile.os_disk = get_os_disk(
447
+ virtual_hard_disk,
448
+ params[:azure_os_disk_name],
449
+ params[:azure_os_disk_caching],
450
+ params[:azure_os_disk_create_option]
451
+ )
452
+
453
+ storage_profile
454
+ end
455
+
456
+ def create_storage_account(storage_account_name, location, storage_account_type, resource_group_name)
457
+ storage_props = Azure::ARM::Storage::Models::StorageAccountPropertiesCreateParameters.new
458
+ storage_props.account_type = storage_account_type
459
+
460
+ storage_params = Azure::ARM::Storage::Models::StorageAccountCreateParameters.new
461
+ storage_params.location = location
462
+ storage_params.properties = storage_props
463
+
464
+ storage = storage_management_client.storage_accounts.create(resource_group_name, storage_account_name, storage_params).value!.body
465
+ storage
466
+ end
467
+
468
+ def get_vhd(storage_account_name, os_disk_name)
469
+ virtual_hard_disk = VirtualHardDisk.new
470
+ virtual_hard_disk.uri = "http://#{storage_account_name}.blob.core.windows.net/vhds/#{os_disk_name}.vhd"
471
+ virtual_hard_disk
472
+ end
473
+
474
+ def get_image_reference(publisher, offer, sku, version)
475
+ image_reference = ImageReference.new
476
+ image_reference.publisher = publisher
477
+ image_reference.offer = offer
478
+ image_reference.sku = sku
479
+ image_reference.version = version
480
+ image_reference
481
+ end
482
+
483
+ def get_os_disk(virtual_hard_disk, os_disk_name, os_disk_caching, os_disk_create_option)
484
+ os_disk = OSDisk.new
485
+ os_disk.name = os_disk_name
486
+ os_disk.vhd = virtual_hard_disk
487
+ os_disk.caching = os_disk_caching
488
+ os_disk.create_option = os_disk_create_option
489
+ os_disk
490
+ end
491
+
492
+ def vnet_exist?(resource_group_name, vnet_name)
493
+ begin
494
+ network_resource_client.virtual_networks.get(resource_group_name, vnet_name).value!.body
495
+ rescue
496
+ return false
497
+ end
498
+ end
499
+
500
+ def subnet_exist?(resource_group_name, vnet_name, subnet_name)
501
+ begin
502
+ network_resource_client.subnets.get(resource_group_name, vnet_name, subnet_name).value!.body
503
+ rescue
504
+ return false
505
+ end
506
+ end
507
+
508
+ def create_network_profile(params)
509
+ if vnet_exist?(params[:azure_resource_group_name], params[:azure_vnet_name])
510
+ vnet = network_resource_client.virtual_networks.get(params[:azure_resource_group_name], params[:azure_vnet_name]).value!.body
511
+ Chef::Log.info("Found existing vnet #{vnet.name}...")
512
+ else
513
+ ui.log("Creating VirtualNetwork....\n\n")
514
+ vnet = create_virtual_network(
515
+ params[:azure_resource_group_name],
516
+ params[:azure_vnet_name],
517
+ params[:azure_service_location]
518
+ )
519
+ Chef::Log.info("VirtualNetwork creation successfull.")
520
+ end
521
+
522
+ Chef::Log.info("Virtual Network name is: #{vnet.name}")
523
+
524
+ Chef::Log.info("Virtual Network ID is: #{vnet.id}")
525
+
526
+ if subnet_exist?(params[:azure_resource_group_name], vnet.name, params[:azure_vnet_subnet_name])
527
+ sbn = network_resource_client.subnets.get(params[:azure_resource_group_name], vnet.name, params[:azure_vnet_subnet_name]).value!.body
528
+
529
+ Chef::Log.info("Found subnet #{sbn.name} under virtual network #{vnet.name} ...")
530
+
531
+ else
532
+ ui.log("Creating Subnet....\n\n")
533
+ sbn = create_subnet(
534
+ params[:azure_resource_group_name],
535
+ params[:azure_vnet_subnet_name],
536
+ vnet
537
+ )
538
+ Chef::Log.info("Subnet creation successfull.")
539
+ end
540
+
541
+ Chef::Log.info("Subnet name is: #{sbn.name}")
542
+ Chef::Log.info("Subnet ID is: #{sbn.id}")
543
+
544
+ ui.log("Creating NetworkInterface....\n\n")
545
+ nic = create_network_interface(
546
+ params[:azure_resource_group_name],
547
+ params[:azure_vm_name],
548
+ params[:azure_service_location],
549
+ sbn
550
+ )
551
+ Chef::Log.info("NetworkInterface creation successfull.")
552
+ Chef::Log.info("Network Interface name is: #{nic.name}")
553
+ Chef::Log.info("Network Interface ID is: #{nic.id}")
554
+
555
+ network_profile = NetworkProfile.new
556
+ network_profile.network_interfaces = [nic]
557
+ network_profile
558
+ end
559
+
560
+ def create_virtual_network(resource_group_name, virtual_network_name, service_location)
561
+ address_space = AddressSpace.new
562
+ address_space.address_prefixes = ['10.0.0.0/16']
563
+
564
+ vnet_props = VirtualNetworkPropertiesFormat.new
565
+ vnet_props.address_space = address_space
566
+
567
+ vnet_params = VirtualNetwork.new
568
+ vnet_params.name = virtual_network_name
569
+ vnet_params.location = service_location
570
+ vnet_params.properties = vnet_props
571
+
572
+ begin
573
+ vnet = network_resource_client.virtual_networks.create_or_update(resource_group_name, vnet_params.name, vnet_params).value!.body
574
+ rescue Exception => e
575
+ Chef::Log.error("Failed to create the Virtual Network -- exception being rescued: #{e.to_s}")
576
+ backtrace_message = "#{e.class}: #{e}\n#{e.backtrace.join("\n")}"
577
+ Chef::Log.debug("#{backtrace_message}")
578
+ end
579
+ vnet
580
+ end
581
+
582
+ def create_subnet(resource_group_name, subnet_name, virtual_network)
583
+ sbn_prop = SubnetPropertiesFormat.new
584
+ sbn_prop.address_prefix = '10.0.1.0/24'
585
+
586
+ sbn_params = Subnet.new
587
+ sbn_params.name = subnet_name
588
+ sbn_params.properties = sbn_prop
589
+
590
+ begin
591
+ sbn = network_resource_client.subnets.create_or_update(resource_group_name, virtual_network.name, sbn_params.name, sbn_params).value!.body
592
+ rescue Exception => e
593
+ Chef::Log.error("Failed to create the Subnet -- exception being rescued: #{e.to_s}")
594
+ backtrace_message = "#{e.class}: #{e}\n#{e.backtrace.join("\n")}"
595
+ Chef::Log.debug("#{backtrace_message}")
596
+ end
597
+ sbn
598
+ end
599
+
600
+ def create_network_interface(resource_group_name, vm_name, service_location, subnet)
601
+ network_ip_configuration_properties = NetworkInterfaceIPConfigurationPropertiesFormat.new
602
+ network_ip_configuration_properties.private_ipallocation_method = 'Dynamic'
603
+
604
+ network_ip_configuration_properties.public_ipaddress = create_public_ip_config(
605
+ resource_group_name,
606
+ vm_name,
607
+ service_location
608
+ )
609
+
610
+ network_ip_configuration_properties.subnet = subnet
611
+
612
+ network_interface_ip_configuration = NetworkInterfaceIPConfiguration.new
613
+ network_interface_ip_configuration.properties = network_ip_configuration_properties
614
+ network_interface_ip_configuration.name = vm_name
615
+
616
+ network_interface_props_format = NetworkInterfacePropertiesFormat.new
617
+ network_interface_props_format.ip_configurations = [network_interface_ip_configuration]
618
+ network_interface_props_format.network_security_group = create_network_security_group(
619
+ resource_group_name,
620
+ vm_name,
621
+ service_location
622
+ )
623
+
624
+ network_interface = NetworkInterface.new
625
+ network_interface.location = service_location
626
+ network_interface.name = vm_name
627
+ network_interface.properties = network_interface_props_format
628
+
629
+ begin
630
+ nic = network_resource_client.network_interfaces.create_or_update(resource_group_name, network_interface.name, network_interface).value!.body
631
+ rescue Exception => e
632
+ Chef::Log.error("Failed to create the Network Interface -- exception being rescued: #{e.to_s}")
633
+ backtrace_message = "#{e.class}: #{e}\n#{e.backtrace.join("\n")}"
634
+ Chef::Log.debug("#{backtrace_message}")
635
+ end
636
+
637
+ nic
638
+ end
639
+
640
+ def create_public_ip_config(resource_group_name, vm_name, service_location)
641
+ public_ip_props = PublicIPAddressPropertiesFormat.new
642
+ public_ip_props.public_ipallocation_method = 'Dynamic'
643
+
644
+ public_ip = PublicIPAddress.new
645
+ public_ip.name = vm_name
646
+ public_ip.location = service_location
647
+ public_ip.properties = public_ip_props
648
+
649
+ begin
650
+ public_ip_address = network_resource_client.public_ipaddresses.create_or_update(
651
+ resource_group_name,
652
+ public_ip.name,
653
+ public_ip
654
+ ).value!.body
655
+ rescue Exception => e
656
+ Chef::Log.error("Failed to create the Public IP Address -- exception being rescued: #{e.to_s}")
657
+ backtrace_message = "#{e.class}: #{e}\n#{e.backtrace.join("\n")}"
658
+ Chef::Log.debug("#{backtrace_message}")
659
+ end
660
+
661
+ public_ip_address
662
+ end
663
+
664
+ def create_network_security_group(resource_group_name, vm_name, service_location)
665
+ network_security_group_prop_format = NetworkSecurityGroupPropertiesFormat.new
666
+ network_security_group = NetworkSecurityGroup.new
667
+ network_security_group.name = vm_name
668
+ network_security_group.location = service_location
669
+ network_security_group.properties = network_security_group_prop_format
670
+
671
+ begin
672
+ nsg = network_resource_client.network_security_groups.create_or_update(
673
+ resource_group_name,
674
+ network_security_group.name,
675
+ network_security_group
676
+ ).value!.body
677
+ rescue Exception => e
678
+ Chef::Log.error("Failed to create the Network Security Group -- exception being rescued: #{e.to_s}")
679
+ backtrace_message = "#{e.class}: #{e}\n#{e.backtrace.join("\n")}"
680
+ Chef::Log.debug("#{backtrace_message}")
681
+ end
682
+
683
+ security_rules = []
684
+ if @platform == 'Windows'
685
+ security_rules << add_security_rule('3389', "RDP", 1000, resource_group_name, vm_name, network_security_group)
686
+ else
687
+ security_rules << add_security_rule("22", "SSH", 1000, resource_group_name, vm_name, network_security_group)
688
+ end
689
+ network_security_group_prop_format.default_security_rules = security_rules
690
+
691
+ nsg
692
+ end
693
+
694
+ def add_security_rule(port, description, priority, resource_group_name, vm_name, network_security_group)
695
+ security_rule_props = SecurityRulePropertiesFormat.new
696
+ security_rule_props.description = description
697
+ security_rule_props.destination_port_range = port
698
+ security_rule_props.protocol = "Tcp"
699
+ security_rule_props.source_port_range = "*"
700
+ security_rule_props.source_address_prefix = "*"
701
+ security_rule_props.destination_address_prefix = "*"
702
+ security_rule_props.access = "Allow"
703
+ security_rule_props.priority = priority
704
+ security_rule_props.direction = "Inbound"
705
+
706
+ security_rule = SecurityRule.new
707
+ security_rule.name = vm_name
708
+ security_rule.properties = security_rule_props
709
+
710
+ begin
711
+ security_rule = network_resource_client.security_rules.create_or_update(
712
+ resource_group_name,
713
+ network_security_group.name,
714
+ security_rule.name,
715
+ security_rule
716
+ ).value!.body
717
+ rescue Exception => e
718
+ Chef::Log.error("Failed to create the Security Rule -- exception being rescued: #{e.to_s}")
719
+ backtrace_message = "#{e.class}: #{e}\n#{e.backtrace.join("\n")}"
720
+ Chef::Log.debug("#{backtrace_message}")
721
+ end
722
+
723
+ security_rule
724
+ end
725
+
726
+ def create_vm_extension(params)
727
+ vm_ext_props = VirtualMachineExtensionProperties.new
728
+ vm_ext_props.publisher = params[:chef_extension_publisher]
729
+ vm_ext_props.type = params[:chef_extension]
730
+ vm_ext_props.type_handler_version = params[:chef_extension_version].nil? ? get_latest_chef_extension_version(params) : params[:chef_extension_version]
731
+ vm_ext_props.auto_upgrade_minor_version = false
732
+ vm_ext_props.settings = params[:chef_extension_public_param]
733
+ vm_ext_props.protected_settings = params[:chef_extension_private_param]
734
+
735
+ vm_ext = VirtualMachineExtension.new
736
+ vm_ext.name = params[:chef_extension]
737
+ vm_ext.location = params[:azure_service_location]
738
+ vm_ext.properties = vm_ext_props
739
+
740
+ begin
741
+ vm_extension = compute_management_client.virtual_machine_extensions.create_or_update(
742
+ params[:azure_resource_group_name],
743
+ params[:azure_vm_name],
744
+ vm_ext.name,
745
+ vm_ext
746
+ ).value!.body
747
+ rescue Exception => e
748
+ Chef::Log.error("Failed to create the Virtual Machine Extension -- exception being rescued.")
749
+
750
+ if e.class == MsRestAzure::AzureOperationError && e.body
751
+ if e.body['error']['code'] == 'DeploymentFailed'
752
+ ui.error("#{error.body['error']['message']}")
753
+ else
754
+ ui.error(e.body)
755
+ end
756
+ else
757
+ ui.error("#{error.message}")
758
+ Chef::Log.debug("#{error.backtrace.join("\n")}")
759
+ end
760
+
761
+ backtrace_message = "#{e.class}: #{e}\n#{e.backtrace.join("\n")}"
762
+ Chef::Log.debug("#{backtrace_message}")
763
+ end
764
+
765
+ vm_extension
766
+ end
767
+
768
+ def extension_already_installed?(server)
769
+ server.resources.each do |extension|
770
+ return true if (extension.properties.type == "ChefClient" || extension.properties.type == "LinuxChefClient")
771
+ end if server.resources
772
+ false
773
+ end
774
+
775
+ def get_latest_chef_extension_version(params)
776
+ ext_version = compute_management_client.virtual_machine_extension_images.list_versions(
777
+ params[:azure_service_location],
778
+ params[:chef_extension_publisher],
779
+ params[:chef_extension]).value!.body.last.name
780
+ ext_version_split_values = ext_version.split(".")
781
+ ext_version = ext_version_split_values[0] + "." + ext_version_split_values[1]
782
+ ext_version
783
+ end
784
+
785
+ def delete_resource_group(resource_group_name)
786
+ ui.info 'Resource group deletion takes some time. Please wait ...'
787
+ begin
788
+ print '.'
789
+ promise = resource_management_client.resource_groups.delete(resource_group_name)
790
+ end until promise.value!.body.nil?
791
+ puts "\n"
792
+ end
793
+ end
794
+ end
795
+ end