kitchen-azurerm 0.15.1 → 1.1.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: '03965a2dd5197abe1ccce35906e33c7ba4dbf996c047b305b1d5b29abb3bc54c'
4
- data.tar.gz: 7d46770f6836c397717d6324d3af29bc57d4ce0ce87055decf8b21388aa326c1
3
+ metadata.gz: c53c5b50e83af16c56722ecbc2baa30b5a28621cc17f4edde82a59b22420812f
4
+ data.tar.gz: cd39338fdf0d29d39780e93b6a9fc8c27cd955daf59b6fc79f3be56801c7d891
5
5
  SHA512:
6
- metadata.gz: 97e66a086d8565a6f2d4e7674dce0c527f2563465db2804d2de266b3e5eaf4d08f376704a6291cef68f0374d61803dfbc48a11c8c3704d3aa2fe9e019f975c43
7
- data.tar.gz: e63882365344c12bfface30030b4c6b32caf8c786cd39947047085cd40d23e1b8160b87a98c0647b1a41a4b1863de172e850903ce5e2ee81a490b7fc3acc25c8
6
+ metadata.gz: d62cae11a2fa2a679657fc9c81e6b03547802cd7773f6b801b644388beb09b6fe44de2f0d5f2e9a1601cf3eee92b7a2d86e1f29e7dd86b09e3e6445fdceaebd5
7
+ data.tar.gz: 8bbccc8d27fef365420257f9a11d89cd8b326cf6e60c97e737c1fe304b14f28ee543793c314e9e638096b606c6ccc64986e7d4db94345d53463981b84eadc931
data/LICENSE CHANGED
@@ -186,7 +186,7 @@
186
186
  same "printed page" as the copyright notice for easier
187
187
  identification within third-party archives.
188
188
 
189
- Copyright 2017 Pendrica Ltd.
189
+ Copyright [yyyy] [name of copyright owner]
190
190
 
191
191
  Licensed under the Apache License, Version 2.0 (the "License");
192
192
  you may not use this file except in compliance with the License.
data/README.md CHANGED
@@ -1,24 +1,43 @@
1
1
  # kitchen-azurerm
2
2
 
