1#!/usr/local/bin/python3.8 2# -*- coding: utf-8 -*- 3 4# Copyright: (c) 2020, Zainab Alsaffar <Zainab.Alsaffar@mail.rit.edu> 5# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) 6 7from __future__ import absolute_import, division, print_function 8__metaclass__ = type 9 10DOCUMENTATION = r''' 11--- 12module: pagerduty_user 13short_description: Manage a user account on PagerDuty 14description: 15 - This module manages the creation/removal of a user account on PagerDuty. 16version_added: '1.3.0' 17author: Zainab Alsaffar (@zanssa) 18requirements: 19 - pdpyras python module = 4.1.1 20 - PagerDuty API Access 21options: 22 access_token: 23 description: 24 - An API access token to authenticate with the PagerDuty REST API. 25 required: true 26 type: str 27 pd_user: 28 description: 29 - Name of the user in PagerDuty. 30 required: true 31 type: str 32 pd_email: 33 description: 34 - The user's email address. 35 - I(pd_email) is the unique identifier used and cannot be updated using this module. 36 required: true 37 type: str 38 pd_role: 39 description: 40 - The user's role. 41 choices: ['global_admin', 'manager', 'responder', 'observer', 'stakeholder', 'limited_stakeholder', 'restricted_access'] 42 default: 'responder' 43 type: str 44 state: 45 description: 46 - State of the user. 47 - On C(present), it creates a user if the user doesn't exist. 48 - On C(absent), it removes a user if the account exists. 49 choices: ['present', 'absent'] 50 default: 'present' 51 type: str 52 pd_teams: 53 description: 54 - The teams to which the user belongs. 55 - Required if I(state=present). 56 type: list 57 elements: str 58notes: 59 - Supports C(check_mode). 60''' 61 62EXAMPLES = r''' 63- name: Create a user account on PagerDuty 64 community.general.pagerduty_user: 65 access_token: 'Your_Access_token' 66 pd_user: user_full_name 67 pd_email: user_email 68 pd_role: user_pd_role 69 pd_teams: user_pd_teams 70 state: "present" 71 72- name: Remove a user account from PagerDuty 73 community.general.pagerduty_user: 74 access_token: 'Your_Access_token' 75 pd_user: user_full_name 76 pd_email: user_email 77 state: "absent" 78''' 79 80RETURN = r''' # ''' 81 82from ansible.module_utils.basic import AnsibleModule, missing_required_lib 83import traceback 84from os import path 85 86try: 87 from pdpyras import APISession 88 HAS_PD_PY = True 89except ImportError: 90 HAS_PD_PY = False 91 PD_IMPORT_ERR = traceback.format_exc() 92 93try: 94 from pdpyras import PDClientError 95 HAS_PD_CLIENT_ERR = True 96except ImportError: 97 HAS_PD_CLIENT_ERR = False 98 PD_CLIENT_ERR_IMPORT_ERR = traceback.format_exc() 99 100 101class PagerDutyUser(object): 102 def __init__(self, module, session): 103 self._module = module 104 self._apisession = session 105 106 # check if the user exists 107 def does_user_exist(self, pd_email): 108 for user in self._apisession.iter_all('users'): 109 if user['email'] == pd_email: 110 return user['id'] 111 112 # create a user account on PD 113 def add_pd_user(self, pd_name, pd_email, pd_role): 114 try: 115 user = self._apisession.persist('users', 'email', { 116 "name": pd_name, 117 "email": pd_email, 118 "type": "user", 119 "role": pd_role, 120 }) 121 return user 122 123 except PDClientError as e: 124 if e.response.status_code == 400: 125 self._module.fail_json( 126 msg="Failed to add %s due to invalid argument" % (pd_name)) 127 if e.response.status_code == 401: 128 self._module.fail_json(msg="Failed to add %s due to invalid API key" % (pd_name)) 129 if e.response.status_code == 402: 130 self._module.fail_json( 131 msg="Failed to add %s due to inability to perform the action within the API token" % (pd_name)) 132 if e.response.status_code == 403: 133 self._module.fail_json( 134 msg="Failed to add %s due to inability to review the requested resource within the API token" % (pd_name)) 135 if e.response.status_code == 429: 136 self._module.fail_json( 137 msg="Failed to add %s due to reaching the limit of making requests" % (pd_name)) 138 139 # delete a user account from PD 140 def delete_user(self, pd_user_id, pd_name): 141 try: 142 user_path = path.join('/users/', pd_user_id) 143 self._apisession.rdelete(user_path) 144 145 except PDClientError as e: 146 if e.response.status_code == 404: 147 self._module.fail_json( 148 msg="Failed to remove %s as user was not found" % (pd_name)) 149 if e.response.status_code == 403: 150 self._module.fail_json( 151 msg="Failed to remove %s due to inability to review the requested resource within the API token" % (pd_name)) 152 if e.response.status_code == 401: 153 # print out the list of incidents 154 pd_incidents = self.get_incidents_assigned_to_user(pd_user_id) 155 self._module.fail_json(msg="Failed to remove %s as user has assigned incidents %s" % (pd_name, pd_incidents)) 156 if e.response.status_code == 429: 157 self._module.fail_json( 158 msg="Failed to remove %s due to reaching the limit of making requests" % (pd_name)) 159 160 # get incidents assigned to a user 161 def get_incidents_assigned_to_user(self, pd_user_id): 162 incident_info = {} 163 incidents = self._apisession.list_all('incidents', params={'user_ids[]': [pd_user_id]}) 164 165 for incident in incidents: 166 incident_info = { 167 'title': incident['title'], 168 'key': incident['incident_key'], 169 'status': incident['status'] 170 } 171 return incident_info 172 173 # add a user to a team/teams 174 def add_user_to_teams(self, pd_user_id, pd_teams, pd_role): 175 updated_team = None 176 for team in pd_teams: 177 team_info = self._apisession.find('teams', team, attribute='name') 178 if team_info is not None: 179 try: 180 updated_team = self._apisession.rput('/teams/' + team_info['id'] + '/users/' + pd_user_id, json={ 181 'role': pd_role 182 }) 183 except PDClientError: 184 updated_team = None 185 return updated_team 186 187 188def main(): 189 module = AnsibleModule( 190 argument_spec=dict( 191 access_token=dict(type='str', required=True, no_log=True), 192 pd_user=dict(type='str', required=True), 193 pd_email=dict(type='str', required=True), 194 state=dict(type='str', default='present', choices=['present', 'absent']), 195 pd_role=dict(type='str', default='responder', 196 choices=['global_admin', 'manager', 'responder', 'observer', 'stakeholder', 'limited_stakeholder', 'restricted_access']), 197 pd_teams=dict(type='list', elements='str', required=False)), 198 required_if=[['state', 'present', ['pd_teams']], ], 199 supports_check_mode=True, 200 ) 201 202 if not HAS_PD_PY: 203 module.fail_json(msg=missing_required_lib('pdpyras', url='https://github.com/PagerDuty/pdpyras'), exception=PD_IMPORT_ERR) 204 205 if not HAS_PD_CLIENT_ERR: 206 module.fail_json(msg=missing_required_lib('PDClientError', url='https://github.com/PagerDuty/pdpyras'), exception=PD_CLIENT_ERR_IMPORT_ERR) 207 208 access_token = module.params['access_token'] 209 pd_user = module.params['pd_user'] 210 pd_email = module.params['pd_email'] 211 state = module.params['state'] 212 pd_role = module.params['pd_role'] 213 pd_teams = module.params['pd_teams'] 214 215 if pd_role: 216 pd_role_gui_value = { 217 'global_admin': 'admin', 218 'manager': 'user', 219 'responder': 'limited_user', 220 'observer': 'observer', 221 'stakeholder': 'read_only_user', 222 'limited_stakeholder': 'read_only_limited_user', 223 'restricted_access': 'restricted_access' 224 } 225 pd_role = pd_role_gui_value[pd_role] 226 227 # authenticate with PD API 228 try: 229 session = APISession(access_token) 230 except PDClientError as e: 231 module.fail_json(msg="Failed to authenticate with PagerDuty: %s" % e) 232 233 user = PagerDutyUser(module, session) 234 235 user_exists = user.does_user_exist(pd_email) 236 237 if user_exists: 238 if state == "absent": 239 # remove user 240 if not module.check_mode: 241 user.delete_user(user_exists, pd_user) 242 module.exit_json(changed=True, result="Successfully deleted user %s" % pd_user) 243 else: 244 module.exit_json(changed=False, result="User %s already exists." % pd_user) 245 246 # in case that the user does not exist 247 else: 248 if state == "absent": 249 module.exit_json(changed=False, result="User %s was not found." % pd_user) 250 251 else: 252 # add user, adds user with the default notification rule and contact info (email) 253 if not module.check_mode: 254 user.add_pd_user(pd_user, pd_email, pd_role) 255 # get user's id 256 pd_user_id = user.does_user_exist(pd_email) 257 # add a user to the team/s 258 user.add_user_to_teams(pd_user_id, pd_teams, pd_role) 259 module.exit_json(changed=True, result="Successfully created & added user %s to team %s" % (pd_user, pd_teams)) 260 261 262if __name__ == "__main__": 263 main() 264