đ Executive Summary
TL;DR: The article presents a Python script to automate the manual, error-prone process of employee offboarding from AWS and GitHub. This solution efficiently revokes access by disabling AWS console logins, deleting access keys, and removing users from GitHub organizations, significantly enhancing security and saving time.
đŻ Key Takeaways
- Automated offboarding for AWS involves deleting login profiles, revoking all access keys, detaching policies, and removing users from IAM groups, rather than immediate user deletion, to maintain audit trails.
- Securely manage credentials using
python-dotenvand aconfig.envfile, ensuring sensitive AWSAccess Key ID/Secret Access Keyand GitHubPersonal Access Tokenare not hardcoded and are excluded from source control. - GitHub access revocation is achieved via a
DELETErequest to the GitHub APIâs organization members endpoint, requiring aPersonal Access Tokenwithadmin:orgscope. - The script requires specific AWS IAM permissions (e.g.,
iam:DeleteLoginProfile,iam:DeleteAccessKey,iam:RemoveUserFromGroup) and a GitHub PAT withadmin:orgscope for successful execution. - Robust error handling is included for scenarios like âNo login profile foundâ or âUser not found in organization,â making the script resilient to pre-existing states.
Automate Employee Offboarding: Revoke AWS & GitHub Access Script
Hey there, Darian Vance here. As a Senior DevOps Engineer at TechResolve, Iâve seen my fair share of urgent, late-night offboarding requests. For a long time, this was a manual, error-prone checklist: disable the AWS console password, hunt down and delete IAM keys, remove the user from the GitHub org⌠you know the drill. It was a time sink and, frankly, a security risk if a step was missed.
I finally decided to automate the whole process. This script Iâm about to walk you through has been a game-changer. It turns a 30-minute, multi-tab headache into a 10-second command. It ensures consistency, speed, and peace of mind. Letâs get this set up so you can reclaim some of your valuable time.
Prerequisites
Before we dive in, make sure you have the following ready to go:
-
AWS IAM User Credentials: Youâll need an
Access Key IDandSecret Access Keyfor an IAM user (or role) that has programmatic access. The user must have permissions to manage other IAM users. Specifically, youâll need policies allowing actions likeiam:DeleteLoginProfile,iam:RemoveUserFromGroup,iam:ListAccessKeys,iam:DeleteAccessKey, andiam:DetachUserPolicy. -
GitHub Personal Access Token (PAT): This token needs to have the
admin:orgscope to be able to remove users from your organization. - Python 3: The script is written in Python, so youâll need it installed on the machine where you plan to run this.
-
Required Python Libraries: Youâll need to install
boto3(the AWS SDK for Python),requests(for making HTTP requests to the GitHub API), andpython-dotenv(for securely managing our credentials).
The Guide: Step-by-Step
Step 1: Setting Up Your Project
Alright, letâs get our workspace ready. Iâll skip the standard virtual environment setup commands since you likely have your own workflow for that. The important part is to create a project directory and install the necessary Python libraries. From your terminal, you would typically run the commands to install boto3, requests, and python-dotenv using pip.
Next, inside your project directory, create two files: offboard_script.py for our main logic, and a config.env file to store our secrets.
Step 2: Storing Credentials Securely in config.env
Hardcoding credentials directly in a script is a huge security no-go. Weâll use a config.env file to keep them separate. This file should be added to your .gitignore to ensure it never gets committed to source control.
Your config.env file should look like this:
# AWS Credentials
AWS_ACCESS_KEY_ID=YOUR_AWS_ACCESS_KEY_ID
AWS_SECRET_ACCESS_KEY=YOUR_AWS_SECRET_ACCESS_KEY
AWS_REGION=us-east-1
# GitHub Credentials
GITHUB_ORG_NAME=your-github-organization
GITHUB_PAT=your_personal_access_token
Just replace the placeholder values with your actual credentials.
Step 3: The Revocation Logic
Now for the core of the script. Weâll build this in a few functions to keep things clean and readable. The main idea is to create one function for handling AWS and another for GitHub, then a main function to orchestrate the process.
Revoking AWS Access
For AWS, our goal isnât to delete the user immediately but to completely lock them out. This preserves the user object for any future auditing needs. Our function will perform these actions in order:
- Delete the userâs login profile (disables console access).
- Find and delete all of the userâs access keys (disables CLI/API access).
- Detach all managed policies.
- Remove the user from any IAM groups.
Pro Tip: In my production setups, I donât delete the IAM user for at least 90 days. Deactivating them this way is a security best practice. It prevents a new user from accidentally being created with the same name and inheriting permissions unexpectedly, and it gives you an audit trail.
Revoking GitHub Access
The GitHub part is more straightforward. We just need to make a single API call to remove the specified user from our organization. Error handling is key here to confirm the action was successful.
Pro Tip: Watch out for API rate limits. If youâre offboarding multiple users in a loop, you could hit GitHubâs limit. For a simple, one-off script like this, itâs not a major concern, but for larger-scale automation, youâd want to build in some checks and back-off logic.
Step 4: The Complete Script (offboard_script.py)
Here is the full Python script. Iâve added comments to explain each part of the logic, from loading credentials to executing the revocation steps.
import os
import sys
import boto3
import requests
from dotenv import load_dotenv
def revoke_aws_access(iam_client, username):
"""Locks an AWS IAM user out of their account completely."""
print(f"--- Starting AWS Revocation for user: {username} ---")
try:
# 1. Delete login profile (disables console access)
print("Step 1: Deleting console login profile...")
try:
iam_client.delete_login_profile(UserName=username)
print(f"[SUCCESS] Deleted login profile for {username}.")
except iam_client.exceptions.NoSuchEntityException:
print(f"[INFO] No login profile found for {username}, skipping.")
# 2. Deactivate and delete all access keys
print("Step 2: Deleting all access keys...")
paginator = iam_client.get_paginator('list_access_keys')
for response in paginator.paginate(UserName=username):
for key in response['AccessKeyMetadata']:
print(f" - Deleting access key {key['AccessKeyId']}...")
iam_client.delete_access_key(UserName=username, AccessKeyId=key['AccessKeyId'])
print("[SUCCESS] All access keys deleted.")
# 3. Detach all user policies
print("Step 3: Detaching all managed policies...")
policies = iam_client.list_attached_user_policies(UserName=username)['AttachedPolicies']
for policy in policies:
print(f" - Detaching policy {policy['PolicyArn']}...")
iam_client.detach_user_policy(UserName=username, PolicyArn=policy['PolicyArn'])
print("[SUCCESS] All managed policies detached.")
# 4. Remove from all groups
print("Step 4: Removing from all IAM groups...")
groups = iam_client.list_groups_for_user(UserName=username)['Groups']
for group in groups:
print(f" - Removing from group {group['GroupName']}...")
iam_client.remove_user_from_group(UserName=group['GroupName'], UserName=username)
print(f"[SUCCESS] Removed {username} from all groups.")
print(f"--- AWS Revocation for {username} completed successfully. ---")
return True
except Exception as e:
print(f"[ERROR] An unexpected error occurred during AWS revocation: {e}")
return False
def revoke_github_access(github_org, github_pat, username):
"""Removes a user from the GitHub organization."""
print(f"--- Starting GitHub Revocation for user: {username} ---")
url = f"https://api.github.com/orgs/{github_org}/members/{username}"
headers = {
"Accept": "application/vnd.github.v3+json",
"Authorization": f"token {github_pat}"
}
try:
response = requests.delete(url, headers=headers)
# A 204 No Content response means success
if response.status_code == 204:
print(f"[SUCCESS] Successfully removed {username} from the {github_org} GitHub org.")
return True
elif response.status_code == 404:
print(f"[INFO] User {username} not found in the organization. They may have already been removed.")
return True
else:
print(f"[ERROR] Failed to remove {username} from GitHub. Status: {response.status_code}, Response: {response.text}")
return False
except requests.exceptions.RequestException as e:
print(f"[ERROR] A network error occurred while trying to revoke GitHub access: {e}")
return False
def main():
"""Main function to orchestrate the offboarding process."""
load_dotenv('config.env')
# Load credentials from environment
aws_key_id = os.getenv("AWS_ACCESS_KEY_ID")
aws_secret_key = os.getenv("AWS_SECRET_ACCESS_KEY")
aws_region = os.getenv("AWS_REGION")
github_org = os.getenv("GITHUB_ORG_NAME")
github_pat = os.getenv("GITHUB_PAT")
if not all([aws_key_id, aws_secret_key, aws_region, github_org, github_pat]):
print("[ERROR] Missing one or more credentials in config.env. Please check the file.")
return
# Get username from command-line arguments
if len(sys.argv) < 2:
print("Usage: python3 offboard_script.py <username>")
return
username_to_offboard = sys.argv[1]
print(f"Starting offboarding process for: {username_to_offboard}\n")
# Initialize AWS IAM client
iam_client = boto3.client(
'iam',
aws_access_key_id=aws_key_id,
aws_secret_access_key=aws_secret_key,
region_name=aws_region
)
# Run revocation functions
aws_success = revoke_aws_access(iam_client, username_to_offboard)
print("\n") # Adding space for readability
github_success = revoke_github_access(github_org, github_pat, username_to_offboard)
print("\n--- Offboarding Summary ---")
print(f"AWS Access Revocation: {'SUCCESS' if aws_success else 'FAILED'}")
print(f"GitHub Access Revocation: {'SUCCESS' if github_success else 'FAILED'}")
print("-------------------------")
if __name__ == "__main__":
main()
Step 5: Running the Script
To execute the offboarding process, you simply run the script from your terminal and pass the username as a command-line argument.
Example: python3 offboard_script.py jane.doe
The script will print its progress, letting you know exactly what itâs doing. For automation, you could trigger this script via an API call from your HR system or even a service ticket. For a scheduled cleanup, you might use a cron job. For instance, to run a script at 2 AM every Monday, the command would be something like: 0 2 * * 1 python3 script.py. Just be sure the script is adapted to get the username from a file or list in that case.
Common Pitfalls
Here are a few places where Iâve tripped up before, so you can avoid them:
-
Permissions, Permissions, Permissions: The most common error is an âAccess Deniedâ message. 99% of the time, this means the IAM user running the script is missing a required permission, or the GitHub PAT doesnât have the
admin:orgscope. Double-check them first. - Username Mismatches: This script assumes the AWS IAM username and the GitHub username are the same. In many organizations, they are not. In a more advanced setup, I use a mapping file (like a CSV or JSON) to link a personâs identity across different services.
- User Not Found: The script handles cases where a user might already be removed from a service, but itâs good to be aware that a simple typo in the username will also result in a ânot foundâ message. Always double-check the username you pass in.
Conclusion
And there you have it. With a single script, youâve created a consistent, reliable, and secure process for offboarding employees from two critical systems. This isnât just about saving time; itâs about reducing your companyâs security exposure by ensuring access is revoked promptly and completely.
Feel free to expand on this. You could add logging to a file, send notifications to a Slack channel, or integrate it with other services like G Suite or Jira. This script is a solid foundation for a fully automated offboarding workflow.
Hope this helps streamline your operations. Happy automating!
â Darian Vance
đ Read the original article on TechResolve.blog
â Support my work
If this article helped you, you can buy me a coffee:

Top comments (0)