mirror of
https://github.com/ansible-collections/community.general.git
synced 2024-09-14 20:13:21 +02:00
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
This commit is contained in:
parent
8c7bd8beb4
commit
9e48cf875f
2 changed files with 84 additions and 36 deletions
|
@ -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, "
|
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 log in with Azure CLI (`az login`).")
|
||||||
|
else:
|
||||||
|
self.fail("Failed to get credentials. Either pass as parameters, set environment variables, "
|
||||||
|
"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…
Reference in a new issue