mirror of
https://github.com/ansible-collections/google.cloud.git
synced 2025-04-04 18:00:26 -07:00
Merge 21a84df536
into 99ac225a1a
This commit is contained in:
commit
a5efb7e0da
3 changed files with 298 additions and 0 deletions
104
plugins/lookup/gcp_secret_access.py
Normal file
104
plugins/lookup/gcp_secret_access.py
Normal file
|
@ -0,0 +1,104 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright: (c) 2020, Pavlo Bashynskyi (@levonet) <levonet@gmail.com>
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
DOCUMENTATION = r"""
|
||||
---
|
||||
lookup: gcp_secret_access
|
||||
author:
|
||||
- Pavlo Bashynskyi (@levonet)
|
||||
short_description: Retrieve secrets from GCP Secret Manager
|
||||
requirements:
|
||||
- python >= 2.7
|
||||
- google-auth >= 1.26.0
|
||||
- google-cloud-secret-manager >= 1.0.0
|
||||
description:
|
||||
- Retrieve secret contents from GCP Secret Manager.
|
||||
- Accessing to secret content requires the Secret Manager Secret Accessor role (C(roles/secretmanager.secretAccessor)) on the secret, project, folder, or organization.
|
||||
options:
|
||||
secret:
|
||||
description:
|
||||
- Secret name or resource id. Resource id should be in format C(projects/*/secrets/*/versions/*).
|
||||
- The project option is required if a secret name is used instead of resource id.
|
||||
required: True
|
||||
type: str
|
||||
version:
|
||||
description: Version id of secret. You can also access the latest version of a secret by specifying "C(latest)" as the version.
|
||||
type: str
|
||||
default: latest
|
||||
project:
|
||||
description: The Google Cloud Platform project to use.
|
||||
type: str
|
||||
env:
|
||||
- name: GCP_PROJECT
|
||||
access_token:
|
||||
description:
|
||||
- The Google Cloud access token. If specified, C(service_account_file) will be ignored.
|
||||
type: str
|
||||
env:
|
||||
- name: GCP_ACCESS_TOKEN
|
||||
service_account_file:
|
||||
description:
|
||||
- The path of a Service Account JSON file if serviceaccount is selected as type.
|
||||
type: path
|
||||
env:
|
||||
- name: GOOGLE_APPLICATION_CREDENTIALS
|
||||
- name: GCP_SERVICE_ACCOUNT_FILE
|
||||
notes:
|
||||
- When I(secret) is the first option in the term string, C(secret=) is not required (see examples).
|
||||
- If you’re running your application elsewhere, you should download a service account JSON keyfile and point to it using the secret option or an environment variable C(GOOGLE_APPLICATION_CREDENTIALS="/path/to/keyfile.json").
|
||||
"""
|
||||
|
||||
EXAMPLES = r"""
|
||||
- ansible.builtin.debug:
|
||||
msg: "{{ lookup('google.cloud.gcp_secret_access', secret='hola', project='test_project') }}"
|
||||
|
||||
- ansible.builtin.debug:
|
||||
msg: "{{ lookup('google.cloud.gcp_secret_access', 'hola', project='test_project') }}"
|
||||
|
||||
- name: using resource id instead of secret name
|
||||
ansible.builtin.debug:
|
||||
msg: "{{ lookup('google.cloud.gcp_secret_access', 'projects/112233445566/secrets/hola/versions/1') }}"
|
||||
|
||||
- name: using service account file
|
||||
ansible.builtin.debug:
|
||||
msg: "{{ lookup('google.cloud.gcp_secret_access', 'hola', project='test_project', service_account_file='/path/to/keyfile.json') }}"
|
||||
"""
|
||||
|
||||
RETURN = r"""
|
||||
_raw:
|
||||
description:
|
||||
- secrets requested
|
||||
"""
|
||||
|
||||
from ansible.errors import AnsibleError
|
||||
from ansible.plugins.lookup import LookupBase
|
||||
from ansible_collections.google.cloud.plugins.plugin_utils.gcp_utils import GcpSecretLookup
|
||||
|
||||
try:
|
||||
from google.cloud import secretmanager
|
||||
|
||||
HAS_GOOGLE_SECRET_MANAGER_LIBRARY = True
|
||||
except ImportError:
|
||||
HAS_GOOGLE_SECRET_MANAGER_LIBRARY = False
|
||||
|
||||
|
||||
class GcpSecretAccessLookup(GcpSecretLookup):
|
||||
def run(self, terms, variables=None, **kwargs):
|
||||
self.set_plugin_name('google.cloud.gcp_secret_access')
|
||||
self.process_options(terms, variables=None, **kwargs)
|
||||
|
||||
response = self.client(secretmanager).access_secret_version(request={"name": self.name})
|
||||
payload = response.payload.data.decode("UTF-8")
|
||||
|
||||
return [payload]
|
||||
|
||||
|
||||
class LookupModule(LookupBase):
|
||||
def run(self, terms, variables=None, **kwargs):
|
||||
if not HAS_GOOGLE_SECRET_MANAGER_LIBRARY:
|
||||
raise AnsibleError("Please install the google-cloud-secret-manager Python library")
|
||||
|
||||
return GcpSecretAccessLookup().run(terms, variables=variables, **kwargs)
|
92
plugins/lookup/gcp_secret_resource_id.py
Normal file
92
plugins/lookup/gcp_secret_resource_id.py
Normal file
|
@ -0,0 +1,92 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright: (c) 2020, Pavlo Bashynskyi (@levonet) <levonet@gmail.com>
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
DOCUMENTATION = r"""
|
||||
---
|
||||
lookup: gcp_secret_resource_id
|
||||
author:
|
||||
- Pavlo Bashynskyi (@levonet)
|
||||
short_description: Retrieve resource id of secret version from GCP Secret Manager
|
||||
requirements:
|
||||
- python >= 2.7
|
||||
- google-auth >= 1.26.0
|
||||
- google-cloud-secret-manager >= 1.0.0
|
||||
description:
|
||||
- Retrieve resource id of secret version from GCP Secret Manager.
|
||||
options:
|
||||
secret:
|
||||
description:
|
||||
- Secret name or resource id. Resource id should be in format C(projects/*/secrets/*/versions/*).
|
||||
- The project option is required if a secret name is used instead of resource id.
|
||||
required: True
|
||||
type: str
|
||||
version:
|
||||
description: Version id of secret. You can also access the latest version of a secret by specifying "C(latest)" as the version.
|
||||
type: str
|
||||
default: latest
|
||||
project:
|
||||
description: The Google Cloud Platform project to use.
|
||||
type: str
|
||||
env:
|
||||
- name: GCP_PROJECT
|
||||
access_token:
|
||||
description:
|
||||
- The Google Cloud access token. If specified, C(service_account_file) will be ignored.
|
||||
type: str
|
||||
env:
|
||||
- name: GCP_ACCESS_TOKEN
|
||||
service_account_file:
|
||||
description:
|
||||
- The path of a Service Account JSON file if serviceaccount is selected as type.
|
||||
type: path
|
||||
env:
|
||||
- name: GOOGLE_APPLICATION_CREDENTIALS
|
||||
- name: GCP_SERVICE_ACCOUNT_FILE
|
||||
notes:
|
||||
- When I(secret) is the first option in the term string, C(secret=) is not required (see examples).
|
||||
- If you’re running your application elsewhere, you should download a service account JSON keyfile and point to it using the secret option or an environment variable C(GOOGLE_APPLICATION_CREDENTIALS="/path/to/keyfile.json").
|
||||
"""
|
||||
|
||||
EXAMPLES = r"""
|
||||
- ansible.builtin.debug:
|
||||
msg: "{{ lookup('google.cloud.gcp_secret_resource_id', secret='hola', project='test_project') }}"
|
||||
"""
|
||||
|
||||
RETURN = r"""
|
||||
_raw:
|
||||
description:
|
||||
- resource id of secret version
|
||||
"""
|
||||
|
||||
from ansible.errors import AnsibleError
|
||||
from ansible.plugins.lookup import LookupBase
|
||||
from ansible_collections.google.cloud.plugins.plugin_utils.gcp_utils import GcpSecretLookup
|
||||
|
||||
try:
|
||||
from google.cloud import secretmanager
|
||||
|
||||
HAS_GOOGLE_SECRET_MANAGER_LIBRARY = True
|
||||
except ImportError:
|
||||
HAS_GOOGLE_SECRET_MANAGER_LIBRARY = False
|
||||
|
||||
|
||||
class GcpSecretResourceIdLookup(GcpSecretLookup):
|
||||
def run(self, terms, variables=None, **kwargs):
|
||||
self.set_plugin_name('google.cloud.gcp_secret_resource_id')
|
||||
self.process_options(terms, variables=None, **kwargs)
|
||||
|
||||
response = self.client(secretmanager).get_secret_version(request={"name": self.name})
|
||||
|
||||
return [response.name]
|
||||
|
||||
|
||||
class LookupModule(LookupBase):
|
||||
def run(self, terms, variables=None, **kwargs):
|
||||
|
||||
if not HAS_GOOGLE_SECRET_MANAGER_LIBRARY:
|
||||
raise AnsibleError("Please install the google-cloud-secret-manager Python library")
|
||||
|
||||
return GcpSecretResourceIdLookup().run(terms, variables=variables, **kwargs)
|
102
plugins/plugin_utils/gcp_utils.py
Normal file
102
plugins/plugin_utils/gcp_utils.py
Normal file
|
@ -0,0 +1,102 @@
|
|||
from __future__ import (absolute_import, division, print_function)
|
||||
|
||||
__metaclass__ = type
|
||||
|
||||
import io
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
|
||||
try:
|
||||
import google.oauth2.credentials
|
||||
from google.auth import identity_pool
|
||||
from google.oauth2 import service_account
|
||||
HAS_GOOGLE_LIBRARIES = True
|
||||
except ImportError:
|
||||
HAS_GOOGLE_LIBRARIES = False
|
||||
|
||||
from ansible.errors import AnsibleError
|
||||
|
||||
|
||||
# Handles all authentication and options for GCP Secrets Manager API calls in Lookup plugins.
|
||||
class GcpSecretLookup():
|
||||
def __init__(self):
|
||||
if not HAS_GOOGLE_LIBRARIES:
|
||||
raise AnsibleError("Please install the google-auth library")
|
||||
|
||||
self.plugin_name = ''
|
||||
self.secret_id = None
|
||||
self.version_id = None
|
||||
self.project_id = None
|
||||
self.access_token = None
|
||||
self.service_account_file = None
|
||||
self.scope = ["https://www.googleapis.com/auth/cloud-platform"]
|
||||
|
||||
def set_plugin_name(self, name):
|
||||
self.plugin_name = name
|
||||
|
||||
def client(self, secretmanager):
|
||||
if self.access_token is not None:
|
||||
credentials=google.oauth2.credentials.Credentials(self.access_token)
|
||||
return secretmanager.SecretManagerServiceClient(credentials=credentials)
|
||||
|
||||
if self.service_account_file is not None:
|
||||
path = os.path.realpath(os.path.expanduser(self.service_account_file))
|
||||
if not os.path.exists(path):
|
||||
raise AnsibleError("File {} was not found.".format(path))
|
||||
|
||||
with io.open(path, "r") as file_obj:
|
||||
try:
|
||||
info = json.load(file_obj)
|
||||
except ValueError as e:
|
||||
raise AnsibleError("File {} is not a valid json file.".format(path))
|
||||
|
||||
credential_type = info.get("type")
|
||||
if credential_type == "authorized_user":
|
||||
credentials = google.oauth2.credentials.Credentials.from_authorized_user_info(info, scopes=self.scope)
|
||||
elif credential_type == "service_account":
|
||||
credentials = service_account.Credentials.from_service_account_info(info, scopes=self.scope)
|
||||
elif credential_type == "external_account":
|
||||
if info.get("subject_token_type") == "urn:ietf:params:aws:token-type:aws4_request":
|
||||
from google.auth import aws
|
||||
credentials = aws.Credentials.from_info(info, scopes=self.scope)
|
||||
else:
|
||||
credentials = identity_pool.Credentials.from_info(info, scopes=self.scope)
|
||||
else:
|
||||
raise AnsibleError(
|
||||
"Type is {}, expected one of authorized_user, service_account, external_account.".format(credential_type)
|
||||
)
|
||||
|
||||
return secretmanager.SecretManagerServiceClient(credentials=credentials)
|
||||
|
||||
return secretmanager.SecretManagerServiceClient()
|
||||
|
||||
def process_options(self, terms, variables=None, **kwargs):
|
||||
self.secret_id = kwargs.get('secret')
|
||||
self.version_id = kwargs.get('version', 'latest')
|
||||
self.project_id = kwargs.get('project', os.getenv('GCP_PROJECT'))
|
||||
self.access_token = kwargs.get('access_token', os.getenv('GCP_ACCESS_TOKEN'))
|
||||
self.service_account_file = kwargs.get('service_account_file', os.getenv('GOOGLE_APPLICATION_CREDENTIALS'))
|
||||
|
||||
if len(terms) > 1:
|
||||
raise AnsibleError("{0} lookup plugin can have only one secret name or resource id".format(self.plugin_name))
|
||||
|
||||
if self.secret_id is None and len(terms) == 1:
|
||||
self.secret_id = terms[0]
|
||||
|
||||
regex = r'^projects/([^/]+)/secrets/([^/]+)/versions/(.+)$'
|
||||
match = re.match(regex, self.secret_id)
|
||||
if match:
|
||||
self.name = self.secret_id
|
||||
self.project_id = match.group(1)
|
||||
self.secret_id = match.group(2)
|
||||
self.version_id = match.group(3)
|
||||
return
|
||||
|
||||
if self.project_id is None:
|
||||
raise AnsibleError("{0} lookup plugin required option: project or resource id".format(self.plugin_name))
|
||||
|
||||
if self.secret_id is None:
|
||||
raise AnsibleError("{0} lookup plugin required option: secret or resource id".format(self.plugin_name))
|
||||
|
||||
self.name = f"projects/{self.project_id}/secrets/{self.secret_id}/versions/{self.version_id}"
|
Loading…
Add table
Reference in a new issue