redhat_cloudforms_azure_arm.../ansible-ad-group-add/adgroup.yml

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