Automate Temporary Access with PagerDuty Schedules
If you're using PagerDuty, then you already have on-call schedules mapped out for critical roles. But when someone is on-call, they may need more database or server access than they'd otherwise use. This is where strongDM temporary grants come in: you can integrate your PagerDuty on-call schedule with strongDM to automatically grant strongDM users access to additional resources during their on-call shifts. This Python example shows a simple way of managing the process.
The script has two major portions: first, look up who is on call for a specific schedule over a certain time period; second, parse these assignments with the strongDM SDK to grant temporary access to a datasource or server. One wrinkle is that two API calls are necessary to PagerDuty: first, getting the list of who is on call will give a list of users and user IDs, but not email addresses. Second, specific user lookups get us the email addresses of who is on call.
To get this script working in your environment, you'll need the following:
- A strongDM API key and secret with Datasource list and grant and User assign and list rights
- A strongDM datasource name
- A PagerDuty API key with read-only rights
- The schedule ID of a PagerDuty schedule you wish to use as the basis of the temporary grants
In order for this automation to work, your users will need to be identified by the same email addresses in PagerDuty and in strongDM.
Add this script to your crontab to run on a regular schedule; modify the UNTIL
calculation to match the interval you're running it at. For instance, if you're running it weekly, that line would look like this:
UNTIL = (datetime.timedelta(days=7) + datetime.datetime.utcnow()).isoformat() + 'Z'
Script Listing
#!/usr/bin/env pythonimport requests,json,datetime,subprocess,strongdm,refrom datetime import timezone# PagerDuty API keyAPI_KEY = 'PD_API_KEY'# strongDM API keys: requires datasource list,grant and user list,assignaccess_key = "SDM_ACCESS_KEY"secret_key = "SDM_SECREY_KEY"# name of strongDM Datasource to which you are granting accessDATASOURCE = 'DATASOURCE_NAME'# Set your time zoneTIME_ZONE = 'UTC'# Get this ID from the PagerDuty admin UI, or via their 'schedules' API endpointSCHEDULE_IDS = ['PD_SCHEDULE_ID']# for the PD API requests. Modify UNTIL with the proper time offsetUNTIL = (datetime.timedelta(days=1) + datetime.datetime.utcnow()).isoformat() + 'Z'def get_oncalls():url = 'https://api.pagerduty.com/oncalls'headers = {'Accept': 'application/vnd.pagerduty+json;version=2','Authorization': 'Token token={token}'.format(token=API_KEY)}payload = {'time_zone': TIME_ZONE,'schedule_ids[]': SCHEDULE_IDS,'until': UNTIL,}r = requests.get(url, headers=headers, params=payload)struct = r.json()output = []for record in struct["oncalls"]:# get user's email addressr = requests.get(record["user"]["self"], headers=headers)output.append({"email" : r.json()["user"]["email"],"from" : record["start"],"to" : record["end"]})return outputdef grant_access(access_list):client = strongdm.Client(access_key, secret_key)# Get Datasource(s)resources = list(client.resources.list('name:{}'.format(DATASOURCE)) )resourceID = resources[0].id# Cycle through the output from PagerDutyfor item in access_list:# Use the email address to get the user.id from SDMprint('Current PD user is: ' + item["email"])users = list(client.accounts.list('email:{}'.format(item["email"])))if len(users) > 0:print('SDM user found!')myUserID = users[0].id# Convert the date strings from PD into a datetime objects = datetime.datetime.strptime(item["from"], '%Y-%m-%dT%H:%M:%SZ')e = datetime.datetime.strptime(item["to"], '%Y-%m-%dT%H:%M:%SZ')# Make both objects 'aware' (with TZ) as required by the strongDM SDKstart = s.replace(tzinfo=timezone.utc)end = e.replace(tzinfo=timezone.utc)# Create the grant objectmyGrant = strongdm.AccountGrant(resource_id='{}'.format(resourceID),account_id='{}'.format(myUserID),start_from=start, valid_until=end)# Perform the granttry:respGrant = client.account_grants.create(myGrant)except Exception as ex:print("\nSkipping user " + item["email"] + " on account of error: " + str(ex))else:print("\nGrant succeeded for user " + item["email"] + " to resource " + DATASOURCE + " from {} to {}".format(start,end))print('---\n')def main():access = get_oncalls()grant_access(access)main()
If you have any questions or problems with this, please reach out to strongDM support.