551 lines
22 KiB
YAML
Executable File
551 lines
22 KiB
YAML
Executable File
---
|
|
- name: Query automate workspace
|
|
hosts: localhost
|
|
gather_facts: False
|
|
vars_files:
|
|
vars/main.yml
|
|
|
|
tasks:
|
|
|
|
- set_fact:
|
|
endpoint: "{{ manageiq.api_url }}"
|
|
auth_token: "{{ manageiq.api_token }}"
|
|
request_id: "{{ (manageiq.request).split('/')[-1] }}"
|
|
service_id: "{{ (manageiq.service).split('/')[-1] }}"
|
|
user_id: "{{ (manageiq.user).split('/')[-1] }}"
|
|
when: manageiq is defined
|
|
|
|
# when users run ansible their manageiq auth token does not have sufficient rights to interact with the API, no combination of rights in a role for a non admin user are sufficient
|
|
# use the local admin credentials to get an auth token
|
|
- name: Get auth token
|
|
uri:
|
|
# ansible runner timeout, something changed from 5.11.1.2 to 5.11.6.0?
|
|
#url: "{{ endpoint }}/api/auth"
|
|
url: "https://127.0.0.1/api/auth"
|
|
validate_certs: no
|
|
method: GET
|
|
user: "{{ api_user }}"
|
|
password: "{{ api_pass }}"
|
|
status_code: 200
|
|
register: login
|
|
when: manageiq is defined
|
|
|
|
- set_fact:
|
|
auth_token: "{{ login.json.auth_token }}"
|
|
when: manageiq is defined
|
|
|
|
- name: Get requester user attributes
|
|
uri:
|
|
#url: "{{ endpoint }}/api/users/{{ user_id }}"
|
|
url: "https://127.0.0.1/api/users/{{ user_id }}"
|
|
validate_certs: no
|
|
method: GET
|
|
headers:
|
|
X-Auth-Token: "{{ auth_token }}"
|
|
status_code: 200
|
|
register: user
|
|
when: manageiq is defined
|
|
|
|
- set_fact:
|
|
requester_email: "{{ user.json.email }}"
|
|
when: manageiq is defined and user.json.email is not none # cloudforms admin user has no email address (unless set), this is a null field in json
|
|
|
|
- set_fact:
|
|
requester_email: "{{ from_email }}" # from_email should be able to recieve mail via relay
|
|
when: requester_email is not defined
|
|
|
|
- set_fact:
|
|
requester_user: "{{ user.json.name }}"
|
|
when: manageiq is defined
|
|
|
|
# when run from the command line set a default requester user
|
|
- set_fact:
|
|
requester_user: "command line invocation"
|
|
when: manageiq is not defined
|
|
|
|
# - name: DEBUG print all user attributes
|
|
# debug:
|
|
# msg:
|
|
# - "{{ user }}"
|
|
#
|
|
# useful fields:
|
|
# "email": "ucats@exmail.nottingham.ac.uk"
|
|
# "name": "Toby Seed"
|
|
# "userid": "toby.seed@nottingham.ac.uk"
|
|
#
|
|
# we see that the UON active directory schema has a different correlation between the AD short login id and the lookup of the userid and name
|
|
# the above user generally would use the login id "ucats" across the UON estate to authenticate against AD
|
|
# interestingly cloudforms queries several fields in the schema and will allow login as "ucats" and "toby.seed@nottingham.ac.uk"
|
|
# to detect if this script is being run in self service mode, a match is performed against the requesting user and a single entry in groupmember list
|
|
# we can only retrieve the expected AD short login id from the email field using UON AD
|
|
|
|
# remove after testing - self service mode wasnt tested enough on dev system where cloudforms admin has no email
|
|
# - name: get AD account name
|
|
# set_fact:
|
|
# requester_user_ad: "{{ (user.json.email).split('@')[0] }}"
|
|
# when: manageiq is defined
|
|
|
|
- name: get AD account name
|
|
set_fact:
|
|
requester_user_ad: "{{ (requester_email).split('@')[0] }}"
|
|
when: manageiq is defined
|
|
|
|
- name: get service
|
|
uri:
|
|
#url: "{{ endpoint }}/api/services/{{ service_id }}"
|
|
url: "https://127.0.0.1/api/services/{{ service_id }}"
|
|
validate_certs: no
|
|
method: GET
|
|
headers:
|
|
X-Auth-Token: "{{ auth_token }}"
|
|
status_code: 200
|
|
register: service
|
|
when: manageiq is defined
|
|
|
|
- set_fact:
|
|
service_name: "{{ service.json.name }}"
|
|
new_service_name: "{{ service.json.name }} {{ request_id }}"
|
|
when: manageiq is defined
|
|
|
|
- set_fact:
|
|
service_name: "command line invocation"
|
|
new_service_name: "command line invocation"
|
|
when: manageiq is not defined
|
|
|
|
- name: set service name
|
|
uri:
|
|
#url: "{{ endpoint }}/api/services/{{ service_id }}"
|
|
url: "https://127.0.0.1/api/services/{{ service_id }}"
|
|
validate_certs: no
|
|
method: POST
|
|
headers:
|
|
X-Auth-Token: "{{ auth_token }}"
|
|
body_format: json
|
|
body: { "action" : "edit", "resource" : { "name" : "{{ new_service_name }}" }}
|
|
status_code: 200, 204
|
|
register: service
|
|
when: manageiq is defined
|
|
|
|
|
|
- hosts: localhost
|
|
gather_facts: false
|
|
name: Validation tasks
|
|
vars:
|
|
groupmemberslist: []
|
|
groupmemberslistvalidate: []
|
|
vars_files:
|
|
vars/main.yml
|
|
|
|
tasks:
|
|
|
|
- name: Build inventory for AD server
|
|
add_host: >
|
|
name=adserver
|
|
groups=windows
|
|
ansible_host="{{ ad_host }}"
|
|
|
|
- name: Fail Where Requisite Vars Not Set
|
|
fail:
|
|
msg: "Parameter {{item.key}} has value {{item.value}}, {{item.key}} is required to be passed from Cloudforms"
|
|
when: item.value == 'placeholder'
|
|
loop: "{{ lookup('dict', vars ) }}" #vars is a special variable of a list containing all variables in the playbook
|
|
no_log: "{{ suppress_vars_output }}"
|
|
|
|
- name: Split groupmembers parameter on , delimiter
|
|
set_fact:
|
|
groupmemberslist: "{{ groupmemberslist }} + [ '{{ item }}' ]"
|
|
with_items: "{{ groupmembers.split(',') }}"
|
|
|
|
- name: Remove empty fields from groupmembers parameter
|
|
set_fact:
|
|
groupmemberslistvalidate: "{{ groupmemberslistvalidate }} + [ '{{ item }}' ]"
|
|
when: item | length != 0
|
|
with_items: "{{ groupmemberslist }}"
|
|
|
|
- hosts: adserver
|
|
gather_facts: false
|
|
name: Check AD user exists
|
|
vars:
|
|
# set present/absent flag for netapp modules from create/delete values in the perform parameter, included here for action to perform
|
|
state: "{{ 'present' if perform == 'create' else ( 'absent' if perform == 'delete' else 'placeholder') }}"
|
|
groupmemberslistvalidate: "{{ hostvars['localhost']['groupmemberslistvalidate'] }}"
|
|
email_customer: []
|
|
no_aduser: []
|
|
email_recipients: []
|
|
user_no_action: []
|
|
body_service_name: "{{ hostvars['localhost']['new_service_name'] }}"
|
|
body_requester_user: "{{ hostvars['localhost']['requester_user'] }}"
|
|
# NOT USED body_requester_user_ad: "{{ hostvars['localhost']['requester_user_ad }}"
|
|
vars_files:
|
|
vars/main.yml
|
|
|
|
tasks:
|
|
|
|
# to avoid using group_vars we set_facts that were not accepted by add_host, add_host does not work with many windows winrm connectivity vars
|
|
- name: Add connectivity variables for adserver
|
|
set_fact:
|
|
ansible_user: "{{ ad_user }}"
|
|
ansible_password: "{{ ad_pass }}"
|
|
ansible_connection: "{{ ad_connection }}"
|
|
ansible_winrm_transport: "{{ ad_winrm_transport }}"
|
|
ansible_winrm_kinit_mode: "{{ ad_winrm_kinit_mode }}"
|
|
ansible_winrm_message_encryption: "{{ ad_winrm_message_encryption }}"
|
|
ansible_port: "{{ ad_port }}"
|
|
ansible_winrm_scheme: "{{ ad_winrm_scheme }}"
|
|
ansible_winrm_server_cert_validation: "{{ ad_winrm_server_cert_validation }}"
|
|
|
|
- name: Check AD user exists
|
|
win_shell: ([ADSISearcher] "(sAMAccountName={{ item }})").FindOne()
|
|
register: command_result
|
|
with_items: "{{ groupmemberslistvalidate }}"
|
|
|
|
- name: Flag fail where AD user not exist
|
|
set_fact:
|
|
no_aduser: "{{ no_aduser }} + [ '{{ item.item }}' ]"
|
|
when: item.stdout | length == 0
|
|
with_items: '{{ command_result.results }}'
|
|
changed_when: true
|
|
notify: topic_noad
|
|
|
|
- name: Get Domain user email address
|
|
win_shell: Get-ADUser {{ item }} -Properties mail | Select-Object -ExpandProperty mail
|
|
register: email_result
|
|
with_items: "{{ groupmemberslistvalidate }}"
|
|
when: no_aduser | length == 0
|
|
|
|
# this will crash out where customer account has no associated email - needs logic for empty elements
|
|
- name: Add customer email to list of email recipients
|
|
set_fact:
|
|
email_customer: "{{ email_customer }} + [ '{{ item.stdout_lines[0] }}' ]"
|
|
with_items: "{{ email_result.results }}"
|
|
when: no_aduser | length == 0
|
|
|
|
- name: Build list of user and accociated email address
|
|
set_fact:
|
|
account_email: "{{ account_email | default([]) + [dict(name=item[0], email=item[1])] }}"
|
|
loop: "{{ groupmemberslistvalidate | zip (email_customer) | list }}"
|
|
when: no_aduser | length == 0
|
|
|
|
- name: Add Domain user/group to Domain Group
|
|
win_domain_group_membership:
|
|
name: "{{ group }}"
|
|
members: "{{ groupmemberslistvalidate }}"
|
|
state: "{{ state }}"
|
|
register: result
|
|
when: no_aduser | length == 0
|
|
|
|
- name: Build list of user emails requiring notification for add operation
|
|
set_fact:
|
|
email_recipients: "{{ email_recipients | default([]) + [item.email] }}"
|
|
with_items: "{{ account_email }}"
|
|
when: ( no_aduser | length == 0 and (result.added | length > 0 and state == 'present')) and item.name in result.added
|
|
|
|
- name: Build list of user emails requiring notification for delete operation
|
|
set_fact:
|
|
email_recipients: "{{ email_recipients | default([]) + [item.email] }}"
|
|
with_items: "{{ account_email }}"
|
|
when: ( no_aduser | length == 0 and (result.removed | length > 0 and state == 'absent')) and item.name in result.removed
|
|
|
|
# users in this list were not added/removed from AD group as they were already present/not-present
|
|
- name: Build a list of users where no action was taken
|
|
set_fact:
|
|
user_no_action: "{{ user_no_action | default([]) + [item.name] }}"
|
|
with_items: "{{ account_email }}"
|
|
when: no_aduser | length == 0 and (item.name not in result.removed and item.name not in result.added)
|
|
|
|
- name: Build a list of emails for users where no action was taken
|
|
set_fact:
|
|
email_recipients_no_action: "{{ email_recipients_no_action | default([]) + [item.email] }}"
|
|
with_items: "{{ account_email }}"
|
|
when: no_aduser | length == 0 and (item.name not in result.removed and item.name not in result.added)
|
|
|
|
# - debug:
|
|
# msg:
|
|
# - "email recipients change {{ email_recipients }}"
|
|
# - "email recipients nochange {{ email_recipients_no_action }}"
|
|
# - "users not actioned {{ user_no_action }}"
|
|
|
|
- set_fact:
|
|
template_prefix: "default"
|
|
when: template_prefix is not defined
|
|
|
|
handlers:
|
|
|
|
- name: Build invalid user(s) email body
|
|
set_fact:
|
|
mail_body: "{{ lookup('template', 'templates/{{ template_prefix }}/mail-invalid.j2') }}"
|
|
delegate_to: localhost
|
|
listen: topic_noad
|
|
|
|
- name: Send status email
|
|
mail:
|
|
host: "{{ smtp_relay }}"
|
|
port: "{{ smtp_port }}"
|
|
charset: utf-8
|
|
from: "{{ from_email }}"
|
|
to: "{{ hostvars['localhost']['requester_email'] }}"
|
|
subject: "Result of request {{ body_service_name }} : {{ perform }} user(s) in {{ group }}"
|
|
body: "{{ mail_body }}"
|
|
delegate_to: localhost
|
|
when: hostvars['localhost']['requester_email'] is defined and enable_requester_email == 'true'
|
|
listen:
|
|
- topic_noad
|
|
|
|
- name: Condition fail on invalid AD user
|
|
debug:
|
|
msg:
|
|
- "invalid AD user(s) to {{ perform }} in group {{ group }}"
|
|
- ---------------------------------------
|
|
- "{{ no_aduser }}"
|
|
- ---------------------------------------
|
|
delegate_to: localhost
|
|
listen: topic_noad
|
|
|
|
# provides failure exit code for cloudforms and terminates playbook
|
|
- name: Hard exit
|
|
fail:
|
|
listen: topic_noad
|
|
|
|
|
|
- hosts: localhost
|
|
gather_facts: false
|
|
name: Report Results
|
|
vars:
|
|
state: "{{ 'present' if perform == 'create' else ( 'absent' if perform == 'delete' else 'placeholder') }}"
|
|
result: "{{ hostvars['adserver']['result'] }}"
|
|
requester_email: "{{ hostvars['localhost']['requester_email'] }}"
|
|
body_service_name_noreqid: "{{ hostvars['localhost']['service_name'] }}" # service_name matches cloudforms tile name (i.e Automated Transcription Service), we want this for the customer email subject line - welcome to <service_name>
|
|
body_service_name: "{{ hostvars['localhost']['new_service_name'] }}"
|
|
body_requester_user: "{{ hostvars['localhost']['requester_user'] }}"
|
|
body_requester_user_ad: "{{ hostvars['localhost']['requester_user_ad'] }}"
|
|
groupmemberslistvalidate: "{{ hostvars['localhost']['groupmemberslistvalidate'] }}"
|
|
email_recipients: []
|
|
email_recipients_no_action: []
|
|
user_no_action: []
|
|
self_service_user: false
|
|
vars_files:
|
|
vars/main.yml
|
|
|
|
tasks:
|
|
|
|
# the initial design of the script catered for user(s) being added to a group and the requester getting status emails (add / remove / invalid-user / no-change) and the user(s) recieving (add / remove) emails
|
|
# a use case where the requester is requesting only himself could mean status emails and user emails being recieved
|
|
# logic at the end of this script evaluates if the requester user matches a single entry in groupmember list and then disables requester/status emails
|
|
# to test this behaviour on the command line ensure the following fact (requester_user_ad) is set to the same value as the single item in the groupmembers parameter
|
|
# this condition is replicated with the spoof_self_service parameter
|
|
- name: checking for spoof self service mode
|
|
set_fact:
|
|
body_requester_user_ad: "{{ groupmemberslistvalidate[0] }}"
|
|
when: spoof_self_service == "true"
|
|
|
|
- name: detect if the requester user is adding/removing only itself to group (only active when using a custom template_prefix)
|
|
set_fact:
|
|
self_service_user: true
|
|
when: (groupmemberslistvalidate | length == 1 and groupmemberslistvalidate[0] == body_requester_user_ad) and template_prefix != 'default'
|
|
|
|
- debug:
|
|
msg: "running in self service mode"
|
|
when: self_service_user
|
|
|
|
- name: set list of email recipients where action has been taken upon their account
|
|
set_fact:
|
|
email_recipients: "{{ hostvars['adserver']['email_recipients'] }}"
|
|
when: hostvars['adserver']['email_recipients'] is defined
|
|
|
|
- name: set list of email recipients where no action has been taken upon their account
|
|
set_fact:
|
|
email_recipients_no_action: "{{ hostvars['adserver']['email_recipients_no_action'] }}"
|
|
when: hostvars['adserver']['email_recipients_no_action'] is defined
|
|
|
|
- name: set list of user names where no action has been taken upon their account
|
|
set_fact:
|
|
user_no_action: "{{ hostvars['adserver']['user_no_action'] }}"
|
|
when: hostvars['adserver']['user_no_action'] is defined
|
|
|
|
- name: Print email recipient information
|
|
debug:
|
|
msg:
|
|
- "requester to recieve status email: {{ requester_email }}"
|
|
- "enable requester mail: {{ enable_requester_email }}"
|
|
- "enable customer mail: {{ enable_customer_email }}"
|
|
- "self service mode: {{ self_service_user }}"
|
|
- "user(s) that can recieve change notification: {{ email_recipients }}"
|
|
- "user(s) with no action undertaken: {{ user_no_action }}"
|
|
- "user(s) that can recieve nochange notification: {{ email_recipients_no_action }}"
|
|
|
|
- set_fact:
|
|
template_prefix: "default"
|
|
when: template_prefix is not defined
|
|
|
|
- name: Build nochange email status body
|
|
set_fact:
|
|
mail_body: "{{ lookup('template', 'templates/{{ template_prefix }}/mail-nochange.j2') }}"
|
|
when: not result.changed
|
|
|
|
- debug: # changed_when: false in 'build nochange email' doesnt force notification of topic, use debug as a work around to always get a changed: true status to ensure a desired handler is executed
|
|
msg: "Invoke nochange handler"
|
|
changed_when: true
|
|
when: not result.changed
|
|
notify: topic_nochange
|
|
|
|
- name: Build add email status body
|
|
set_fact:
|
|
mail_body: "{{ lookup('template', 'templates/{{ template_prefix }}/mail-add.j2') }}"
|
|
changed_when: true
|
|
when: result.changed and result.added |length > 0
|
|
notify: topic_add
|
|
|
|
- name: Build remove email status body
|
|
set_fact:
|
|
mail_body: "{{ lookup('template', 'templates/{{ template_prefix }}/mail-remove.j2') }}"
|
|
changed_when: true
|
|
when: result.changed and result.removed |length > 0
|
|
notify: topic_remove
|
|
|
|
- name: Build add email customer body
|
|
set_fact:
|
|
customer_change_mail_body: "{{ lookup('template', 'templates/{{ template_prefix }}/customer-mail-add.j2') }}"
|
|
changed_when: true
|
|
when: (result.changed and result.added |length > 0) and template_prefix != 'default'
|
|
notify: topic_add
|
|
|
|
- name: Build remove email customer body
|
|
set_fact:
|
|
customer_change_mail_body: "{{ lookup('template', 'templates/{{ template_prefix }}/customer-mail-remove.j2') }}"
|
|
changed_when: true
|
|
when: (result.changed and result.removed |length > 0) and template_prefix != 'default'
|
|
notify: topic_remove
|
|
|
|
- name: Build nochange add email customer body
|
|
set_fact:
|
|
customer_nochange_add_mail_body: "{{ lookup('template', 'templates/{{ template_prefix }}/customer-mail-nochange.j2') }}"
|
|
changed_when: true
|
|
when: user_no_action |length >0 and template_prefix != 'default'
|
|
notify: topic_nochange
|
|
|
|
handlers:
|
|
|
|
- name: Send status email
|
|
mail:
|
|
host: "{{ smtp_relay }}"
|
|
port: "{{ smtp_port }}"
|
|
charset: utf-8
|
|
from: "{{ from_email }}"
|
|
to: "{{ requester_email }}"
|
|
subject: "Result of request {{ body_service_name }} : {{ perform }} user(s) in {{ group }}"
|
|
body: "{{ mail_body }}"
|
|
#body: "{{ lookup('file', '/var/www/release.txt') }}" # not using files due to WSL/ANSIBLE_RUNNER virtual environments
|
|
#when: hostvars['localhost']['requester_email'] is defined and enable_requester_email == 'true'
|
|
when: (hostvars['localhost']['requester_email'] is defined and enable_requester_email == 'true') and not self_service_user
|
|
listen:
|
|
- topic_nochange
|
|
- topic_add
|
|
- topic_remove
|
|
|
|
- name: Send customer add / remove email
|
|
mail:
|
|
host: "{{ smtp_relay }}"
|
|
port: "{{ smtp_port }}"
|
|
charset: utf-8
|
|
from: "{{ from_email }}"
|
|
to: "{{ email_recipients }}"
|
|
#subject: "Cloudforms - Result of {{ body_service_name }}"
|
|
subject: "Welcome to the {{ body_service_name_noreqid }}"
|
|
# no attachments, cloudforms ansible-runner has trouble using directory paths
|
|
# we cant use file filter with binary files such as images
|
|
# instead our html body should reference the logo @ https://www.nottingham.ac.uk/SiteElements/Images/Base/logo.png
|
|
#attach: 'templates/{{ template_prefix }}/images/logo.png'
|
|
subtype: html
|
|
body: "{{ customer_change_mail_body }}"
|
|
when: (enable_customer_email == 'true' and email_recipients | length >0) and template_prefix != 'default'
|
|
listen:
|
|
- topic_add
|
|
- topic_remove
|
|
|
|
- name: Send customer nochange add email
|
|
mail:
|
|
host: "{{ smtp_relay }}"
|
|
port: "{{ smtp_port }}"
|
|
charset: utf-8
|
|
from: "{{ from_email }}"
|
|
to: "{{ email_recipients_no_action }}"
|
|
subject: "Welcome to the {{ body_service_name_noreqid }}"
|
|
subtype: html
|
|
body: "{{ customer_nochange_add_mail_body }}"
|
|
#when: (enable_customer_email == 'true' and email_recipients_no_action | length >0) and template_prefix != 'default'
|
|
when: ((enable_customer_email == 'true' and email_recipients_no_action | length >0) and template_prefix != 'default') and state == 'present'
|
|
listen:
|
|
- topic_nochange
|
|
|
|
# commit message "disable customer email for scenario: no change with delete action"
|
|
# quick change so the customer nochange email (no longer being rendered with create/delete value of the perform variable) is only sent if a user is being added and is already in a group
|
|
# there is no nochange email for a user who is being removed from a group who is already not a member
|
|
# we likely dont want to fail on this condition when doing lookup on AD as other users still want to be processed (we only fail on invalid users for safety)
|
|
# is a customer nochange remove email useful? probably not
|
|
# Clarify with Mike
|
|
|
|
# - name: Send email
|
|
# debug:
|
|
# msg: "{{ mail_body }}"
|
|
# listen:
|
|
# - topic_nochange
|
|
# - topic_add
|
|
# - topic_remove
|
|
|
|
- name: Condition no change
|
|
debug:
|
|
msg:
|
|
- "no changes made to group {{ group }}"
|
|
- ---------------------------------------
|
|
- "users that had no action taken due to already being {{ state }} in group"
|
|
- ---------------------------------------
|
|
- "{{ user_no_action }}"
|
|
- ---------------------------------------
|
|
- "members of group {{ group }}"
|
|
- ---------------------------------------
|
|
- "{{ result.members }}"
|
|
- ---------------------------------------
|
|
listen: topic_nochange
|
|
|
|
- name: Condition user(s) added
|
|
debug:
|
|
msg:
|
|
- "user(s) added to group {{ group }}"
|
|
- ---------------------------------------
|
|
- "{{ result.added }}"
|
|
- ---------------------------------------
|
|
- "users that had no action taken due to already being {{ state }} in group"
|
|
- ---------------------------------------
|
|
- "{{ user_no_action }}"
|
|
- ---------------------------------------
|
|
- "members of group {{ group }}"
|
|
- ---------------------------------------
|
|
- "{{ result.members }}"
|
|
- ---------------------------------------
|
|
listen: topic_add
|
|
|
|
- name: Condition user(s) removed
|
|
debug:
|
|
msg:
|
|
- "user(s) removed from group {{ group }}"
|
|
- ---------------------------------------
|
|
- "{{ result.removed }}"
|
|
- ---------------------------------------
|
|
- "users that had no action taken due to already being {{ state }} in group"
|
|
- ---------------------------------------
|
|
- "{{ user_no_action }}"
|
|
- ---------------------------------------
|
|
- "members of group {{ group }}"
|
|
- ---------------------------------------
|
|
- "{{ result.members }}"
|
|
- ---------------------------------------
|
|
listen: topic_remove
|
|
|
|
# may need to test verbosity levels with handlers for clean log output in cloudforms, example of using verbosity level
|
|
#
|
|
# - name: Print Results
|
|
# debug:
|
|
# var: result
|
|
# verbosity: 0
|
|
|