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 +4 -4
- data/README.md +60 -28
- data/lib/kitchen/driver/azure_credentials.rb +48 -11
- data/lib/kitchen/driver/azurerm.rb +132 -24
- data/templates/internal.erb +4 -4
- data/templates/public.erb +4 -4
- metadata +9 -9
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 51b127c1a7e5d1c197dde573ce77d5e9900fe2297d12e5d8cbcd4d6d1e328ccc
|
4
|
+
data.tar.gz: d59c1a55f910f415a271a4818a8879a16e06dff3192d3d3a64d5ce1c32313e6c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
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
|
-
|
15
|
+
If you're not using Chef Workstation and need to install the plugin as a gem run:
|
16
16
|
|
17
|
-
|
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
|
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
|
-
[
|
36
|
-
client_id = "
|
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 = "
|
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="
|
63
|
+
AZURE_CLIENT_ID="your-azure-client-id-here"
|
45
64
|
AZURE_CLIENT_SECRET="your-client-secret-here"
|
46
|
-
AZURE_TENANT_ID="
|
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: '
|
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: '
|
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: '
|
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: '
|
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: '
|
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: '
|
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: '
|
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: '
|
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: '
|
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: '
|
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: '
|
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: '
|
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
|
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 ```
|
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 ||
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
require "
|
9
|
-
|
10
|
-
require "
|
11
|
-
require "
|
12
|
-
require "
|
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(
|
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:
|
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[
|
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
|
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],
|
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
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
data/templates/internal.erb
CHANGED
@@ -178,10 +178,10 @@
|
|
178
178
|
}
|
179
179
|
},
|
180
180
|
"userAssignedIdentities": {
|
181
|
-
"type": "
|
182
|
-
"defaultValue":
|
181
|
+
"type": "object",
|
182
|
+
"defaultValue": {},
|
183
183
|
"metadata": {
|
184
|
-
"description": "
|
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
|
-
"
|
418
|
+
"userAssignedIdentities": "[if(empty(parameters('userAssignedIdentities')), json('null'), parameters('userAssignedIdentities'))]"
|
419
419
|
},
|
420
420
|
"tags": {
|
421
421
|
<%= vm_tags unless vm_tags.empty? %>
|
data/templates/public.erb
CHANGED
@@ -178,10 +178,10 @@
|
|
178
178
|
}
|
179
179
|
},
|
180
180
|
"userAssignedIdentities": {
|
181
|
-
"type": "
|
182
|
-
"defaultValue":
|
181
|
+
"type": "object",
|
182
|
+
"defaultValue": {},
|
183
183
|
"metadata": {
|
184
|
-
"description": "
|
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
|
-
"
|
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:
|
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-
|
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:
|
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:
|
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: []
|