From 4bda5c777abb45223bee2fc2b5a60d2de5138e37 Mon Sep 17 00:00:00 2001 From: Daniel Hougaard <62331820+DanielHougaard@users.noreply.github.com> Date: Mon, 11 Mar 2024 15:00:38 +0100 Subject: [PATCH 1/3] Create .gitignore --- .gitignore | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..58cd12e --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +.venv/**/* +ansible.cfg +inventory +test.yml +__pycache__ \ No newline at end of file From 3734effe53b1d99235cbdb11ac67840a9b16902a Mon Sep 17 00:00:00 2001 From: Daniel Hougaard <62331820+DanielHougaard@users.noreply.github.com> Date: Mon, 11 Mar 2024 15:00:47 +0100 Subject: [PATCH 2/3] Feat: Update to new Python SDK --- plugins/lookup/read_secrets.py | 81 +++++++++++++++++++++++++--------- 1 file changed, 61 insertions(+), 20 deletions(-) diff --git a/plugins/lookup/read_secrets.py b/plugins/lookup/read_secrets.py index acd89f1..d1459e8 100644 --- a/plugins/lookup/read_secrets.py +++ b/plugins/lookup/read_secrets.py @@ -3,9 +3,10 @@ from ansible.plugins.lookup import LookupBase HAS_INFISICAL = False try: - from infisical import InfisicalClient + from infisical_client import InfisicalClient, ClientSettings, GetSecretOptions, ListSecretsOptions HAS_INFISICAL = True -except ImportError: +except ImportError as e: + print(e) HAS_INFISICAL = False DOCUMENTATION = r""" @@ -19,10 +20,17 @@ description: - Secrets can be located either by their name for individual secret loopups or by environment/folder path to return all secrets within the given scope. options: - token: - description: The Infisical token used to authenticate + universal_auth_client_id: + description: The Machine Identity Client ID used to authenticate env: - - name: INFISICAL_TOKEN + - name: UNIVERSAL_AUTH_MACHINE_IDENTITY_CLIENT_ID + required: True + type: string + version_added: 1.0.0 + universal_auth_client_secret: + description: The Machine Identity Client Secret used to authenticate + env: + - name: UNIVERSAL_AUTH_MACHINE_IDENTITY_CLIENT_SECRET required: True type: string version_added: 1.0.0 @@ -44,6 +52,11 @@ options: required: True type: string version_added: 1.0.0 + project_id: + description: "The ID of the project where the secrets are stored" + required: True + type: string + version_added: 1.0.0 secret_name: description: The name of the secret that should be fetched. The name should be exactly as it appears in Infisical required: False @@ -53,10 +66,10 @@ options: EXAMPLES = r""" vars: - read_all_secrets_within_scope: "{{ lookup('infisical_vault', token='<>', path='/', env_slug='dev', url='https://spotify.infisical.com') }}" + read_all_secrets_within_scope: "{{ lookup('infisical_vault', universal_auth_client_id='<>', universal_auth_client_secret='<>', project_id='<>', path='/', env_slug='dev', url='https://spotify.infisical.com') }}" # [{ "key": "HOST", "value": "google.com" }, { "key": "SMTP", "value": "gmail.smtp.edu" }] - read_secret_by_name_within_scope: "{{ lookup('infisical_vault', token='<>', path='/', env_slug='dev', secret_name='HOST', url='https://spotify.infisical.com') }}" + read_secret_by_name_within_scope: "{{ lookup('infisical_vault', universal_auth_client_id='<>', universal_auth_client_secret='<>', project_id='<>', path='/', env_slug='dev', secret_name='HOST', url='https://spotify.infisical.com') }}" # [{ "key": "HOST", "value": "google.com" }] """ @@ -65,38 +78,66 @@ class LookupModule(LookupBase): self.set_options(var_options=variables, direct=kwargs) if not HAS_INFISICAL: - raise AnsibleError("Please pip install infisical to use the infisical_vault lookup module.") + raise AnsibleError("Please pip install infisical-python to use the infisical_vault lookup module.") - infisical_token = self.get_option("token") + machine_identity_client_id = self.get_option("universal_auth_client_id") + machine_identity_client_secret = self.get_option("universal_auth_client_secret") url = self.get_option("url") - if not infisical_token: - raise AnsibleError("Infisical token is required") + # Check if the required environment variables are set + if not machine_identity_client_id or not machine_identity_client_secret: + raise AnsibleError("Please provide the universal_auth_client_id and universal_auth_client_secret") + + + + + # Create the client settings + settings = ClientSettings( + client_id=machine_identity_client_id, + client_secret=machine_identity_client_secret, + site_url=url + ) # Initialize the Infisical client - client = InfisicalClient(token=infisical_token, site_url=url) + client = InfisicalClient(settings=settings) secretName = kwargs.get('secret_name') envSlug = kwargs.get('env_slug') path = kwargs.get('path') + project_id = kwargs.get('project_id') if secretName: - return self.get_single_secret(client, secretName, envSlug, path) + return self.get_single_secret(client, project_id, secretName, envSlug, path) else: - return self.get_all_secrets(client, envSlug, path) + return self.get_all_secrets(client, project_id, envSlug, path) - def get_single_secret(self, client, secret_name, environment, path): + def get_single_secret(self, client, project_id, secret_name, environment, path): try: - secret = client.get_secret(secret_name=secret_name, environment=environment, path=path) - return [{"value": secret.secret_value, "key": secret.secret_name}] + + options = GetSecretOptions( + environment=environment, + project_id=project_id, + secret_name=secret_name, + path=path, + type="shared" + ) + + secret = client.getSecret(options=options) + return [{"value": secret.secret_value, "key": secret.secret_key}] except Exception as e: print(e) raise AnsibleError(f"Error fetching single secret {e}") - def get_all_secrets(self, client, environment="dev", path="/"): + def get_all_secrets(self, client, project_id, environment="dev", path="/"): try: - secrets = client.get_all_secrets(environment=environment, path=path) - return [{"value": s.secret_value, "key": s.secret_name} for s in secrets] + options = ListSecretsOptions( + environment=environment, + project_id=project_id, + path=path, + ) + secrets = client.listSecrets(options=options) + + return [{"value": s.secret_value, "key": s.secret_key} for s in secrets] except Exception as e: raise AnsibleError(f"Error fetching all secrets {e}") From 054c8eecdae8b3edc7089d2c9905ded29fc366fd Mon Sep 17 00:00:00 2001 From: Daniel Hougaard <62331820+DanielHougaard@users.noreply.github.com> Date: Mon, 11 Mar 2024 15:03:21 +0100 Subject: [PATCH 3/3] Update README.md --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index cad4dfa..aec7583 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ You can install the Infisical collection with the Ansible Galaxy CLI: The python module dependencies are not installed by `ansible-galaxy`. They can be manually installed using pip: - pip install infisical + pip install infisical-python ## Using this collection @@ -31,10 +31,10 @@ You can either call modules by their Fully Qualified Collection Name (FQCN), suc ```yaml --- vars: - read_all_secrets_within_scope: "{{ lookup('infisical.vault.read_secrets', token='<>', path='/', env_slug='dev', url='https://spotify.infisical.com') }}" + read_all_secrets_within_scope: "{{ lookup('infisical.vault.read_secrets', universal_auth_client_id='<>', universal_auth_client_secret='<>', project_id='<>', path='/', env_slug='dev', url='https://spotify.infisical.com') }}" # [{ "key": "HOST", "value": "google.com" }, { "key": "SMTP", "value": "gmail.smtp.edu" }] - read_secret_by_name_within_scope: "{{ lookup('infisical.vault.read_secrets', token='<>', path='/', env_slug='dev', secret_name='HOST', url='https://spotify.infisical.com') }}" + read_secret_by_name_within_scope: "{{ lookup('infisical.vault.read_secrets', universal_auth_client_id='<>', universal_auth_client_secret='<>', project_id='<>', path='/', env_slug='dev', secret_name='HOST', url='https://spotify.infisical.com') }}" # [{ "key": "HOST", "value": "google.com" }] ```