knife-azure 3.0.6 → 5.0.1
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/lib/azure/azure_interface.rb +1 -1
- data/lib/azure/custom_errors.rb +2 -2
- data/lib/azure/helpers.rb +1 -1
- data/lib/azure/resource_management/ARM_deployment_template.rb +82 -27
- data/lib/azure/resource_management/ARM_interface.rb +112 -118
- data/lib/azure/resource_management/rest/arm_client.rb +208 -0
- data/lib/azure/resource_management/rest/environments.rb +89 -0
- data/lib/azure/resource_management/rest/errors.rb +57 -0
- data/lib/azure/resource_management/rest/http.rb +335 -0
- data/lib/azure/resource_management/rest/rest_object.rb +103 -0
- data/lib/azure/resource_management/rest/secure_connection.rb +57 -0
- data/lib/azure/resource_management/rest/token_provider.rb +125 -0
- data/lib/azure/resource_management/vnet_config.rb +4 -4
- data/lib/azure/resource_management/windows_credentials.rb +3 -3
- data/lib/chef/knife/azurerm_server_create.rb +22 -10
- data/lib/chef/knife/azurerm_server_delete.rb +1 -1
- data/lib/chef/knife/azurerm_server_list.rb +1 -1
- data/lib/chef/knife/azurerm_server_show.rb +1 -1
- data/lib/chef/knife/bootstrap/bootstrapper.rb +142 -14
- data/lib/chef/knife/bootstrap/common_bootstrap_options.rb +4 -6
- data/lib/chef/knife/bootstrap_azurerm.rb +1 -1
- data/lib/chef/knife/helpers/azurerm_base.rb +170 -9
- data/lib/knife-azure/version.rb +2 -2
- metadata +37 -95
- data/lib/azure/service_management/ASM_interface.rb +0 -310
- data/lib/azure/service_management/ag.rb +0 -99
- data/lib/azure/service_management/certificate.rb +0 -235
- data/lib/azure/service_management/connection.rb +0 -102
- data/lib/azure/service_management/deploy.rb +0 -221
- data/lib/azure/service_management/disk.rb +0 -68
- data/lib/azure/service_management/host.rb +0 -184
- data/lib/azure/service_management/image.rb +0 -94
- data/lib/azure/service_management/loadbalancer.rb +0 -78
- data/lib/azure/service_management/rest.rb +0 -126
- data/lib/azure/service_management/role.rb +0 -717
- data/lib/azure/service_management/storageaccount.rb +0 -127
- data/lib/azure/service_management/utility.rb +0 -40
- data/lib/azure/service_management/vnet.rb +0 -134
- data/lib/chef/knife/azure_ag_create.rb +0 -73
- data/lib/chef/knife/azure_ag_list.rb +0 -35
- data/lib/chef/knife/azure_image_list.rb +0 -56
- data/lib/chef/knife/azure_internal-lb_create.rb +0 -74
- data/lib/chef/knife/azure_internal-lb_list.rb +0 -35
- data/lib/chef/knife/azure_server_create.rb +0 -531
- data/lib/chef/knife/azure_server_delete.rb +0 -136
- data/lib/chef/knife/azure_server_list.rb +0 -38
- data/lib/chef/knife/azure_server_show.rb +0 -41
- data/lib/chef/knife/azure_vnet_create.rb +0 -74
- data/lib/chef/knife/azure_vnet_list.rb +0 -35
- data/lib/chef/knife/bootstrap_azure.rb +0 -191
- data/lib/chef/knife/helpers/azure_base.rb +0 -392
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#
|
|
2
2
|
# Author:: Aliasgar Batterywala (aliasgar.batterywala@clogeny.com)
|
|
3
|
-
# Copyright:: Copyright (c)
|
|
3
|
+
# Copyright:: Copyright (c) 2012-2026 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved.
|
|
4
4
|
# License:: Apache License, Version 2.0
|
|
5
5
|
#
|
|
6
6
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
@@ -36,8 +36,8 @@ module Azure::ARM
|
|
|
36
36
|
end
|
|
37
37
|
|
|
38
38
|
def get_vnet(resource_group_name, vnet_name)
|
|
39
|
-
|
|
40
|
-
rescue
|
|
39
|
+
arm_client.get_virtual_network(resource_group_name, vnet_name)
|
|
40
|
+
rescue Azure::ResourceManagement::Rest::OperationError => error
|
|
41
41
|
if error.body
|
|
42
42
|
err_json = JSON.parse(error.response.body)
|
|
43
43
|
if err_json["error"]["code"] == "ResourceNotFound"
|
|
@@ -50,7 +50,7 @@ module Azure::ARM
|
|
|
50
50
|
|
|
51
51
|
## lists all subnets under a virtual network or lists subnets of only a particular address space ##
|
|
52
52
|
def subnets_list(resource_group_name, vnet_name, address_prefix = nil)
|
|
53
|
-
list =
|
|
53
|
+
list = arm_client.list_subnets(resource_group_name, vnet_name)
|
|
54
54
|
!address_prefix.nil? && !list.empty? ? subnets_list_for_specific_address_space(address_prefix, list) : list
|
|
55
55
|
end
|
|
56
56
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#
|
|
2
2
|
# Author:: Nimisha Sharad (nimisha.sharad@clogeny.com)
|
|
3
|
-
# Copyright:: Copyright (c)
|
|
3
|
+
# Copyright:: Copyright (c) 2012-2026 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved.
|
|
4
4
|
# License:: Apache License, Version 2.0
|
|
5
5
|
#
|
|
6
6
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
@@ -66,7 +66,7 @@ module Azure
|
|
|
66
66
|
:Attributes, CREDENTIAL_ATTRIBUTE,
|
|
67
67
|
:TargetAlias, :LPTSTR,
|
|
68
68
|
:UserName, :LPTSTR
|
|
69
|
-
|
|
69
|
+
end
|
|
70
70
|
|
|
71
71
|
# Ref: https://msdn.microsoft.com/en-us/library/windows/desktop/aa374804(v=vs.85).aspx
|
|
72
72
|
safe_attach_function :CredReadW, %i{LPCTSTR DWORD DWORD pointer}, :BOOL
|
|
@@ -178,5 +178,5 @@ module Azure
|
|
|
178
178
|
end
|
|
179
179
|
end
|
|
180
180
|
end
|
|
181
|
-
end
|
|
181
|
+
end
|
|
182
182
|
end
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#
|
|
2
2
|
# Author:: Aliasgar Batterywala (aliasgar.batterywala@clogeny.com)
|
|
3
|
-
# Copyright:: Copyright (c)
|
|
3
|
+
# Copyright:: Copyright (c) 2012-2026 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved.
|
|
4
4
|
# License:: Apache License, Version 2.0
|
|
5
5
|
#
|
|
6
6
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
@@ -41,13 +41,14 @@ class Chef
|
|
|
41
41
|
|
|
42
42
|
option :azure_storage_account_type,
|
|
43
43
|
long: "--azure-storage-account-type TYPE",
|
|
44
|
-
description: "Optional.
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
44
|
+
description: "Optional. Sets the storage account type (SKU) of the VM's managed OS
|
|
45
|
+
disk (case-sensitive). One of the following:
|
|
46
|
+
Standard_LRS (Standard HDD locally-redundant storage)
|
|
47
|
+
StandardSSD_LRS (Standard SSD locally-redundant storage)
|
|
48
|
+
Premium_LRS (Premium SSD locally-redundant storage)
|
|
49
|
+
StandardSSD_ZRS (Standard SSD zone-redundant storage)
|
|
50
|
+
Premium_ZRS (Premium SSD zone-redundant storage)",
|
|
51
|
+
default: "Standard_LRS"
|
|
51
52
|
|
|
52
53
|
option :azure_image_reference_publisher,
|
|
53
54
|
long: "--azure-image-reference-publisher PUBLISHER_NAME",
|
|
@@ -128,6 +129,7 @@ class Chef
|
|
|
128
129
|
:azure_service_location
|
|
129
130
|
)
|
|
130
131
|
validate_params!
|
|
132
|
+
warn_if_storage_account_ignored!
|
|
131
133
|
end
|
|
132
134
|
|
|
133
135
|
def plugin_create_instance!
|
|
@@ -181,7 +183,7 @@ class Chef
|
|
|
181
183
|
|
|
182
184
|
server_def[:tcp_endpoints] = config[:tcp_endpoints] if config[:tcp_endpoints]
|
|
183
185
|
|
|
184
|
-
# We assign azure_vm_name to chef_node_name
|
|
186
|
+
# We assign azure_vm_name to chef_node_name if node name is nil.
|
|
185
187
|
config[:chef_node_name] ||= config[:azure_vm_name]
|
|
186
188
|
|
|
187
189
|
server_def[:azure_storage_account] = config[:azure_vm_name] if server_def[:azure_storage_account].nil?
|
|
@@ -239,6 +241,16 @@ class Chef
|
|
|
239
241
|
|
|
240
242
|
private
|
|
241
243
|
|
|
244
|
+
def warn_if_storage_account_ignored!
|
|
245
|
+
return if config[:azure_storage_account].nil?
|
|
246
|
+
|
|
247
|
+
ui.warn("--azure-storage-account is deprecated and has no effect: VM OS disks now use " \
|
|
248
|
+
"Azure managed disks instead of an unmanaged VHD in a storage account. This option " \
|
|
249
|
+
"is kept only for backward compatibility with existing command invocations and will " \
|
|
250
|
+
"be removed in a future release. Use --azure-storage-account-type to control the " \
|
|
251
|
+
"managed disk SKU instead.")
|
|
252
|
+
end
|
|
253
|
+
|
|
242
254
|
def set_defaults
|
|
243
255
|
# set_default_image_reference!
|
|
244
256
|
set_configs
|
|
@@ -292,7 +304,7 @@ class Chef
|
|
|
292
304
|
def set_os_image(publisher, img_offer, default_os_version)
|
|
293
305
|
config[:azure_image_reference_publisher] = publisher
|
|
294
306
|
config[:azure_image_reference_offer] = img_offer
|
|
295
|
-
config[:azure_image_reference_sku] = config[:azure_image_reference_sku]
|
|
307
|
+
config[:azure_image_reference_sku] = config[:azure_image_reference_sku] || default_os_version
|
|
296
308
|
end
|
|
297
309
|
|
|
298
310
|
def is_image_os_type?
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
# Author:: Barry Davis (barryd@jetstreamsoftware.com)
|
|
3
3
|
# Author:: Adam Jacob (<adam@chef.io>)
|
|
4
4
|
# Author:: Seth Chisamore (<schisamo@chef.io>)
|
|
5
|
-
# Copyright:: Copyright (c)
|
|
5
|
+
# Copyright:: Copyright (c) 2012-2026 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved.
|
|
6
6
|
# License:: Apache License, Version 2.0
|
|
7
7
|
#
|
|
8
8
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#
|
|
2
2
|
# Author:: Adam Jacob (<adam@chef.io>)
|
|
3
|
-
# Copyright:: Copyright (c)
|
|
3
|
+
# Copyright:: Copyright (c) 2012-2026 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved.
|
|
4
4
|
# License:: Apache License, Version 2.0
|
|
5
5
|
#
|
|
6
6
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#
|
|
2
2
|
# Author:: Meera Navale (meera.navale@msystechnologies.com)
|
|
3
|
-
# Copyright:: Copyright (c)
|
|
3
|
+
# Copyright:: Copyright (c) 2012-2026 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved.
|
|
4
4
|
# License:: Apache License, Version 2.0
|
|
5
5
|
#
|
|
6
6
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#
|
|
2
2
|
# Author:: Aliasgar Batterywala (aliasgar.batterywala@clogeny.com)
|
|
3
|
-
# Copyright:: Copyright (c)
|
|
3
|
+
# Copyright:: Copyright (c) 2012-2026 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved.
|
|
4
4
|
# License:: Apache License, Version 2.0
|
|
5
5
|
#
|
|
6
6
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
@@ -49,16 +49,11 @@ class Chef
|
|
|
49
49
|
config[:azure_chef_extension_version]
|
|
50
50
|
else
|
|
51
51
|
chef_extension_name ||= get_chef_extension_name
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
)
|
|
58
|
-
elsif @service.instance_of? Azure::ServiceManagement::ASMInterface
|
|
59
|
-
extensions = service.get_extension(chef_extension_name, get_chef_extension_publisher)
|
|
60
|
-
extensions.css("Version").max.text.split(".").first + ".*"
|
|
61
|
-
end
|
|
52
|
+
service.get_latest_chef_extension_version(
|
|
53
|
+
azure_service_location: config[:azure_service_location],
|
|
54
|
+
chef_extension_publisher: get_chef_extension_publisher,
|
|
55
|
+
chef_extension: chef_extension_name
|
|
56
|
+
)
|
|
62
57
|
end
|
|
63
58
|
end
|
|
64
59
|
|
|
@@ -73,12 +68,33 @@ class Chef
|
|
|
73
68
|
|
|
74
69
|
def get_chef_extension_public_params
|
|
75
70
|
pub_config = {}
|
|
71
|
+
|
|
76
72
|
if config[:azure_extension_client_config]
|
|
77
73
|
pub_config[:client_rb] = File.read(File.expand_path(config[:azure_extension_client_config]))
|
|
78
74
|
else
|
|
79
|
-
|
|
75
|
+
# `chef_license` is set here (rather than left to interactive/env-var
|
|
76
|
+
# acceptance) because the VM extension's first chef-client run
|
|
77
|
+
# otherwise fails with "Chef Infra Client cannot execute without
|
|
78
|
+
# accepting the license" -- there's no TTY/CHEF_LICENSE env var
|
|
79
|
+
# available inside the freshly-provisioned VM.
|
|
80
|
+
pub_config[:client_rb] = "chef_server_url \t #{Chef::Config[:chef_server_url].to_json}\nvalidation_client_name\t#{Chef::Config[:validation_client_name].to_json}\nchef_license\t\"accept-no-persist\""
|
|
80
81
|
end
|
|
81
82
|
|
|
83
|
+
# The `chef_license` line in client_rb above only covers chef-client
|
|
84
|
+
# invocations that read that config file (`-c client.rb`). The VM
|
|
85
|
+
# extension also runs a separate `chef-apply -e "cron '...' do ... end"`
|
|
86
|
+
# step (with no `-c` flag) to install the periodic chef-client cron job,
|
|
87
|
+
# which does not read client.rb and fails with the same license error.
|
|
88
|
+
# The extension's own shared.sh reads this top-level "CHEF_LICENSE"
|
|
89
|
+
# public setting and exports it as an environment variable before
|
|
90
|
+
# running any step, so setting it here covers that cron step too.
|
|
91
|
+
pub_config[:CHEF_LICENSE] = "accept-no-persist"
|
|
92
|
+
|
|
93
|
+
# chef_license_key is now forwarded via get_chef_extension_private_params
|
|
94
|
+
# below (protectedSettings), not here -- see
|
|
95
|
+
# chef-partners/azure-chef-extension#413 (version 1210.15.11.1,
|
|
96
|
+
# rolled out to all regions), which added a decrypt path for it.
|
|
97
|
+
|
|
82
98
|
pub_config[:runlist] = config[:run_list].empty? ? "" : config[:run_list].join(",").to_json
|
|
83
99
|
pub_config[:custom_json_attr] = config[:json_attributes] || {}
|
|
84
100
|
pub_config[:extendedLogs] = config[:extended_logs] ? "true" : "false"
|
|
@@ -88,12 +104,29 @@ class Chef
|
|
|
88
104
|
|
|
89
105
|
# bootstrap attributes
|
|
90
106
|
pub_config[:bootstrap_options] = {}
|
|
91
|
-
|
|
107
|
+
# The Chef VM extension always renders this value into a "-E <value>"
|
|
108
|
+
# chef-client argument. If it's left blank (nil, or an explicitly
|
|
109
|
+
# empty string, both of which are truthy in Ruby), chef-client's
|
|
110
|
+
# option parser fails with "missing argument: -E" because the flag
|
|
111
|
+
# gets emitted without a value. Default to Chef's standard
|
|
112
|
+
# "_default" environment whenever --environment isn't supplied or
|
|
113
|
+
# was supplied blank.
|
|
114
|
+
pub_config[:bootstrap_options][:environment] = config[:environment].to_s.empty? ? "_default" : config[:environment]
|
|
92
115
|
pub_config[:bootstrap_options][:chef_node_name] = config[:chef_node_name] if config[:chef_node_name]
|
|
93
116
|
pub_config[:bootstrap_options][:chef_server_url] = Chef::Config[:chef_server_url] if Chef::Config[:chef_server_url]
|
|
94
117
|
pub_config[:bootstrap_options][:validation_client_name] = Chef::Config[:validation_client_name] if Chef::Config[:validation_client_name]
|
|
95
118
|
pub_config[:bootstrap_options][:node_verify_api_cert] = config[:node_verify_api_cert] ? "true" : "false" if config.key?(:node_verify_api_cert)
|
|
96
|
-
|
|
119
|
+
# If --bootstrap-version isn't given, the extension's chef-install.sh
|
|
120
|
+
# treats bootstrap_version as blank, can't tell the major version is
|
|
121
|
+
# >= 19, and falls back to installing the legacy "chef" product
|
|
122
|
+
# (currently capped in the 18.x line) instead of "chef-ice" (19.x+).
|
|
123
|
+
# Chef::Knife::Bootstrap#version_to_install (used by the stock
|
|
124
|
+
# SSH-based chef-full.erb bootstrap that knife-ec2/knife-google rely
|
|
125
|
+
# on) defaults to the major version of the `chef` gem bundled
|
|
126
|
+
# alongside `knife` itself (`Chef::VERSION.split(".").first`) in
|
|
127
|
+
# that same situation, so mirror that default here too.
|
|
128
|
+
resolved_bootstrap_version = config[:bootstrap_version] || Chef::VERSION.split(".").first
|
|
129
|
+
pub_config[:bootstrap_options][:bootstrap_version] = resolved_bootstrap_version
|
|
97
130
|
pub_config[:bootstrap_options][:node_ssl_verify_mode] = config[:node_ssl_verify_mode] if config[:node_ssl_verify_mode]
|
|
98
131
|
pub_config[:bootstrap_options][:bootstrap_proxy] = config[:bootstrap_proxy] if config[:bootstrap_proxy]
|
|
99
132
|
pub_config
|
|
@@ -163,9 +196,104 @@ class Chef
|
|
|
163
196
|
# encrypted_data_bag_secret key for encrypting/decrypting the data bags
|
|
164
197
|
pri_config[:encrypted_data_bag_secret] = load_correct_secret
|
|
165
198
|
|
|
199
|
+
# The extension's install scripts (chef-install.sh/shared.sh,
|
|
200
|
+
# chef-install.psm1/shared.ps1) now decrypt protectedSettings for
|
|
201
|
+
# chef_license_key and prefer it over the deprecated publicSettings
|
|
202
|
+
# location (chef-partners/azure-chef-extension#413, shipped as
|
|
203
|
+
# version 1210.15.11.1, rolled out to all regions for both
|
|
204
|
+
# ChefClient and LinuxChefClient). Forwarding it here instead of via
|
|
205
|
+
# get_chef_extension_public_params means it is delivered to the VM
|
|
206
|
+
# encrypted (CMS/PKCS7, decrypted locally using the cert the Azure
|
|
207
|
+
# Guest Agent provisions), matching the treatment already given to
|
|
208
|
+
# validation_key/chef_server_crt/encrypted_data_bag_secret above,
|
|
209
|
+
# instead of as recoverable plaintext.
|
|
210
|
+
#
|
|
211
|
+
# chef_license_key is optional, not required: when it (and
|
|
212
|
+
# config[:license_id]) are both absent, the extension's
|
|
213
|
+
# chef-install.sh/shared.sh simply fall back to the unlicensed
|
|
214
|
+
# omnitruck.chef.io download host with a warning; there is no
|
|
215
|
+
# "chef_license_bypass" setting to set and no exit/failure path
|
|
216
|
+
# to work around.
|
|
217
|
+
#
|
|
218
|
+
# NOTE: chef-partners/azure-chef-extension#384 briefly made
|
|
219
|
+
# chef_license_key required-by-default with a chef_license_bypass
|
|
220
|
+
# escape hatch, but #409 ("Route to omnitruck when no license_id
|
|
221
|
+
# is specified, instead of chef_license_bypass") removed
|
|
222
|
+
# chef_license_bypass entirely and restored the always-optional,
|
|
223
|
+
# warning-only fallback described above for both the Linux and
|
|
224
|
+
# Windows install scripts. Do not reintroduce bypass handling
|
|
225
|
+
# here based on #384 alone -- check the extension's current HEAD
|
|
226
|
+
# first, since that requirement no longer exists as of #409.
|
|
227
|
+
#
|
|
228
|
+
# Auto-forwarding here when a key IS available
|
|
229
|
+
# (rather than requiring --chef-license-key on every single
|
|
230
|
+
# invocation) mirrors exactly what upstream
|
|
231
|
+
# Chef::Knife::Core::BootstrapContext/WindowsBootstrapContext do for
|
|
232
|
+
# the stock SSH-based bootstrap that knife-ec2/knife-google rely on:
|
|
233
|
+
# prefer an explicitly passed --chef-license-key, else fall back to
|
|
234
|
+
# config[:license_id] (the already-persisted/validated local
|
|
235
|
+
# license that Chef::Knife::Bootstrap#run populates via
|
|
236
|
+
# fetch_license before any plugin_* hook runs). Unlike the stock
|
|
237
|
+
# SSH-based context classes, there is no knife-azure-specific
|
|
238
|
+
# handling of --disable-license-activation: that upstream option
|
|
239
|
+
# isn't honored by this VM-extension delivery path, matching
|
|
240
|
+
# knife-ec2/knife-google, which also have no special-case code for
|
|
241
|
+
# it (their stock bootstrap context handles it generically for
|
|
242
|
+
# SSH/WinRM delivery only, which doesn't apply here either).
|
|
243
|
+
license_key = config[:chef_license_key] || config[:license_id]
|
|
244
|
+
if license_key
|
|
245
|
+
reject_pinned_extension_without_protected_license_support!(license_key)
|
|
246
|
+
pri_config[:chef_license_key] = license_key
|
|
247
|
+
end
|
|
248
|
+
|
|
166
249
|
pri_config
|
|
167
250
|
end
|
|
168
251
|
|
|
252
|
+
# chef-partners/azure-chef-extension release that first decrypts
|
|
253
|
+
# protectedSettings for chef_license_key (see the comment above the
|
|
254
|
+
# pri_config[:chef_license_key] assignment); builds older than this
|
|
255
|
+
# only read the deprecated, plaintext publicSettings location.
|
|
256
|
+
MIN_LICENSE_CAPABLE_EXTENSION_VERSION = "1210.15.11.1".freeze
|
|
257
|
+
|
|
258
|
+
# --azure-chef-extension-version lets a user pin an older extension
|
|
259
|
+
# build via get_chef_extension_version. If that pinned build predates
|
|
260
|
+
# MIN_LICENSE_CAPABLE_EXTENSION_VERSION, it cannot read the
|
|
261
|
+
# protectedSettings-only chef_license_key we now forward, so the VM
|
|
262
|
+
# would silently receive no usable license and the licensed Chef
|
|
263
|
+
# Infra Client install would fail. Fail fast here instead, before the
|
|
264
|
+
# ARM deployment is even built.
|
|
265
|
+
def reject_pinned_extension_without_protected_license_support!(license_key)
|
|
266
|
+
return unless license_key
|
|
267
|
+
|
|
268
|
+
pinned_version = config[:azure_chef_extension_version]
|
|
269
|
+
# Not pinned (resolved via get_latest_chef_extension_version) or a
|
|
270
|
+
# "<major>.*" family selector: Azure resolves either to a current
|
|
271
|
+
# build that supports protectedSettings, so there's nothing to
|
|
272
|
+
# reject here.
|
|
273
|
+
return if pinned_version.nil? || pinned_version.include?("*")
|
|
274
|
+
|
|
275
|
+
begin
|
|
276
|
+
pinned = Gem::Version.new(pinned_version)
|
|
277
|
+
rescue ArgumentError
|
|
278
|
+
# Not a comparable version string; let the Azure API validate (or
|
|
279
|
+
# reject) it instead of guessing here.
|
|
280
|
+
return
|
|
281
|
+
end
|
|
282
|
+
|
|
283
|
+
return if pinned >= Gem::Version.new(MIN_LICENSE_CAPABLE_EXTENSION_VERSION)
|
|
284
|
+
|
|
285
|
+
ui.error(
|
|
286
|
+
"--azure-chef-extension-version #{pinned_version} is older than " \
|
|
287
|
+
"#{MIN_LICENSE_CAPABLE_EXTENSION_VERSION}, the chef-partners/azure-chef-extension " \
|
|
288
|
+
"release that added protectedSettings support for chef_license_key. That version " \
|
|
289
|
+
"only reads the deprecated, plaintext publicSettings location, so the forwarded " \
|
|
290
|
+
"license key would be silently ignored and the licensed Chef Infra Client install " \
|
|
291
|
+
"would fail. Pin a version >= #{MIN_LICENSE_CAPABLE_EXTENSION_VERSION}, or omit " \
|
|
292
|
+
"--azure-chef-extension-version to use the latest extension build."
|
|
293
|
+
)
|
|
294
|
+
exit 1
|
|
295
|
+
end
|
|
296
|
+
|
|
169
297
|
end
|
|
170
298
|
end
|
|
171
299
|
end
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#
|
|
2
2
|
# Author:: Aliasgar Batterywala (aliasgar.batterywala@clogeny.com)
|
|
3
|
-
# Copyright:: Copyright (c)
|
|
3
|
+
# Copyright:: Copyright (c) 2012-2026 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved.
|
|
4
4
|
# License:: Apache License, Version 2.0
|
|
5
5
|
#
|
|
6
6
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
@@ -51,11 +51,9 @@ class Chef
|
|
|
51
51
|
option :azure_storage_account,
|
|
52
52
|
short: "-a NAME",
|
|
53
53
|
long: "--azure-storage-account NAME",
|
|
54
|
-
description: "
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
This name is the DNS prefix name and can be used to access blobs, queues, and tables in the storage account.
|
|
58
|
-
For example: http://ServiceName.blob.core.windows.net/mycontainer/"
|
|
54
|
+
description: "Deprecated. No longer has any effect: VM OS disks now use Azure managed
|
|
55
|
+
disks instead of an unmanaged VHD in a storage account. Kept only for
|
|
56
|
+
backward compatibility with existing command invocations."
|
|
59
57
|
|
|
60
58
|
option :azure_vm_name,
|
|
61
59
|
long: "--azure-vm-name NAME",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#
|
|
2
2
|
# Author:: Nimisha Sharad (nimisha.sharad@clogeny.com)
|
|
3
|
-
# Copyright:: Copyright (c)
|
|
3
|
+
# Copyright:: Copyright (c) 2012-2026 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved.
|
|
4
4
|
# License:: Apache License, Version 2.0
|
|
5
5
|
#
|
|
6
6
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#
|
|
2
2
|
# Author:: Aliasgar Batterywala (aliasgar.batterywala@clogeny.com)
|
|
3
3
|
#
|
|
4
|
-
# Copyright:: Copyright (c)
|
|
4
|
+
# Copyright:: Copyright (c) 2012-2026 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved.
|
|
5
5
|
# License:: Apache License, Version 2.0
|
|
6
6
|
#
|
|
7
7
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
@@ -39,7 +39,7 @@ class Chef
|
|
|
39
39
|
require "json" unless defined?(JSON)
|
|
40
40
|
|
|
41
41
|
if Chef::Platform.windows?
|
|
42
|
-
require_relative "
|
|
42
|
+
require_relative "../../../azure/resource_management/windows_credentials"
|
|
43
43
|
include Azure::ARM::WindowsCredentials
|
|
44
44
|
end
|
|
45
45
|
end
|
|
@@ -48,12 +48,18 @@ class Chef
|
|
|
48
48
|
short: "-r RESOURCE_GROUP_NAME",
|
|
49
49
|
long: "--azure-resource-group-name RESOURCE_GROUP_NAME",
|
|
50
50
|
description: "The Resource Group name."
|
|
51
|
+
|
|
52
|
+
option :azure_environment,
|
|
53
|
+
long: "--azure-environment ENVIRONMENT",
|
|
54
|
+
description: "Optional. The Azure cloud to target: AzureCloud (default), " \
|
|
55
|
+
"AzureUSGovernment, AzureChinaCloud, or AzureGermanCloud.",
|
|
56
|
+
default: "AzureCloud"
|
|
51
57
|
end
|
|
52
58
|
end
|
|
53
59
|
|
|
54
60
|
def service
|
|
55
61
|
details = authentication_details
|
|
56
|
-
details.update(azure_subscription_id: config[:azure_subscription_id])
|
|
62
|
+
details.update(azure_subscription_id: config[:azure_subscription_id], azure_environment: config[:azure_environment])
|
|
57
63
|
@service ||= begin
|
|
58
64
|
require_relative "../../../azure/resource_management/ARM_interface"
|
|
59
65
|
service = Azure::ResourceManagement::ARMInterface.new(details)
|
|
@@ -76,7 +82,7 @@ class Chef
|
|
|
76
82
|
errors = []
|
|
77
83
|
keys.each do |k|
|
|
78
84
|
if config[k].nil?
|
|
79
|
-
errors << "You did not provide a valid '#{pretty_key(k)}' value. Please set knife[:#{k}] in your knife.rb."
|
|
85
|
+
errors << "You did not provide a valid '#{pretty_key(k)}' value. Please set knife[:#{k}] in your config.rb (knife.rb)."
|
|
80
86
|
end
|
|
81
87
|
end
|
|
82
88
|
if errors.each { |e| ui.error(e) }.any?
|
|
@@ -98,13 +104,38 @@ class Chef
|
|
|
98
104
|
|
|
99
105
|
def get_azure_cli_version
|
|
100
106
|
if @azure_version != ""
|
|
101
|
-
get_version =
|
|
107
|
+
get_version = azure_cli_raw_version_output
|
|
102
108
|
@azure_version = get_version.gsub(/[^0-9.]/, "")
|
|
103
109
|
end
|
|
104
110
|
@azure_prefix = @azure_version.to_i < 2 ? "azure" : "az"
|
|
105
111
|
@azure_version
|
|
106
112
|
end
|
|
107
113
|
|
|
114
|
+
# Detects whether the deprecated xplat "azure" CLI or the modern "az"
|
|
115
|
+
# CLI is installed and returns its raw version output. Filtering for
|
|
116
|
+
# the "azure-cli" line is done in Ruby (rather than via `grep`, and
|
|
117
|
+
# without relying on unix-style shell chaining like `||`/`|`) so this
|
|
118
|
+
# works identically on Windows, where `grep` isn't available and
|
|
119
|
+
# Mixlib::ShellOut runs commands through cmd.exe.
|
|
120
|
+
def azure_cli_raw_version_output
|
|
121
|
+
begin
|
|
122
|
+
result = shell_out("azure -v")
|
|
123
|
+
return result.stdout if result.exitstatus.zero?
|
|
124
|
+
rescue Errno::ENOENT
|
|
125
|
+
# The deprecated "azure" xplat CLI isn't installed on this system's PATH,
|
|
126
|
+
# which is expected/fine for the vast majority of users today. Fall
|
|
127
|
+
# through and try the modern "az" CLI instead.
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
begin
|
|
131
|
+
result = shell_out!("az -v")
|
|
132
|
+
rescue Errno::ENOENT
|
|
133
|
+
raise "Could not find either the 'azure' (deprecated) or the 'az' Azure CLI on this system's PATH. " \
|
|
134
|
+
"Please install the Azure CLI: https://learn.microsoft.com/cli/azure/install-azure-cli"
|
|
135
|
+
end
|
|
136
|
+
result.stdout.each_line.find { |line| line.include?("azure-cli") } || result.stdout
|
|
137
|
+
end
|
|
138
|
+
|
|
108
139
|
def token_details_for_windows
|
|
109
140
|
if is_old_xplat?
|
|
110
141
|
token_details_from_WCM
|
|
@@ -176,8 +207,13 @@ class Chef
|
|
|
176
207
|
require "base64" unless defined?(Base64)
|
|
177
208
|
require "openssl" unless defined?(OpenSSL)
|
|
178
209
|
require "uri" unless defined?(URI)
|
|
210
|
+
retried_with_legacy_provider = false
|
|
179
211
|
begin
|
|
180
|
-
|
|
212
|
+
# Use the block form of File.open so the file handle is always closed
|
|
213
|
+
# after parsing, rather than left open for the GC to close later. This
|
|
214
|
+
# matters on Windows, where an open handle prevents a Tempfile-based
|
|
215
|
+
# fixture (used in specs) from being unlinked.
|
|
216
|
+
doc = File.open(find_file(filename)) { |file| Nokogiri::XML(file) }
|
|
181
217
|
profile = doc.at_css("PublishProfile")
|
|
182
218
|
subscription = profile.at_css("Subscription")
|
|
183
219
|
# check given PublishSettings XML file format.Currently PublishSettings file have two different XML format
|
|
@@ -192,12 +228,105 @@ class Chef
|
|
|
192
228
|
end
|
|
193
229
|
config[:azure_mgmt_cert] = management_cert.certificate.to_pem + management_cert.key.to_pem
|
|
194
230
|
config[:azure_subscription_id] = doc.at_css("Subscription").attribute("Id").value
|
|
231
|
+
rescue OpenSSL::PKCS12::PKCS12Error => error
|
|
232
|
+
# Older Azure publish settings files use PKCS12 certificates encrypted with
|
|
233
|
+
# the legacy RC2-40-CBC cipher, which OpenSSL 3.x disables by default. On
|
|
234
|
+
# OpenSSL 3.x the raised error usually doesn't mention "RC2-40-CBC" at all -
|
|
235
|
+
# it's typically the generic "PKCS12_parse: unsupported" - so treat any
|
|
236
|
+
# "unsupported"-style PKCS12Error as a candidate for the legacy-cipher retry,
|
|
237
|
+
# not just messages that explicitly say RC2-40-CBC. Only load OpenSSL's
|
|
238
|
+
# "legacy" provider (widening the process-wide crypto surface) if we
|
|
239
|
+
# actually hit one of these failures, and only retry once.
|
|
240
|
+
if !retried_with_legacy_provider && legacy_cipher_error?(error) && load_openssl_legacy_provider
|
|
241
|
+
retried_with_legacy_provider = true
|
|
242
|
+
retry
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
if legacy_cipher_error?(error)
|
|
246
|
+
ui.error("Cannot parse certificate: #{error.message}")
|
|
247
|
+
if retried_with_legacy_provider
|
|
248
|
+
# We already successfully loaded OpenSSL's legacy provider and retried,
|
|
249
|
+
# but parsing still failed with what looks like a legacy-cipher error.
|
|
250
|
+
# The cipher isn't "unavailable" in this case -- something else about
|
|
251
|
+
# the certificate is unparseable -- so don't tell the user to
|
|
252
|
+
# regenerate it with a "more recent cipher" as if the provider were
|
|
253
|
+
# still missing.
|
|
254
|
+
ui.error("The PKCS12 certificate could not be parsed even after enabling OpenSSL's " \
|
|
255
|
+
"legacy provider (used for ciphers such as RC2-40-CBC). The file may be corrupt " \
|
|
256
|
+
"or use a cipher that isn't supported even by the legacy provider.")
|
|
257
|
+
else
|
|
258
|
+
ui.error("The PKCS12 certificate may use the legacy RC2-40-CBC cipher, which is unavailable " \
|
|
259
|
+
"in the current OpenSSL configuration. Please regenerate the publish settings file " \
|
|
260
|
+
"with a more recent cipher.")
|
|
261
|
+
end
|
|
262
|
+
else
|
|
263
|
+
ui.error("Error parsing PKCS12 certificate: #{error.message}")
|
|
264
|
+
end
|
|
265
|
+
exit 1
|
|
195
266
|
rescue => error
|
|
196
267
|
puts "#{error.class} and #{error.message}"
|
|
197
268
|
exit 1
|
|
198
269
|
end
|
|
199
270
|
end
|
|
200
271
|
|
|
272
|
+
# Returns true if the given OpenSSL::PKCS12::PKCS12Error looks like it was
|
|
273
|
+
# caused by a legacy/deprecated cipher (such as RC2-40-CBC) being disabled by
|
|
274
|
+
# default on OpenSSL 3.x. On OpenSSL 3.x this commonly surfaces as a generic
|
|
275
|
+
# "unsupported" error rather than one that names RC2-40-CBC explicitly.
|
|
276
|
+
def legacy_cipher_error?(error)
|
|
277
|
+
error.message =~ /RC2-40-CBC/i || error.message =~ /unsupported/i
|
|
278
|
+
end
|
|
279
|
+
|
|
280
|
+
# Attempts to load OpenSSL's "legacy" provider (needed to decrypt PKCS12 files
|
|
281
|
+
# using deprecated ciphers such as RC2-40-CBC). Returns true if the provider was
|
|
282
|
+
# loaded successfully, false otherwise (e.g. it isn't available on this system).
|
|
283
|
+
def load_openssl_legacy_provider
|
|
284
|
+
if defined?(OpenSSL::Provider)
|
|
285
|
+
OpenSSL::Provider.load("legacy")
|
|
286
|
+
OpenSSL::Provider.load("default")
|
|
287
|
+
return true
|
|
288
|
+
end
|
|
289
|
+
|
|
290
|
+
# `OpenSSL::Provider` was only added to the "openssl" Ruby gem in version 3.0,
|
|
291
|
+
# which ships as a default gem starting with Ruby 3.2. On supported Ruby 3.1
|
|
292
|
+
# installations (bundled openssl gem < 3.0) there's no Ruby API to load an
|
|
293
|
+
# OpenSSL 3.x provider, even though the underlying libcrypto may itself be
|
|
294
|
+
# OpenSSL 3.x and support providers. Fall back to calling libcrypto's
|
|
295
|
+
# OSSL_PROVIDER_load directly via FFI (already a runtime dependency of this
|
|
296
|
+
# gem) so legacy PKCS12 files can still be decrypted on Ruby 3.1.
|
|
297
|
+
load_openssl_legacy_provider_via_ffi
|
|
298
|
+
rescue StandardError, LoadError
|
|
299
|
+
false
|
|
300
|
+
end
|
|
301
|
+
|
|
302
|
+
# Loads the OpenSSL "legacy" and "default" providers by calling libcrypto's
|
|
303
|
+
# OSSL_PROVIDER_load function directly through FFI. This only works when the
|
|
304
|
+
# linked libcrypto is OpenSSL 3.x (the function doesn't exist on OpenSSL 1.1.1
|
|
305
|
+
# or LibreSSL); any failure to locate the library/symbol is treated as the
|
|
306
|
+
# provider simply being unavailable.
|
|
307
|
+
def load_openssl_legacy_provider_via_ffi
|
|
308
|
+
require "ffi" unless defined?(FFI)
|
|
309
|
+
|
|
310
|
+
# Bind to OSSL_PROVIDER_load in the *current process's* already-loaded
|
|
311
|
+
# symbol table (FFI::Library::CURRENT_PROCESS) rather than dlopen-ing "ssl"/"crypto"
|
|
312
|
+
# again. Re-loading libcrypto as a second, separate mapping alongside the
|
|
313
|
+
# one Ruby's own "openssl" extension already loaded can make OpenSSL 3.x
|
|
314
|
+
# detect what it considers an unsafe double-load and abort the whole
|
|
315
|
+
# process (observed as "libcrypto in an unsafe way" on macOS); attaching to
|
|
316
|
+
# the existing in-process symbols avoids that entirely.
|
|
317
|
+
provider_loader = Module.new do
|
|
318
|
+
extend FFI::Library
|
|
319
|
+
ffi_lib FFI::Library::CURRENT_PROCESS
|
|
320
|
+
attach_function :OSSL_PROVIDER_load, %i{pointer string}, :pointer
|
|
321
|
+
end
|
|
322
|
+
|
|
323
|
+
legacy = provider_loader.OSSL_PROVIDER_load(nil, "legacy")
|
|
324
|
+
default = provider_loader.OSSL_PROVIDER_load(nil, "default")
|
|
325
|
+
!legacy.null? && !default.null?
|
|
326
|
+
rescue StandardError, LoadError
|
|
327
|
+
false
|
|
328
|
+
end
|
|
329
|
+
|
|
201
330
|
def find_file(name)
|
|
202
331
|
name = ::File.expand_path(name)
|
|
203
332
|
config_dir = Chef::Knife.chef_config_dir
|
|
@@ -275,10 +404,10 @@ class Chef
|
|
|
275
404
|
end
|
|
276
405
|
|
|
277
406
|
if !is_image_windows?
|
|
278
|
-
if (config[:azure_vm_name].match
|
|
407
|
+
if (config[:azure_vm_name].match(/^(?=.*[a-zA-Z-])([a-zA-Z0-9-]{1,64})$/)).nil?
|
|
279
408
|
raise ArgumentError, "VM name can only contain alphanumeric and hyphen(-) characters and maximum length cannot exceed 64 characters."
|
|
280
409
|
end
|
|
281
|
-
elsif (config[:azure_vm_name].match
|
|
410
|
+
elsif (config[:azure_vm_name].match(/^(?=.*[a-zA-Z-])([a-zA-Z0-9-]{1,15})$/)).nil?
|
|
282
411
|
raise ArgumentError, "VM name can only contain alphanumeric and hyphen(-) characters and maximum length cannot exceed 15 characters."
|
|
283
412
|
end
|
|
284
413
|
|
|
@@ -302,6 +431,38 @@ class Chef
|
|
|
302
431
|
end
|
|
303
432
|
end
|
|
304
433
|
|
|
434
|
+
if config[:azure_storage_account_type]
|
|
435
|
+
# --azure-storage-account-type now sets the managed OS disk's SKU rather than a
|
|
436
|
+
# storage account's replication type; the old storage-account-only replication
|
|
437
|
+
# values (Standard_ZRS, Standard_GRS, Standard_RAGRS) are not valid managed disk
|
|
438
|
+
# SKUs and would otherwise be sent through to Azure and fail remotely.
|
|
439
|
+
legacy_storage_account_types = %w{Standard_ZRS Standard_GRS Standard_RAGRS}
|
|
440
|
+
valid_managed_disk_types = %w{Standard_LRS StandardSSD_LRS Premium_LRS StandardSSD_ZRS Premium_ZRS}
|
|
441
|
+
if legacy_storage_account_types.include?(config[:azure_storage_account_type])
|
|
442
|
+
raise ArgumentError, "'#{config[:azure_storage_account_type]}' is a storage-account replication type " \
|
|
443
|
+
"that is no longer valid for --azure-storage-account-type, since VMs now use managed disks. " \
|
|
444
|
+
"Please use one of the managed disk SKUs instead: #{valid_managed_disk_types.join(", ")}."
|
|
445
|
+
elsif !valid_managed_disk_types.include?(config[:azure_storage_account_type])
|
|
446
|
+
raise ArgumentError, "Invalid value '#{config[:azure_storage_account_type]}' for --azure-storage-account-type. " \
|
|
447
|
+
"Use one of the following managed disk SKUs: #{valid_managed_disk_types.join(", ")}."
|
|
448
|
+
end
|
|
449
|
+
end
|
|
450
|
+
|
|
451
|
+
if config[:azure_availability_set]
|
|
452
|
+
# Azure availability set SKUs are immutable once created. The deployment
|
|
453
|
+
# template always creates/updates the set as "Aligned" (required for VMs with
|
|
454
|
+
# managed disks), so reusing the name of a pre-existing legacy "Classic" set
|
|
455
|
+
# (e.g. from an older unmanaged-disk deployment) would fail remotely with a
|
|
456
|
+
# cryptic ARM error. Detect that case here and fail fast with guidance instead.
|
|
457
|
+
existing_sku = service.existing_availability_set_sku(config[:azure_resource_group_name], config[:azure_availability_set])
|
|
458
|
+
if existing_sku != :not_found && existing_sku != "Aligned"
|
|
459
|
+
raise ArgumentError, "The availability set '#{config[:azure_availability_set]}' already exists as a " \
|
|
460
|
+
"legacy 'Classic' (unmanaged-disk) availability set. Azure availability set SKUs are immutable, so " \
|
|
461
|
+
"it cannot be converted to 'Aligned' for use with managed disks. Please choose a different, unused " \
|
|
462
|
+
"name for --azure-availability-set, or delete and recreate the existing availability set before reusing it."
|
|
463
|
+
end
|
|
464
|
+
end
|
|
465
|
+
|
|
305
466
|
config[:ohai_hints] = format_ohai_hints(config[:ohai_hints])
|
|
306
467
|
validate_ohai_hints unless config[:ohai_hints].casecmp("default").zero?
|
|
307
468
|
end
|
|
@@ -349,7 +510,7 @@ class Chef
|
|
|
349
510
|
## in Windows Credentails Manager (WCM).
|
|
350
511
|
## Newer versions use the same pattern across platforms where credentials gets
|
|
351
512
|
## stored in ~/.azure/accessTokens.json file.
|
|
352
|
-
"Please run XPLAT's '#{@azure_prefix} login' command OR specify azure_tenant_id, azure_subscription_id, azure_client_id, azure_client_secret in your knife.rb"
|
|
513
|
+
"Please run XPLAT's '#{@azure_prefix} login' command OR specify azure_tenant_id, azure_subscription_id, azure_client_id, azure_client_secret in your config.rb (knife.rb)."
|
|
353
514
|
end
|
|
354
515
|
end
|
|
355
516
|
end
|