-
Notifications
You must be signed in to change notification settings - Fork 334
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Keyvault Lookup Plugin Added * Keyvault Lookup Plugin Added * Apply suggestions from code review Co-authored-by: Fred-sun <37327967+Fred-sun@users.noreply.github.com> * Update plugins/lookup/azure_keyvault_secret.py Co-authored-by: Triantafyllos <88321799+ttsakpc@users.noreply.github.com> * linting * Update plugins/lookup/azure_keyvault_secret.py Co-authored-by: Fred-sun <37327967+Fred-sun@users.noreply.github.com> * Update plugins/lookup/azure_keyvault_secret.py Co-authored-by: Fred-sun <37327967+Fred-sun@users.noreply.github.com> * Update plugins/lookup/azure_keyvault_secret.py Co-authored-by: Fred-sun <37327967+Fred-sun@users.noreply.github.com> * Update plugins/lookup/azure_keyvault_secret.py Co-authored-by: Fred-sun <37327967+Fred-sun@users.noreply.github.com> * Update plugins/lookup/azure_keyvault_secret.py Co-authored-by: Fred-sun <37327967+Fred-sun@users.noreply.github.com> * Update plugins/lookup/azure_keyvault_secret.py Co-authored-by: Fred-sun <37327967+Fred-sun@users.noreply.github.com> * Update plugins/lookup/azure_keyvault_secret.py Co-authored-by: Fred-sun <37327967+Fred-sun@users.noreply.github.com> * Update azure_keyvault_secret.py * Update plugins/lookup/azure_keyvault_secret.py Co-authored-by: Fred-sun <37327967+Fred-sun@users.noreply.github.com> * Update plugins/lookup/azure_keyvault_secret.py Co-authored-by: Fred-sun <37327967+Fred-sun@users.noreply.github.com> * Update plugins/lookup/azure_keyvault_secret.py Co-authored-by: Fred-sun <37327967+Fred-sun@users.noreply.github.com> * fix doc error Co-authored-by: Stefan Seiz <stefan.seiz@swisscom.com> Co-authored-by: Fred-sun <37327967+Fred-sun@users.noreply.github.com> Co-authored-by: Triantafyllos <88321799+ttsakpc@users.noreply.github.com> Co-authored-by: Stefan Seiz <stefan.seiz@six-group.com> Co-authored-by: Zooopx <xuzhang3@microsoft.com>
- Loading branch information
1 parent
a62d117
commit 7f8e745
Showing
1 changed file
with
199 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,199 @@ | ||
from __future__ import (absolute_import, division, print_function) | ||
__metaclass__ = type | ||
|
||
DOCUMENTATION = """ | ||
lookup: azure_keyvault_secret | ||
author: | ||
- Hai Cao <t-haicao@microsoft.com> | ||
version_added: 1.12.0 | ||
requirements: | ||
- requests | ||
- azure | ||
- msrest | ||
short_description: Read secret from Azure Key Vault. | ||
description: | ||
- This lookup returns the content of secret saved in Azure Key Vault. | ||
- When ansible host is MSI enabled Azure VM, user don't need provide any credential to access to Azure Key Vault. | ||
options: | ||
_terms: | ||
description: Secret name, version can be included like secret_name/secret_version. | ||
required: True | ||
vault_url: | ||
description: Url of Azure Key Vault. | ||
required: True | ||
client_id: | ||
description: Client id of service principal that has access to the Azure Key Vault | ||
secret: | ||
description: Secret of the service principal. | ||
tenant_id: | ||
description: Tenant id of service principal. | ||
notes: | ||
- If version is not provided, this plugin will return the latest version of the secret. | ||
- If ansible is running on Azure Virtual Machine with MSI enabled, client_id, secret and tenant isn't required. | ||
- For enabling MSI on Azure VM, please refer to this doc https://docs.microsoft.com/en-us/azure/active-directory/managed-service-identity/ | ||
- After enabling MSI on Azure VM, remember to grant access of the Key Vault to the VM by adding a new Acess Policy in Azure Portal. | ||
- If MSI is not enabled on ansible host, it's required to provide a valid service principal which has access to the key vault. | ||
- To use a plugin from a collection, please reference the full namespace, collection name, and lookup plugin name that you want to use. | ||
""" | ||
|
||
EXAMPLE = """ | ||
- name: Look up secret when ansible host is MSI enabled Azure VM | ||
debug: | ||
msg: "the value of this secret is {{ | ||
lookup( | ||
'azure.azcollection.azure_keyvault_secret', | ||
'testSecret/version', | ||
vault_url='https://yourvault.vault.azure.net' | ||
) | ||
}}" | ||
- name: Look up secret when ansible host is general VM | ||
vars: | ||
url: 'https://yourvault.vault.azure.net' | ||
secretname: 'testSecret/version' | ||
client_id: '123456789' | ||
secret: 'abcdefg' | ||
tenant: 'uvwxyz' | ||
debug: | ||
msg: "the value of this secret is {{ | ||
lookup( | ||
'azure.azcollection.azure_keyvault_secret', | ||
secretname, | ||
vault_url=url, | ||
client_id=client_id, | ||
secret=secret, | ||
tenant_id=tenant | ||
) | ||
}}" | ||
# Example below creates an Azure Virtual Machine with SSH public key from key vault using 'azure_keyvault_secret' lookup plugin. | ||
- name: Create Azure VM | ||
hosts: localhost | ||
connection: local | ||
no_log: True | ||
vars: | ||
resource_group: myResourceGroup | ||
vm_name: testvm | ||
location: eastus | ||
ssh_key: "{{ lookup('azure.azcollection.azure_keyvault_secret','myssh_key') }}" | ||
- name: Create VM | ||
azure_rm_virtualmachine: | ||
resource_group: "{{ resource_group }}" | ||
name: "{{ vm_name }}" | ||
vm_size: Standard_DS1_v2 | ||
admin_username: azureuser | ||
ssh_password_enabled: false | ||
ssh_public_keys: | ||
- path: /home/azureuser/.ssh/authorized_keys | ||
key_data: "{{ ssh_key }}" | ||
network_interfaces: "{{ vm_name }}" | ||
image: | ||
offer: UbuntuServer | ||
publisher: Canonical | ||
sku: 16.04-LTS | ||
version: latest | ||
""" | ||
|
||
RETURN = """ | ||
_raw: | ||
description: secret content string | ||
""" | ||
|
||
from ansible.errors import AnsibleError, AnsibleParserError | ||
from ansible.plugins.lookup import LookupBase | ||
from ansible.utils.display import Display | ||
try: | ||
import requests | ||
except ImportError: | ||
pass | ||
|
||
display = Display() | ||
|
||
TOKEN_ACQUIRED = False | ||
|
||
token_params = { | ||
'api-version': '2018-02-01', | ||
'resource': 'https://vault.azure.net' | ||
} | ||
|
||
token_headers = { | ||
'Metadata': 'true' | ||
} | ||
|
||
token = None | ||
|
||
try: | ||
token_res = requests.get('http://169.254.169.254/metadata/identity/oauth2/token', params=token_params, headers=token_headers, timeout=(3.05, 27)) | ||
if token_res.ok: | ||
token = token_res.json().get("access_token") | ||
if token is not None: | ||
TOKEN_ACQUIRED = True | ||
else: | ||
display.v('Successfully called MSI endpoint, but no token was available. Will use service principal if provided.') | ||
else: | ||
display.v("Unable to query MSI endpoint, Error Code %s. Will use service principal if provided" % token_res.status_code) | ||
except Exception: | ||
display.v('Unable to fetch MSI token. Will use service principal if provided.') | ||
TOKEN_ACQUIRED = False | ||
|
||
|
||
def lookup_secret_non_msi(terms, vault_url, kwargs): | ||
import logging | ||
logging.getLogger('msrestazure.azure_active_directory').addHandler(logging.NullHandler()) | ||
logging.getLogger('msrest.service_client').addHandler(logging.NullHandler()) | ||
|
||
try: | ||
from azure.common.credentials import ServicePrincipalCredentials | ||
from azure.keyvault import KeyVaultClient | ||
from msrest.exceptions import AuthenticationError, ClientRequestError | ||
from azure.keyvault.models.key_vault_error import KeyVaultErrorException | ||
except ImportError: | ||
raise AnsibleError('The azure_keyvault_secret lookup plugin requires azure.keyvault and azure.common.credentials to be installed.') | ||
|
||
client_id = kwargs.pop('client_id', None) | ||
secret = kwargs.pop('secret', None) | ||
tenant_id = kwargs.pop('tenant_id', None) | ||
|
||
try: | ||
credentials = ServicePrincipalCredentials( | ||
client_id=client_id, | ||
secret=secret, | ||
tenant=tenant_id | ||
) | ||
client = KeyVaultClient(credentials) | ||
except AuthenticationError: | ||
raise AnsibleError('Invalid credentials provided.') | ||
|
||
ret = [] | ||
for term in terms: | ||
try: | ||
secret_val = client.get_secret(vault_url, term, '').value | ||
ret.append(secret_val) | ||
except ClientRequestError: | ||
raise AnsibleError('Error occurred in request') | ||
except KeyVaultErrorException: | ||
raise AnsibleError('Failed to fetch secret ' + term + '.') | ||
return ret | ||
|
||
|
||
class LookupModule(LookupBase): | ||
|
||
def run(self, terms, variables, **kwargs): | ||
ret = [] | ||
vault_url = kwargs.pop('vault_url', None) | ||
if vault_url is None: | ||
raise AnsibleError('Failed to get valid vault url.') | ||
if TOKEN_ACQUIRED: | ||
secret_params = {'api-version': '2016-10-01'} | ||
secret_headers = {'Authorization': 'Bearer ' + token} | ||
for term in terms: | ||
try: | ||
secret_res = requests.get(vault_url + '/secrets/' + term, params=secret_params, headers=secret_headers) | ||
ret.append(secret_res.json()["value"]) | ||
except KeyError: | ||
raise AnsibleError('Failed to fetch secret ' + term + '.') | ||
except Exception: | ||
raise AnsibleError('Failed to fetch secret: ' + term + ' via MSI endpoint.') | ||
return ret | ||
else: | ||
return lookup_secret_non_msi(terms, vault_url, kwargs) |