3
- [![Gem Version](https://badge.fury.io/rb/kitchen-azurerm.svg)](http://badge.fury.io/rb/kitchen-azurerm) [![Build Status](https://travis-ci.org/test-kitchen/kitchen-azurerm.svg)](https://travis-ci.org/test-kitchen/kitchen-azurerm)
3
+ [![Gem Version](https://badge.fury.io/rb/kitchen-azurerm.svg)](http://badge.fury.io/rb/kitchen-azurerm) ![CI](https://github.com/test-kitchen/kitchen-azurerm/workflows/CI/badge.svg?branch=master)
4
4
 
5
- **kitchen-azurerm** is a driver for the popular test harness [Test Kitchen](http://kitchen.ci) that allows Microsoft Azure resources to be provisioned prior to testing. This driver uses the new Microsoft Azure Resource Management REST API via the [azure-sdk-for-ruby](https://github.com/azure/azure-sdk-for-ruby).
5
+ **kitchen-azurerm** is a driver for the popular test harness [Test Kitchen](http://kitchen.ci) that allows Microsoft Azure resources to be provisioned before testing. This driver uses the new Microsoft Azure Resource Management REST API via the [azure-sdk-for-ruby](https://github.com/azure/azure-sdk-for-ruby).
6
6
 
7
- This version has been tested on Windows, OS/X and Ubuntu. If you encounter a problem on your platform, please raise an issue.
7
+ This version has been tested on Windows, macOS, and Ubuntu. If you encounter a problem on your platform, please raise an issue.
8
8
 
9
9
  ## Quick-start
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
 
@@ -29,25 +48,34 @@ You are now ready to configure kitchen-azurerm to use the credentials from the s
29
48
  3. **Client Secret/Password**: this will be the password you supplied in the command in step 2.
30
49
  4. **Tenant ID**: use the command detailed in "Manually provide credentials through Azure CLI" step 1 to get the TenantId.
31
50
 
32
- 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**
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
@@ -84,7 +109,7 @@ suites:
84
109
 
85
110
  ### Concurrent execution
86
111
 
87
- Concurrent execution of create/converge/destroy is supported via the --concurrency parameter. Each machine is created in it's own Azure Resource Group so has no shared lifecycle with the other machines in the test run. To take advantage of parallel execution use the following command:
112
+ Concurrent execution of create/converge/destroy is supported via the --concurrency parameter. Each machine is created in its own Azure Resource Group so it has no shared lifecycle with the other machines in the test run. To take advantage of parallel execution use the following command:
88
113
 
89
114
  ```kitchen test --concurrency <n>```
90
115
 
@@ -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:
@@ -125,16 +153,16 @@ suites:
125
153
  ### .kitchen.yml example 3 - "pre-deployment" ARM template
126
154
 
127
155
  The following example introduces the ```pre_deployment_template``` and ```pre_deployment_parameters``` properties in the configuration file.
128
- You can use this capability to execute an ARM template containing Azure resources to provision before the system under test is created.
156
+ You can use this capability to execute an ARM template containing Azure resources to provision before the system under test is created.
129
157
 
130
- In the example the ARM template in the file ```predeploy.json``` would be executed with the parameters that are specified under ```pre_deployment_parameters```.
158
+ In the example the ARM template in the file ```predeploy.json``` would be executed with the parameters that are specified under ```pre_deployment_parameters```.
131
159
  These resources will be created in the same Azure Resource Group as the VM under test, and therefore will be destroyed when you type ```kitchen destroy```.
132
160
 
133
161
  ```yaml
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
@@ -198,7 +226,7 @@ Example predeploy.json:
198
226
 
199
227
  ### .kitchen.yml example 4 - deploy VM to existing virtual network/subnet (use for ExpressRoute/VPN scenarios)
200
228
 
201
- The following example introduces the ```vnet_id``` and ```subnet_id``` properties under "driver" in the configuration file. This can be applied at the top level, or per platform.
229
+ The following example introduces the ```vnet_id``` and ```subnet_id``` properties under "driver" in the configuration file. This can be applied at the top level, or per platform.
202
230
  You can use this capability to create the VM on an existing virtual network and subnet created in a different resource group.
203
231
 
204
232
  In this case, the public IP address is not used unless ```public_ip``` is set to ```true```
@@ -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
 
@@ -270,7 +298,7 @@ suites:
270
298
 
271
299
  This example a classic Custom VM Image (aka a VHD file) is used. As the Image VHD must be in the same storage account then the disk of the instance, the os disk is created in an existing image account.
272
300
 
273
- Note: When the resource group ís deleted, the os disk is left in the extsing storage account blob. You must cleanup manually.
301
+ Note: When the resource group ís deleted, the os disk is left in the existing storage account blob. You must clean up manually.
274
302
 
275
303
  This example will:
276
304
 
@@ -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
 
@@ -358,13 +386,13 @@ suites:
358
386
 
359
387
  This example demonstrates how to add 3 additional Managed data disks to a Windows Server 2016 VM. Not supported with legacy (pre-managed disk) storage accounts.
360
388
 
361
- Note the availability of a `format_data_disks` option (default: `false`). When set to true, a PowerShell script will execute at first boot to initialize and format the disks with an NTFS filesystem. This option has no effect on Linux machines.
389
+ Note the availability of a `format_data_disks` option (default: `false`). When set to true, a PowerShell script will execute at first boot to initialize and format the disks with an NTFS filesystem. This option does not affect Linux machines.
362
390
 
363
391
  ```yaml
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
 
@@ -394,16 +422,16 @@ suites:
394
422
  ### .kitchen.yml example 9 - "post-deployment" ARM template with MSI authentication
395
423
 
396
424
  The following example introduces the ```post_deployment_template``` and ```post_deployment_parameters``` properties in the configuration file.
397
- You can use this capability to execute an ARM template containing Azure resources to provision after the system under test is created.
425
+ You can use this capability to execute an ARM template containing Azure resources to provision after the system under test is created.
398
426
 
399
- In the example the ARM template in the file ```postdeploy.json``` would be executed with the parameters that are specified under ```post_deployment_parameters```.
427
+ In the example the ARM template in the file ```postdeploy.json``` would be executed with the parameters that are specified under ```post_deployment_parameters```.
400
428
  These resources will be created in the same Azure Resource Group as the VM under test, and therefore will be destroyed when you type ```kitchen destroy```.
401
429
 
402
430
  ```yaml
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'
@@ -546,7 +574,7 @@ suites:
546
574
 
547
575
  ## Support for Government and Sovereign Clouds (China and Germany)
548
576
 
549
- Starting with v0.9.0 this driver has support for Azure Government and Sovereign Clouds via the use of the ```azure_environment``` setting. Valid Azure environments are ```Azure```, ```AzureUSGovernment```, ```AzureChina``` and ```AzureGermanCloud```
577
+ Starting with v0.9.0 this driver has support for Azure Government and Sovereign Clouds via the use of the ```azure_environment``` setting. Valid Azure environments are ```Azure```, ```AzureUSGovernment```, ```AzureChina``` and ```AzureGermanCloud```
550
578
 
551
579
  Note that the ```use_managed_disks``` option should be set to false until supported by AzureUSGovernment.
552
580
 
@@ -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 also takes a ```username``` and ```password``` parameter, the defaults if these are not specified are "azure" and "P2ssw0rd" respectively.
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
 
@@ -626,6 +654,8 @@ info: vm image list command OK
626
654
 
627
655
  * The optional ```vm_tags``` parameter allows you to define key:value pairs to tag VMs with on creation.
628
656
 
657
+ * The optional ```plan``` parameter allows you to define plan information when creating VMs from Marketplace images. Please refer to [Deploy an image with Marketplace terms](https://aka.ms/azuremarketplaceapideployment) for more details. Not all Marketplace images support programmatic deployment, and support is controlled by the image publisher.
658
+
629
659
  * Managed disks are now enabled by default, to use the Storage account set ```use_managed_disks``` (default: true).
630
660
 
631
661
  * The ```image_url``` (unmanaged disks only) parameter can be used to specify a custom vhd (This VHD must be in the same storage account as the disks of the VM, therefore ```existing_storage_account_blob_url``` must also be set and ```use_managed_disks``` must be set to false)
@@ -640,7 +670,7 @@ info: vm image list command OK
640
670
 
641
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.
642
672
 
643
- * 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"] %>```
644
674
 
645
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.
646
676
 
@@ -683,3 +713,25 @@ Contributions to the project are welcome via submitting Pull Requests.
683
713
  3. Commit your changes (`git commit -am 'Add some feature'`)
684
714
  4. Push to the branch (`git push origin my-new-feature`)
685
715
  5. Create a new Pull Request
716
+
717
+ ## Author
718
+
719
+ Stuart Preston
720
+
721
+ ## License and Copyright
722
+
723
+ Copyright 2015-2020, Chef Software, Inc.
724
+
725
+ ```
726
+ Licensed under the Apache License, Version 2.0 (the "License");
727
+ you may not use this file except in compliance with the License.
728
+ You may obtain a copy of the License at
729
+
730
+ http://www.apache.org/licenses/LICENSE-2.0
731
+
732
+ Unless required by applicable law or agreed to in writing, software
733
+ distributed under the License is distributed on an "AS IS" BASIS,
734
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
735
+ See the License for the specific language governing permissions and
736
+ limitations under the License.
737
+ ```
@@ -0,0 +1,136 @@
1
+ require "inifile"
2
+ require "kitchen/logging"
3
+
4
+ module Kitchen
5
+ module Driver
6
+ #
7
+ # AzureCredentials
8
+ #
9
+ class AzureCredentials
10
+ include Kitchen::Logging
11
+
12
+ CONFIG_PATH = "#{ENV["HOME"]}/.azure/credentials".freeze
13
+
14
+ #
15
+ # @return [String]
16
+ #
17
+ attr_reader :subscription_id
18
+
19
+ #
20
+ # @return [String]
21
+ #
22
+ attr_reader :environment
23
+
24
+ #
25
+ # Creates and initializes a new instance of the Credentials class.
26
+ #
27
+ def initialize(subscription_id:, environment: "Azure")
28
+ @subscription_id = subscription_id
29
+ @environment = environment
30
+ end
31
+
32
+ #
33
+ # Retrieves an object containing options and credentials
34
+ #
35
+ # @return [Object] Object that can be supplied along with all Azure client requests.
36
+ #
37
+ def azure_options
38
+ options = { tenant_id: tenant_id!,
39
+ subscription_id: subscription_id,
40
+ credentials: ::MsRest::TokenCredentials.new(token_provider),
41
+ active_directory_settings: ad_settings,
42
+ base_url: endpoint_settings.resource_manager_endpoint_url }
43
+ options[:client_id] = client_id if client_id
44
+ options[:client_secret] = client_secret if client_secret
45
+ options
46
+ end
47
+
48
+ private
49
+
50
+ def logger
51
+ Kitchen.logger
52
+ end
53
+
54
+ def config_path
55
+ @config_path ||= File.expand_path(ENV["AZURE_CONFIG_FILE"] || CONFIG_PATH)
56
+ end
57
+
58
+ def credentials
59
+ @credentials ||= begin
60
+ if File.file?(config_path)
61
+ IniFile.load(config_path)
62
+ else
63
+ warn "#{config_path} was not found or not accessible. Will attempt to use Managed Identity."
64
+ {}
65
+ end
66
+ end
67
+ end
68
+
69
+ def credentials_property(property)
70
+ credentials[subscription_id]&.[](property)
71
+ end
72
+
73
+ def tenant_id!
74
+ tenant_id || raise("Must provide tenant id. Use AZURE_TENANT_ID environment variable or set it in credentials file (#{config_path})")
75
+ end
76
+
77
+ def tenant_id
78
+ ENV["AZURE_TENANT_ID"] || credentials_property("tenant_id")
79
+ end
80
+
81
+ def client_id
82
+ ENV["AZURE_CLIENT_ID"] || credentials_property("client_id")
83
+ end
84
+
85
+ def client_secret
86
+ ENV["AZURE_CLIENT_SECRET"] || credentials_property("client_secret")
87
+ end
88
+
89
+ def token_provider
90
+ if client_id && client_secret
91
+ ::MsRestAzure::ApplicationTokenProvider.new(tenant_id, client_id, client_secret, ad_settings)
92
+ elsif client_id
93
+ ::MsRestAzure::MSITokenProvider.new(50342, ad_settings, { client_id: client_id })
94
+ else
95
+ ::MsRestAzure::MSITokenProvider.new(50342, ad_settings)
96
+ end
97
+ end
98
+
99
+ #
100
+ # Retrieves a [MsRestAzure::ActiveDirectoryServiceSettings] object representing the AD settings for the given cloud.
101
+ #
102
+ # @return [MsRestAzure::ActiveDirectoryServiceSettings] Settings to be used for subsequent requests
103
+ #
104
+ def ad_settings
105
+ case environment.downcase
106
+ when "azureusgovernment"
107
+ ::MsRestAzure::ActiveDirectoryServiceSettings.get_azure_us_government_settings
108
+ when "azurechina"
109
+ ::MsRestAzure::ActiveDirectoryServiceSettings.get_azure_china_settings
110
+ when "azuregermancloud"
111
+ ::MsRestAzure::ActiveDirectoryServiceSettings.get_azure_german_settings
112
+ when "azure"
113
+ ::MsRestAzure::ActiveDirectoryServiceSettings.get_azure_settings
114
+ end
115
+ end
116
+
117
+ #
118
+ # Retrieves a [MsRestAzure::AzureEnvironment] object representing endpoint settings for the given cloud.
119
+ #
120
+ # @return [MsRestAzure::AzureEnvironment] Settings to be used for subsequent requests
121
+ #
122
+ def endpoint_settings
123
+ case environment.downcase
124
+ when "azureusgovernment"
125
+ ::MsRestAzure::AzureEnvironments::AzureUSGovernment
126
+ when "azurechina"
127
+ ::MsRestAzure::AzureEnvironments::AzureChinaCloud
128
+ when "azuregermancloud"
129
+ ::MsRestAzure::AzureEnvironments::AzureGermanCloud
130
+ when "azure"
131
+ ::MsRestAzure::AzureEnvironments::AzureCloud
132
+ end
133
+ end
134
+ end
135
+ end
136
+ end
@@ -1,5 +1,5 @@
1
1
  require "kitchen"
2
- require_relative "credentials"
2
+ require_relative "azure_credentials"
3
3
  require "securerandom"
4
4
  require "azure_mgmt_resources"
5
5
  require "azure_mgmt_network"
@@ -9,6 +9,7 @@ require "fileutils"
9
9
  require "erb"
10
10
  require "ostruct"
11
11
  require "json"
12
+ require "faraday"
12
13
 
13
14
  module Kitchen
14
15
  module Driver
@@ -17,6 +18,9 @@ module Kitchen
17
18
  #
18
19
  class Azurerm < Kitchen::Driver::Base
19
20
  attr_accessor :resource_management_client
21
+ attr_accessor :network_management_client
22
+
23
+ kitchen_driver_api_version 2
20
24
 
21
25
  default_config(:azure_resource_group_prefix) do |_config|
22
26
  "kitchen-"
@@ -71,7 +75,7 @@ module Kitchen
71
75
  end
72
76
 
73
77
  default_config(:password) do |_config|
74
- "P2ssw0rd"
78
+ SecureRandom.base64(25)
75
79
  end
76
80
 
77
81
  default_config(:vm_name) do |_config|
@@ -130,6 +134,10 @@ module Kitchen
130
134
  {}
131
135
  end
132
136
 
137
+ default_config(:plan) do |_config|
138
+ {}
139
+ end
140
+
133
141
  default_config(:vm_tags) do |_config|
134
142
  {}
135
143
  end
@@ -190,6 +198,10 @@ module Kitchen
190
198
  ENV["AZURE_SUBSCRIPTION_ID"]
191
199
  end
192
200
 
201
+ default_config(:azure_api_retries) do |_config|
202
+ 5
203
+ end
204
+
193
205
  def create(state)
194
206
  state = validate_state(state)
195
207
  deployment_parameters = {
@@ -199,18 +211,21 @@ module Kitchen
199
211
  bootDiagnosticsEnabled: config[:boot_diagnostics_enabled],
200
212
  newStorageAccountName: "storage#{state[:uuid]}",
201
213
  adminUsername: state[:username],
202
- adminPassword: state[:password] || "P2ssw0rd",
203
214
  dnsNameForPublicIP: "kitchen-#{state[:uuid]}",
204
215
  vmName: state[:vm_name],
205
216
  systemAssignedIdentity: config[:system_assigned_identity],
206
- userAssignedIdentities: config[:user_assigned_identities],
217
+ userAssignedIdentities: config[:user_assigned_identities].map { |identity| [identity, {}] }.to_h,
207
218
  secretUrl: config[:secret_url],
208
219
  vaultName: config[:vault_name],
209
220
  vaultResourceGroup: config[:vault_resource_group],
210
221
  }
211
222
 
223
+ if instance.transport[:ssh_key].nil?
224
+ deployment_parameters["adminPassword"] = state[:password]
225
+ end
226
+
212
227
  if config[:subscription_id].to_s == ""
213
- raise "A subscription_id config value was not detected and kitchen-azurerm cannot continue. Please check your .kitchen.yml configuration. Exiting."
228
+ raise "A subscription_id config value was not detected and kitchen-azurerm cannot continue. Please check your kitchen.yml configuration. Exiting."
214
229
  end
215
230
 
216
231
  if config[:nic_name].to_s == ""
@@ -255,7 +270,8 @@ module Kitchen
255
270
  deployment_parameters["imageVersion"] = image_version
256
271
  end
257
272
 
258
- options = Kitchen::Driver::Credentials.new.azure_options_for_subscription(config[:subscription_id], config[:azure_environment])
273
+ options = Kitchen::Driver::AzureCredentials.new(subscription_id: config[:subscription_id],
274
+ environment: config[:azure_environment]).azure_options
259
275
 
260
276
  debug "Azure environment: #{config[:azure_environment]}"
261
277
  @resource_management_client = ::Azure::Resources::Profiles::Latest::Mgmt::Client.new(options)
@@ -266,7 +282,7 @@ module Kitchen
266
282
  resource_group.tags = config[:resource_group_tags]
267
283
  begin
268
284
  info "Creating Resource Group: #{state[:azure_resource_group_name]}"
269
- resource_management_client.resource_groups.create_or_update(state[:azure_resource_group_name], resource_group)
285
+ create_resource_group(state[:azure_resource_group_name], resource_group)
270
286
  rescue ::MsRestAzure::AzureOperationError => operation_error
271
287
  error operation_error.body
272
288
  raise operation_error
@@ -277,17 +293,17 @@ module Kitchen
277
293
  if File.file?(config[:pre_deployment_template])
278
294
  pre_deployment_name = "pre-deploy-#{state[:uuid]}"
279
295
  info "Creating deployment: #{pre_deployment_name}"
280
- resource_management_client.deployments.begin_create_or_update_async(state[:azure_resource_group_name], pre_deployment_name, pre_deployment(config[:pre_deployment_template], config[:pre_deployment_parameters])).value!
296
+ create_deployment_async(state[:azure_resource_group_name], pre_deployment_name, pre_deployment(config[:pre_deployment_template], config[:pre_deployment_parameters])).value!
281
297
  follow_deployment_until_end_state(state[:azure_resource_group_name], pre_deployment_name)
282
298
  end
283
299
  deployment_name = "deploy-#{state[:uuid]}"
284
300
  info "Creating deployment: #{deployment_name}"
285
- resource_management_client.deployments.begin_create_or_update_async(state[:azure_resource_group_name], deployment_name, deployment(deployment_parameters)).value!
301
+ create_deployment_async(state[:azure_resource_group_name], deployment_name, deployment(deployment_parameters)).value!
286
302
  follow_deployment_until_end_state(state[:azure_resource_group_name], deployment_name)
287
303
  if File.file?(config[:post_deployment_template])
288
304
  post_deployment_name = "post-deploy-#{state[:uuid]}"
289
305
  info "Creating deployment: #{post_deployment_name}"
290
- resource_management_client.deployments.begin_create_or_update_async(state[:azure_resource_group_name], post_deployment_name, post_deployment(config[:post_deployment_template], config[:post_deployment_parameters])).value!
306
+ create_deployment_async(state[:azure_resource_group_name], post_deployment_name, post_deployment(config[:post_deployment_template], config[:post_deployment_parameters])).value!
291
307
  follow_deployment_until_end_state(state[:azure_resource_group_name], post_deployment_name)
292
308
  end
293
309
  rescue ::MsRestAzure::AzureOperationError => operation_error
@@ -302,17 +318,16 @@ module Kitchen
302
318
  end
303
319
  end
304
320
 
305
- network_management_client = ::Azure::Network::Profiles::Latest::Mgmt::Client.new(options)
321
+ @network_management_client = ::Azure::Network::Profiles::Latest::Mgmt::Client.new(options)
306
322
 
307
323
  if config[:vnet_id] == "" || config[:public_ip]
308
324
  # Retrieve the public IP from the resource group:
309
- result = network_management_client.public_ipaddresses.get(state[:azure_resource_group_name], "publicip")
325
+ result = get_public_ip(state[:azure_resource_group_name], "publicip")
310
326
  info "IP Address is: #{result.ip_address} [#{result.dns_settings.fqdn}]"
311
327
  state[:hostname] = result.ip_address
312
328
  else
313
329
  # Retrieve the internal IP from the resource group:
314
- network_interfaces = ::Azure::Network::Profiles::Latest::Mgmt::NetworkInterfaces.new(network_management_client)
315
- result = network_interfaces.get(state[:azure_resource_group_name], vmnic.to_s)
330
+ result = get_network_interface(state[:azure_resource_group_name], vmnic.to_s)
316
331
  info "IP Address is: #{result.ip_configurations[0].private_ipaddress}"
317
332
  state[:hostname] = result.ip_configurations[0].private_ipaddress
318
333
  end
@@ -475,7 +490,7 @@ module Kitchen
475
490
  until end_provisioning_state_reached
476
491
  list_outstanding_deployment_operations(resource_group, deployment_name)
477
492
  sleep config[:deployment_sleep]
478
- deployment_provisioning_state = deployment_state(resource_group, deployment_name)
493
+ deployment_provisioning_state = get_deployment_state(resource_group, deployment_name)
479
494
  end_provisioning_state_reached = end_provisioning_states.split(",").include?(deployment_provisioning_state)
480
495
  end
481
496
  info "Resource Template deployment reached end state of '#{deployment_provisioning_state}'."
@@ -483,7 +498,7 @@ module Kitchen
483
498
  end
484
499
 
485
500
  def show_failed_operations(resource_group, deployment_name)
486
- failed_operations = resource_management_client.deployment_operations.list(resource_group, deployment_name)
501
+ failed_operations = list_deployment_operations(resource_group, deployment_name)
487
502
  failed_operations.each do |val|
488
503
  resource_code = val.properties.status_code
489
504
  raise val.properties.status_message.inspect.to_s if resource_code != "OK"
@@ -492,7 +507,7 @@ module Kitchen
492
507
 
493
508
  def list_outstanding_deployment_operations(resource_group, deployment_name)
494
509
  end_operation_states = "Failed,Succeeded"
495
- deployment_operations = resource_management_client.deployment_operations.list(resource_group, deployment_name)
510
+ deployment_operations = list_deployment_operations(resource_group, deployment_name)
496
511
  deployment_operations.each do |val|
497
512
  resource_provisioning_state = val.properties.provisioning_state
498
513
  unless val.properties.target_resource.nil?
@@ -506,22 +521,18 @@ module Kitchen
506
521
  end
507
522
  end
508
523
 
509
- def deployment_state(resource_group, deployment_name)
510
- deployments = resource_management_client.deployments.get(resource_group, deployment_name)
511
- deployments.properties.provisioning_state
512
- end
513
-
514
524
  def destroy(state)
515
525
  return if state[:server_id].nil?
516
526
 
517
- options = Kitchen::Driver::Credentials.new.azure_options_for_subscription(state[:subscription_id], state[:azure_environment])
527
+ options = Kitchen::Driver::AzureCredentials.new(subscription_id: state[:subscription_id],
528
+ environment: state[:azure_environment]).azure_options
518
529
  @resource_management_client = ::Azure::Resources::Profiles::Latest::Mgmt::Client.new(options)
519
530
  if config[:destroy_resource_group_contents] == true
520
531
  info "Destroying individual resources within the Resource Group."
521
532
  empty_deployment_name = "empty-deploy-#{state[:uuid]}"
522
533
  begin
523
534
  info "Creating deployment: #{empty_deployment_name}"
524
- resource_management_client.deployments.begin_create_or_update_async(state[:azure_resource_group_name], empty_deployment_name, empty_deployment).value!
535
+ create_deployment_async(state[:azure_resource_group_name], empty_deployment_name, empty_deployment).value!
525
536
  follow_deployment_until_end_state(state[:azure_resource_group_name], empty_deployment_name)
526
537
  rescue ::MsRestAzure::AzureOperationError => operation_error
527
538
  error operation_error.body
@@ -536,7 +547,7 @@ module Kitchen
536
547
  info "Azure environment: #{state[:azure_environment]}"
537
548
  begin
538
549
  info "Destroying Resource Group: #{state[:azure_resource_group_name]}"
539
- resource_management_client.resource_groups.begin_delete(state[:azure_resource_group_name])
550
+ delete_resource_group_async(state[:azure_resource_group_name])
540
551
  info "Destroy operation accepted and will continue in the background."
541
552
  rescue ::MsRestAzure::AzureOperationError => operation_error
542
553
  error operation_error.body
@@ -638,13 +649,25 @@ module Kitchen
638
649
 
639
650
  def virtual_machine_deployment_template
640
651
  if config[:vnet_id] == ""
641
- virtual_machine_deployment_template_file("public.erb", vm_tags: vm_tag_string(config[:vm_tags]), use_managed_disks: config[:use_managed_disks], image_url: config[:image_url], existing_storage_account_blob_url: config[:existing_storage_account_blob_url], image_id: config[:image_id], existing_storage_account_container: config[:existing_storage_account_container], custom_data: config[:custom_data], os_disk_size_gb: config[:os_disk_size_gb], data_disks_for_vm_json: data_disks_for_vm_json, use_ephemeral_osdisk: config[:use_ephemeral_osdisk])
652
+ virtual_machine_deployment_template_file("public.erb", vm_tags: vm_tag_string(config[:vm_tags]), use_managed_disks: config[:use_managed_disks], image_url: config[:image_url], existing_storage_account_blob_url: config[:existing_storage_account_blob_url], image_id: config[:image_id], existing_storage_account_container: config[:existing_storage_account_container], custom_data: config[:custom_data], os_disk_size_gb: config[:os_disk_size_gb], data_disks_for_vm_json: data_disks_for_vm_json, use_ephemeral_osdisk: config[:use_ephemeral_osdisk], ssh_key: instance.transport[:ssh_key], plan_json: plan_json)
642
653
  else
643
654
  info "Using custom vnet: #{config[:vnet_id]}"
644
- virtual_machine_deployment_template_file("internal.erb", vnet_id: config[:vnet_id], subnet_id: config[:subnet_id], public_ip: config[:public_ip], vm_tags: vm_tag_string(config[:vm_tags]), use_managed_disks: config[:use_managed_disks], image_url: config[:image_url], existing_storage_account_blob_url: config[:existing_storage_account_blob_url], image_id: config[:image_id], existing_storage_account_container: config[:existing_storage_account_container], custom_data: config[:custom_data], os_disk_size_gb: config[:os_disk_size_gb], data_disks_for_vm_json: data_disks_for_vm_json, use_ephemeral_osdisk: config[:use_ephemeral_osdisk])
655
+ virtual_machine_deployment_template_file("internal.erb", vnet_id: config[:vnet_id], subnet_id: config[:subnet_id], public_ip: config[:public_ip], vm_tags: vm_tag_string(config[:vm_tags]), use_managed_disks: config[:use_managed_disks], image_url: config[:image_url], existing_storage_account_blob_url: config[:existing_storage_account_blob_url], image_id: config[:image_id], existing_storage_account_container: config[:existing_storage_account_container], custom_data: config[:custom_data], os_disk_size_gb: config[:os_disk_size_gb], data_disks_for_vm_json: data_disks_for_vm_json, use_ephemeral_osdisk: config[:use_ephemeral_osdisk], ssh_key: instance.transport[:ssh_key], plan_json: plan_json)
645
656
  end
646
657
  end
647
658
 
659
+ def plan_json
660
+ return nil if config[:plan].empty?
661
+
662
+ plan = {}
663
+ plan["name"] = config[:plan][:name] if config[:plan][:name]
664
+ plan["product"] = config[:plan][:product] if config[:plan][:product]
665
+ plan["promotionCode"] = config[:plan][:promotion_code] if config[:plan][:promotion_code]
666
+ plan["publisher"] = config[:plan][:publisher] if config[:plan][:publisher]
667
+
668
+ plan.to_json
669
+ end
670
+
648
671
  def virtual_machine_deployment_template_file(template_file, data = {})
649
672
  template = File.read(File.expand_path(File.join(__dir__, "../../../templates", template_file)))
650
673
  render_binding = OpenStruct.new(data)
@@ -676,6 +699,118 @@ module Kitchen
676
699
  end
677
700
  end
678
701
  end
702
+
703
+ private
704
+
705
+ #
706
+ # Wrapper methods for the Azure API calls to retry the calls when getting timeouts.
707
+ #
708
+
709
+ def create_resource_group(resource_group_name, resource_group)
710
+ retries = config[:azure_api_retries]
711
+ begin
712
+ resource_management_client.resource_groups.create_or_update(resource_group_name, resource_group)
713
+ rescue Faraday::TimeoutError, Faraday::ClientError => exception
714
+ send_exception_message(exception, "while creating resource group '#{resource_group_name}'. #{retries} retries left.")
715
+ raise if retries == 0
716
+
717
+ retries -= 1
718
+ retry
719
+ end
720
+ end
721
+
722
+ def create_deployment_async(resource_group, deployment_name, deployment)
723
+ retries = config[:azure_api_retries]
724
+ begin
725
+ resource_management_client.deployments.begin_create_or_update_async(resource_group, deployment_name, deployment)
726
+ rescue Faraday::TimeoutError, Faraday::ClientError => exception
727
+ send_exception_message(exception, "while sending deployment creation request for deployment '#{deployment_name}'. #{retries} retries left.")
728
+ raise if retries == 0
729
+
730
+ retries -= 1
731
+ retry
732
+ end
733
+ end
734
+
735
+ def get_public_ip(resource_group_name, public_ip_name)
736
+ retries = config[:azure_api_retries]
737
+ begin
738
+ network_management_client.public_ipaddresses.get(resource_group_name, public_ip_name)
739
+ rescue Faraday::TimeoutError, Faraday::ClientError => exception
740
+ send_exception_message(exception, "while fetching public ip '#{public_ip_name}' for resource group '#{resource_group_name}'. #{retries} retries left.")
741
+ raise if retries == 0
742
+
743
+ retries -= 1
744
+ retry
745
+ end
746
+ end
747
+
748
+ def get_network_interface(resource_group_name, network_interface_name)
749
+ retries = config[:azure_api_retries]
750
+ begin
751
+ network_interfaces = ::Azure::Network::Profiles::Latest::Mgmt::NetworkInterfaces.new(network_management_client)
752
+ network_interfaces.get(resource_group_name, network_interface_name)
753
+ rescue Faraday::TimeoutError, Faraday::ClientError => exception
754
+ send_exception_message(exception, "while fetching network interface '#{network_interface_name}' for resource group '#{resource_group_name}'. #{retries} retries left.")
755
+ raise if retries == 0
756
+
757
+ retries -= 1
758
+ retry
759
+ end
760
+ end
761
+
762
+ def list_deployment_operations(resource_group, deployment_name)
763
+ retries = config[:azure_api_retries]
764
+ begin
765
+ resource_management_client.deployment_operations.list(resource_group, deployment_name)
766
+ rescue Faraday::TimeoutError, Faraday::ClientError => exception
767
+ send_exception_message(exception, "while listing deployment operations for deployment '#{deployment_name}'. #{retries} retries left.")
768
+ raise if retries == 0
769
+
770
+ retries -= 1
771
+ retry
772
+ end
773
+ end
774
+
775
+ def get_deployment_state(resource_group, deployment_name)
776
+ retries = config[:azure_api_retries]
777
+ begin
778
+ deployments = resource_management_client.deployments.get(resource_group, deployment_name)
779
+ deployments.properties.provisioning_state
780
+ rescue Faraday::TimeoutError, Faraday::ClientError => exception
781
+ send_exception_message(exception, "while retrieving state for deployment '#{deployment_name}'. #{retries} retries left.")
782
+ raise if retries == 0
783
+
784
+ retries -= 1
785
+ retry
786
+ end
787
+ end
788
+
789
+ def delete_resource_group_async(resource_group_name)
790
+ retries = config[:azure_api_retries]
791
+ begin
792
+ resource_management_client.resource_groups.begin_delete(resource_group_name)
793
+ rescue Faraday::TimeoutError, Faraday::ClientError => exception
794
+ send_exception_message(exception, "while sending resource group deletion request for '#{resource_group_name}'. #{retries} retries left.")
795
+ raise if retries == 0
796
+
797
+ retries -= 1
798
+ retry
799
+ end
800
+ end
801
+
802
+ def send_exception_message(exception, message)
803
+ if exception.is_a?(Faraday::TimeoutError)
804
+ header = "Timed out"
805
+ elsif exception.is_a?(Faraday::ClientError)
806
+ header = "Connection reset by peer"
807
+ else
808
+ # Unhandled exception, return early
809
+ info "Unrecognized exception type."
810
+ return
811
+ end
812
+ info "#{header} #{message}"
813
+ end
679
814
  end
680
815
  end
681
816
  end
@@ -26,12 +26,14 @@
26
26
  "description": "User name for the Virtual Machine."
27
27
  }
28
28
  },
29
+ <%- if ssh_key.nil? -%>
29
30
  "adminPassword": {
30
31
  "type": "securestring",
31
32
  "metadata": {
32
33
  "description": "Password for the Virtual Machine."
33
34
  }
34
35
  },
36
+ <%- end -%>
35
37
  "dnsNameForPublicIP": {
36
38
  "type": "string",
37
39
  "metadata": {
@@ -176,10 +178,10 @@
176
178
  }
177
179
  },
178
180
  "userAssignedIdentities": {
179
- "type": "array",
180
- "defaultValue": [],
181
+ "type": "object",
182
+ "defaultValue": {},
181
183
  "metadata": {
182
- "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."
183
185
  }
184
186
  },
185
187
  "bootDiagnosticsEnabled": {
@@ -322,8 +324,10 @@
322
324
  ]
323
325
  ],
324
326
  <%- end -%>
325
- "adminUsername": "[parameters('adminUsername')]",
326
- "adminPassword": "[parameters('adminPassword')]"
327
+ <%- if ssh_key.nil? -%>
328
+ "adminPassword": "[parameters('adminPassword')]",
329
+ <%- end -%>
330
+ "adminUsername": "[parameters('adminUsername')]"
327
331
  },
328
332
  "storageProfile": {
329
333
  <%- if image_url.empty? and image_id.empty? -%>
@@ -406,9 +410,12 @@
406
410
  <%- end -%>
407
411
  }
408
412
  },
413
+ <%- unless plan_json.nil? -%>
414
+ "plan": <%= plan_json %>,
415
+ <%- end -%>
409
416
  "identity": {
410
417
  "type": "[variables('vmIdentityType')]",
411
- "identityIds": "[if(empty(parameters('userAssignedIdentities')), json('null'), parameters('userAssignedIdentities'))]"
418
+ "userAssignedIdentities": "[if(empty(parameters('userAssignedIdentities')), json('null'), parameters('userAssignedIdentities'))]"
412
419
  },
413
420
  "tags": {
414
421
  <%= vm_tags unless vm_tags.empty? %>
@@ -26,12 +26,14 @@
26
26
  "description": "User name for the Virtual Machine."
27
27
  }
28
28
  },
