kitchen-azurerm 0.3.5 → 0.3.6
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/CHANGELOG.md +3 -0
- data/README.md +1 -1
- data/lib/kitchen/driver/azurerm.rb +550 -550
- metadata +7 -7
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: c5bf6aca89882e1148668e693edae5dc6e37d1f7
|
|
4
|
+
data.tar.gz: d4c53242bf99c9aa1d2a0bfb43e3bf694360b5b6
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 60be9d3b4755c863e35cc42446604d09c8707886cf8bd65c0fcfa43a6f58181cb0901b654301ef1a95ed425879167cbdc9a88da4e0589b4935bc4e88197778e5
|
|
7
|
+
data.tar.gz: f94c74f78b15c39b75544e152f04b1ce915ebf72272f972ce8f32af9c0596c36a36ef0bd66951f2ce5456db4a3a58920be0461c7527949a743558185896302b7
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
# kitchen-azurerm Changelog
|
|
2
2
|
|
|
3
|
+
## [0.3.6] - 2016-05-10
|
|
4
|
+
- Remove version pin on inifile gem dependency, compatible with newer ChefDK (@stuartpreston)
|
|
5
|
+
|
|
3
6
|
## [0.3.5] - 2016-03-21
|
|
4
7
|
- Remove transport name restriction on SSH key upload (allow rsync support) (@stuartpreston)
|
|
5
8
|
- Support SSH public keys with newlines as generated by ssh-keygen (@stuartpreston)
|
data/README.md
CHANGED
|
@@ -25,7 +25,7 @@ You are now ready to configure kitchen-azurerm to use the credentials from the s
|
|
|
25
25
|
1. **Subscription ID**: available from the Azure portal
|
|
26
26
|
2. **Client ID**: this will be the Application Id from the application in step 2.
|
|
27
27
|
3. **Client Secret/Password**: this will be the password you supplied in the command in step 2.
|
|
28
|
-
4. **Tenant ID**:
|
|
28
|
+
4. **Tenant ID**: use the command detailed in "Manually provide credentials through Azure CLI" step 1 to get the TenantId.
|
|
29
29
|
|
|
30
30
|
Using a text editor, open or create the file ```~/.azure/credentials``` and add the following section, noting there is one section per Subscription ID. **Make sure you save the file with UTF-8 encoding**
|
|
31
31
|
|
|
@@ -1,550 +1,550 @@
|
|
|
1
|
-
require 'kitchen'
|
|
2
|
-
require 'kitchen/driver/credentials'
|
|
3
|
-
require 'securerandom'
|
|
4
|
-
require 'azure_mgmt_resources'
|
|
5
|
-
require 'azure_mgmt_network'
|
|
6
|
-
require 'base64'
|
|
7
|
-
require 'sshkey'
|
|
8
|
-
require 'fileutils'
|
|
9
|
-
|
|
10
|
-
module Kitchen
|
|
11
|
-
module Driver
|
|
12
|
-
#
|
|
13
|
-
# Azurerm
|
|
14
|
-
#
|
|
15
|
-
class Azurerm < Kitchen::Driver::Base
|
|
16
|
-
attr_accessor :resource_management_client
|
|
17
|
-
|
|
18
|
-
default_config(:azure_resource_group_name) do |config|
|
|
19
|
-
"kitchen-#{config.instance.name}"
|
|
20
|
-
end
|
|
21
|
-
|
|
22
|
-
default_config(:image_urn) do |_config|
|
|
23
|
-
'Canonical:UbuntuServer:14.04.3-LTS:latest'
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
default_config(:username) do |_config|
|
|
27
|
-
'azure'
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
default_config(:password) do |_config|
|
|
31
|
-
'P2ssw0rd'
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
default_config(:vm_name) do |_config|
|
|
35
|
-
'vm'
|
|
36
|
-
end
|
|
37
|
-
|
|
38
|
-
default_config(:storage_account_type) do |_config|
|
|
39
|
-
'Standard_LRS'
|
|
40
|
-
end
|
|
41
|
-
|
|
42
|
-
default_config(:boot_diagnostics_enabled) do |_config|
|
|
43
|
-
'true'
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
default_config(:winrm_powershell_script) do |_config|
|
|
47
|
-
false
|
|
48
|
-
end
|
|
49
|
-
|
|
50
|
-
default_config(:azure_management_url) do |_config|
|
|
51
|
-
'https://management.azure.com'
|
|
52
|
-
end
|
|
53
|
-
|
|
54
|
-
def create(state)
|
|
55
|
-
state = validate_state(state)
|
|
56
|
-
|
|
57
|
-
image_publisher, image_offer, image_sku, image_version = config[:image_urn].split(':', 4)
|
|
58
|
-
deployment_parameters = {
|
|
59
|
-
location: config[:location],
|
|
60
|
-
vmSize: config[:machine_size],
|
|
61
|
-
storageAccountType: config[:storage_account_type],
|
|
62
|
-
bootDiagnosticsEnabled: config[:boot_diagnostics_enabled],
|
|
63
|
-
newStorageAccountName: "storage#{state[:uuid]}",
|
|
64
|
-
adminUsername: state[:username],
|
|
65
|
-
adminPassword: state[:password],
|
|
66
|
-
dnsNameForPublicIP: "kitchen-#{state[:uuid]}",
|
|
67
|
-
imagePublisher: image_publisher,
|
|
68
|
-
imageOffer: image_offer,
|
|
69
|
-
imageSku: image_sku,
|
|
70
|
-
imageVersion: image_version,
|
|
71
|
-
vmName: state[:vm_name]
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
credentials = Kitchen::Driver::Credentials.new.azure_credentials_for_subscription(config[:subscription_id])
|
|
75
|
-
@resource_management_client = ::Azure::ARM::Resources::ResourceManagementClient.new(credentials)
|
|
76
|
-
@resource_management_client.subscription_id = config[:subscription_id]
|
|
77
|
-
|
|
78
|
-
# Create Resource Group
|
|
79
|
-
resource_group = ::Azure::ARM::Resources::Models::ResourceGroup.new
|
|
80
|
-
resource_group.location = config[:location]
|
|
81
|
-
begin
|
|
82
|
-
info "Creating Resource Group: #{state[:azure_resource_group_name]}"
|
|
83
|
-
resource_management_client.resource_groups.create_or_update(state[:azure_resource_group_name], resource_group).value!
|
|
84
|
-
rescue ::MsRestAzure::AzureOperationError => operation_error
|
|
85
|
-
error_message = if operation_error.body.nil? == true
|
|
86
|
-
operation_error.inspect
|
|
87
|
-
else
|
|
88
|
-
operation_error.body['error']
|
|
89
|
-
end
|
|
90
|
-
info error_message
|
|
91
|
-
raise error_message
|
|
92
|
-
end
|
|
93
|
-
|
|
94
|
-
# Execute deployment steps
|
|
95
|
-
begin
|
|
96
|
-
deployment_name = "deploy-#{state[:uuid]}"
|
|
97
|
-
info "Creating Deployment: #{deployment_name}"
|
|
98
|
-
resource_management_client.deployments.create_or_update(state[:azure_resource_group_name], deployment_name, deployment(deployment_parameters)).value!
|
|
99
|
-
rescue ::MsRestAzure::AzureOperationError => operation_error
|
|
100
|
-
rest_error = operation_error.body['error']
|
|
101
|
-
deployment_active = rest_error['code'] == 'DeploymentActive'
|
|
102
|
-
if deployment_active
|
|
103
|
-
info "Deployment for resource group #{state[:azure_resource_group_name]} is ongoing."
|
|
104
|
-
info "If you need to change the deployment template you'll need to rerun `kitchen create` for this instance."
|
|
105
|
-
else
|
|
106
|
-
info rest_error
|
|
107
|
-
raise operation_error
|
|
108
|
-
end
|
|
109
|
-
end
|
|
110
|
-
|
|
111
|
-
# Monitor all operations until completion
|
|
112
|
-
follow_deployment_until_end_state(state[:azure_resource_group_name], deployment_name)
|
|
113
|
-
|
|
114
|
-
# Now retrieve the public IP from the resource group:
|
|
115
|
-
network_management_client = ::Azure::ARM::Network::NetworkResourceProviderClient.new(credentials)
|
|
116
|
-
network_management_client.subscription_id = config[:subscription_id]
|
|
117
|
-
result = network_management_client.public_ip_addresses.get(state[:azure_resource_group_name], 'publicip').value!
|
|
118
|
-
info "IP Address is: #{result.body.properties.ip_address} [#{result.body.properties.dns_settings.fqdn}]"
|
|
119
|
-
state[:hostname] = result.body.properties.ip_address
|
|
120
|
-
end
|
|
121
|
-
|
|
122
|
-
def existing_state_value?(state, property)
|
|
123
|
-
state.key?(property) && !state[property].nil?
|
|
124
|
-
end
|
|
125
|
-
|
|
126
|
-
def validate_state(state = {})
|
|
127
|
-
state[:uuid] = SecureRandom.hex(8) unless existing_state_value?(state, :uuid)
|
|
128
|
-
state[:server_id] = "vm#{state[:uuid]}" unless existing_state_value?(state, :server_id)
|
|
129
|
-
state[:azure_resource_group_name] = azure_resource_group_name unless existing_state_value?(state, :azure_resource_group_name)
|
|
130
|
-
[:subscription_id, :username, :password, :vm_name, :azure_management_url].each do |config_element|
|
|
131
|
-
state[config_element] = config[config_element] unless existing_state_value?(state, config_element)
|
|
132
|
-
end
|
|
133
|
-
|
|
134
|
-
state
|
|
135
|
-
end
|
|
136
|
-
|
|
137
|
-
def azure_resource_group_name
|
|
138
|
-
formatted_time = Time.now.utc.strftime '%Y%m%dT%H%M%S'
|
|
139
|
-
"#{config[:azure_resource_group_name]}-#{formatted_time}"
|
|
140
|
-
end
|
|
141
|
-
|
|
142
|
-
def template_for_transport_name
|
|
143
|
-
template = JSON.parse(virtual_machine_deployment_template)
|
|
144
|
-
if instance.transport.name.casecmp('winrm') == 0
|
|
145
|
-
encoded_command = Base64.strict_encode64(enable_winrm_powershell_script)
|
|
146
|
-
command = command_to_execute
|
|
147
|
-
template['resources'].select { |h| h['type'] == 'Microsoft.Compute/virtualMachines' }.each do |resource|
|
|
148
|
-
resource['properties']['osProfile']['customData'] = encoded_command
|
|
149
|
-
end
|
|
150
|
-
template['resources'] << JSON.parse(custom_script_extension_template(command))
|
|
151
|
-
end
|
|
152
|
-
|
|
153
|
-
unless instance.transport[:ssh_key].nil?
|
|
154
|
-
info "Adding public key from #{File.expand_path(instance.transport[:ssh_key])}.pub to the deployment."
|
|
155
|
-
public_key = public_key_for_deployment(File.expand_path(instance.transport[:ssh_key]))
|
|
156
|
-
template['resources'].select { |h| h['type'] == 'Microsoft.Compute/virtualMachines' }.each do |resource|
|
|
157
|
-
resource['properties']['osProfile']['linuxConfiguration'] = JSON.parse(custom_linux_configuration(public_key))
|
|
158
|
-
end
|
|
159
|
-
end
|
|
160
|
-
template.to_json
|
|
161
|
-
end
|
|
162
|
-
|
|
163
|
-
def public_key_for_deployment(private_key_filename)
|
|
164
|
-
if File.file?(private_key_filename) == false
|
|
165
|
-
k = SSHKey.generate
|
|
166
|
-
|
|
167
|
-
::FileUtils.mkdir_p(File.dirname(private_key_filename))
|
|
168
|
-
|
|
169
|
-
private_key_file = File.new(private_key_filename, 'w')
|
|
170
|
-
private_key_file.syswrite(k.private_key)
|
|
171
|
-
private_key_file.chmod(0600)
|
|
172
|
-
private_key_file.close
|
|
173
|
-
|
|
174
|
-
public_key_file = File.new("#{private_key_filename}.pub", 'w')
|
|
175
|
-
public_key_file.syswrite(k.ssh_public_key)
|
|
176
|
-
public_key_file.chmod(0600)
|
|
177
|
-
public_key_file.close
|
|
178
|
-
|
|
179
|
-
output = k.ssh_public_key
|
|
180
|
-
else
|
|
181
|
-
output = File.read("#{private_key_filename}.pub")
|
|
182
|
-
end
|
|
183
|
-
output.strip
|
|
184
|
-
end
|
|
185
|
-
|
|
186
|
-
def deployment(parameters)
|
|
187
|
-
template = template_for_transport_name
|
|
188
|
-
deployment = ::Azure::ARM::Resources::Models::Deployment.new
|
|
189
|
-
deployment.properties = ::Azure::ARM::Resources::Models::DeploymentProperties.new
|
|
190
|
-
deployment.properties.mode = Azure::ARM::Resources::Models::DeploymentMode::Incremental
|
|
191
|
-
deployment.properties.template = JSON.parse(template)
|
|
192
|
-
deployment.properties.parameters = parameters_in_values_format(parameters)
|
|
193
|
-
debug(deployment.properties.template)
|
|
194
|
-
deployment
|
|
195
|
-
end
|
|
196
|
-
|
|
197
|
-
def parameters_in_values_format(parameters_in)
|
|
198
|
-
parameters = parameters_in.map do |key, value|
|
|
199
|
-
{ key.to_sym => { 'value' => value } }
|
|
200
|
-
end
|
|
201
|
-
parameters.reduce(:merge!)
|
|
202
|
-
end
|
|
203
|
-
|
|
204
|
-
def follow_deployment_until_end_state(resource_group, deployment_name)
|
|
205
|
-
end_provisioning_states = 'Canceled,Failed,Deleted,Succeeded'
|
|
206
|
-
end_provisioning_state_reached = false
|
|
207
|
-
until end_provisioning_state_reached
|
|
208
|
-
list_outstanding_deployment_operations(resource_group, deployment_name)
|
|
209
|
-
sleep 10
|
|
210
|
-
deployment_provisioning_state = deployment_state(resource_group, deployment_name)
|
|
211
|
-
end_provisioning_state_reached = end_provisioning_states.split(',').include?(deployment_provisioning_state)
|
|
212
|
-
end
|
|
213
|
-
info "Resource Template deployment reached end state of '#{deployment_provisioning_state}'."
|
|
214
|
-
show_failed_operations(resource_group, deployment_name) if deployment_provisioning_state == 'Failed'
|
|
215
|
-
end
|
|
216
|
-
|
|
217
|
-
def show_failed_operations(resource_group, deployment_name)
|
|
218
|
-
failed_operations = resource_management_client.deployment_operations.list(resource_group, deployment_name).value!
|
|
219
|
-
failed_operations.body.value.each do |val|
|
|
220
|
-
resource_code = val.properties.status_code
|
|
221
|
-
raise val.properties.status_message.inspect.to_s if resource_code != 'OK'
|
|
222
|
-
end
|
|
223
|
-
end
|
|
224
|
-
|
|
225
|
-
def list_outstanding_deployment_operations(resource_group, deployment_name)
|
|
226
|
-
end_operation_states = 'Failed,Succeeded'
|
|
227
|
-
deployment_operations = resource_management_client.deployment_operations.list(resource_group, deployment_name).value!
|
|
228
|
-
deployment_operations.body.value.each do |val|
|
|
229
|
-
resource_provisioning_state = val.properties.provisioning_state
|
|
230
|
-
resource_name = val.properties.target_resource.resource_name
|
|
231
|
-
resource_type = val.properties.target_resource.resource_type
|
|
232
|
-
end_operation_state_reached = end_operation_states.split(',').include?(resource_provisioning_state)
|
|
233
|
-
unless end_operation_state_reached
|
|
234
|
-
info "Resource #{resource_type} '#{resource_name}' provisioning status is #{resource_provisioning_state}"
|
|
235
|
-
end
|
|
236
|
-
end
|
|
237
|
-
end
|
|
238
|
-
|
|
239
|
-
def deployment_state(resource_group, deployment_name)
|
|
240
|
-
deployments = resource_management_client.deployments.get(resource_group, deployment_name).value!
|
|
241
|
-
deployments.body.properties.provisioning_state
|
|
242
|
-
end
|
|
243
|
-
|
|
244
|
-
def destroy(state)
|
|
245
|
-
return if state[:server_id].nil?
|
|
246
|
-
credentials = Kitchen::Driver::Credentials.new.azure_credentials_for_subscription(state[:subscription_id])
|
|
247
|
-
resource_management_client = ::Azure::ARM::Resources::ResourceManagementClient.new(credentials, state[:azure_management_url])
|
|
248
|
-
resource_management_client.subscription_id = state[:subscription_id]
|
|
249
|
-
begin
|
|
250
|
-
info "Destroying Resource Group: #{state[:azure_resource_group_name]}"
|
|
251
|
-
resource_management_client.resource_groups.begin_delete(state[:azure_resource_group_name]).value!
|
|
252
|
-
info 'Destroy operation accepted and will continue in the background.'
|
|
253
|
-
rescue ::MsRestAzure::AzureOperationError => operation_error
|
|
254
|
-
info operation_error.body['error']
|
|
255
|
-
raise operation_error
|
|
256
|
-
end
|
|
257
|
-
state.delete(:server_id)
|
|
258
|
-
state.delete(:hostname)
|
|
259
|
-
state.delete(:username)
|
|
260
|
-
state.delete(:password)
|
|
261
|
-
end
|
|
262
|
-
|
|
263
|
-
def enable_winrm_powershell_script
|
|
264
|
-
config[:winrm_powershell_script] || <<-PS1
|
|
265
|
-
$cert = New-SelfSignedCertificate -DnsName $env:COMPUTERNAME -CertStoreLocation Cert:\\LocalMachine\\My
|
|
266
|
-
$config = '@{CertificateThumbprint="' + $cert.Thumbprint + '"}'
|
|
267
|
-
winrm create winrm/config/listener?Address=*+Transport=HTTPS $config
|
|
268
|
-
winrm set winrm/config/service/auth '@{Basic="true";Kerberos="false";Negotiate="true";Certificate="false";CredSSP="true"}'
|
|
269
|
-
New-NetFirewallRule -DisplayName "Windows Remote Management (HTTPS-In)" -Name "Windows Remote Management (HTTPS-In)" -Profile Any -LocalPort 5986 -Protocol TCP
|
|
270
|
-
winrm set winrm/config/service '@{AllowUnencrypted="true"}'
|
|
271
|
-
New-NetFirewallRule -DisplayName "Windows Remote Management (HTTP-In)" -Name "Windows Remote Management (HTTP-In)" -Profile Any -LocalPort 5985 -Protocol TCP
|
|
272
|
-
PS1
|
|
273
|
-
end
|
|
274
|
-
|
|
275
|
-
def command_to_execute
|
|
276
|
-
'copy /y c:\\\\azuredata\\\\customdata.bin c:\\\\azuredata\\\\customdata.ps1 && powershell.exe -ExecutionPolicy Unrestricted -Command \\"start-process powershell.exe -verb runas -argumentlist c:\\\\azuredata\\\\customdata.ps1\\"'
|
|
277
|
-
end
|
|
278
|
-
|
|
279
|
-
def custom_linux_configuration(public_key)
|
|
280
|
-
<<-EOH
|
|
281
|
-
{
|
|
282
|
-
"disablePasswordAuthentication": "true",
|
|
283
|
-
"ssh": {
|
|
284
|
-
"publicKeys": [
|
|
285
|
-
{
|
|
286
|
-
"path": "[concat('/home/',parameters('adminUsername'),'/.ssh/authorized_keys')]",
|
|
287
|
-
"keyData": "#{public_key}"
|
|
288
|
-
}
|
|
289
|
-
]
|
|
290
|
-
}
|
|
291
|
-
}
|
|
292
|
-
EOH
|
|
293
|
-
end
|
|
294
|
-
|
|
295
|
-
def custom_script_extension_template(command)
|
|
296
|
-
<<-EOH
|
|
297
|
-
{
|
|
298
|
-
"type": "Microsoft.Compute/virtualMachines/extensions",
|
|
299
|
-
"name": "[concat(variables('vmName'),'/','enableWinRM')]",
|
|
300
|
-
"apiVersion": "2015-05-01-preview",
|
|
301
|
-
"location": "[variables('location')]",
|
|
302
|
-
"dependsOn": [
|
|
303
|
-
"[concat('Microsoft.Compute/virtualMachines/',variables('vmName'))]"
|
|
304
|
-
],
|
|
305
|
-
"properties": {
|
|
306
|
-
"publisher": "Microsoft.Compute",
|
|
307
|
-
"type": "CustomScriptExtension",
|
|
308
|
-
"typeHandlerVersion": "1.4",
|
|
309
|
-
"settings": {
|
|
310
|
-
"commandToExecute": "#{command}"
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
}
|
|
314
|
-
EOH
|
|
315
|
-
end
|
|
316
|
-
|
|
317
|
-
def virtual_machine_deployment_template
|
|
318
|
-
<<-EOH
|
|
319
|
-
{
|
|
320
|
-
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
|
|
321
|
-
"contentVersion": "1.0.0.0",
|
|
322
|
-
"parameters": {
|
|
323
|
-
"location": {
|
|
324
|
-
"type": "string",
|
|
325
|
-
"metadata": {
|
|
326
|
-
"description": "The location where the resources will be created."
|
|
327
|
-
}
|
|
328
|
-
},
|
|
329
|
-
"vmSize": {
|
|
330
|
-
"type": "string",
|
|
331
|
-
"metadata": {
|
|
332
|
-
"description": "The size of the VM to be created"
|
|
333
|
-
}
|
|
334
|
-
},
|
|
335
|
-
"newStorageAccountName": {
|
|
336
|
-
"type": "string",
|
|
337
|
-
"metadata": {
|
|
338
|
-
"description": "Unique DNS Name for the Storage Account where the Virtual Machine's disks will be placed."
|
|
339
|
-
}
|
|
340
|
-
},
|
|
341
|
-
"adminUsername": {
|
|
342
|
-
"type": "string",
|
|
343
|
-
"metadata": {
|
|
344
|
-
"description": "User name for the Virtual Machine."
|
|
345
|
-
}
|
|
346
|
-
},
|
|
347
|
-
"adminPassword": {
|
|
348
|
-
"type": "securestring",
|
|
349
|
-
"metadata": {
|
|
350
|
-
"description": "Password for the Virtual Machine."
|
|
351
|
-
}
|
|
352
|
-
},
|
|
353
|
-
"dnsNameForPublicIP": {
|
|
354
|
-
"type": "string",
|
|
355
|
-
"metadata": {
|
|
356
|
-
"description": "Unique DNS Name for the Public IP used to access the Virtual Machine."
|
|
357
|
-
}
|
|
358
|
-
},
|
|
359
|
-
"imagePublisher": {
|
|
360
|
-
"type": "string",
|
|
361
|
-
"defaultValue": "Canonical",
|
|
362
|
-
"metadata": {
|
|
363
|
-
"description": "Publisher for the VM, e.g. Canonical, MicrosoftWindowsServer"
|
|
364
|
-
}
|
|
365
|
-
},
|
|
366
|
-
"imageOffer": {
|
|
367
|
-
"type": "string",
|
|
368
|
-
"defaultValue": "UbuntuServer",
|
|
369
|
-
"metadata": {
|
|
370
|
-
"description": "Offer for the VM, e.g. UbuntuServer, WindowsServer."
|
|
371
|
-
}
|
|
372
|
-
},
|
|
373
|
-
"imageSku": {
|
|
374
|
-
"type": "string",
|
|
375
|
-
"defaultValue": "14.04.3-LTS",
|
|
376
|
-
"metadata": {
|
|
377
|
-
"description": "Sku for the VM, e.g. 14.04.3-LTS"
|
|
378
|
-
}
|
|
379
|
-
},
|
|
380
|
-
"imageVersion": {
|
|
381
|
-
"type": "string",
|
|
382
|
-
"defaultValue": "latest",
|
|
383
|
-
"metadata": {
|
|
384
|
-
"description": "Either a date or latest."
|
|
385
|
-
}
|
|
386
|
-
},
|
|
387
|
-
"vmName": {
|
|
388
|
-
"type": "string",
|
|
389
|
-
"defaultValue": "vm",
|
|
390
|
-
"metadata": {
|
|
391
|
-
"description": "The vm name created inside of the resource group."
|
|
392
|
-
}
|
|
393
|
-
},
|
|
394
|
-
"storageAccountType": {
|
|
395
|
-
"type": "string",
|
|
396
|
-
"defaultValue": "Standard_LRS",
|
|
397
|
-
"metadata": {
|
|
398
|
-
"description": "The type of storage to use (e.g. Standard_LRS or Premium_LRS)."
|
|
399
|
-
}
|
|
400
|
-
},
|
|
401
|
-
"bootDiagnosticsEnabled": {
|
|
402
|
-
"type": "string",
|
|
403
|
-
"defaultValue": "true",
|
|
404
|
-
"metadata": {
|
|
405
|
-
"description": "Whether to enable (true) or disable (false) boot diagnostics. Default: true (requires Standard storage)."
|
|
406
|
-
}
|
|
407
|
-
}
|
|
408
|
-
},
|
|
409
|
-
"variables": {
|
|
410
|
-
"location": "[parameters('location')]",
|
|
411
|
-
"OSDiskName": "osdisk",
|
|
412
|
-
"nicName": "nic",
|
|
413
|
-
"addressPrefix": "10.0.0.0/16",
|
|
414
|
-
"subnetName": "Subnet",
|
|
415
|
-
"subnetPrefix": "10.0.0.0/24",
|
|
416
|
-
"storageAccountType": "[parameters('storageAccountType')]",
|
|
417
|
-
"publicIPAddressName": "publicip",
|
|
418
|
-
"publicIPAddressType": "Dynamic",
|
|
419
|
-
"vmStorageAccountContainerName": "vhds",
|
|
420
|
-
"vmName": "[parameters('vmName')]",
|
|
421
|
-
"vmSize": "[parameters('vmSize')]",
|
|
422
|
-
"virtualNetworkName": "vnet",
|
|
423
|
-
"vnetID": "[resourceId('Microsoft.Network/virtualNetworks',variables('virtualNetworkName'))]",
|
|
424
|
-
"subnetRef": "[concat(variables('vnetID'),'/subnets/',variables('subnetName'))]"
|
|
425
|
-
},
|
|
426
|
-
"resources": [
|
|
427
|
-
{
|
|
428
|
-
"type": "Microsoft.Storage/storageAccounts",
|
|
429
|
-
"name": "[parameters('newStorageAccountName')]",
|
|
430
|
-
"apiVersion": "2015-05-01-preview",
|
|
431
|
-
"location": "[variables('location')]",
|
|
432
|
-
"properties": {
|
|
433
|
-
"accountType": "[variables('storageAccountType')]"
|
|
434
|
-
}
|
|
435
|
-
},
|
|
436
|
-
{
|
|
437
|
-
"apiVersion": "2015-05-01-preview",
|
|
438
|
-
"type": "Microsoft.Network/publicIPAddresses",
|
|
439
|
-
"name": "[variables('publicIPAddressName')]",
|
|
440
|
-
"location": "[variables('location')]",
|
|
441
|
-
"properties": {
|
|
442
|
-
"publicIPAllocationMethod": "[variables('publicIPAddressType')]",
|
|
443
|
-
"dnsSettings": {
|
|
444
|
-
"domainNameLabel": "[parameters('dnsNameForPublicIP')]"
|
|
445
|
-
}
|
|
446
|
-
}
|
|
447
|
-
},
|
|
448
|
-
{
|
|
449
|
-
"apiVersion": "2015-05-01-preview",
|
|
450
|
-
"type": "Microsoft.Network/virtualNetworks",
|
|
451
|
-
"name": "[variables('virtualNetworkName')]",
|
|
452
|
-
"location": "[variables('location')]",
|
|
453
|
-
"properties": {
|
|
454
|
-
"addressSpace": {
|
|
455
|
-
"addressPrefixes": [
|
|
456
|
-
"[variables('addressPrefix')]"
|
|
457
|
-
]
|
|
458
|
-
},
|
|
459
|
-
"subnets": [
|
|
460
|
-
{
|
|
461
|
-
"name": "[variables('subnetName')]",
|
|
462
|
-
"properties": {
|
|
463
|
-
"addressPrefix": "[variables('subnetPrefix')]"
|
|
464
|
-
}
|
|
465
|
-
}
|
|
466
|
-
]
|
|
467
|
-
}
|
|
468
|
-
},
|
|
469
|
-
{
|
|
470
|
-
"apiVersion": "2015-05-01-preview",
|
|
471
|
-
"type": "Microsoft.Network/networkInterfaces",
|
|
472
|
-
"name": "[variables('nicName')]",
|
|
473
|
-
"location": "[variables('location')]",
|
|
474
|
-
"dependsOn": [
|
|
475
|
-
"[concat('Microsoft.Network/publicIPAddresses/', variables('publicIPAddressName'))]",
|
|
476
|
-
"[concat('Microsoft.Network/virtualNetworks/', variables('virtualNetworkName'))]"
|
|
477
|
-
],
|
|
478
|
-
"properties": {
|
|
479
|
-
"ipConfigurations": [
|
|
480
|
-
{
|
|
481
|
-
"name": "ipconfig1",
|
|
482
|
-
"properties": {
|
|
483
|
-
"privateIPAllocationMethod": "Dynamic",
|
|
484
|
-
"publicIPAddress": {
|
|
485
|
-
"id": "[resourceId('Microsoft.Network/publicIPAddresses',variables('publicIPAddressName'))]"
|
|
486
|
-
},
|
|
487
|
-
"subnet": {
|
|
488
|
-
"id": "[variables('subnetRef')]"
|
|
489
|
-
}
|
|
490
|
-
}
|
|
491
|
-
}
|
|
492
|
-
]
|
|
493
|
-
}
|
|
494
|
-
},
|
|
495
|
-
{
|
|
496
|
-
"apiVersion": "2015-06-15",
|
|
497
|
-
"type": "Microsoft.Compute/virtualMachines",
|
|
498
|
-
"name": "[variables('vmName')]",
|
|
499
|
-
"location": "[variables('location')]",
|
|
500
|
-
"dependsOn": [
|
|
501
|
-
"[concat('Microsoft.Storage/storageAccounts/', parameters('newStorageAccountName'))]",
|
|
502
|
-
"[concat('Microsoft.Network/networkInterfaces/', variables('nicName'))]"
|
|
503
|
-
],
|
|
504
|
-
"properties": {
|
|
505
|
-
"hardwareProfile": {
|
|
506
|
-
"vmSize": "[variables('vmSize')]"
|
|
507
|
-
},
|
|
508
|
-
"osProfile": {
|
|
509
|
-
"computername": "[variables('vmName')]",
|
|
510
|
-
"adminUsername": "[parameters('adminUsername')]",
|
|
511
|
-
"adminPassword": "[parameters('adminPassword')]"
|
|
512
|
-
},
|
|
513
|
-
"storageProfile": {
|
|
514
|
-
"imageReference": {
|
|
515
|
-
"publisher": "[parameters('imagePublisher')]",
|
|
516
|
-
"offer": "[parameters('imageOffer')]",
|
|
517
|
-
"sku": "[parameters('imageSku')]",
|
|
518
|
-
"version": "[parameters('imageVersion')]"
|
|
519
|
-
},
|
|
520
|
-
"osDisk": {
|
|
521
|
-
"name": "osdisk",
|
|
522
|
-
"vhd": {
|
|
523
|
-
"uri": "[concat('http://',parameters('newStorageAccountName'),'.blob.core.windows.net/',variables('vmStorageAccountContainerName'),'/',variables('OSDiskName'),'.vhd')]"
|
|
524
|
-
},
|
|
525
|
-
"caching": "ReadWrite",
|
|
526
|
-
"createOption": "FromImage"
|
|
527
|
-
}
|
|
528
|
-
},
|
|
529
|
-
"networkProfile": {
|
|
530
|
-
"networkInterfaces": [
|
|
531
|
-
{
|
|
532
|
-
"id": "[resourceId('Microsoft.Network/networkInterfaces',variables('nicName'))]"
|
|
533
|
-
}
|
|
534
|
-
]
|
|
535
|
-
},
|
|
536
|
-
"diagnosticsProfile": {
|
|
537
|
-
"bootDiagnostics": {
|
|
538
|
-
"enabled": "[parameters('bootDiagnosticsEnabled')]",
|
|
539
|
-
"storageUri": "[concat('http://',parameters('newStorageAccountName'),'.blob.core.windows.net')]"
|
|
540
|
-
}
|
|
541
|
-
}
|
|
542
|
-
}
|
|
543
|
-
}
|
|
544
|
-
]
|
|
545
|
-
}
|
|
546
|
-
EOH
|
|
547
|
-
end
|
|
548
|
-
end
|
|
549
|
-
end
|
|
550
|
-
end
|
|
1
|
+
require 'kitchen'
|
|
2
|
+
require 'kitchen/driver/credentials'
|
|
3
|
+
require 'securerandom'
|
|
4
|
+
require 'azure_mgmt_resources'
|
|
5
|
+
require 'azure_mgmt_network'
|
|
6
|
+
require 'base64'
|
|
7
|
+
require 'sshkey'
|
|
8
|
+
require 'fileutils'
|
|
9
|
+
|
|
10
|
+
module Kitchen
|
|
11
|
+
module Driver
|
|
12
|
+
#
|
|
13
|
+
# Azurerm
|
|
14
|
+
#
|
|
15
|
+
class Azurerm < Kitchen::Driver::Base
|
|
16
|
+
attr_accessor :resource_management_client
|
|
17
|
+
|
|
18
|
+
default_config(:azure_resource_group_name) do |config|
|
|
19
|
+
"kitchen-#{config.instance.name}"
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
default_config(:image_urn) do |_config|
|
|
23
|
+
'Canonical:UbuntuServer:14.04.3-LTS:latest'
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
default_config(:username) do |_config|
|
|
27
|
+
'azure'
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
default_config(:password) do |_config|
|
|
31
|
+
'P2ssw0rd'
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
default_config(:vm_name) do |_config|
|
|
35
|
+
'vm'
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
default_config(:storage_account_type) do |_config|
|
|
39
|
+
'Standard_LRS'
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
default_config(:boot_diagnostics_enabled) do |_config|
|
|
43
|
+
'true'
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
default_config(:winrm_powershell_script) do |_config|
|
|
47
|
+
false
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
default_config(:azure_management_url) do |_config|
|
|
51
|
+
'https://management.azure.com'
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def create(state)
|
|
55
|
+
state = validate_state(state)
|
|
56
|
+
|
|
57
|
+
image_publisher, image_offer, image_sku, image_version = config[:image_urn].split(':', 4)
|
|
58
|
+
deployment_parameters = {
|
|
59
|
+
location: config[:location],
|
|
60
|
+
vmSize: config[:machine_size],
|
|
61
|
+
storageAccountType: config[:storage_account_type],
|
|
62
|
+
bootDiagnosticsEnabled: config[:boot_diagnostics_enabled],
|
|
63
|
+
newStorageAccountName: "storage#{state[:uuid]}",
|
|
64
|
+
adminUsername: state[:username],
|
|
65
|
+
adminPassword: state[:password],
|
|
66
|
+
dnsNameForPublicIP: "kitchen-#{state[:uuid]}",
|
|
67
|
+
imagePublisher: image_publisher,
|
|
68
|
+
imageOffer: image_offer,
|
|
69
|
+
imageSku: image_sku,
|
|
70
|
+
imageVersion: image_version,
|
|
71
|
+
vmName: state[:vm_name]
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
credentials = Kitchen::Driver::Credentials.new.azure_credentials_for_subscription(config[:subscription_id])
|
|
75
|
+
@resource_management_client = ::Azure::ARM::Resources::ResourceManagementClient.new(credentials)
|
|
76
|
+
@resource_management_client.subscription_id = config[:subscription_id]
|
|
77
|
+
|
|
78
|
+
# Create Resource Group
|
|
79
|
+
resource_group = ::Azure::ARM::Resources::Models::ResourceGroup.new
|
|
80
|
+
resource_group.location = config[:location]
|
|
81
|
+
begin
|
|
82
|
+
info "Creating Resource Group: #{state[:azure_resource_group_name]}"
|
|
83
|
+
resource_management_client.resource_groups.create_or_update(state[:azure_resource_group_name], resource_group).value!
|
|
84
|
+
rescue ::MsRestAzure::AzureOperationError => operation_error
|
|
85
|
+
error_message = if operation_error.body.nil? == true
|
|
86
|
+
operation_error.inspect
|
|
87
|
+
else
|
|
88
|
+
operation_error.body['error']
|
|
89
|
+
end
|
|
90
|
+
info error_message
|
|
91
|
+
raise error_message
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
# Execute deployment steps
|
|
95
|
+
begin
|
|
96
|
+
deployment_name = "deploy-#{state[:uuid]}"
|
|
97
|
+
info "Creating Deployment: #{deployment_name}"
|
|
98
|
+
resource_management_client.deployments.create_or_update(state[:azure_resource_group_name], deployment_name, deployment(deployment_parameters)).value!
|
|
99
|
+
rescue ::MsRestAzure::AzureOperationError => operation_error
|
|
100
|
+
rest_error = operation_error.body['error']
|
|
101
|
+
deployment_active = rest_error['code'] == 'DeploymentActive'
|
|
102
|
+
if deployment_active
|
|
103
|
+
info "Deployment for resource group #{state[:azure_resource_group_name]} is ongoing."
|
|
104
|
+
info "If you need to change the deployment template you'll need to rerun `kitchen create` for this instance."
|
|
105
|
+
else
|
|
106
|
+
info rest_error
|
|
107
|
+
raise operation_error
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
# Monitor all operations until completion
|
|
112
|
+
follow_deployment_until_end_state(state[:azure_resource_group_name], deployment_name)
|
|
113
|
+
|
|
114
|
+
# Now retrieve the public IP from the resource group:
|
|
115
|
+
network_management_client = ::Azure::ARM::Network::NetworkResourceProviderClient.new(credentials)
|
|
116
|
+
network_management_client.subscription_id = config[:subscription_id]
|
|
117
|
+
result = network_management_client.public_ip_addresses.get(state[:azure_resource_group_name], 'publicip').value!
|
|
118
|
+
info "IP Address is: #{result.body.properties.ip_address} [#{result.body.properties.dns_settings.fqdn}]"
|
|
119
|
+
state[:hostname] = result.body.properties.ip_address
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def existing_state_value?(state, property)
|
|
123
|
+
state.key?(property) && !state[property].nil?
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def validate_state(state = {})
|
|
127
|
+
state[:uuid] = SecureRandom.hex(8) unless existing_state_value?(state, :uuid)
|
|
128
|
+
state[:server_id] = "vm#{state[:uuid]}" unless existing_state_value?(state, :server_id)
|
|
129
|
+
state[:azure_resource_group_name] = azure_resource_group_name unless existing_state_value?(state, :azure_resource_group_name)
|
|
130
|
+
[:subscription_id, :username, :password, :vm_name, :azure_management_url].each do |config_element|
|
|
131
|
+
state[config_element] = config[config_element] unless existing_state_value?(state, config_element)
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
state
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def azure_resource_group_name
|
|
138
|
+
formatted_time = Time.now.utc.strftime '%Y%m%dT%H%M%S'
|
|
139
|
+
"#{config[:azure_resource_group_name]}-#{formatted_time}"
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
def template_for_transport_name
|
|
143
|
+
template = JSON.parse(virtual_machine_deployment_template)
|
|
144
|
+
if instance.transport.name.casecmp('winrm') == 0
|
|
145
|
+
encoded_command = Base64.strict_encode64(enable_winrm_powershell_script)
|
|
146
|
+
command = command_to_execute
|
|
147
|
+
template['resources'].select { |h| h['type'] == 'Microsoft.Compute/virtualMachines' }.each do |resource|
|
|
148
|
+
resource['properties']['osProfile']['customData'] = encoded_command
|
|
149
|
+
end
|
|
150
|
+
template['resources'] << JSON.parse(custom_script_extension_template(command))
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
unless instance.transport[:ssh_key].nil?
|
|
154
|
+
info "Adding public key from #{File.expand_path(instance.transport[:ssh_key])}.pub to the deployment."
|
|
155
|
+
public_key = public_key_for_deployment(File.expand_path(instance.transport[:ssh_key]))
|
|
156
|
+
template['resources'].select { |h| h['type'] == 'Microsoft.Compute/virtualMachines' }.each do |resource|
|
|
157
|
+
resource['properties']['osProfile']['linuxConfiguration'] = JSON.parse(custom_linux_configuration(public_key))
|
|
158
|
+
end
|
|
159
|
+
end
|
|
160
|
+
template.to_json
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
def public_key_for_deployment(private_key_filename)
|
|
164
|
+
if File.file?(private_key_filename) == false
|
|
165
|
+
k = SSHKey.generate
|
|
166
|
+
|
|
167
|
+
::FileUtils.mkdir_p(File.dirname(private_key_filename))
|
|
168
|
+
|
|
169
|
+
private_key_file = File.new(private_key_filename, 'w')
|
|
170
|
+
private_key_file.syswrite(k.private_key)
|
|
171
|
+
private_key_file.chmod(0600)
|
|
172
|
+
private_key_file.close
|
|
173
|
+
|
|
174
|
+
public_key_file = File.new("#{private_key_filename}.pub", 'w')
|
|
175
|
+
public_key_file.syswrite(k.ssh_public_key)
|
|
176
|
+
public_key_file.chmod(0600)
|
|
177
|
+
public_key_file.close
|
|
178
|
+
|
|
179
|
+
output = k.ssh_public_key
|
|
180
|
+
else
|
|
181
|
+
output = File.read("#{private_key_filename}.pub")
|
|
182
|
+
end
|
|
183
|
+
output.strip
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
def deployment(parameters)
|
|
187
|
+
template = template_for_transport_name
|
|
188
|
+
deployment = ::Azure::ARM::Resources::Models::Deployment.new
|
|
189
|
+
deployment.properties = ::Azure::ARM::Resources::Models::DeploymentProperties.new
|
|
190
|
+
deployment.properties.mode = Azure::ARM::Resources::Models::DeploymentMode::Incremental
|
|
191
|
+
deployment.properties.template = JSON.parse(template)
|
|
192
|
+
deployment.properties.parameters = parameters_in_values_format(parameters)
|
|
193
|
+
debug(deployment.properties.template)
|
|
194
|
+
deployment
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
def parameters_in_values_format(parameters_in)
|
|
198
|
+
parameters = parameters_in.map do |key, value|
|
|
199
|
+
{ key.to_sym => { 'value' => value } }
|
|
200
|
+
end
|
|
201
|
+
parameters.reduce(:merge!)
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
def follow_deployment_until_end_state(resource_group, deployment_name)
|
|
205
|
+
end_provisioning_states = 'Canceled,Failed,Deleted,Succeeded'
|
|
206
|
+
end_provisioning_state_reached = false
|
|
207
|
+
until end_provisioning_state_reached
|
|
208
|
+
list_outstanding_deployment_operations(resource_group, deployment_name)
|
|
209
|
+
sleep 10
|
|
210
|
+
deployment_provisioning_state = deployment_state(resource_group, deployment_name)
|
|
211
|
+
end_provisioning_state_reached = end_provisioning_states.split(',').include?(deployment_provisioning_state)
|
|
212
|
+
end
|
|
213
|
+
info "Resource Template deployment reached end state of '#{deployment_provisioning_state}'."
|
|
214
|
+
show_failed_operations(resource_group, deployment_name) if deployment_provisioning_state == 'Failed'
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
def show_failed_operations(resource_group, deployment_name)
|
|
218
|
+
failed_operations = resource_management_client.deployment_operations.list(resource_group, deployment_name).value!
|
|
219
|
+
failed_operations.body.value.each do |val|
|
|
220
|
+
resource_code = val.properties.status_code
|
|
221
|
+
raise val.properties.status_message.inspect.to_s if resource_code != 'OK'
|
|
222
|
+
end
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
def list_outstanding_deployment_operations(resource_group, deployment_name)
|
|
226
|
+
end_operation_states = 'Failed,Succeeded'
|
|
227
|
+
deployment_operations = resource_management_client.deployment_operations.list(resource_group, deployment_name).value!
|
|
228
|
+
deployment_operations.body.value.each do |val|
|
|
229
|
+
resource_provisioning_state = val.properties.provisioning_state
|
|
230
|
+
resource_name = val.properties.target_resource.resource_name
|
|
231
|
+
resource_type = val.properties.target_resource.resource_type
|
|
232
|
+
end_operation_state_reached = end_operation_states.split(',').include?(resource_provisioning_state)
|
|
233
|
+
unless end_operation_state_reached
|
|
234
|
+
info "Resource #{resource_type} '#{resource_name}' provisioning status is #{resource_provisioning_state}"
|
|
235
|
+
end
|
|
236
|
+
end
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
def deployment_state(resource_group, deployment_name)
|
|
240
|
+
deployments = resource_management_client.deployments.get(resource_group, deployment_name).value!
|
|
241
|
+
deployments.body.properties.provisioning_state
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
def destroy(state)
|
|
245
|
+
return if state[:server_id].nil?
|
|
246
|
+
credentials = Kitchen::Driver::Credentials.new.azure_credentials_for_subscription(state[:subscription_id])
|
|
247
|
+
resource_management_client = ::Azure::ARM::Resources::ResourceManagementClient.new(credentials, state[:azure_management_url])
|
|
248
|
+
resource_management_client.subscription_id = state[:subscription_id]
|
|
249
|
+
begin
|
|
250
|
+
info "Destroying Resource Group: #{state[:azure_resource_group_name]}"
|
|
251
|
+
resource_management_client.resource_groups.begin_delete(state[:azure_resource_group_name]).value!
|
|
252
|
+
info 'Destroy operation accepted and will continue in the background.'
|
|
253
|
+
rescue ::MsRestAzure::AzureOperationError => operation_error
|
|
254
|
+
info operation_error.body['error']
|
|
255
|
+
raise operation_error
|
|
256
|
+
end
|
|
257
|
+
state.delete(:server_id)
|
|
258
|
+
state.delete(:hostname)
|
|
259
|
+
state.delete(:username)
|
|
260
|
+
state.delete(:password)
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
def enable_winrm_powershell_script
|
|
264
|
+
config[:winrm_powershell_script] || <<-PS1
|
|
265
|
+
$cert = New-SelfSignedCertificate -DnsName $env:COMPUTERNAME -CertStoreLocation Cert:\\LocalMachine\\My
|
|
266
|
+
$config = '@{CertificateThumbprint="' + $cert.Thumbprint + '"}'
|
|
267
|
+
winrm create winrm/config/listener?Address=*+Transport=HTTPS $config
|
|
268
|
+
winrm set winrm/config/service/auth '@{Basic="true";Kerberos="false";Negotiate="true";Certificate="false";CredSSP="true"}'
|
|
269
|
+
New-NetFirewallRule -DisplayName "Windows Remote Management (HTTPS-In)" -Name "Windows Remote Management (HTTPS-In)" -Profile Any -LocalPort 5986 -Protocol TCP
|
|
270
|
+
winrm set winrm/config/service '@{AllowUnencrypted="true"}'
|
|
271
|
+
New-NetFirewallRule -DisplayName "Windows Remote Management (HTTP-In)" -Name "Windows Remote Management (HTTP-In)" -Profile Any -LocalPort 5985 -Protocol TCP
|
|
272
|
+
PS1
|
|
273
|
+
end
|
|
274
|
+
|
|
275
|
+
def command_to_execute
|
|
276
|
+
'copy /y c:\\\\azuredata\\\\customdata.bin c:\\\\azuredata\\\\customdata.ps1 && powershell.exe -ExecutionPolicy Unrestricted -Command \\"start-process powershell.exe -verb runas -argumentlist c:\\\\azuredata\\\\customdata.ps1\\"'
|
|
277
|
+
end
|
|
278
|
+
|
|
279
|
+
def custom_linux_configuration(public_key)
|
|
280
|
+
<<-EOH
|
|
281
|
+
{
|
|
282
|
+
"disablePasswordAuthentication": "true",
|
|
283
|
+
"ssh": {
|
|
284
|
+
"publicKeys": [
|
|
285
|
+
{
|
|
286
|
+
"path": "[concat('/home/',parameters('adminUsername'),'/.ssh/authorized_keys')]",
|
|
287
|
+
"keyData": "#{public_key}"
|
|
288
|
+
}
|
|
289
|
+
]
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
EOH
|
|
293
|
+
end
|
|
294
|
+
|
|
295
|
+
def custom_script_extension_template(command)
|
|
296
|
+
<<-EOH
|
|
297
|
+
{
|
|
298
|
+
"type": "Microsoft.Compute/virtualMachines/extensions",
|
|
299
|
+
"name": "[concat(variables('vmName'),'/','enableWinRM')]",
|
|
300
|
+
"apiVersion": "2015-05-01-preview",
|
|
301
|
+
"location": "[variables('location')]",
|
|
302
|
+
"dependsOn": [
|
|
303
|
+
"[concat('Microsoft.Compute/virtualMachines/',variables('vmName'))]"
|
|
304
|
+
],
|
|
305
|
+
"properties": {
|
|
306
|
+
"publisher": "Microsoft.Compute",
|
|
307
|
+
"type": "CustomScriptExtension",
|
|
308
|
+
"typeHandlerVersion": "1.4",
|
|
309
|
+
"settings": {
|
|
310
|
+
"commandToExecute": "#{command}"
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
EOH
|
|
315
|
+
end
|
|
316
|
+
|
|
317
|
+
def virtual_machine_deployment_template
|
|
318
|
+
<<-EOH
|
|
319
|
+
{
|
|
320
|
+
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
|
|
321
|
+
"contentVersion": "1.0.0.0",
|
|
322
|
+
"parameters": {
|
|
323
|
+
"location": {
|
|
324
|
+
"type": "string",
|
|
325
|
+
"metadata": {
|
|
326
|
+
"description": "The location where the resources will be created."
|
|
327
|
+
}
|
|
328
|
+
},
|
|
329
|
+
"vmSize": {
|
|
330
|
+
"type": "string",
|
|
331
|
+
"metadata": {
|
|
332
|
+
"description": "The size of the VM to be created"
|
|
333
|
+
}
|
|
334
|
+
},
|
|
335
|
+
"newStorageAccountName": {
|
|
336
|
+
"type": "string",
|
|
337
|
+
"metadata": {
|
|
338
|
+
"description": "Unique DNS Name for the Storage Account where the Virtual Machine's disks will be placed."
|
|
339
|
+
}
|
|
340
|
+
},
|
|
341
|
+
"adminUsername": {
|
|
342
|
+
"type": "string",
|
|
343
|
+
"metadata": {
|
|
344
|
+
"description": "User name for the Virtual Machine."
|
|
345
|
+
}
|
|
346
|
+
},
|
|
347
|
+
"adminPassword": {
|
|
348
|
+
"type": "securestring",
|
|
349
|
+
"metadata": {
|
|
350
|
+
"description": "Password for the Virtual Machine."
|
|
351
|
+
}
|
|
352
|
+
},
|
|
353
|
+
"dnsNameForPublicIP": {
|
|
354
|
+
"type": "string",
|
|
355
|
+
"metadata": {
|
|
356
|
+
"description": "Unique DNS Name for the Public IP used to access the Virtual Machine."
|
|
357
|
+
}
|
|
358
|
+
},
|
|
359
|
+
"imagePublisher": {
|
|
360
|
+
"type": "string",
|
|
361
|
+
"defaultValue": "Canonical",
|
|
362
|
+
"metadata": {
|
|
363
|
+
"description": "Publisher for the VM, e.g. Canonical, MicrosoftWindowsServer"
|
|
364
|
+
}
|
|
365
|
+
},
|
|
366
|
+
"imageOffer": {
|
|
367
|
+
"type": "string",
|
|
368
|
+
"defaultValue": "UbuntuServer",
|
|
369
|
+
"metadata": {
|
|
370
|
+
"description": "Offer for the VM, e.g. UbuntuServer, WindowsServer."
|
|
371
|
+
}
|
|
372
|
+
},
|
|
373
|
+
"imageSku": {
|
|
374
|
+
"type": "string",
|
|
375
|
+
"defaultValue": "14.04.3-LTS",
|
|
376
|
+
"metadata": {
|
|
377
|
+
"description": "Sku for the VM, e.g. 14.04.3-LTS"
|
|
378
|
+
}
|
|
379
|
+
},
|
|
380
|
+
"imageVersion": {
|
|
381
|
+
"type": "string",
|
|
382
|
+
"defaultValue": "latest",
|
|
383
|
+
"metadata": {
|
|
384
|
+
"description": "Either a date or latest."
|
|
385
|
+
}
|
|
386
|
+
},
|
|
387
|
+
"vmName": {
|
|
388
|
+
"type": "string",
|
|
389
|
+
"defaultValue": "vm",
|
|
390
|
+
"metadata": {
|
|
391
|
+
"description": "The vm name created inside of the resource group."
|
|
392
|
+
}
|
|
393
|
+
},
|
|
394
|
+
"storageAccountType": {
|
|
395
|
+
"type": "string",
|
|
396
|
+
"defaultValue": "Standard_LRS",
|
|
397
|
+
"metadata": {
|
|
398
|
+
"description": "The type of storage to use (e.g. Standard_LRS or Premium_LRS)."
|
|
399
|
+
}
|
|
400
|
+
},
|
|
401
|
+
"bootDiagnosticsEnabled": {
|
|
402
|
+
"type": "string",
|
|
403
|
+
"defaultValue": "true",
|
|
404
|
+
"metadata": {
|
|
405
|
+
"description": "Whether to enable (true) or disable (false) boot diagnostics. Default: true (requires Standard storage)."
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
},
|
|
409
|
+
"variables": {
|
|
410
|
+
"location": "[parameters('location')]",
|
|
411
|
+
"OSDiskName": "osdisk",
|
|
412
|
+
"nicName": "nic",
|
|
413
|
+
"addressPrefix": "10.0.0.0/16",
|
|
414
|
+
"subnetName": "Subnet",
|
|
415
|
+
"subnetPrefix": "10.0.0.0/24",
|
|
416
|
+
"storageAccountType": "[parameters('storageAccountType')]",
|
|
417
|
+
"publicIPAddressName": "publicip",
|
|
418
|
+
"publicIPAddressType": "Dynamic",
|
|
419
|
+
"vmStorageAccountContainerName": "vhds",
|
|
420
|
+
"vmName": "[parameters('vmName')]",
|
|
421
|
+
"vmSize": "[parameters('vmSize')]",
|
|
422
|
+
"virtualNetworkName": "vnet",
|
|
423
|
+
"vnetID": "[resourceId('Microsoft.Network/virtualNetworks',variables('virtualNetworkName'))]",
|
|
424
|
+
"subnetRef": "[concat(variables('vnetID'),'/subnets/',variables('subnetName'))]"
|
|
425
|
+
},
|
|
426
|
+
"resources": [
|
|
427
|
+
{
|
|
428
|
+
"type": "Microsoft.Storage/storageAccounts",
|
|
429
|
+
"name": "[parameters('newStorageAccountName')]",
|
|
430
|
+
"apiVersion": "2015-05-01-preview",
|
|
431
|
+
"location": "[variables('location')]",
|
|
432
|
+
"properties": {
|
|
433
|
+
"accountType": "[variables('storageAccountType')]"
|
|
434
|
+
}
|
|
435
|
+
},
|
|
436
|
+
{
|
|
437
|
+
"apiVersion": "2015-05-01-preview",
|
|
438
|
+
"type": "Microsoft.Network/publicIPAddresses",
|
|
439
|
+
"name": "[variables('publicIPAddressName')]",
|
|
440
|
+
"location": "[variables('location')]",
|
|
441
|
+
"properties": {
|
|
442
|
+
"publicIPAllocationMethod": "[variables('publicIPAddressType')]",
|
|
443
|
+
"dnsSettings": {
|
|
444
|
+
"domainNameLabel": "[parameters('dnsNameForPublicIP')]"
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
},
|
|
448
|
+
{
|
|
449
|
+
"apiVersion": "2015-05-01-preview",
|
|
450
|
+
"type": "Microsoft.Network/virtualNetworks",
|
|
451
|
+
"name": "[variables('virtualNetworkName')]",
|
|
452
|
+
"location": "[variables('location')]",
|
|
453
|
+
"properties": {
|
|
454
|
+
"addressSpace": {
|
|
455
|
+
"addressPrefixes": [
|
|
456
|
+
"[variables('addressPrefix')]"
|
|
457
|
+
]
|
|
458
|
+
},
|
|
459
|
+
"subnets": [
|
|
460
|
+
{
|
|
461
|
+
"name": "[variables('subnetName')]",
|
|
462
|
+
"properties": {
|
|
463
|
+
"addressPrefix": "[variables('subnetPrefix')]"
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
]
|
|
467
|
+
}
|
|
468
|
+
},
|
|
469
|
+
{
|
|
470
|
+
"apiVersion": "2015-05-01-preview",
|
|
471
|
+
"type": "Microsoft.Network/networkInterfaces",
|
|
472
|
+
"name": "[variables('nicName')]",
|
|
473
|
+
"location": "[variables('location')]",
|
|
474
|
+
"dependsOn": [
|
|
475
|
+
"[concat('Microsoft.Network/publicIPAddresses/', variables('publicIPAddressName'))]",
|
|
476
|
+
"[concat('Microsoft.Network/virtualNetworks/', variables('virtualNetworkName'))]"
|
|
477
|
+
],
|
|
478
|
+
"properties": {
|
|
479
|
+
"ipConfigurations": [
|
|
480
|
+
{
|
|
481
|
+
"name": "ipconfig1",
|
|
482
|
+
"properties": {
|
|
483
|
+
"privateIPAllocationMethod": "Dynamic",
|
|
484
|
+
"publicIPAddress": {
|
|
485
|
+
"id": "[resourceId('Microsoft.Network/publicIPAddresses',variables('publicIPAddressName'))]"
|
|
486
|
+
},
|
|
487
|
+
"subnet": {
|
|
488
|
+
"id": "[variables('subnetRef')]"
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
]
|
|
493
|
+
}
|
|
494
|
+
},
|
|
495
|
+
{
|
|
496
|
+
"apiVersion": "2015-06-15",
|
|
497
|
+
"type": "Microsoft.Compute/virtualMachines",
|
|
498
|
+
"name": "[variables('vmName')]",
|
|
499
|
+
"location": "[variables('location')]",
|
|
500
|
+
"dependsOn": [
|
|
501
|
+
"[concat('Microsoft.Storage/storageAccounts/', parameters('newStorageAccountName'))]",
|
|
502
|
+
"[concat('Microsoft.Network/networkInterfaces/', variables('nicName'))]"
|
|
503
|
+
],
|
|
504
|
+
"properties": {
|
|
505
|
+
"hardwareProfile": {
|
|
506
|
+
"vmSize": "[variables('vmSize')]"
|
|
507
|
+
},
|
|
508
|
+
"osProfile": {
|
|
509
|
+
"computername": "[variables('vmName')]",
|
|
510
|
+
"adminUsername": "[parameters('adminUsername')]",
|
|
511
|
+
"adminPassword": "[parameters('adminPassword')]"
|
|
512
|
+
},
|
|
513
|
+
"storageProfile": {
|
|
514
|
+
"imageReference": {
|
|
515
|
+
"publisher": "[parameters('imagePublisher')]",
|
|
516
|
+
"offer": "[parameters('imageOffer')]",
|
|
517
|
+
"sku": "[parameters('imageSku')]",
|
|
518
|
+
"version": "[parameters('imageVersion')]"
|
|
519
|
+
},
|
|
520
|
+
"osDisk": {
|
|
521
|
+
"name": "osdisk",
|
|
522
|
+
"vhd": {
|
|
523
|
+
"uri": "[concat('http://',parameters('newStorageAccountName'),'.blob.core.windows.net/',variables('vmStorageAccountContainerName'),'/',variables('OSDiskName'),'.vhd')]"
|
|
524
|
+
},
|
|
525
|
+
"caching": "ReadWrite",
|
|
526
|
+
"createOption": "FromImage"
|
|
527
|
+
}
|
|
528
|
+
},
|
|
529
|
+
"networkProfile": {
|
|
530
|
+
"networkInterfaces": [
|
|
531
|
+
{
|
|
532
|
+
"id": "[resourceId('Microsoft.Network/networkInterfaces',variables('nicName'))]"
|
|
533
|
+
}
|
|
534
|
+
]
|
|
535
|
+
},
|
|
536
|
+
"diagnosticsProfile": {
|
|
537
|
+
"bootDiagnostics": {
|
|
538
|
+
"enabled": "[parameters('bootDiagnosticsEnabled')]",
|
|
539
|
+
"storageUri": "[concat('http://',parameters('newStorageAccountName'),'.blob.core.windows.net')]"
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
]
|
|
545
|
+
}
|
|
546
|
+
EOH
|
|
547
|
+
end
|
|
548
|
+
end
|
|
549
|
+
end
|
|
550
|
+
end
|
metadata
CHANGED
|
@@ -1,29 +1,29 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: kitchen-azurerm
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.3.
|
|
4
|
+
version: 0.3.6
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Stuart Preston
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2016-
|
|
11
|
+
date: 2016-05-10 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: inifile
|
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
|
16
16
|
requirements:
|
|
17
|
-
- - "
|
|
17
|
+
- - ">="
|
|
18
18
|
- !ruby/object:Gem::Version
|
|
19
|
-
version:
|
|
19
|
+
version: 3.0.0
|
|
20
20
|
type: :runtime
|
|
21
21
|
prerelease: false
|
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
|
23
23
|
requirements:
|
|
24
|
-
- - "
|
|
24
|
+
- - ">="
|
|
25
25
|
- !ruby/object:Gem::Version
|
|
26
|
-
version:
|
|
26
|
+
version: 3.0.0
|
|
27
27
|
- !ruby/object:Gem::Dependency
|
|
28
28
|
name: azure_mgmt_resources
|
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -182,7 +182,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
182
182
|
version: '0'
|
|
183
183
|
requirements: []
|
|
184
184
|
rubyforge_project:
|
|
185
|
-
rubygems_version: 2.
|
|
185
|
+
rubygems_version: 2.6.3
|
|
186
186
|
signing_key:
|
|
187
187
|
specification_version: 4
|
|
188
188
|
summary: Test Kitchen driver for Azure Resource Manager.
|