kitchen-azurerm 0.17.0 → 1.4.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 79955d1437d49030459425fce323174e40d3a437c75dbe52393d9d5cc85df4b3
4
- data.tar.gz: b08a9f9715e9f04fadb5965565604ce95d71a3fc2b38cb836a99b16c1f883df4
3
+ metadata.gz: 51b127c1a7e5d1c197dde573ce77d5e9900fe2297d12e5d8cbcd4d6d1e328ccc
4
+ data.tar.gz: d59c1a55f910f415a271a4818a8879a16e06dff3192d3d3a64d5ce1c32313e6c
5
5
  SHA512:
6
- metadata.gz: a8e2a9e7905a2c670e295d912a995c657021f4171d0e902d77c4c62d780ab1ebfe29365ed943a57224af53d87e0257f261065e3189f986bde8462975745943e0
7
- data.tar.gz: 1857240fc7bf3642dd91e3f8f3be067b4d88e53754afd1ee61b9c9ce1ef730a10a71edc1f145e6e7ec64a8e8e51a07b8e93ff2ac5022ee90f326b16e5bd8c91a
6
+ metadata.gz: 1e21e9a0cce3a113f9d6fd9206a9272282eb1d54c637e2c9f5b629a42ccbf144889355d18db7127a7693e088d8056005139cdf142e3232ed70870c72b23db3f0
7
+ data.tar.gz: '0690d8abddf2c745c7631e463e7479299a0e1e8a83af57caca797e8555a840bc0559c383d7d703b35c7bb7ec4c65b7556a15e95cf2b4582c153f8e45d0dd97e9'
data/README.md CHANGED
@@ -10,15 +10,34 @@ This version has been tested on Windows, macOS, and Ubuntu. If you encounter a p
10
10
 
11
11
  ### Installation
12
12
 