29
+ <%- if ssh_key.nil? -%>
29
30
  "adminPassword": {
30
31
  "type": "securestring",
31
32
  "metadata": {
32
33
  "description": "Password for the Virtual Machine."
33
34
  }
34
35
  },
36
+ <%- end -%>
35
37
  "dnsNameForPublicIP": {
36
38
  "type": "string",
37
39
  "metadata": {
@@ -176,10 +178,10 @@
176
178
  }
177
179
  },
178
180
  "userAssignedIdentities": {
179
- "type": "array",
180
- "defaultValue": [],
181
+ "type": "object",
182
+ "defaultValue": {},
181
183
  "metadata": {
182
- "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."
183
185
  }
184
186
  },
185
187
  "bootDiagnosticsEnabled": {
@@ -341,8 +343,10 @@
341
343
  ]
342
344
  ],
343
345
  <%- end -%>
344
- "adminUsername": "[parameters('adminUsername')]",
345
- "adminPassword": "[parameters('adminPassword')]"
346
+ <%- if ssh_key.nil? -%>
347
+ "adminPassword": "[parameters('adminPassword')]",
348
+ <%- end -%>
349
+ "adminUsername": "[parameters('adminUsername')]"
346
350
  },
347
351
  "storageProfile": {
348
352
  <%- if image_url.empty? and image_id.empty? -%>
@@ -425,9 +429,12 @@
425
429
  <%- end -%>
426
430
  }
