Skip to main content

Command Palette

Search for a command to run...

Automating Linux User Lifecycle with GitHub Actions: A Practical DevOps Guide

Published
6 min read
Automating Linux User Lifecycle with GitHub Actions: A Practical DevOps Guide
C

https://abhaypatil001.github.io/abhay-portfolio/

Introduction

User access management is one of the most fundamental responsibilities in any infrastructure environment, yet it is often handled in an ad hoc and manual way. In many organizations, especially during rapid development cycles, access is granted quickly but rarely reviewed or revoked with the same rigor.

Developers, vendors, and support engineers frequently require temporary access to systems. While the intention is short-term access, the implementation often becomes long-term exposure.

This article walks through a practical approach to solving this problem using GitHub Actions, starting from basic concepts and moving toward a production-ready automation pattern.


Problem Statement

In a typical environment, user access is managed manually through system administrators. A request comes in, a user is created, and access is granted. However, several issues arise with this approach:

  • There is no standard mechanism to enforce access duration

  • Users are often not removed after their task is completed

  • There is limited or no audit trail of who created the user and why

  • Access reviews are either infrequent or non-existent

  • Security policies like least privilege are not consistently followed

Consider a simple command:

sudo useradd ammar

This creates a user with no expiry. Unless someone explicitly removes this user later, the account remains active indefinitely.

Over time, this leads to:

  • Accumulation of unused accounts

  • Increased attack surface

  • Compliance and audit failures

  • Difficulty in tracking ownership of accounts

This is not a tooling problem; it is a process and automation problem.


Objective

The goal is to build a system that:

  • Creates users in a controlled and repeatable manner

  • Enforces an expiry date at the time of creation

  • Provides traceability through pipeline logs

  • Reduces manual intervention

  • Aligns with security best practices


Core Concept: User Expiry in Linux

Linux provides a built-in way to set an account expiry date at the time of user creation.

sudo useradd -e 2026-12-07 ammar

This ensures that the account becomes inactive automatically after the specified date.

To verify:

sudo chage -l ammar

This command shows account expiry and password aging details.

This is a simple but powerful feature that is often underutilized.


Solution Overview

Instead of creating users manually, we automate the process using a CI/CD pipeline.

The high-level flow looks like this:

  1. A request is made to create a user (username + expiry date)

  2. A GitHub Actions workflow is triggered

  3. The workflow connects to the target server via SSH

  4. A script runs to create the user with the specified expiry

  5. Logs are stored in the pipeline for auditing

This approach ensures consistency, repeatability, and visibility.


Implementation

Step 1: Repository Structure

Create a repository with the following structure:

user-access-automation/
├── .github/workflows/
│   └── create-user.yml
├── scripts/
│   └── create_user.sh
└── README.md

Step 2: Bash Script

This script handles user creation logic.

#!/bin/bash

set -e

USERNAME=$1
EXPIRY_DATE=$2

if [[ -z "\(USERNAME" || -z "\)EXPIRY_DATE" ]]; then
  echo "Usage: $0 <username> <expiry_date>"
  exit 1
fi

echo "Creating user: $USERNAME"
echo "Expiry date: $EXPIRY_DATE"

sudo useradd -m -e "\(EXPIRY_DATE" "\)USERNAME"

PASSWORD=$(openssl rand -base64 12)
echo "\(USERNAME:\)PASSWORD" | sudo chpasswd

sudo chage -d 0 "$USERNAME"

sudo chage -l "$USERNAME"

Step 3: GitHub Actions Workflow

name: Create Temporary User

on:
  workflow_dispatch:
    inputs:
      username:
        description: "Enter username"
        required: true
      expiry_date:
        description: "Expiry date (YYYY-MM-DD)"
        required: true

jobs:
  create-user:
    runs-on: ubuntu-latest

    steps:
      - name: Create user via SSH
        uses: appleboy/ssh-action@v1.0.0
        with:
          host: ${{ secrets.SERVER_IP }}
          username: ${{ secrets.SERVER_USER }}
          key: ${{ secrets.SSH_PRIVATE_KEY }}
          script: |
            sudo useradd -m -e "\({{ github.event.inputs.expiry_date }}" "\){{ github.event.inputs.username }}"
            sudo chage -l "${{ github.event.inputs.username }}"

Step 4: Configure Secrets

In your GitHub repository:

Settings → Secrets and variables → Actions

Add:

  • SERVER_IP

  • SERVER_USER

  • SSH_PRIVATE_KEY

These values are injected securely during workflow execution.


Step 5: Execute the Workflow

  • Navigate to the Actions tab

  • Select the workflow

  • Click "Run workflow"

  • Provide username and expiry date

The user will be created on the target server with the defined expiry.


Testing Strategy

You can test the script directly on a GitHub-hosted runner:

runs-on: ubuntu-latest

However, this only validates the logic because the runner is temporary. Any user created there will be removed once the job completes.

For real testing, always validate on a target server via SSH.


Security Considerations

  • Use SSH key-based authentication instead of passwords

  • Avoid direct root login; use a controlled sudo user

  • Use non-interactive shells for service accounts when applicable

  • Store all sensitive data in GitHub Secrets

  • Implement approval gates for production workflows


Advanced Enhancements

Once the basic system is working, you can extend it further:

  • Schedule a job to remove expired users automatically

  • Integrate approval workflows using GitHub Environments

  • Add logging to centralized systems like ELK or Azure Monitor

  • Replace raw SSH execution with configuration management tools like Ansible

  • Introduce role-based access control for different environments


Real-World Impact

This approach provides:

  • Consistent and repeatable user provisioning

  • Reduced operational overhead

  • Improved auditability

  • Better alignment with security policies

It also demonstrates a mature DevOps mindset where manual processes are systematically replaced with controlled automation.


Conclusion

User access management is often treated as a routine administrative task, but it has significant security implications. By introducing automation with expiry enforcement, you can reduce risk and improve operational efficiency.

The solution described here is simple to implement but scalable enough to be extended into enterprise-grade workflows.


About the Author

Abhay Patil DevOps Engineer

Blog: https://clouddecode.in LinkedIn: https://www.linkedin.com/in/abhay-patil-devops/


Final Note

Automating small operational tasks like user management can have a significant impact on overall system security and reliability.

2 views

More from this blog

C

CloudDecode

11 posts

CloudDecode simplifies cloud & DevOps—covering Azure, AWS, Kubernetes, Terraform, CI/CD & more—with clear guides to help you decode, learn, and build with confidence.