knife-azure 1.8.0 → 1.8.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/LICENSE +201 -201
- data/lib/azure/resource_management/ARM_deployment_template.rb +1 -1
- data/lib/azure/service_management/certificate.rb +0 -0
- data/lib/azure/service_management/connection.rb +0 -0
- data/lib/azure/service_management/deploy.rb +0 -0
- data/lib/azure/service_management/disk.rb +0 -0
- data/lib/azure/service_management/host.rb +0 -0
- data/lib/azure/service_management/image.rb +0 -0
- data/lib/azure/service_management/rest.rb +0 -0
- data/lib/azure/service_management/role.rb +0 -0
- data/lib/azure/service_management/utility.rb +0 -0
- data/lib/chef/knife/azure_ag_list.rb +0 -2
- data/lib/chef/knife/azure_base.rb +70 -0
- data/lib/chef/knife/azure_image_list.rb +0 -0
- data/lib/chef/knife/azure_internal-lb_list.rb +0 -2
- data/lib/chef/knife/azure_server_create.rb +2 -83
- data/lib/chef/knife/azure_server_delete.rb +0 -0
- data/lib/chef/knife/azure_server_list.rb +0 -0
- data/lib/chef/knife/azure_server_show.rb +0 -0
- data/lib/chef/knife/azure_vnet_list.rb +0 -2
- data/lib/chef/knife/azurerm_base.rb +71 -52
- data/lib/chef/knife/azurerm_server_create.rb +38 -45
- data/lib/chef/knife/azurerm_server_delete.rb +35 -27
- data/lib/chef/knife/azurerm_server_list.rb +20 -0
- data/lib/chef/knife/azurerm_server_show.rb +3 -1
- data/lib/chef/knife/bootstrap/bootstrapper.rb +11 -6
- data/lib/knife-azure/version.rb +1 -1
- metadata +3 -17
@@ -124,7 +124,77 @@ class Chef
|
|
124
124
|
end
|
125
125
|
|
126
126
|
# validate command pre-requisites (cli options)
|
127
|
+
# (locate_config_value(:winrm_password).length <= 6 && locate_config_value(:winrm_password).length >= 72)
|
127
128
|
def validate_params!
|
129
|
+
if locate_config_value(:winrm_password) && !locate_config_value(:winrm_password).strip.size.between?(6, 72)
|
130
|
+
ui.error("The supplied password must be 6-72 characters long and meet password complexity requirements")
|
131
|
+
exit 1
|
132
|
+
end
|
133
|
+
|
134
|
+
if locate_config_value(:ssh_password) && !locate_config_value(:ssh_password).empty? && !locate_config_value(:ssh_password).strip.size.between?(6, 72)
|
135
|
+
ui.error("The supplied ssh password must be 6-72 characters long and meet password complexity requirements")
|
136
|
+
exit 1
|
137
|
+
end
|
138
|
+
|
139
|
+
if locate_config_value(:azure_connect_to_existing_dns) && locate_config_value(:azure_vm_name).nil?
|
140
|
+
ui.error("Specify the VM name using --azure-vm-name option, since you are connecting to existing dns")
|
141
|
+
exit 1
|
142
|
+
end
|
143
|
+
|
144
|
+
if locate_config_value(:azure_service_location) && locate_config_value(:azure_affinity_group)
|
145
|
+
ui.error("Cannot specify both --azure-service-location and --azure-affinity-group, use one or the other.")
|
146
|
+
exit 1
|
147
|
+
elsif locate_config_value(:azure_service_location).nil? && locate_config_value(:azure_affinity_group).nil?
|
148
|
+
ui.error("Must specify either --azure-service-location or --azure-affinity-group.")
|
149
|
+
exit 1
|
150
|
+
end
|
151
|
+
|
152
|
+
if locate_config_value(:winrm_authentication_protocol) && ! %w{basic negotiate kerberos}.include?(locate_config_value(:winrm_authentication_protocol).downcase)
|
153
|
+
ui.error("Invalid value for --winrm-authentication-protocol option. Use valid protocol values i.e [basic, negotiate, kerberos]")
|
154
|
+
exit 1
|
155
|
+
end
|
156
|
+
|
157
|
+
if !(service.valid_image?(locate_config_value(:azure_source_image)))
|
158
|
+
ui.error("Image '#{locate_config_value(:azure_source_image)}' is invalid")
|
159
|
+
exit 1
|
160
|
+
end
|
161
|
+
|
162
|
+
# Validate join domain requirements.
|
163
|
+
if locate_config_value(:azure_domain_name) || locate_config_value(:azure_domain_user)
|
164
|
+
if locate_config_value(:azure_domain_user).nil? || locate_config_value(:azure_domain_passwd).nil?
|
165
|
+
ui.error("Must specify both --azure-domain-user and --azure-domain-passwd.")
|
166
|
+
exit 1
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
if locate_config_value(:winrm_transport) == "ssl" && locate_config_value(:thumbprint).nil? && ( locate_config_value(:winrm_ssl_verify_mode).nil? || locate_config_value(:winrm_ssl_verify_mode) == :verify_peer )
|
171
|
+
ui.error("The SSL transport was specified without the --thumbprint option. Specify a thumbprint, or alternatively set the --winrm-ssl-verify-mode option to 'verify_none' to skip verification.")
|
172
|
+
exit 1
|
173
|
+
end
|
174
|
+
|
175
|
+
if locate_config_value(:extended_logs) && locate_config_value(:bootstrap_protocol) != 'cloud-api'
|
176
|
+
ui.error("--extended-logs option only works with --bootstrap-protocol cloud-api")
|
177
|
+
exit 1
|
178
|
+
end
|
179
|
+
|
180
|
+
if locate_config_value(:bootstrap_protocol) == 'cloud-api' && locate_config_value(:azure_vm_name).nil? && locate_config_value(:azure_dns_name).nil?
|
181
|
+
ui.error("Specifying the DNS name using --azure-dns-name or VM name using --azure-vm-name option is required with --bootstrap-protocol cloud-api")
|
182
|
+
exit 1
|
183
|
+
end
|
184
|
+
|
185
|
+
if locate_config_value(:daemon)
|
186
|
+
unless is_image_windows?
|
187
|
+
raise ArgumentError, "The daemon option is only supported for Windows nodes."
|
188
|
+
end
|
189
|
+
|
190
|
+
unless locate_config_value(:bootstrap_protocol) == 'cloud-api'
|
191
|
+
raise ArgumentError, "The --daemon option requires the use of --bootstrap-protocol cloud-api"
|
192
|
+
end
|
193
|
+
|
194
|
+
unless %w{none service task}.include?(locate_config_value(:daemon).downcase)
|
195
|
+
raise ArgumentError, "Invalid value for --daemon option. Valid values are 'none', 'service' and 'task'."
|
196
|
+
end
|
197
|
+
end
|
128
198
|
end
|
129
199
|
|
130
200
|
# validates keys
|
File without changes
|
@@ -232,7 +232,6 @@ class Chef
|
|
232
232
|
|
233
233
|
def wait_until_virtual_machine_ready(retry_interval_in_seconds = 30)
|
234
234
|
vm_status = nil
|
235
|
-
|
236
235
|
begin
|
237
236
|
azure_vm_startup_timeout = locate_config_value(:azure_vm_startup_timeout).to_i
|
238
237
|
azure_vm_ready_timeout = locate_config_value(:azure_vm_ready_timeout).to_i
|
@@ -355,12 +354,9 @@ class Chef
|
|
355
354
|
if role.at_css("RoleName").text == locate_config_value(:azure_vm_name)
|
356
355
|
lnx_waagent_fail_msg = "Failed to deserialize the status reported by the Guest Agent"
|
357
356
|
waagent_status_msg = role.at_css("GuestAgentStatus FormattedMessage Message").text
|
358
|
-
|
359
357
|
if role.at_css("GuestAgentStatus Status").text == "Ready"
|
360
358
|
extn_status = role.at_css("ResourceExtensionStatusList Status").text
|
361
|
-
|
362
359
|
Chef::Log.debug("Resource extension status is #{extn_status}")
|
363
|
-
|
364
360
|
if extn_status == "Installing"
|
365
361
|
extension_status[:status] = :extension_installing
|
366
362
|
extension_status[:message] = role.at_css("ResourceExtensionStatusList FormattedMessage Message").text
|
@@ -387,30 +383,20 @@ class Chef
|
|
387
383
|
else
|
388
384
|
extension_status[:status] = :extension_status_not_detected
|
389
385
|
end
|
390
|
-
|
391
386
|
return extension_status
|
392
387
|
end
|
393
388
|
|
394
389
|
def run
|
395
390
|
$stdout.sync = true
|
396
|
-
|
397
391
|
storage = nil
|
398
|
-
|
399
392
|
Chef::Log.info("validating...")
|
400
393
|
validate_asm_keys!(:azure_source_image)
|
401
|
-
|
402
394
|
validate_params!
|
403
|
-
|
404
395
|
ssh_override_winrm if !is_image_windows?
|
405
|
-
|
406
396
|
Chef::Log.info("creating...")
|
407
|
-
|
408
397
|
config[:azure_dns_name] = get_dns_name(locate_config_value(:azure_dns_name))
|
409
|
-
|
410
|
-
|
411
|
-
config[:azure_vm_name] = locate_config_value(:azure_dns_name)
|
412
|
-
end
|
413
|
-
|
398
|
+
config[:azure_vm_name] = locate_config_value(:azure_dns_name) unless locate_config_value(:azure_vm_name)
|
399
|
+
config[:chef_node_name] = locate_config_value(:azure_vm_name) unless locate_config_value(:chef_node_name)
|
414
400
|
service.create_server(create_server_def)
|
415
401
|
wait_until_virtual_machine_ready()
|
416
402
|
if locate_config_value(:bootstrap_protocol) == 'cloud-api' && locate_config_value(:extended_logs)
|
@@ -423,73 +409,6 @@ class Chef
|
|
423
409
|
bootstrap_exec(server) unless locate_config_value(:bootstrap_protocol) == 'cloud-api'
|
424
410
|
end
|
425
411
|
|
426
|
-
def validate_params!
|
427
|
-
if locate_config_value(:winrm_password) && (locate_config_value(:winrm_password).length <= 6 && locate_config_value(:winrm_password).length >= 72)
|
428
|
-
ui.error("The supplied password must be 6-72 characters long and meet password complexity requirements")
|
429
|
-
exit 1
|
430
|
-
end
|
431
|
-
|
432
|
-
if locate_config_value(:ssh_password) && (locate_config_value(:ssh_password).length <= 6 && locate_config_value(:ssh_password).length >= 72)
|
433
|
-
ui.error("The supplied password must be 6-72 characters long and meet password complexity requirements")
|
434
|
-
exit 1
|
435
|
-
end
|
436
|
-
|
437
|
-
if locate_config_value(:azure_connect_to_existing_dns) && locate_config_value(:azure_vm_name).nil?
|
438
|
-
ui.error("Specify the VM name using --azure-vm-name option, since you are connecting to existing dns")
|
439
|
-
exit 1
|
440
|
-
end
|
441
|
-
|
442
|
-
if locate_config_value(:azure_service_location) && locate_config_value(:azure_affinity_group)
|
443
|
-
ui.error("Cannot specify both --azure-service-location and --azure-affinity-group, use one or the other.")
|
444
|
-
exit 1
|
445
|
-
elsif locate_config_value(:azure_service_location).nil? && locate_config_value(:azure_affinity_group).nil?
|
446
|
-
ui.error("Must specify either --azure-service-location or --azure-affinity-group.")
|
447
|
-
exit 1
|
448
|
-
end
|
449
|
-
|
450
|
-
if locate_config_value(:winrm_authentication_protocol) && ! %w{basic negotiate kerberos}.include?(locate_config_value(:winrm_authentication_protocol))
|
451
|
-
ui.error("Invalid value for --winrm-authentication-protocol option. Use valid protocol values i.e [basic, negotiate, kerberos]")
|
452
|
-
exit 1
|
453
|
-
end
|
454
|
-
|
455
|
-
if !(service.valid_image?(locate_config_value(:azure_source_image)))
|
456
|
-
ui.error("Image provided is invalid")
|
457
|
-
exit 1
|
458
|
-
end
|
459
|
-
|
460
|
-
# Validate join domain requirements.
|
461
|
-
if locate_config_value(:azure_domain_name) || locate_config_value(:azure_domain_user)
|
462
|
-
if locate_config_value(:azure_domain_user).nil? || locate_config_value(:azure_domain_passwd).nil?
|
463
|
-
ui.error("Must specify both --azure-domain-user and --azure-domain-passwd.")
|
464
|
-
exit 1
|
465
|
-
end
|
466
|
-
end
|
467
|
-
|
468
|
-
if locate_config_value(:winrm_transport) == "ssl" && locate_config_value(:thumbprint).nil? && ( locate_config_value(:winrm_ssl_verify_mode).nil? || locate_config_value(:winrm_ssl_verify_mode) == :verify_peer )
|
469
|
-
ui.error("The SSL transport was specified without the --thumbprint option. Specify a thumbprint, or alternatively set the --winrm-ssl-verify-mode option to 'verify_none' to skip verification.")
|
470
|
-
exit 1
|
471
|
-
end
|
472
|
-
|
473
|
-
if locate_config_value(:extended_logs) && locate_config_value(:bootstrap_protocol) != 'cloud-api'
|
474
|
-
ui.error("--extended-logs option works with --bootstrap-protocol cloud-api")
|
475
|
-
exit 1
|
476
|
-
end
|
477
|
-
|
478
|
-
if locate_config_value(:daemon)
|
479
|
-
unless is_image_windows?
|
480
|
-
raise ArgumentError, "The daemon option is only support for Windows nodes."
|
481
|
-
end
|
482
|
-
|
483
|
-
unless locate_config_value(:bootstrap_protocol) == 'cloud-api'
|
484
|
-
raise ArgumentError, "--daemon option works with --bootstrap-protocol cloud-api"
|
485
|
-
end
|
486
|
-
|
487
|
-
unless %w{none service task}.include?(locate_config_value(:daemon))
|
488
|
-
raise ArgumentError, "Invalid value for --daemon option. Use valid daemon values i.e 'none', 'service' and 'task'."
|
489
|
-
end
|
490
|
-
end
|
491
|
-
end
|
492
|
-
|
493
412
|
def create_server_def
|
494
413
|
server_def = {
|
495
414
|
:azure_storage_account => locate_config_value(:azure_storage_account),
|
File without changes
|
File without changes
|
File without changes
|
@@ -1,7 +1,7 @@
|
|
1
1
|
|
2
2
|
# Author:: Aliasgar Batterywala (aliasgar.batterywala@clogeny.com)
|
3
3
|
#
|
4
|
-
# Copyright:: Copyright
|
4
|
+
# Copyright:: Copyright 2009-2018, Chef Software Inc.
|
5
5
|
# License:: Apache License, Version 2.0
|
6
6
|
#
|
7
7
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
@@ -40,7 +40,6 @@ class Chef
|
|
40
40
|
|
41
41
|
def self.included(includer)
|
42
42
|
includer.class_eval do
|
43
|
-
|
44
43
|
deps do
|
45
44
|
require 'readline'
|
46
45
|
require 'chef/json_compat'
|
@@ -50,7 +49,6 @@ class Chef
|
|
50
49
|
:short => "-r RESOURCE_GROUP_NAME",
|
51
50
|
:long => "--azure-resource-group-name RESOURCE_GROUP_NAME",
|
52
51
|
:description => "The Resource Group name."
|
53
|
-
|
54
52
|
end
|
55
53
|
end
|
56
54
|
|
@@ -71,13 +69,13 @@ class Chef
|
|
71
69
|
|
72
70
|
# validates ARM mandatory keys
|
73
71
|
def validate_arm_keys!(*keys)
|
74
|
-
parse_publish_settings_file(locate_config_value(:azure_publish_settings_file))
|
72
|
+
parse_publish_settings_file(locate_config_value(:azure_publish_settings_file)) unless locate_config_value(:azure_publish_settings_file).nil?
|
75
73
|
keys.push(:azure_subscription_id)
|
76
74
|
|
77
|
-
if
|
75
|
+
if azure_cred?
|
78
76
|
validate_azure_login
|
79
77
|
else
|
80
|
-
|
78
|
+
keys.concat([:azure_tenant_id, :azure_client_id, :azure_client_secret])
|
81
79
|
end
|
82
80
|
|
83
81
|
errors = []
|
@@ -92,7 +90,7 @@ class Chef
|
|
92
90
|
end
|
93
91
|
|
94
92
|
def authentication_details
|
95
|
-
if
|
93
|
+
if is_azure_cred?
|
96
94
|
return {:azure_tenant_id => locate_config_value(:azure_tenant_id), :azure_client_id => locate_config_value(:azure_client_id), :azure_client_secret => locate_config_value(:azure_client_secret)}
|
97
95
|
elsif Chef::Platform.windows?
|
98
96
|
token_details = token_details_for_windows()
|
@@ -103,16 +101,13 @@ class Chef
|
|
103
101
|
token_details
|
104
102
|
end
|
105
103
|
|
106
|
-
def
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
def is_WCM_env_var_set?
|
115
|
-
ENV['AZURE_USE_SECURE_TOKEN_STORAGE'].nil? ? false : true
|
104
|
+
def get_azure_cli_version
|
105
|
+
if @azure_version != ""
|
106
|
+
get_version = shell_out!("azure -v || az -v | grep azure-cli", { returns: [0] }).stdout
|
107
|
+
@azure_version = get_version.gsub(/[^0-9.]/, '')
|
108
|
+
end
|
109
|
+
@azure_prefix = @azure_version.to_i < 2 ? "azure" : "az"
|
110
|
+
@azure_version
|
116
111
|
end
|
117
112
|
|
118
113
|
def token_details_for_windows
|
@@ -141,56 +136,49 @@ class Chef
|
|
141
136
|
return false
|
142
137
|
elsif time_difference <= 600 # 600sec = 10min
|
143
138
|
# This is required otherwise a long running command may fail inbetween if the token gets expired.
|
144
|
-
raise "Token will expire within 10 minutes. Please run '
|
139
|
+
raise "Token will expire within 10 minutes. Please run '#{@azure_prefix} login' command"
|
145
140
|
else
|
146
141
|
return true
|
147
142
|
end
|
148
143
|
end
|
149
144
|
|
150
145
|
def refresh_token
|
146
|
+
azure_authentication
|
147
|
+
token_details = Chef::Platform.windows? ? token_details_for_windows() : token_details_for_linux()
|
148
|
+
end
|
149
|
+
|
150
|
+
def azure_authentication
|
151
151
|
begin
|
152
152
|
ui.log("Authenticating...")
|
153
|
-
Mixlib::ShellOut.new("
|
153
|
+
Mixlib::ShellOut.new("#{@azure_prefix} vm show 'knifetest@resourcegroup' testvm", :timeout => 30).run_command
|
154
154
|
rescue Mixlib::ShellOut::CommandTimeout
|
155
155
|
rescue Exception
|
156
|
-
|
156
|
+
raise_azure_status
|
157
157
|
end
|
158
|
-
if Chef::Platform.windows?
|
159
|
-
token_details = token_details_for_windows()
|
160
|
-
else
|
161
|
-
token_details = token_details_for_linux()
|
162
|
-
end
|
163
|
-
token_details
|
164
158
|
end
|
165
159
|
|
166
160
|
def check_token_validity(token_details)
|
167
|
-
|
168
|
-
token_details = refresh_token
|
169
|
-
|
170
|
-
|
161
|
+
unless is_token_valid?(token_details)
|
162
|
+
token_details = refresh_token
|
163
|
+
unless is_token_valid?(token_details)
|
164
|
+
raise_azure_status
|
171
165
|
end
|
172
166
|
end
|
173
167
|
token_details
|
174
168
|
end
|
175
169
|
|
176
170
|
def validate_azure_login
|
177
|
-
err_string = "Please run XPLAT's 'azure login' command OR specify azure_tenant_id, azure_subscription_id, azure_client_id, azure_client_secret in your knife.rb"
|
178
|
-
|
179
|
-
## Older versions of the Azure CLI on Windows stored credentials in a unique way
|
180
|
-
## in Windows Credentails Manager (WCM).
|
181
|
-
## Newer versions use the same pattern across platforms where credentials gets
|
182
|
-
## stored in ~/.azure/accessTokens.json file.
|
183
171
|
if Chef::Platform.windows? && (is_old_xplat? || is_WCM_env_var_set?)
|
184
172
|
# cmdkey command is used for accessing windows credential manager
|
185
173
|
xplat_creds_cmd = Mixlib::ShellOut.new("cmdkey /list | findstr AzureXplatCli")
|
186
174
|
result = xplat_creds_cmd.run_command
|
187
175
|
if result.stdout.nil? || result.stdout.empty?
|
188
|
-
raise
|
176
|
+
raise login_message
|
189
177
|
end
|
190
178
|
else
|
191
179
|
home_dir = File.expand_path('~')
|
192
180
|
if !File.exists?(home_dir + "/.azure/accessTokens.json") || File.size?(home_dir + '/.azure/accessTokens.json') <= 2
|
193
|
-
raise
|
181
|
+
raise login_message
|
194
182
|
end
|
195
183
|
end
|
196
184
|
end
|
@@ -238,20 +226,6 @@ class Chef
|
|
238
226
|
file
|
239
227
|
end
|
240
228
|
|
241
|
-
def pretty_key(key)
|
242
|
-
key.to_s.gsub(/_/, ' ').gsub(/\w+/){ |w| (w =~ /(ssh)|(aws)/i) ? w.upcase : w.capitalize }
|
243
|
-
end
|
244
|
-
|
245
|
-
def is_image_windows?
|
246
|
-
locate_config_value(:azure_image_reference_offer) =~ /WindowsServer.*/
|
247
|
-
end
|
248
|
-
|
249
|
-
def msg_pair(label, value, color=:cyan)
|
250
|
-
if value && !value.to_s.empty?
|
251
|
-
puts "#{ui.color(label, color)}: #{value}"
|
252
|
-
end
|
253
|
-
end
|
254
|
-
|
255
229
|
def msg_server_summary(server)
|
256
230
|
puts "\n\n"
|
257
231
|
if server.provisioningstate == 'Succeeded'
|
@@ -335,6 +309,51 @@ class Chef
|
|
335
309
|
config[:ohai_hints] = format_ohai_hints(locate_config_value(:ohai_hints))
|
336
310
|
validate_ohai_hints if ! locate_config_value(:ohai_hints).casecmp('default').zero?
|
337
311
|
end
|
312
|
+
|
313
|
+
private
|
314
|
+
|
315
|
+
def msg_pair(label, value, color=:cyan)
|
316
|
+
if value && !value.to_s.empty?
|
317
|
+
puts "#{ui.color(label, color)}: #{value}"
|
318
|
+
end
|
319
|
+
end
|
320
|
+
|
321
|
+
def pretty_key(key)
|
322
|
+
key.to_s.gsub(/_/, ' ').gsub(/\w+/){ |w| (w =~ /(ssh)|(aws)/i) ? w.upcase : w.capitalize }
|
323
|
+
end
|
324
|
+
|
325
|
+
def is_image_windows?
|
326
|
+
locate_config_value(:azure_image_reference_offer) =~ /WindowsServer.*/
|
327
|
+
end
|
328
|
+
|
329
|
+
def is_azure_cred?
|
330
|
+
locate_config_value(:azure_tenant_id) && locate_config_value(:azure_client_id) && locate_config_value(:azure_client_secret)
|
331
|
+
end
|
332
|
+
|
333
|
+
def azure_cred?
|
334
|
+
locate_config_value(:azure_tenant_id).nil? || locate_config_value(:azure_client_id).nil? || locate_config_value(:azure_client_secret).nil?
|
335
|
+
end
|
336
|
+
|
337
|
+
def is_old_xplat?
|
338
|
+
return true unless @azure_version
|
339
|
+
Gem::Version.new(@azure_version) < Gem::Version.new(XPLAT_VERSION_WITH_WCM_DEPRECATED)
|
340
|
+
end
|
341
|
+
|
342
|
+
def is_WCM_env_var_set?
|
343
|
+
ENV['AZURE_USE_SECURE_TOKEN_STORAGE'].nil? ? false : true
|
344
|
+
end
|
345
|
+
|
346
|
+
def raise_azure_status
|
347
|
+
raise "Token has expired. Please run '#{@azure_prefix} login' command"
|
348
|
+
end
|
349
|
+
|
350
|
+
def login_message
|
351
|
+
## Older versions of the Azure CLI on Windows stored credentials in a unique way
|
352
|
+
## in Windows Credentails Manager (WCM).
|
353
|
+
## Newer versions use the same pattern across platforms where credentials gets
|
354
|
+
## stored in ~/.azure/accessTokens.json file.
|
355
|
+
"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"
|
356
|
+
end
|
338
357
|
end
|
339
358
|
end
|
340
359
|
end
|