13
- This plugin is distributed as a [Ruby Gem](https://rubygems.org/gems/kitchen-azurerm). To install it, run:
13
+ This plugin ships in Chef Workstation out of the box so there is no need to install it when using Chef Workstation[https://downloads.chef.io/products/workstation].
14
14
 
15
- ```$ gem install kitchen-azurerm```
15
+ If you're not using Chef Workstation and need to install the plugin as a gem run:
16
16
 
17
- Note if you are running the ChefDK you may need to prefix the command with chef, i.e. ```$ chef gem install kitchen-azurerm```
17
+ ```$ gem install kitchen-azurerm```
18
18
 
19
19
  ### Configuration
20
20
 
21
- For the driver to interact with the Microsoft Azure Resource management REST API, a Service Principal needs to be configured with Contributor rights against the specific subscription being targeted. Using an Organizational (AAD) account and related password is no longer supported. To create a Service Principal and apply the correct permissions, you will need to [create and authenticate a service principal](https://azure.microsoft.com/en-us/documentation/articles/resource-group-authenticate-service-principal/#authenticate-service-principal-with-password---azure-cli) using the [Azure CLI](https://azure.microsoft.com/en-us/documentation/articles/xplat-cli-install/). Make sure you stay within the section titled 'Authenticate service principal with password - Azure CLI'.
21
+ For the driver to interact with the Microsoft Azure Resource management REST API, a Service Principal needs to be configured with Contributor rights against the specific subscription being targeted. Using an Organizational (AAD) account and related password is no longer supported. To create a Service Principal and apply the correct permissions, you will need to [create an Azure service principal with the Azure CLI](https://docs.microsoft.com/en-us/cli/azure/create-an-azure-service-principal-azure-cli?view=azure-cli-latest#create-a-service-principal) using the [Azure CLI](https://azure.microsoft.com/en-us/documentation/articles/xplat-cli-install/). Make sure you stay within the section titled 'Authenticate service principal with password - Azure CLI'.
22
+
23
+ If the above is TLDR then try this after `az login` using your target subscription ID and the desired SP name:
24
+
25
+ ```bash
26
+ # Create a Service Principal using the desired subscription id from the command above
27
+ az ad sp create-for-rbac --name="kitchen-azurerm" --role="Contributor" --scopes="/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
28
+
29
+ #Output
30
+ #
31
+ #{
32
+ # "appId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", <- Also known as the Client ID
33
+ # "displayName": "azure-cli-2018-12-12-14-15-39",
34
+ # "name": "http://azure-cli-2018-12-12-14-15-39",
35
+ # "password": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
36
+ # "tenant": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
37
+ #}
38
+ ```
39
+
40
+ NOTE: Don't forget to save the values from the output -- most importantly the `password`.
22
41
 
23
42
  You will also need to ensure you have an active Azure subscription (you can get started [for free](https://azure.microsoft.com/en-us/free/) or use your [MSDN Subscription](https://azure.microsoft.com/en-us/pricing/member-offers/msdn-benefits/)).
24
43
 
@@ -32,22 +51,31 @@ You are now ready to configure kitchen-azurerm to use the credentials from the s
32
51
  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**
33
52
 
34
53
  ```ruby
35
- [abcd1234-YOUR-SUBSCRIPTION-ID-HERE-abcdef123456]
36
- client_id = "48b9bba3-YOUR-GUID-HERE-90f0b68ce8ba"
54
+ [ADD-YOUR-AZURE-SUBSCRIPTION-ID-HERE-IN-SQUARE-BRACKET]
55
+ client_id = "your-azure-client-id-here"
37
56
  client_secret = "your-client-secret-here"
38
- tenant_id = "9c117323-YOUR-GUID-HERE-9ee430723ba3"
57
+ tenant_id = "your-azure-tenant-id-here"
39
58
  ```
40
59
 
41
60
  If preferred, you may also set the following environment variables, however this would be incompatible with supporting multiple Azure subscriptions.
42
61
 
43
62
  ```ruby
44
- AZURE_CLIENT_ID="48b9bba3-YOUR-GUID-HERE-90f0b68ce8ba"
63
+ AZURE_CLIENT_ID="your-azure-client-id-here"
45
64
  AZURE_CLIENT_SECRET="your-client-secret-here"
46
- AZURE_TENANT_ID="9c117323-YOUR-GUID-HERE-9ee430723ba3"
65
+ AZURE_TENANT_ID="your-azure-tenant-id-here"
47
66
  ```
48
67
 
49
68
  Note that the environment variables, if set, take preference over the values in a configuration file.
50
69
 
70
+ After adjusting your ```~/.azure/credentials``` file you will need to adjust your ```kitchen.yml``` file to leverage the azurerm driver. Use the following examples to achieve this, then check your configuration with standard kitchen commands. For example,
71
+
72
+ ```bash
73
+ % kitchen list
74
+ Instance Driver Provisioner Verifier Transport Last Action Last Error
75
+ wsus-windows-2019 Azurerm ChefZero Inspec Winrm <Not Created> <None>
76
+ wsus-windows-2016 Azurerm ChefZero Inspec Winrm <Not Created> <None>
77
+ ```
78
+
51
79
  ### .kitchen.yml example 1 - Linux/Ubuntu
52
80
 
53
81
  Here's an example ```.kitchen.yml``` file that provisions an Ubuntu Server, using Chef Zero as the provisioner and SSH as the transport. Note that if the key does not exist at the specified location, it will be created. Also note that if ```ssh_key``` is supplied, Test Kitchen will use this in preference to any default/configured passwords that are supplied.
@@ -56,7 +84,7 @@ Here's an example ```.kitchen.yml``` file that provisions an Ubuntu Server, usin
56
84
  ---
57
85
  driver:
58
86
  name: azurerm
59
- subscription_id: '4801fa9d-YOUR-GUID-HERE-b265ff49ce21'
87
+ subscription_id: 'your-azure-subscription-id-here'
60
88
  location: 'West Europe'
61
89
  machine_size: 'Standard_D1'
62
90
 
@@ -71,9 +99,6 @@ platforms:
71
99
  driver:
72
100
  image_urn: Canonical:UbuntuServer:14.04.4-LTS:latest
73
101
  vm_name: trusty-vm
74
- vm_tags:
75
- ostype: linux
76
- distro: ubuntu
77
102
 
78
103
  suites:
79
104
  - name: default
@@ -98,7 +123,7 @@ Here's a further example ```.kitchen.yml``` file that will provision a Windows S
98
123
  ---
99
124
  driver:
100
125
  name: azurerm
101
- subscription_id: '4801fa9d-YOUR-GUID-HERE-b265ff49ce21'
126
+ subscription_id: 'your-subscription-id-here'
102
127
  location: 'West Europe'
103
128
  machine_size: 'Standard_DS2_v2'
104
129
 
@@ -113,6 +138,9 @@ platforms:
113
138
  resource_group_tags:
114
139
  project: 'My Cool Project'
115
140
  contact: 'me@somewhere.com'
141
+ vm_tags:
142
+ my_tag: its value
143
+ another_tag: its awesome value
116
144
  transport:
117
145
  name: winrm
118
146
  suites:
@@ -134,7 +162,7 @@ These resources will be created in the same Azure Resource Group as the VM under
134
162
  ---
135
163
  driver:
136
164
  name: azurerm
137
- subscription_id: '4801fa9d-YOUR-GUID-HERE-b265ff49ce21'
165
+ subscription_id: 'your-azure-subscription-id-here'
138
166
  location: 'West Europe'
139
167
  machine_size: 'Standard_D1'
140
168
  pre_deployment_template: predeploy.json
@@ -207,7 +235,7 @@ In this case, the public IP address is not used unless ```public_ip``` is set to
207
235
  ---
208
236
  driver:
209
237
  name: azurerm
210
- subscription_id: '4801fa9d-YOUR-GUID-HERE-b265ff49ce21'
238
+ subscription_id: 'your-azure-subscription-id-here'
211
239
  location: 'West Europe'
212
240
  machine_size: 'Standard_D1'
213
241
 
@@ -241,7 +269,7 @@ Note: The image must be available first. On deletion the disk and everything is
241
269
  ---
242
270
  driver:
243
271
  name: azurerm
244
- subscription_id: '4801fa9d-YOUR-GUID-HERE-b265ff49ce21'
272
+ subscription_id: 'your-azure-subscription-id-here'
245
273
  location: 'West Europe'
246
274
  machine_size: 'Standard_D1'
247
275
 
@@ -282,7 +310,7 @@ This example will:
282
310
  ---
283
311
  driver:
284
312
  name: azurerm
285
- subscription_id: '4801fa9d-YOUR-GUID-HERE-b265ff49ce21'
313
+ subscription_id: 'your-azure-subscription-id-here'
286
314
  location: 'West Europe'
287
315
  machine_size: 'Standard_D1'
288
316
 
@@ -319,7 +347,7 @@ Note: Custom data can be custom data or a file to custom data. Please also note
319
347
  ---
320
348
  driver:
321
349
  name: azurerm
322
- subscription_id: '4801fa9d-YOUR-GUID-HERE-b265ff49ce21'
350
+ subscription_id: 'your-azure-subscription-id-here'
323
351
  location: 'West Europe'
324
352
  machine_size: 'Standard_D1'
325
353
 
@@ -364,7 +392,7 @@ Note the availability of a `format_data_disks` option (default: `false`). When s
364
392
  ---
365
393
  driver:
366
394
  name: azurerm
367
- subscription_id: '4801fa9d-YOUR-GUID-HERE-b265ff49ce21'
395
+ subscription_id: 'your-azure-subscription-id-here'
368
396
  location: 'West Europe'
369
397
  machine_size: 'Standard_F2s'
370
398
 
@@ -403,7 +431,7 @@ These resources will be created in the same Azure Resource Group as the VM under
403
431
  ---
404
432
  driver:
405
433
  name: azurerm
406
- subscription_id: '4801fa9d-YOUR-GUID-HERE-b265ff49ce21'
434
+ subscription_id: 'your-azure-subscription-id-here'
407
435
  location: 'West Europe'
408
436
  machine_size: 'Standard_D1'
409
437
  post_deployment_template: postdeploy.json
@@ -488,7 +516,7 @@ See the [Managed identities for Azure resources](https://docs.microsoft.com/en-u
488
516
  ---
489
517
  driver:
490
518
  name: azurerm
491
- subscription_id: '4801fa9d-YOUR-GUID-HERE-b265ff49ce21'
519
+ subscription_id: 'your-azure-subscription-id-here'
492
520
  location: 'West Europe'
493
521
  machine_size: 'Standard_D1'
494
522
 
@@ -521,7 +549,7 @@ This following example introduces ```secret_url```, ```vault_name```, and ```vau
521
549
  ---
522
550
  driver:
523
551
  name: azurerm
524
- subscription_id: '4801fa9d-YOUR-GUID-HERE-b265ff49ce21'
552
+ subscription_id: 'your-azure-subscription-id-here'
525
553
  location: 'CentralUS'
526
554
  machine_size: 'Standard_D2s_v3'
527
555
  secret_url: 'https://YOUR-SECRET-PATH'
@@ -556,7 +584,7 @@ Note that the ```use_managed_disks``` option should be set to false until suppor
556
584
  ---
557
585
  driver:
558
586
  name: azurerm
559
- subscription_id: 'abcdabcd-YOUR-GUID-HERE-abcdabcdabcd'
587
+ subscription_id: 'your-azure-subscription-id-here'
560
588
  azure_environment: 'AzureUSGovernment'
561
589
  location: 'US Gov Iowa'
562
590
  machine_size: 'Standard_D2_v2_Promo'
@@ -616,9 +644,9 @@ data: Canonical UbuntuServer 15.10-DAILY 15.10.201509220 westeurope
616
644
  info: vm image list command OK
617
645
  ```
618
646
 
619
- ### Additional parameters that can be specified
647
+ ### Additional parameters that can be specified in your `kitchen.yml` or added to your personal `kitchen.local.yml`
620
648
 
621
- * Note that the ```driver``` section can also takes a ```username``` and ```password```. The default username is "azure" and the password is a randomly generated 12 character password that can be found in your local kitchen state file (typically .kitchen/<instance-name>.yml) if you require it for any reason.
649
+ * Note that the ```driver``` section can also take explicit values for ```username``` and ```password```. Otherwise, the default username is "azure" and the password is a randomly generated 24 character password that can be found in your local kitchen state file (typically `.kitchen/<instance-name>.yml`) if you require it for any reason.
622
650
 
623
651
  * The ```storage_account_type``` parameter defaults to 'Standard_LRS' and allows you to switch to premium storage (e.g. 'Premium_LRS')
624
652
 
@@ -642,9 +670,11 @@ info: vm image list command OK
642
670
 
643
671
  * The ```azure_resource_group_prefix``` and ```azure_resource_group_suffix``` can be used to further disambiguate Azure resource group names created by the driver.
644
672
 
645
- * The ```explicit_resource_group_name``` and ```destroy_explicit_resource_group``` (default: "true") parameters can be used in scenarios where you are provided a pre-created Resource Group. Example usage: ```explicit_resource_group_name: kitchen-<%= ENV["USERNAME"] %>```
673
+ * The ```explicit_resource_group_name``` and ```destroy_explicit_resource_group``` (default: "true") parameters can be used in scenarios where you are provided a pre-created Resource Group. Example usage: ```explicit_resource_group_name: kitchen-<%= ENV["USERNAME"] %>```. The ```destroy_explicit_resource_group``` option can now be used after using the ```destroy_resource_group_contents``` option creates an empty resource group to destroy the resource group previously created.
674
+
675
+ * The ```destroy_resource_group_contents``` (default: "false") parameter can be used when you want to destroy the resources within a resource group without destroying the resource group itself. For example, the following configuration options used in combination would use an existing resource group (or create one if it doesn't exist) and will destroy the contents of the resource group in the ```kitchen destroy``` phase. If you wish to destroy the empty resource group created after you empty the resource group with this flag you can now set the ```destroy_explicit_resource_group``` to "true" to destroy the empty resource group.
646
676
 
647
- * The ```destroy_resource_group_contents``` (default: "false") parameter can be used when you want to destroy the resources within a resource group without destroying the resource group itself. For example, the following configuration options used in combination would use an existing resource group (or create one if it doesn't exist) and will destroy the contents of the resource group in the ```kitchen destroy``` phase.
677
+ * The ```destroy_explicit_resource_group_tags``` (default: "true") parameter can be used when you want to remove tags associated with an explicit resource group. The default setting is set to "true" to remain consistent with previous behavior. This should be used in combination with an ```explicit_resource_group_name``` and will be honored during the ```kitchen destroy``` phase.
648
678
 
649
679
  ```yaml
650
680
  ---
@@ -658,6 +688,8 @@ driver:
658
688
 
659
689
  * The ```secret_url```, ```vault_name```, and ```vault_resource_group``` parameters can be used to deploy VM with specified key vault certificate.
660
690
 
691
+ * The ```use_fqdn_hostname``` (default: "false") parameter can be used to determine how kitchen communicates with the Virtual Machine. When true, Kitchen will use the FQDN that is assigned to the Virtual Machine. When false, kitchen will use the public IP address of the machine. This may overcome issues with Corporate firewalls or VPNs blocking Public IP addresses.
692
+
661
693
  ## Enabling alternative WinRM configurations
662
694
 
663
695
  * By default on Windows machines, a PowerShell script runs that enables WinRM over the SSL transport, for Basic, Negotiate and CredSSP connections. To supply your own PowerShell script (e.g. to enable HTTP), use the `winrm_powershell_script` parameter. Windows 2008 R2 example:
@@ -1,4 +1,6 @@
1
1
  require "inifile"
2
+ require "kitchen/logging"
3
+ autoload :MsRest, "ms_rest"
2
4
 
3
5
  module Kitchen
4
6
  module Driver
@@ -6,6 +8,8 @@ module Kitchen
6
8
  # AzureCredentials
7
9
  #
8
10
  class AzureCredentials
11
+ include Kitchen::Logging
12
+
9
13
  CONFIG_PATH = "#{ENV["HOME"]}/.azure/credentials".freeze
10
14
 
11
15
  #
@@ -24,12 +28,6 @@ module Kitchen
24
28
  def initialize(subscription_id:, environment: "Azure")
25
29
  @subscription_id = subscription_id
26
30
  @environment = environment
27
- config_file = ENV["AZURE_CONFIG_FILE"] || File.expand_path(CONFIG_PATH)
28
- if File.file?(config_file)
29
- @credentials = IniFile.load(File.expand_path(config_file))
30
- else
31
- warn "#{CONFIG_PATH} was not found or not accessible. Will use environment variables or MSI."
32
- end
33
31
  end
34
32
 
35
33
  #
@@ -50,8 +48,23 @@ module Kitchen
50
48
 
51
49
  private
52
50
 
51
+ def logger
52
+ Kitchen.logger
53
+ end
54
+
55
+ def config_path
56
+ @config_path ||= File.expand_path(ENV["AZURE_CONFIG_FILE"] || CONFIG_PATH)
57
+ end
58
+
53
59
  def credentials
54
- @credentials ||= {}
60
+ @credentials ||= begin
61
+ if File.file?(config_path)
62
+ IniFile.load(config_path)
63
+ else
64
+ warn "#{config_path} was not found or not accessible."
65
+ {}
66
+ end
67
+ end
55
68
  end
56
69
 
57
70
  def credentials_property(property)
@@ -59,7 +72,7 @@ module Kitchen
59
72
  end
60
73
 
61
74
  def tenant_id!
62
- tenant_id || raise("Must provide tenant id. Use AZURE_TENANT_ID environment variable or set it in credentials file")
75
+ tenant_id || warn("(#{config_path}) does not contain tenant_id neither is the AZURE_TENANT_ID environment variable set.")
63
76
  end
64
77
 
65
78
  def tenant_id
@@ -74,13 +87,37 @@ module Kitchen
74
87
  ENV["AZURE_CLIENT_SECRET"] || credentials_property("client_secret")
75
88
  end
76
89
 
90
+ # Retrieve a token based upon the preferred authentication method.
91
+ #
92
+ # @return [::MsRest::TokenProvider] A new token provider object.
77
93
  def token_provider
78
- if client_id && client_secret
94
+ # Login with a credentials file or setting the environment variables
95
+ #
96
+ # Typically used with a service principal.
97
+ #
98
+ # SPN with client_id, client_secret and tenant_id
99
+ if client_id && client_secret && tenant_id
79
100
  ::MsRestAzure::ApplicationTokenProvider.new(tenant_id, client_id, client_secret, ad_settings)
80
- elsif client_id
101
+ # Login with a Managed Service Identity.
102
+ #
103
+ # Typically used with a Managed Service Identity when you have a particular object registered in a tenant.
104
+ #
105
+ # MSI with client_id and tenant_id (aka User Assigned Identity).
106
+ elsif client_id && tenant_id
81
107
  ::MsRestAzure::MSITokenProvider.new(50342, ad_settings, { client_id: client_id })
82
- else
108
+ # Default approach to inheriting existing object permissions (application or device this code is running on).
109
+ #
110
+ # Typically used when you want to inherit the permissions of the system you're running on that are in a tenant.
111
+ #
112
+ # MSI with just tenant_id (aka System Assigned Identity).
113
+ elsif tenant_id
83
114
  ::MsRestAzure::MSITokenProvider.new(50342, ad_settings)
115
+ # Login using the Azure CLI
116
+ #
117
+ # Typically used when you want to rely upon `az login` as your preferred authentication method.
118
+ else
119
+ warn("Using tenant id set through `az login`.")
120
+ ::MsRestAzure::AzureCliTokenProvider.new(ad_settings)
84
121
  end
85
122
  end
86
123
 
@@ -1,21 +1,27 @@
1
1
  require "kitchen"
2
+
3
+ autoload :MsRestAzure, "ms_rest_azure"
2
4
  require_relative "azure_credentials"
3
- require "securerandom"
4
- require "azure_mgmt_resources"
5
- require "azure_mgmt_network"
6
- require "base64"
7
- require "sshkey"
8
- require "fileutils"
9
- require "erb"
10
- require "ostruct"
11
- require "json"
12
- require "faraday"
5
+ require "securerandom" unless defined?(SecureRandom)
6
+ module Azure
7
+ autoload :Resources, "azure_mgmt_resources"
8
+ autoload :Network, "azure_mgmt_network"
9
+ end
10
+ require "base64" unless defined?(Base64)
11
+ autoload :SSHKey, "sshkey"
12
+ require "fileutils" unless defined?(FileUtils)
13
+ require "erb" unless defined?(Erb)
14
+ require "ostruct" unless defined?(OpenStruct)
15
+ require "json" unless defined?(JSON)
16
+ autoload :Faraday, "faraday"
13
17
 
14
18
  module Kitchen
15
19
  module Driver
16
20
  #
17
21
  # Azurerm
22
+ # Create a new resource group object and set the location and tags attributes then return it.
18
23
  #
24
+ # @return [::Azure::Resources::Profiles::Latest::Mgmt::Models::ResourceGroup] A new resource group object.
19
25
  class Azurerm < Kitchen::Driver::Base
20
26
  attr_accessor :resource_management_client
21
27
  attr_accessor :network_management_client
@@ -75,7 +81,7 @@ module Kitchen
75
81
  end
76
82
 
77
83
  default_config(:password) do |_config|
78
- SecureRandom.base64(12)
84
+ SecureRandom.base64(25)
79
85
  end
80
86
 
81
87
  default_config(:vm_name) do |_config|
@@ -174,6 +180,10 @@ module Kitchen
174
180
  true
175
181
  end
176
182
 
183
+ default_config(:destroy_explicit_resource_group_tags) do |_config|
184
+ true
185
+ end
186
+
177
187
  default_config(:destroy_resource_group_contents) do |_config|
178
188
  false
179
189
  end
@@ -202,6 +212,10 @@ module Kitchen
202
212
  5
203
213
  end
204
214
 
215
+ default_config(:use_fqdn_hostname) do |_config|
216
+ false
217
+ end
218
+
205
219
  def create(state)
206
220
  state = validate_state(state)
207
221
  deployment_parameters = {
@@ -210,22 +224,22 @@ module Kitchen
210
224
  storageAccountType: config[:storage_account_type],
211
225
  bootDiagnosticsEnabled: config[:boot_diagnostics_enabled],
212
226
  newStorageAccountName: "storage#{state[:uuid]}",
213
- adminUsername: state[:username],
227
+ adminUsername: config[:username],
214
228
  dnsNameForPublicIP: "kitchen-#{state[:uuid]}",
215
229
  vmName: state[:vm_name],
216
230
  systemAssignedIdentity: config[:system_assigned_identity],
217
- userAssignedIdentities: config[:user_assigned_identities],
231
+ userAssignedIdentities: config[:user_assigned_identities].map { |identity| [identity, {}] }.to_h,
218
232
  secretUrl: config[:secret_url],
219
233
  vaultName: config[:vault_name],
220
234
  vaultResourceGroup: config[:vault_resource_group],
221
235
  }
222
236
 
223
237
  if instance.transport[:ssh_key].nil?
224
- deployment_parameters["adminPassword"] = state[:password]
238
+ deployment_parameters[:adminPassword] = config[:password]
225
239
  end
226
240
 
227
241
  if config[:subscription_id].to_s == ""
228
- raise "A subscription_id config value was not detected and kitchen-azurerm cannot continue. Please check your .kitchen.yml configuration. Exiting."
242
+ raise "A subscription_id config value was not detected and kitchen-azurerm cannot continue. Please check your kitchen.yml configuration. Exiting."
229
243
  end
230
244
 
231
245
  if config[:nic_name].to_s == ""
@@ -277,12 +291,9 @@ module Kitchen
277
291
  @resource_management_client = ::Azure::Resources::Profiles::Latest::Mgmt::Client.new(options)
278
292
 
279
293
  # Create Resource Group
280
- resource_group = ::Azure::Resources::Profiles::Latest::Mgmt::Models::ResourceGroup.new
281
- resource_group.location = config[:location]
282
- resource_group.tags = config[:resource_group_tags]
283
294
  begin
284
295
  info "Creating Resource Group: #{state[:azure_resource_group_name]}"
285
- create_resource_group(state[:azure_resource_group_name], resource_group)
296
+ create_resource_group(state[:azure_resource_group_name], get_resource_group)
286
297
  rescue ::MsRestAzure::AzureOperationError => operation_error
287
298
  error operation_error.body
288
299
  raise operation_error
@@ -300,6 +311,9 @@ module Kitchen
300
311
  info "Creating deployment: #{deployment_name}"
301
312
  create_deployment_async(state[:azure_resource_group_name], deployment_name, deployment(deployment_parameters)).value!
302
313
  follow_deployment_until_end_state(state[:azure_resource_group_name], deployment_name)
314
+ state[:username] = deployment_parameters[:adminUsername] unless existing_state_value?(state, :username)
315
+ state[:password] = deployment_parameters[:adminPassword] unless existing_state_value?(state, :password) && instance.transport[:ssh_key].nil?
316
+
303
317
  if File.file?(config[:post_deployment_template])
304
318
  post_deployment_name = "post-deploy-#{state[:uuid]}"
305
319
  info "Creating deployment: #{post_deployment_name}"
@@ -325,6 +339,10 @@ module Kitchen
325
339
  result = get_public_ip(state[:azure_resource_group_name], "publicip")
326
340
  info "IP Address is: #{result.ip_address} [#{result.dns_settings.fqdn}]"
327
341
  state[:hostname] = result.ip_address
342
+ if config[:use_fqdn_hostname]
343
+ info "Using FQDN to communicate instead of IP"
344
+ state[:hostname] = result.dns_settings.fqdn
345
+ end
328
346
  else
329
347
  # Retrieve the internal IP from the resource group:
330
348
  result = get_network_interface(state[:azure_resource_group_name], vmnic.to_s)
@@ -333,15 +351,24 @@ module Kitchen
333
351
  end
334
352
  end
335
353
 
354
+ # Return a True of False if the state is already stored for a particular property.
355
+ #
356
+ # @param [Hash] Hash of existing state values.
357
+ # @param [String] A property to check
358
+ # @return [Boolean]
336
359
  def existing_state_value?(state, property)
337
360
  state.key?(property) && !state[property].nil?
338
361
  end
339
362
 
363
+ # Leverage existing state values or bring state into existence from a configuration file.
364
+ #
365
+ # @param [Hash] Existing Hash of state values.
366
+ # @return [Hash] Updated Hash of state values.
340
367
  def validate_state(state = {})
341
368
  state[:uuid] = SecureRandom.hex(8) unless existing_state_value?(state, :uuid)
342
369
  state[:server_id] = "vm#{state[:uuid]}" unless existing_state_value?(state, :server_id)
343
370
  state[:azure_resource_group_name] = azure_resource_group_name unless existing_state_value?(state, :azure_resource_group_name)
344
- %i{subscription_id username password vm_name azure_environment use_managed_disks}.each do |config_element|
371
+ %i{subscription_id vm_name azure_environment use_managed_disks}.each do |config_element|
345
372
  state[config_element] = config[config_element] unless existing_state_value?(state, config_element)
346
373
  end
347
374
  state.delete(:password) unless instance.transport[:ssh_key].nil?
@@ -522,11 +549,38 @@ module Kitchen
522
549
  end
523
550
 
524
551
  def destroy(state)
525
- return if state[:server_id].nil?
552
+ # TODO: We have some not so fun state issues we need to clean up
553
+ state[:azure_environment] = config[:azure_environment] unless state[:azure_environment]
554
+ state[:subscription_id] = config[:subscription_id] unless state[:subscription_id]
526
555
 
556
+ # Setup our authentication components for the SDK
527
557
  options = Kitchen::Driver::AzureCredentials.new(subscription_id: state[:subscription_id],
528
- environment: state[:azure_environment]).azure_options
558
+ environment: state[:azure_environment]).azure_options
529
559
  @resource_management_client = ::Azure::Resources::Profiles::Latest::Mgmt::Client.new(options)
560
+
561
+ # If we don't have any instances, let's check to see if the user wants to delete a resource group and if so let's delete!
562
+ if state[:server_id].nil? && state[:azure_resource_group_name].nil? && !config[:explicit_resource_group_name].nil? && config[:destroy_explicit_resource_group]
563
+ if resource_group_exists?(config[:explicit_resource_group_name])
564
+ info "This instance doesn't exist but you asked to delete the resource group."
565
+ begin
566
+ info "Destroying Resource Group: #{config[:explicit_resource_group_name]}"
567
+ delete_resource_group_async(config[:explicit_resource_group_name])
568
+ info "Destroy operation accepted and will continue in the background."
569
+ return
570
+ rescue ::MsRestAzure::AzureOperationError => operation_error
571
+ error operation_error.body
572
+ raise operation_error
573
+ end
574
+ end
575
+ end
576
+
577
+ # Our working environment
578
+ info "Azure environment: #{state[:azure_environment]}"
579
+
580
+ # Skip if we don't have any instances
581
+ return if state[:server_id].nil?
582
+
583
+ # Destroy resource group contents
530
584
  if config[:destroy_resource_group_contents] == true
531
585
  info "Destroying individual resources within the Resource Group."
532
586
  empty_deployment_name = "empty-deploy-#{state[:uuid]}"
@@ -534,25 +588,49 @@ module Kitchen
534
588
  info "Creating deployment: #{empty_deployment_name}"
535
589
  create_deployment_async(state[:azure_resource_group_name], empty_deployment_name, empty_deployment).value!
536
590
  follow_deployment_until_end_state(state[:azure_resource_group_name], empty_deployment_name)
591
+
592
+ # NOTE: We are using the internal wrapper function create_resource_group() which wraps the API
593
+ # method of create_or_update()
594
+ begin
595
+ # Maintain tags on the resource group
596
+ create_resource_group(state[:azure_resource_group_name], get_resource_group) unless config[:destroy_explicit_resource_group_tags] == true
597
+ warn 'The "destroy_explicit_resource_group_tags" setting value is set to "false". The tags on the resource group will NOT be removed.' unless config[:destroy_explicit_resource_group_tags] == true
598
+ # Corner case where we want to use kitchen to remove the tags
599
+ resource_group = get_resource_group
600
+ resource_group.tags = {}
601
+ create_resource_group(state[:azure_resource_group_name], resource_group) unless config[:destroy_explicit_resource_group_tags] == false
602
+ warn 'The "destroy_explicit_resource_group_tags" setting value is set to "true". The tags on the resource group will be removed.' unless config[:destroy_explicit_resource_group_tags] == false
603
+ rescue ::MsRestAzure::AzureOperationError => operation_error
604
+ error operation_error.body
605
+ raise operation_error
606
+ end
607
+
537
608
  rescue ::MsRestAzure::AzureOperationError => operation_error
538
609
  error operation_error.body
539
610
  raise operation_error
540
611
  end
541
612
  end
613
+
614
+ # Do not remove the explicitly named resource group
542
615
  if config[:destroy_explicit_resource_group] == false && !config[:explicit_resource_group_name].nil?
543
616
  warn 'The "destroy_explicit_resource_group" setting value is set to "false". The resource group will not be deleted.'
544
617
  warn 'Remember to manually destroy resources, or set "destroy_resource_group_contents: true" to save costs!' unless config[:destroy_resource_group_contents] == true
545
- return
618
+ return state
546
619
  end
547
- info "Azure environment: #{state[:azure_environment]}"
620
+
621
+ # Destroy the world
548
622
  begin
549
623
  info "Destroying Resource Group: #{state[:azure_resource_group_name]}"
550
624
  delete_resource_group_async(state[:azure_resource_group_name])
551
625
  info "Destroy operation accepted and will continue in the background."
626
+ # Remove resource group name from driver state
627
+ state.delete(:azure_resource_group_name)
552
628
  rescue ::MsRestAzure::AzureOperationError => operation_error
553
629
  error operation_error.body
554
630
  raise operation_error
555
631
  end
632
+
633
+ # Clear state of components
556
634
  state.delete(:server_id)
557
635
  state.delete(:hostname)
558
636
  state.delete(:username)
@@ -706,6 +784,36 @@ module Kitchen
706
784
  # Wrapper methods for the Azure API calls to retry the calls when getting timeouts.
707
785
  #
708
786
 
787
+ # Create a new resource group object and set the location and tags attributes then return it.
788
+ #
789
+ # @return [::Azure::Resources::Profiles::Latest::Mgmt::Models::ResourceGroup] A new resource group object.
790
+ def get_resource_group
791
+ resource_group = ::Azure::Resources::Profiles::Latest::Mgmt::Models::ResourceGroup.new
792
+ resource_group.location = config[:location]
793
+ resource_group.tags = config[:resource_group_tags]
794
+ resource_group
795
+ end
796
+
797
+ # Checks whether a resource group exists.
798
+ #
799
+ # @param resource_group_name [String] The name of the resource group to check.
800
+ # The name is case insensitive.
801
+ #
802
+ # @return [Boolean] operation results.
803
+ #
804
+ def resource_group_exists?(resource_group_name)
805
+ retries = config[:azure_api_retries]
806
+ begin
807
+ resource_management_client.resource_groups.check_existence(resource_group_name)
808
+ rescue Faraday::TimeoutError, Faraday::ClientError => exception
809
+ send_exception_message(exception, "while checking if resource group '#{resource_group_name}' exists. #{retries} retries left.")
810
+ raise if retries == 0
811
+
812
+ retries -= 1
813
+ retry
814
+ end
815
+ end
816
+
709
817
  def create_resource_group(resource_group_name, resource_group)
710
818
  retries = config[:azure_api_retries]
711
819
  begin
@@ -178,10 +178,10 @@
178
178
  }
179
179
  },
180
180
  "userAssignedIdentities": {
181
- "type": "array",
182
- "defaultValue": [],
181
+ "type": "object",
182
+ "defaultValue": {},
183
183
  "metadata": {
184
- "description": "A list of resource IDs for user identities to associate with the Virtual Machine, or empty to disable user assigned identities."
184
+ "description": "An object whose keys are resource IDs for user identities to associate with the Virtual Machine and whose values are empty objects, or empty to disable user assigned identities."
185
185
  }
186
186
  },
187
187
  "bootDiagnosticsEnabled": {
@@ -415,7 +415,7 @@
415
415
  <%- end -%>
416
416
  "identity": {
417
417
  "type": "[variables('vmIdentityType')]",
418
- "identityIds": "[if(empty(parameters('userAssignedIdentities')), json('null'), parameters('userAssignedIdentities'))]"
418
+ "userAssignedIdentities": "[if(empty(parameters('userAssignedIdentities')), json('null'), parameters('userAssignedIdentities'))]"
419
419
  },
420
420
  "tags": {
421
421
  <%= vm_tags unless vm_tags.empty? %>
@@ -178,10 +178,10 @@
178
178
  }
179
179
  },
180
180
  "userAssignedIdentities": {
181
- "type": "array",
182
- "defaultValue": [],
181
+ "type": "object",
182
+ "defaultValue": {},
183
183
  "metadata": {
184
- "description": "A list of resource IDs for user identities to associate with the Virtual Machine, or empty to disable user assigned identities."
184
+ "description": "An object whose keys are resource IDs for user identities to associate with the Virtual Machine and whose values are empty objects, or empty to disable user assigned identities."
185
185
  }
186
186
  },
187
187
  "bootDiagnosticsEnabled": {
@@ -434,7 +434,7 @@
434
434
  <%- end -%>
435
435
  "identity": {
436
436
  "type": "[variables('vmIdentityType')]",
437
- "identityIds": "[if(empty(parameters('userAssignedIdentities')), json('null'), parameters('userAssignedIdentities'))]"
437
+ "userAssignedIdentities": "[if(empty(parameters('userAssignedIdentities')), json('null'), parameters('userAssignedIdentities'))]"
438
438
  },
439
439
  "tags": {
440
440
  <%= vm_tags unless vm_tags.empty? %>
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: kitchen-azurerm
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.17.0
4
+ version: 1.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Stuart Preston
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-04-23 00:00:00.000000000 Z
11
+ date: 2020-09-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: azure_mgmt_network
@@ -128,16 +128,16 @@ dependencies:
128
128
  name: chefstyle
129
129
  requirement: !ruby/object:Gem::Requirement
130
130
  requirements:
131
- - - ">="
131
+ - - '='
132
132
  - !ruby/object:Gem::Version
133
- version: '0'
133
+ version: 1.4.2
134
134
  type: :development
135
135
  prerelease: false
136
136
  version_requirements: !ruby/object:Gem::Requirement
137
137
  requirements:
138
- - - ">="
138
+ - - '='
139
139
  - !ruby/object:Gem::Version
140
- version: '0'
140
+ version: 1.4.2
141
141
  - !ruby/object:Gem::Dependency
142
142
  name: rspec
143
143
  requirement: !ruby/object:Gem::Requirement
@@ -212,7 +212,7 @@ homepage: https://github.com/test-kitchen/kitchen-azurerm
212
212
  licenses:
213
213
  - Apache-2.0
214
214
  metadata: {}
215
- post_install_message:
215
+ post_install_message:
216
216
  rdoc_options: []
217
217
  require_paths:
218
218
  - lib
@@ -228,7 +228,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
228
228
  version: '0'
229
229
  requirements: []
230
230
  rubygems_version: 3.1.2
231
- signing_key:
231
+ signing_key:
232
232
  specification_version: 4
233
233
  summary: Test Kitchen driver for Azure Resource Manager.
234
234
  test_files: []