Azure: added auth_source to override the source of the credentials (#35213)

* Azure: added auth_source to override the source of the credentials

* removed broken cli auth and fix some review changes

* missed some more cli auth stuff that isn't required

* Added cli auth back in

* Added docs around cli auth

* fix azure CLI auth issues

* tweak error messages
* fix auto fallback to cli
* fix import issues when azure-cli not installed
pull/35152/merge
Jordan Borean 7 years ago committed by Matt Davis
parent 8c7bd8beb4
commit 9e48cf875f

@ -35,7 +35,10 @@ except ImportError:
ANSIBLE_VERSION = 'unknown' ANSIBLE_VERSION = 'unknown'
AZURE_COMMON_ARGS = dict( AZURE_COMMON_ARGS = dict(
cli_default_profile=dict(type='bool'), auth_source=dict(
type='str',
choices=['auto', 'cli', 'env', 'credential_file']
),
profile=dict(type='str'), profile=dict(type='str'),
subscription_id=dict(type='str', no_log=True), subscription_id=dict(type='str', no_log=True),
client_id=dict(type='str', no_log=True), client_id=dict(type='str', no_log=True),
@ -49,7 +52,6 @@ AZURE_COMMON_ARGS = dict(
) )
AZURE_CREDENTIAL_ENV_MAPPING = dict( AZURE_CREDENTIAL_ENV_MAPPING = dict(
cli_default_profile='AZURE_CLI_DEFAULT_PROFILE',
profile='AZURE_PROFILE', profile='AZURE_PROFILE',
subscription_id='AZURE_SUBSCRIPTION_ID', subscription_id='AZURE_SUBSCRIPTION_ID',
client_id='AZURE_CLIENT_ID', client_id='AZURE_CLIENT_ID',
@ -134,13 +136,13 @@ except ImportError as exc:
HAS_AZURE_EXC = exc HAS_AZURE_EXC = exc
HAS_AZURE = False HAS_AZURE = False
try: try:
from azure.cli.core.util import CLIError from azure.cli.core.util import CLIError
from azure.common.credentials import get_azure_cli_credentials, get_cli_profile from azure.common.credentials import get_azure_cli_credentials, get_cli_profile
from azure.common.cloud import get_cli_active_cloud from azure.common.cloud import get_cli_active_cloud
except ImportError: except ImportError:
HAS_AZURE_CLI_CORE = False HAS_AZURE_CLI_CORE = False
CLIError = Exception
def azure_id_to_dict(id): def azure_id_to_dict(id):
@ -254,8 +256,12 @@ class AzureRMModuleBase(object):
# authenticate # authenticate
self.credentials = self._get_credentials(self.module.params) self.credentials = self._get_credentials(self.module.params)
if not self.credentials: if not self.credentials:
if HAS_AZURE_CLI_CORE:
self.fail("Failed to get credentials. Either pass as parameters, set environment variables, "
"define a profile in ~/.azure/credentials, or log in with Azure CLI (`az login`).")
else:
self.fail("Failed to get credentials. Either pass as parameters, set environment variables, " self.fail("Failed to get credentials. Either pass as parameters, set environment variables, "
"or define a profile in ~/.azure/credentials or be logged using AzureCLI.") "define a profile in ~/.azure/credentials, or install Azure CLI and log in (`az login`).")
# cert validation mode precedence: module-arg, credential profile, env, "validate" # cert validation mode precedence: module-arg, credential profile, env, "validate"
self._cert_validation_mode = self.module.params['cert_validation_mode'] or self.credentials.get('cert_validation_mode') or \ self._cert_validation_mode = self.module.params['cert_validation_mode'] or self.credentials.get('cert_validation_mode') or \
@ -266,7 +272,9 @@ class AzureRMModuleBase(object):
# if cloud_environment specified, look up/build Cloud object # if cloud_environment specified, look up/build Cloud object
raw_cloud_env = self.credentials.get('cloud_environment') raw_cloud_env = self.credentials.get('cloud_environment')
if not raw_cloud_env: if self.credentials.get('credentials') is not None and raw_cloud_env is not None:
self._cloud_environment = raw_cloud_env
elif not raw_cloud_env:
self._cloud_environment = azure_cloud.AZURE_PUBLIC_CLOUD # SDK default self._cloud_environment = azure_cloud.AZURE_PUBLIC_CLOUD # SDK default
else: else:
# try to look up "well-known" values via the name attribute on azure_cloud members # try to look up "well-known" values via the name attribute on azure_cloud members
@ -284,12 +292,15 @@ class AzureRMModuleBase(object):
except Exception as e: except Exception as e:
self.fail("cloud_environment {0} could not be resolved: {1}".format(raw_cloud_env, e.message), exception=traceback.format_exc(e)) self.fail("cloud_environment {0} could not be resolved: {1}".format(raw_cloud_env, e.message), exception=traceback.format_exc(e))
if self.credentials.get('subscription_id', None) is None: if self.credentials.get('subscription_id', None) is None and self.credentials.get('credentials') is None:
self.fail("Credentials did not include a subscription_id value.") self.fail("Credentials did not include a subscription_id value.")
self.log("setting subscription_id") self.log("setting subscription_id")
self.subscription_id = self.credentials['subscription_id'] self.subscription_id = self.credentials['subscription_id']
if self.credentials.get('client_id') is not None and \ if self.credentials.get('credentials') is not None:
# AzureCLI credentials
self.azure_credentials = self.credentials['credentials']
elif self.credentials.get('client_id') is not None and \
self.credentials.get('secret') is not None and \ self.credentials.get('secret') is not None and \
self.credentials.get('tenant') is not None: self.credentials.get('tenant') is not None:
self.azure_credentials = ServicePrincipalCredentials(client_id=self.credentials['client_id'], self.azure_credentials = ServicePrincipalCredentials(client_id=self.credentials['client_id'],
@ -437,19 +448,6 @@ class AzureRMModuleBase(object):
except Exception as exc: except Exception as exc:
self.fail("Error retrieving resource group {0} - {1}".format(resource_group, str(exc))) self.fail("Error retrieving resource group {0} - {1}".format(resource_group, str(exc)))
def _get_azure_cli_profile(self):
if not HAS_AZURE_CLI_CORE:
self.fail("Do you have azure-cli-core installed? Try `pip install 'azure-cli-core' --upgrade`")
try:
credentials, subscription_id = get_azure_cli_credentials()
self._cloud_environment = get_cli_active_cloud()
return {
'credentials': credentials,
'subscription_id': subscription_id
}
except CLIError as err:
self.fail("AzureCLI profile cannot be loaded - {0}".format(err))
def _get_profile(self, profile="default"): def _get_profile(self, profile="default"):
path = expanduser("~/.azure/credentials") path = expanduser("~/.azure/credentials")
try: try:
@ -470,14 +468,22 @@ class AzureRMModuleBase(object):
return None return None
def _get_azure_cli_credentials(self):
credentials, subscription_id = get_azure_cli_credentials()
cloud_environment = get_cli_active_cloud()
cli_credentials = {
'credentials': credentials,
'subscription_id': subscription_id,
'cloud_environment': cloud_environment
}
return cli_credentials
def _get_env_credentials(self): def _get_env_credentials(self):
env_credentials = dict() env_credentials = dict()
for attribute, env_variable in AZURE_CREDENTIAL_ENV_MAPPING.items(): for attribute, env_variable in AZURE_CREDENTIAL_ENV_MAPPING.items():
env_credentials[attribute] = os.environ.get(env_variable, None) env_credentials[attribute] = os.environ.get(env_variable, None)
if (env_credentials['cli_default_profile'] or '').lower() in ["true", "yes", "1"]:
return self._get_azure_cli_profile()
if env_credentials['profile']: if env_credentials['profile']:
credentials = self._get_profile(env_credentials['profile']) credentials = self._get_profile(env_credentials['profile'])
return credentials return credentials
@ -489,18 +495,38 @@ class AzureRMModuleBase(object):
def _get_credentials(self, params): def _get_credentials(self, params):
# Get authentication credentials. # Get authentication credentials.
# Precedence: module parameters-> environment variables-> default profile in ~/.azure/credentials.
self.log('Getting credentials') self.log('Getting credentials')
arg_credentials = dict() arg_credentials = dict()
for attribute, env_variable in AZURE_CREDENTIAL_ENV_MAPPING.items(): for attribute, env_variable in AZURE_CREDENTIAL_ENV_MAPPING.items():
arg_credentials[attribute] = params.get(attribute, None) arg_credentials[attribute] = params.get(attribute, None)
if arg_credentials['cli_default_profile']: auth_source = params.get('auth_source', None)
self.log('Retrieving credentials from Azure CLI current profile') if not auth_source:
return self._get_azure_cli_profile() auth_source = os.environ.get('ANSIBLE_AZURE_AUTH_SOURCE', 'auto')
if auth_source == 'cli':
if not HAS_AZURE_CLI_CORE:
self.fail("Azure auth_source is `cli`, but azure-cli package is not available. Try `pip install azure-cli --upgrade`")
try:
self.log('Retrieving credentials from Azure CLI profile')
cli_credentials = self._get_azure_cli_credentials()
return cli_credentials
except CLIError as err:
self.fail("Azure CLI profile cannot be loaded - {0}".format(err))
if auth_source == 'env':
self.log('Retrieving credentials from environment')
env_credentials = self._get_env_credentials()
return env_credentials
if auth_source == 'credential_file':
self.log("Retrieving credentials from credential file")
profile = params.get('profile', 'default')
default_credentials = self._get_profile(profile)
return default_credentials
# auto, precedence: module parameters -> environment variables -> default profile in ~/.azure/credentials
# try module params # try module params
if arg_credentials['profile'] is not None: if arg_credentials['profile'] is not None:
self.log('Retrieving credentials with profile parameter.') self.log('Retrieving credentials with profile parameter.')
@ -523,6 +549,14 @@ class AzureRMModuleBase(object):
self.log('Retrieved default profile credentials from ~/.azure/credentials.') self.log('Retrieved default profile credentials from ~/.azure/credentials.')
return default_credentials return default_credentials
try:
if HAS_AZURE_CLI_CORE:
self.log('Retrieving credentials from AzureCLI profile')
cli_credentials = self._get_azure_cli_credentials()
return cli_credentials
except CLIError as ce:
self.log('Error getting AzureCLI profile credentials - {0}'.format(ce))
return None return None
def serialize_obj(self, obj, class_name, enum_modules=None): def serialize_obj(self, obj, class_name, enum_modules=None):

@ -76,6 +76,20 @@ options:
choices: [validate, ignore] choices: [validate, ignore]
default: null default: null
version_added: 2.5 version_added: 2.5
auth_source:
description:
- Controls the source of the credentials to use for authentication.
- C(auto) will follow the default precedence of module parameters -> environment variables -> default profile in credential file
C(~/.azure/credentials).
- When set to C(cli), the credentials will be sources from the default Azure CLI profile.
- Can also be set via the C(ANSIBLE_AZURE_AUTH_SOURCE) environment variable.
choices:
- auto
- cli
- credential_file
- env
default: auto
version_added: 2.5
requirements: requirements:
- "python >= 2.7" - "python >= 2.7"
- "azure >= 2.0.0" - "azure >= 2.0.0"

Loading…
Cancel
Save