Skip to content

Commit

Permalink
Implement Azure Service Principal Git
Browse files Browse the repository at this point in the history
This adds support for using an Azure Service Principal for project updates.

Signed-off-by: Patrick Uiterwijk <patrick@puiterwijk.org>
  • Loading branch information
puiterwijk committed Dec 22, 2023
1 parent 96a84d1 commit 4f5754b
Show file tree
Hide file tree
Showing 3 changed files with 41 additions and 1 deletion.
6 changes: 6 additions & 0 deletions awx/main/tasks/jobs.py
Original file line number Diff line number Diff line change
Expand Up @@ -1163,6 +1163,7 @@ def build_passwords(self, project_update, runtime_passwords):
passwords['scm_key_unlock'] = project_update.credential.get_input('ssh_key_unlock', default='')
passwords['scm_username'] = project_update.credential.get_input('username', default='')
passwords['scm_password'] = project_update.credential.get_input('password', default='')
passwords['azure_client_secret'] = project_update.credential.get_input('secret', default='')
return passwords

def build_env(self, project_update, private_data_dir, private_data_files=None):
Expand Down Expand Up @@ -1208,6 +1209,10 @@ def _build_scm_url_extra_vars(self, project_update):
if project_update.credential:
scm_username = project_update.credential.get_input('username', default='')
scm_password = project_update.credential.get_input('password', default='')

extra_vars['azure_subscription_id'] = project_update.credential.get_input('subscription', default='')
extra_vars['azure_tenant'] = project_update.credential.get_input('tenant', default='')
extra_vars['azure_client_id'] = project_update.credential.get_input('client', default='')
else:
scm_username = ''
scm_password = ''
Expand Down Expand Up @@ -1316,6 +1321,7 @@ def get_password_prompts(self, passwords={}):
d[r'Password:\s*?$'] = 'scm_password'
d[r'\S+?@\S+?\'s\s+?password:\s*?$'] = 'scm_password'
d[r'Enter passphrase for .*:\s*?$'] = 'scm_key_unlock'
d[r'Azure Client Secret: \s*?$'] = 'azure_client_secret'
d[r'Bad passphrase, try again for .*:\s*?$'] = ''
# FIXME: Configure whether we should auto accept host keys?
d[r'^Are you sure you want to continue connecting \(yes/no\)\?\s*?$'] = 'yes'
Expand Down
34 changes: 34 additions & 0 deletions awx/playbooks/project_update.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,17 @@
# awx_version: Current running version of the awx or tower as a string
# awx_license_type: "open" for AWX; else presume Tower
# gpg_pubkey: the GPG public key to use for validation, when enabled
# azure_subscription_id: Subscription ID for Azure Service Principal
# azure_tenant: Tenant ID for Azure Service Principal
# azure_client_id: Client ID for Azure Service Principal

- hosts: localhost
gather_facts: false
connection: local
name: Update source tree if necessary
vars_prompt:
- name: azure_client_secret
prompt: Azure Client Secret
tasks:
- name: Delete project directory before update
ansible.builtin.shell: set -o pipefail && find . -delete -print | tail -2 # volume mounted, cannot delete folder itself
Expand All @@ -38,6 +44,33 @@
tags:
- update_git
block:
- name: Get Azure access token
when: "azure_client_id != ''"
register: azure_token
no_log: True
check_mode: false
ansible.builtin.uri:
force: true
method: POST
url: "https://login.microsoftonline.com/{{ azure_tenant }}/oauth2/token"
body_format: form-urlencoded
body:
grant_type: client_credentials
client_id: "{{ azure_client_id }}"
# This is the audience for Azure DevOps, as per
# https://learn.microsoft.com/en-us/rest/api/azure/devops/tokens/
resource: "499b84ac-1321-427f-aa17-267ca6975798"
client_secret: "{{ azure_client_secret }}"

- name: Define git environment variables
when: "azure_token is not skipped"
no_log: True
ansible.builtin.set_fact:
git_environment:
GIT_CONFIG_COUNT: 1
GIT_CONFIG_KEY_0: http.extraHeader
GIT_CONFIG_VALUE_0: "Authorization: Bearer {{ azure_token.json.access_token }}"

- name: Update project using git
ansible.builtin.git:
dest: "{{ project_path | quote }}"
Expand All @@ -47,6 +80,7 @@
force: "{{ scm_clean }}"
track_submodules: "{{ scm_track_submodules | default(omit) }}"
accept_hostkey: "{{ scm_accept_hostkey | default(omit) }}"
environment: "{{ git_environment | default({}) }}"
register: git_result

- name: Set the git repository version
Expand Down
2 changes: 1 addition & 1 deletion awxkit/awxkit/api/pages/projects.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ def payload(self, organization, scm_type='git', **kwargs):
def create_payload(self, name='', description='', scm_type='git', scm_url='', scm_branch='', organization=Organization, credential=None, **kwargs):
if credential:
if isinstance(credential, Credential):
if credential.ds.credential_type.namespace not in ('scm', 'insights'):
if credential.ds.credential_type.namespace not in ('scm', 'insights', 'azure_rm'):
credential = None # ignore incompatible credential from HasCreate dependency injection
elif credential in (Credential,):
credential = (Credential, dict(credential_type=(True, dict(kind='scm'))))
Expand Down

0 comments on commit 4f5754b

Please sign in to comment.