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.
- checksums.yaml +4 -4
- data/README.md +304 -8
- data/lib/azure/azure_interface.rb +81 -0
- data/lib/azure/custom_errors.rb +35 -0
- data/lib/azure/helpers.rb +44 -0
- data/lib/azure/resource_management/ARM_base.rb +29 -0
- data/lib/azure/resource_management/ARM_deployment_template.rb +561 -0
- data/lib/azure/resource_management/ARM_interface.rb +795 -0
- data/lib/azure/resource_management/windows_credentials.rb +136 -0
- data/lib/azure/service_management/ASM_interface.rb +301 -0
- data/lib/azure/{ag.rb → service_management/ag.rb} +2 -2
- data/lib/azure/{certificate.rb → service_management/certificate.rb} +2 -2
- data/lib/azure/service_management/connection.rb +102 -0
- data/lib/azure/{deploy.rb → service_management/deploy.rb} +8 -2
- data/lib/azure/{disk.rb → service_management/disk.rb} +2 -2
- data/lib/azure/{host.rb → service_management/host.rb} +2 -2
- data/lib/azure/{image.rb → service_management/image.rb} +2 -2
- data/lib/azure/{loadbalancer.rb → service_management/loadbalancer.rb} +4 -18
- data/lib/azure/{rest.rb → service_management/rest.rb} +15 -10
- data/lib/azure/{role.rb → service_management/role.rb} +174 -6
- data/lib/azure/{storageaccount.rb → service_management/storageaccount.rb} +2 -2
- data/lib/azure/{utility.rb → service_management/utility.rb} +0 -0
- data/lib/azure/{vnet.rb → service_management/vnet.rb} +2 -2
- data/lib/chef/knife/azure_ag_create.rb +3 -6
- data/lib/chef/knife/azure_ag_list.rb +2 -16
- data/lib/chef/knife/azure_base.rb +89 -22
- data/lib/chef/knife/azure_image_list.rb +3 -7
- data/lib/chef/knife/azure_internal-lb_create.rb +2 -5
- data/lib/chef/knife/azure_internal-lb_list.rb +2 -16
- data/lib/chef/knife/azure_server_create.rb +122 -501
- data/lib/chef/knife/azure_server_delete.rb +15 -38
- data/lib/chef/knife/azure_server_list.rb +2 -27
- data/lib/chef/knife/azure_server_show.rb +4 -60
- data/lib/chef/knife/azure_vnet_create.rb +2 -7
- data/lib/chef/knife/azure_vnet_list.rb +2 -17
- data/lib/chef/knife/azurerm_base.rb +228 -0
- data/lib/chef/knife/azurerm_server_create.rb +393 -0
- data/lib/chef/knife/azurerm_server_delete.rb +121 -0
- data/lib/chef/knife/azurerm_server_list.rb +18 -0
- data/lib/chef/knife/azurerm_server_show.rb +37 -0
- data/lib/chef/knife/bootstrap/bootstrap_options.rb +105 -0
- data/lib/chef/knife/bootstrap/bootstrapper.rb +343 -0
- data/lib/chef/knife/bootstrap/common_bootstrap_options.rb +116 -0
- data/lib/chef/knife/bootstrap_azure.rb +110 -0
- data/lib/chef/knife/bootstrap_azurerm.rb +116 -0
- data/lib/knife-azure/version.rb +1 -2
- metadata +132 -16
- 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
|