module Chef::Knife::AzurermBase
Public Class Methods
included(includer)
click to toggle source
# File lib/chef/knife/helpers/azurerm_base.rb, line 31 def self.included(includer) includer.class_eval do deps do require "readline" require "chef/json_compat" require_relative "../../../azure/resource_management/ARM_interface" require "chef/mixin/shell_out" require "time" unless defined?(Time) require "json" unless defined?(JSON) if Chef::Platform.windows? require_relative "../../azure/resource_management/windows_credentials" include Azure::ARM::WindowsCredentials end end option :azure_resource_group_name, short: "-r RESOURCE_GROUP_NAME", long: "--azure-resource-group-name RESOURCE_GROUP_NAME", description: "The Resource Group name." end end
Public Instance Methods
authentication_details()
click to toggle source
# File lib/chef/knife/helpers/azurerm_base.rb, line 87 def authentication_details if is_azure_cred? return { azure_tenant_id: config[:azure_tenant_id], azure_client_id: config[:azure_client_id], azure_client_secret: config[:azure_client_secret] } elsif Chef::Platform.windows? token_details = token_details_for_windows else token_details = token_details_for_linux end check_token_validity(token_details) end
azure_authentication()
click to toggle source
# File lib/chef/knife/helpers/azurerm_base.rb, line 144 def azure_authentication ui.log("Authenticating...") Mixlib::ShellOut.new("#{@azure_prefix} vm show 'knifetest@resourcegroup' testvm", timeout: 30).run_command rescue Mixlib::ShellOut::CommandTimeout rescue Exception raise_azure_status end
check_token_validity(token_details)
click to toggle source
# File lib/chef/knife/helpers/azurerm_base.rb, line 152 def check_token_validity(token_details) unless is_token_valid?(token_details) token_details = refresh_token raise_azure_status unless is_token_valid?(token_details) end token_details end
find_file(name)
click to toggle source
# File lib/chef/knife/helpers/azurerm_base.rb, line 201 def find_file(name) name = ::File.expand_path(name) config_dir = Chef::Knife.chef_config_dir if File.exist? name file = name elsif config_dir && File.exist?(File.join(config_dir, name)) file = File.join(config_dir, name) elsif File.exist?(File.join(ENV["HOME"], ".chef", name)) file = File.join(ENV["HOME"], ".chef", name) else ui.error("Unable to find file - " + name) exit 1 end file end
get_azure_cli_version()
click to toggle source
# File lib/chef/knife/helpers/azurerm_base.rb, line 99 def get_azure_cli_version if @azure_version != "" get_version = shell_out!("azure -v || az -v | grep azure-cli", returns: [0]).stdout @azure_version = get_version.gsub(/[^0-9.]/, "") end @azure_prefix = @azure_version.to_i < 2 ? "azure" : "az" @azure_version end
is_token_valid?(token_details)
click to toggle source
# File lib/chef/knife/helpers/azurerm_base.rb, line 127 def is_token_valid?(token_details) time_difference = Time.parse(token_details[:expiry_time]) - Time.now.utc if time_difference <= 0 false elsif time_difference <= 600 # 600sec = 10min # This is required otherwise a long running command may fail inbetween if the token gets expired. raise "Token will expire within 10 minutes. Please run '#{@azure_prefix} login' command" else true end end
msg_server_summary(server)
click to toggle source
# File lib/chef/knife/helpers/azurerm_base.rb, line 217 def msg_server_summary(server) puts "\n\n" if server.provisioningstate == "Succeeded" Chef::Log.info("Server creation went successful.") puts "\nServer Details are:\n" msg_pair("Server ID", server.id) msg_pair("Server Name", server.name) msg_pair("Server Public IP Address", server.publicipaddress) if is_image_windows? msg_pair("Server RDP Port", server.rdpport) else msg_pair("Server SSH Port", server.sshport) end msg_pair("Server Location", server.locationname) msg_pair("Server OS Type", server.ostype) msg_pair("Server Provisioning State", server.provisioningstate) else Chef::Log.info("Server Creation Failed.") end puts "\n\n" if server.resources.provisioning_state == "Succeeded" Chef::Log.info("Server Extension creation went successful.") puts "\nServer Extension Details are:\n" msg_pair("Server Extension ID", server.resources.id) msg_pair("Server Extension Name", server.resources.name) msg_pair("Server Extension Publisher", server.resources.publisher) msg_pair("Server Extension Type", server.resources.type) msg_pair("Server Extension Type Handler Version", server.resources.type_handler_version) msg_pair("Server Extension Provisioning State", server.resources.provisioning_state) else Chef::Log.info("Server Extension Creation Failed.") end puts "\n" end
parse_publish_settings_file(filename)
click to toggle source
# File lib/chef/knife/helpers/azurerm_base.rb, line 174 def parse_publish_settings_file(filename) require "nokogiri" unless defined?(Nokogiri) require "base64" unless defined?(Base64) require "openssl" unless defined?(OpenSSL) require "uri" unless defined?(URI) begin doc = Nokogiri::XML(File.open(find_file(filename))) profile = doc.at_css("PublishProfile") subscription = profile.at_css("Subscription") # check given PublishSettings XML file format.Currently PublishSettings file have two different XML format if profile.attribute("SchemaVersion").nil? management_cert = OpenSSL::PKCS12.new(Base64.decode64(profile.attribute("ManagementCertificate").value)) config[:azure_api_host_name] = URI(profile.attribute("Url").value).host elsif profile.attribute("SchemaVersion").value == "2.0" management_cert = OpenSSL::PKCS12.new(Base64.decode64(subscription.attribute("ManagementCertificate").value)) config[:azure_api_host_name] = URI(subscription.attribute("ServiceManagementUrl").value).host else ui.error("Publish settings file Schema not supported - " + filename) end config[:azure_mgmt_cert] = management_cert.certificate.to_pem + management_cert.key.to_pem config[:azure_subscription_id] = doc.at_css("Subscription").attribute("Id").value rescue => error puts "#{error.class} and #{error.message}" exit 1 end end
refresh_token()
click to toggle source
# File lib/chef/knife/helpers/azurerm_base.rb, line 139 def refresh_token azure_authentication token_details = Chef::Platform.windows? ? token_details_for_windows : token_details_for_linux end
service()
click to toggle source
# File lib/chef/knife/helpers/azurerm_base.rb, line 54 def service details = authentication_details details.update(azure_subscription_id: config[:azure_subscription_id]) @service ||= begin require_relative "../../../azure/resource_management/ARM_interface" service = Azure::ResourceManagement::ARMInterface.new(details) end @service.ui = ui @service end
token_details_for_linux()
click to toggle source
# File lib/chef/knife/helpers/azurerm_base.rb, line 116 def token_details_for_linux token_details_from_accessToken_file end
token_details_for_windows()
click to toggle source
# File lib/chef/knife/helpers/azurerm_base.rb, line 108 def token_details_for_windows if is_old_xplat? token_details_from_WCM else is_WCM_env_var_set? ? token_details_from_WCM : token_details_from_accessToken_file end end
token_details_from_accessToken_file()
click to toggle source
# File lib/chef/knife/helpers/azurerm_base.rb, line 120 def token_details_from_accessToken_file home_dir = File.expand_path("~") file = File.read(home_dir + "/.azure/accessTokens.json") file = JSON.parse(file) { tokentype: file[-1]["tokenType"], user: file[-1]["userId"], token: file[-1]["accessToken"], clientid: file[-1]["_clientId"], expiry_time: file[-1]["expiresOn"], refreshtoken: file[-1]["refreshToken"] } end
validate_arm_keys!(*keys)
click to toggle source
validates ARM mandatory keys
# File lib/chef/knife/helpers/azurerm_base.rb, line 66 def validate_arm_keys!(*keys) parse_publish_settings_file(config[:azure_publish_settings_file]) unless config[:azure_publish_settings_file].nil? keys.push(:azure_subscription_id) if azure_cred? validate_azure_login else keys.concat(%i{azure_tenant_id azure_client_id azure_client_secret}) end errors = [] keys.each do |k| if config[k].nil? errors << "You did not provide a valid '#{pretty_key(k)}' value. Please set knife[:#{k}] in your config.rb (knife.rb)." end end if errors.each { |e| ui.error(e) }.any? exit 1 end end
validate_azure_login()
click to toggle source
# File lib/chef/knife/helpers/azurerm_base.rb, line 160 def validate_azure_login if Chef::Platform.windows? && (is_old_xplat? || is_WCM_env_var_set?) # cmdkey command is used for accessing windows credential manager xplat_creds_cmd = Mixlib::ShellOut.new("cmdkey /list | findstr AzureXplatCli") result = xplat_creds_cmd.run_command raise login_message if result.stdout.nil? || result.stdout.empty? else home_dir = File.expand_path("~") if !File.exist?(home_dir + "/.azure/accessTokens.json") || ( File.size?(home_dir + "/.azure/accessTokens.json") <= 2 ) raise login_message end end end
validate_params!()
click to toggle source
# File lib/chef/knife/helpers/azurerm_base.rb, line 256 def validate_params! if config[:connection_user].nil? raise ArgumentError, "Please provide --connection-user option for authentication." end unless config[:connection_password].nil? ^ config[:ssh_public_key].nil? raise ArgumentError, "Please specify either --connection-password or --ssh-public-key option for authentication." end if config[:azure_vnet_subnet_name] && !config[:azure_vnet_name] raise ArgumentError, "When --azure-vnet-subnet-name is specified, the --azure-vnet-name must also be specified." end if config[:azure_vnet_subnet_name] == "GatewaySubnet" raise ArgumentError, "GatewaySubnet cannot be used as the name for --azure-vnet-subnet-name option. GatewaySubnet can only be used for virtual network gateways." end if config[:node_ssl_verify_mode] && !%w{none peer}.include?(config[:node_ssl_verify_mode]) raise ArgumentError, "Invalid value '#{config[:node_ssl_verify_mode]}' for --node-ssl-verify-mode. Use Valid values i.e 'none', 'peer'." end if !is_image_windows? if (config[:azure_vm_name].match(/^(?=.*[a-zA-Z-])([a-zA-z0-9-]{1,64})$/)).nil? raise ArgumentError, "VM name can only contain alphanumeric and hyphen(-) characters and maximum length cannot exceed 64 characters." end elsif (config[:azure_vm_name].match(/^(?=.*[a-zA-Z-])([a-zA-z0-9-]{1,15})$/)).nil? raise ArgumentError, "VM name can only contain alphanumeric and hyphen(-) characters and maximum length cannot exceed 15 characters." end if config[:server_count].to_i > 5 raise ArgumentError, "Maximum allowed value of --server-count is 5." end if config[:daemon] unless is_image_windows? raise ArgumentError, "The daemon option is only support for Windows nodes." end unless %w{none service task}.include?(config[:daemon]) raise ArgumentError, "Invalid value for --daemon option. Use valid daemon values i.e 'none', 'service' and 'task'." end end if config[:azure_image_os_type] unless %w{ubuntu centos rhel debian windows}.include?(config[:azure_image_os_type]) raise ArgumentError, "Invalid value of --azure-image-os-type. Accepted values ubuntu|centos|rhel|debian|windows" end end config[:ohai_hints] = format_ohai_hints(config[:ohai_hints]) validate_ohai_hints unless config[:ohai_hints].casecmp("default").zero? end
Private Instance Methods
azure_cred?()
click to toggle source
# File lib/chef/knife/helpers/azurerm_base.rb, line 329 def azure_cred? config[:azure_tenant_id].nil? || config[:azure_client_id].nil? || config[:azure_client_secret].nil? end
is_WCM_env_var_set?()
click to toggle source
# File lib/chef/knife/helpers/azurerm_base.rb, line 339 def is_WCM_env_var_set? ENV["AZURE_USE_SECURE_TOKEN_STORAGE"].nil? ? false : true end
is_azure_cred?()
click to toggle source
# File lib/chef/knife/helpers/azurerm_base.rb, line 325 def is_azure_cred? config[:azure_tenant_id] && config[:azure_client_id] && config[:azure_client_secret] end
is_image_windows?()
click to toggle source
# File lib/chef/knife/helpers/azurerm_base.rb, line 321 def is_image_windows? config[:azure_image_reference_offer] =~ /WindowsServer.*/ end
is_old_xplat?()
click to toggle source
# File lib/chef/knife/helpers/azurerm_base.rb, line 333 def is_old_xplat? return true unless @azure_version Gem::Version.new(@azure_version) < Gem::Version.new(XPLAT_VERSION_WITH_WCM_DEPRECATED) end
login_message()
click to toggle source
# File lib/chef/knife/helpers/azurerm_base.rb, line 347 def login_message ## Older versions of the Azure CLI on Windows stored credentials in a unique way ## in Windows Credentails Manager (WCM). ## Newer versions use the same pattern across platforms where credentials gets ## stored in ~/.azure/accessTokens.json file. "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)." end
msg_pair(label, value, color = :cyan)
click to toggle source
# File lib/chef/knife/helpers/azurerm_base.rb, line 311 def msg_pair(label, value, color = :cyan) if value && !value.to_s.empty? puts "#{ui.color(label, color)}: #{value}" end end
pretty_key(key)
click to toggle source
# File lib/chef/knife/helpers/azurerm_base.rb, line 317 def pretty_key(key) key.to_s.tr("_", " ").gsub(/\w+/) { |w| (w =~ /(ssh)|(aws)/i) ? w.upcase : w.capitalize } end
raise_azure_status()
click to toggle source
# File lib/chef/knife/helpers/azurerm_base.rb, line 343 def raise_azure_status raise "Token has expired. Please run '#{@azure_prefix} login' command" end