427
431
  },
432
+ <%- unless plan_json.nil? -%>
433
+ "plan": <%= plan_json %>,
434
+ <%- end -%>
428
435
  "identity": {
429
436
  "type": "[variables('vmIdentityType')]",
430
- "identityIds": "[if(empty(parameters('userAssignedIdentities')), json('null'), parameters('userAssignedIdentities'))]"
437
+ "userAssignedIdentities": "[if(empty(parameters('userAssignedIdentities')), json('null'), parameters('userAssignedIdentities'))]"
431
438
  },
432
439
  "tags": {
433
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.15.1
4
+ version: 1.1.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-01-15 00:00:00.000000000 Z
11
+ date: 2020-08-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: azure_mgmt_network
@@ -54,22 +54,22 @@ dependencies:
54
54
  name: inifile
55
55
  requirement: !ruby/object:Gem::Requirement
56
56
  requirements:
57
- - - ">="
58
- - !ruby/object:Gem::Version
59
- version: 3.0.0
60
57
  - - "~>"
61
58
  - !ruby/object:Gem::Version
62
59
  version: '3.0'
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ version: 3.0.0
63
63
  type: :runtime
64
64
  prerelease: false
65
65
  version_requirements: !ruby/object:Gem::Requirement
66
66
  requirements:
67
- - - ">="
68
- - !ruby/object:Gem::Version
69
- version: 3.0.0
70
67
  - - "~>"
71
68
  - !ruby/object:Gem::Version
72
69
  version: '3.0'
70
+ - - ">="
71
+ - !ruby/object:Gem::Version
72
+ version: 3.0.0
73
73
  - !ruby/object:Gem::Dependency
74
74
  name: sshkey
75
75
  requirement: !ruby/object:Gem::Requirement
@@ -91,19 +91,25 @@ dependencies:
91
91
  - !ruby/object:Gem::Version
92
92
  version: '3'
93
93
  - !ruby/object:Gem::Dependency
94
- name: bundler
94
+ name: test-kitchen
95
95
  requirement: !ruby/object:Gem::Requirement
96
96
  requirements:
97
97
  - - ">="
98
98
  - !ruby/object:Gem::Version
99
- version: '0'
100
- type: :development
99
+ version: '1.20'
100
+ - - "<"
101
+ - !ruby/object:Gem::Version
102
+ version: '3.0'
103
+ type: :runtime
101
104
  prerelease: false
102
105
  version_requirements: !ruby/object:Gem::Requirement
103
106
  requirements:
104
107
  - - ">="
105
108
  - !ruby/object:Gem::Version
106
- version: '0'
109
+ version: '1.20'
110
+ - - "<"
111
+ - !ruby/object:Gem::Version
112
+ version: '3.0'
107
113
  - !ruby/object:Gem::Dependency
108
114
  name: rake
109
115
  requirement: !ruby/object:Gem::Requirement
@@ -132,6 +138,62 @@ dependencies:
132
138
  - - ">="
133
139
  - !ruby/object:Gem::Version
134
140
  version: '0'
141
+ - !ruby/object:Gem::Dependency
142
+ name: rspec
143
+ requirement: !ruby/object:Gem::Requirement
144
+ requirements:
145
+ - - "~>"
146
+ - !ruby/object:Gem::Version
147
+ version: '3.5'
148
+ type: :development
149
+ prerelease: false
150
+ version_requirements: !ruby/object:Gem::Requirement
151
+ requirements:
152
+ - - "~>"
153
+ - !ruby/object:Gem::Version
154
+ version: '3.5'
155
+ - !ruby/object:Gem::Dependency
156
+ name: rspec-mocks
157
+ requirement: !ruby/object:Gem::Requirement
158
+ requirements:
159
+ - - "~>"
160
+ - !ruby/object:Gem::Version
161
+ version: '3.5'
162
+ type: :development
163
+ prerelease: false
164
+ version_requirements: !ruby/object:Gem::Requirement
165
+ requirements:
166
+ - - "~>"
167
+ - !ruby/object:Gem::Version
168
+ version: '3.5'
169
+ - !ruby/object:Gem::Dependency
170
+ name: rspec-expectations
171
+ requirement: !ruby/object:Gem::Requirement
172
+ requirements:
173
+ - - "~>"
174
+ - !ruby/object:Gem::Version
175
+ version: '3.5'
176
+ type: :development
177
+ prerelease: false
178
+ version_requirements: !ruby/object:Gem::Requirement
179
+ requirements:
180
+ - - "~>"
181
+ - !ruby/object:Gem::Version
182
+ version: '3.5'
183
+ - !ruby/object:Gem::Dependency
184
+ name: rspec-its
185
+ requirement: !ruby/object:Gem::Requirement
186
+ requirements:
187
+ - - "~>"
188
+ - !ruby/object:Gem::Version
189
+ version: 1.3.0
190
+ type: :development
191
+ prerelease: false
192
+ version_requirements: !ruby/object:Gem::Requirement
193
+ requirements:
194
+ - - "~>"
195
+ - !ruby/object:Gem::Version
196
+ version: 1.3.0
135
197
  description: Test Kitchen driver for the Microsoft Azure Resource Manager (ARM) API
136
198
  email:
137
199
  - stuart@chef.io
@@ -141,8 +203,8 @@ extra_rdoc_files: []
141
203
  files:
142
204
  - LICENSE
143
205
  - README.md
206
+ - lib/kitchen/driver/azure_credentials.rb
144
207
  - lib/kitchen/driver/azurerm.rb
145
- - lib/kitchen/driver/credentials.rb
146
208
  - templates/empty.erb
147
209
  - templates/internal.erb
148
210
  - templates/public.erb
@@ -150,7 +212,7 @@ homepage: https://github.com/test-kitchen/kitchen-azurerm
150
212
  licenses:
151
213
  - Apache-2.0
152
214
  metadata: {}
153
- post_install_message:
215
+ post_install_message:
154
216
  rdoc_options: []
155
217
  require_paths:
156
218
  - lib
@@ -165,8 +227,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
165
227
  - !ruby/object:Gem::Version
166
228
  version: '0'
167
229
  requirements: []
168
- rubygems_version: 3.0.3
169
- signing_key:
230
+ rubygems_version: 3.1.2
231
+ signing_key:
170
232
  specification_version: 4
171
233
  summary: Test Kitchen driver for Azure Resource Manager.
172
234
  test_files: []
@@ -1,90 +0,0 @@
1
- require "inifile"
2
-
3
- module Kitchen
4
- module Driver
5
- #
6
- # Credentials
7
- #
8
- class Credentials
9
- CONFIG_PATH = "#{ENV["HOME"]}/.azure/credentials".freeze
10
-
11
- #
12
- # Creates and initializes a new instance of the Credentials class.
13
- #
14
- def initialize
15
- config_file = ENV["AZURE_CONFIG_FILE"] || File.expand_path(CONFIG_PATH)
16
- if File.file?(config_file)
17
- @credentials = IniFile.load(File.expand_path(config_file))
18
- else
19
- warn "#{CONFIG_PATH} was not found or not accessible."
20
- end
21
- end
22
-
23
- #
24
- # Retrieves an object containing options and credentials for the given
25
- # subscription_id and azure_environment.
26
- #
27
- # @param subscription_id [String] The subscription_id to retrieve a token for
28
- # @param azure_environment [String] The azure_environment to use
29
- #
30
- # @return [Object] Object that can be supplied along with all Azure client requests.
31
- #
32
- def azure_options_for_subscription(subscription_id, azure_environment = "Azure")
33
- tenant_id = ENV["AZURE_TENANT_ID"] || @credentials[subscription_id]["tenant_id"]
34
- client_id = ENV["AZURE_CLIENT_ID"] || @credentials[subscription_id]["client_id"]
35
- client_secret = ENV["AZURE_CLIENT_SECRET"] || @credentials[subscription_id]["client_secret"]
36
- token_provider = ::MsRestAzure::ApplicationTokenProvider.new(tenant_id, client_id, client_secret, ad_settings_for_azure_environment(azure_environment))
37
- options = { tenant_id: tenant_id,
38
- client_id: client_id,
39
- client_secret: client_secret,
40
- subscription_id: subscription_id,
41
- credentials: ::MsRest::TokenCredentials.new(token_provider),
42
- active_directory_settings: ad_settings_for_azure_environment(azure_environment),
43
- base_url: endpoint_settings_for_azure_environment(azure_environment).resource_manager_endpoint_url }
44
- options
45
- end
46
-
47
- #
48
- # Retrieves a [MsRestAzure::ActiveDirectoryServiceSettings] object representing the AD settings for the given cloud.
49
- # @param azure_environment [String] The Azure environment to retrieve settings for.
50
- #
51
- # @return [MsRestAzure::ActiveDirectoryServiceSettings] Settings to be used for subsequent requests
52
- #
53
- def ad_settings_for_azure_environment(azure_environment)
54
- case azure_environment.downcase
55
- when "azureusgovernment"
56
- ::MsRestAzure::ActiveDirectoryServiceSettings.get_azure_us_government_settings
57
- when "azurechina"
58
- ::MsRestAzure::ActiveDirectoryServiceSettings.get_azure_china_settings
59
- when "azuregermancloud"
60
- ::MsRestAzure::ActiveDirectoryServiceSettings.get_azure_german_settings
61
- when "azure"
62
- ::MsRestAzure::ActiveDirectoryServiceSettings.get_azure_settings
63
- end
64
- end
65
-
66
- #
67
- # Retrieves a [MsRestAzure::AzureEnvironment] object representing endpoint settings for the given cloud.
68
- # @param azure_environment [String] The Azure environment to retrieve settings for.
69
- #
70
- # @return [MsRestAzure::AzureEnvironment] Settings to be used for subsequent requests
71
- #
72
- def endpoint_settings_for_azure_environment(azure_environment)
73
- case azure_environment.downcase
74
- when "azureusgovernment"
75
- ::MsRestAzure::AzureEnvironments::AzureUSGovernment
76
- when "azurechina"
77
- ::MsRestAzure::AzureEnvironments::AzureChinaCloud
78
- when "azuregermancloud"
79
- ::MsRestAzure::AzureEnvironments::AzureGermanCloud
80
- when "azure"
81
- ::MsRestAzure::AzureEnvironments::AzureCloud
82
- end
83
- end
84
-
85
- def self.singleton
86
- @credentials ||= Credentials.new
87
- end
88
- end
89
- end
90
- end