DevOps-bootcamp · Lektion

Automatisera skapande av användare och grupper

Skapa, ändra och granska konton i bulk med useradd, chage och hantering av sudoers-fragment.

Lektion 1 av 413 steg

Automatisera skapande av användare och grupper är en gratis lektion i DevOps-bootcamp på CoddyKit. Detta är lektion 1 av 4. Du kan läsa vilka 3 lektioner som helst i den här lärvägen kostnadsfritt i sin helhet – därefter låser CoddyKit PRO upp alla lektioner, plus praktisk övning med en inbyggd kodredigerare och en AI-lärare dygnet runt. Den ingår i lärvägen för DevOps-bootcamp, och Era framsteg synkroniseras mellan webben och CoddyKit-appen. Kursen i DevOps-bootcamp innehåller totalt 4 lektioner.

Varför automatisera användarprovisionering?

Att hantera användare en i taget med useradd fungerar bra för en handfull konton, men företagsmiljöer lägger regelbundet till dussintals eller hundratals användare samtidigt. Manuella kommandon blir lätt felbenägna, inkonsekventa och svåra att granska.

Med Bash-skript kan Ni:

  • Skapa användare med standardiserade inställningar (skal, hemkatalog och lösenordsprincip) varje gång
  • Läsa en CSV-fil eller textfil med nya medarbetare och provisionera dem i en enda körning
  • Logga varje åtgärd så att Ni får ett revisionsspår för efterlevnad
  • Integrera med pipelines för konfigurationshantering (Ansible, Chef, Jenkins)

Den här lektionen går igenom hur Ni bygger ett användarprovisioneringsskript av produktionskvalitet från grunden. Den behandlar useradd, chage, usermod, grupphantering, sudoers-drop-ins och granskning efter körningen.

Läsa en lista med användare i bulk

Det vedertagna indataformatet för bulkprovisionering är en avgränsad textfil — en post per rad. En typisk CSV-fil kan se ut så här:

username,full_name,group,shell
alice,Alice Smith,developers,/bin/bash
bob,Bob Jones,ops,/bin/zsh

Använd IFS och read i en while-loop för att tolka filen säkert. Om Ni hoppar över rubrikraden med tail -n +2 blir logiken tydligare.

Viktiga defensiva metoder:

  • Ta bort inledande och avslutande blanksteg från varje fält
  • Hoppa över tomma rader och kommentarsrader som börjar med #
  • Kontrollera att obligatoriska fält inte är tomma innan Ni anropar systemkommandon
#!/usr/bin/env bash
# parse_users.sh — safely read a CSV of users
set -euo pipefail

USER_FILE="${1:-users.csv}"

[[ -f "$USER_FILE" ]] || { echo "ERROR: $USER_FILE not found"; exit 1; }

tail -n +2 "$USER_FILE" | while IFS=',' read -r username full_name group shell; do
  # trim whitespace
  username="${username// /}"
  [[ -z "$username" || "$username" == \#* ]] && continue

  echo "Parsed -> user=$username group=$group shell=$shell"
done

Skapa användare med useradd

useradd är verktyget på låg nivå som skriver till /etc/passwd, /etc/shadow och /etc/group. De viktigaste flaggorna för skript är:

  • -m — skapa hemkatalogen
  • -s — ange inloggningsskalet
  • -c — GECOS-kommentarsfält (fullständigt namn)
  • -G — kompletterande grupper (kommaseparerade)
  • -e — kontots utgångsdatum (YYYY-MM-DD)

Kontrollera alltid om användaren redan finns med id innan Ni anropar useradd. Om det körs för en befintlig användare returneras avslutskod 9 och ett felmeddelande skrivs ut, vilket kan fylla loggarna i onödan.

Obs! useradd kräver root. Lägg en behörighetskontroll överst i skriptet.

#!/usr/bin/env bash
# create_user.sh — idempotent single-user creation
set -euo pipefail

[[ $EUID -ne 0 ]] && { echo "Must run as root"; exit 1; }

USERNAME="$1"
FULL_NAME="${2:-}"
GROUP="${3:-staff}"
SHELL="${4:-/bin/bash}"

if id "$USERNAME" &>/dev/null; then
  echo "[SKIP] User $USERNAME already exists"
else
  useradd \
    --create-home \
    --shell "$SHELL" \
    --comment "$FULL_NAME" \
    --groups "$GROUP" \
    "$USERNAME"
  echo "[OK] Created $USERNAME"
fi

Ange initiala lösenord på ett säkert sätt

Hårdkoda aldrig lösenord i skript. Två säkra metoder för bulkprovisionering är:

  • Generera ett slumpmässigt initialt lösenord med openssl rand eller /dev/urandom, skriv ut det en gång och tvinga användaren att byta det vid den första inloggningen
  • Ange ett förhashat lösenord med usermod -p och en SHA-512-hash, så att klartexten aldrig visas i processlistan

chpasswd är det rekommenderade verktyget för att ange lösenord i skript — det läser par av typen username:password från standardindata, så lösenordet visas aldrig bland kommandoradsargumenten (synliga via ps).

När lösenordet har angetts använder Ni chage -d 0 för att tvinga fram ett omedelbart lösenordsbyte vid nästa inloggning.

#!/usr/bin/env bash
# set_temp_password.sh
set -euo pipefail

[[ $EUID -ne 0 ]] && { echo "Must run as root"; exit 1; }

USERNAME="$1"

# Generate a 16-char random password (alphanumeric only)
TMP_PASS=$(tr -dc 'A-Za-z0-9' < /dev/urandom | head -c 16)

# Set password via chpasswd (password never in argv)
echo "${USERNAME}:${TMP_PASS}" | chpasswd

# Force password change on next login
chage -d 0 "$USERNAME"

echo "[OK] Temporary password for $USERNAME: $TMP_PASS"
echo "[OK] User must change password on first login"

Hantera lösenordsålder med chage

chage (change age) styr principen för lösenordsålder som lagras i /etc/shadow. Säkerhetsprinciper i företagsmiljöer kräver vanligtvis:

  • Maximal lösenordsålder (till exempel 90 dagar)
  • Minsta antal dagar innan ett lösenord får ändras igen
  • Varningsperiod före utgång
  • Låsning av inaktivt konto efter den senaste lösenordsanvändningen

Viktiga flaggor för chage:

  • -M <days> — maximal lösenordsålder
  • -m <days> — minimal lösenordsålder
  • -W <days> — antal varningsdagar före utgång
  • -I <days> — antal inaktiva dagar före kontolåsning
  • -E <date> — absolut utgångsdatum för kontot
  • -l — visa aktuella inställningar för en användare
#!/usr/bin/env bash
# apply_password_policy.sh — enforce org-wide ageing policy
set -euo pipefail

[[ $EUID -ne 0 ]] && { echo "Must run as root"; exit 1; }

# Policy constants
MAX_AGE=90
MIN_AGE=1
WARN_DAYS=14
INACTIVE_DAYS=30

apply_policy() {
  local user="$1"
  chage \
    -M "$MAX_AGE" \
    -m "$MIN_AGE" \
    -W "$WARN_DAYS" \
    -I "$INACTIVE_DAYS" \
    "$user"
  echo "[OK] Policy applied to $user"
}

# Apply to all non-system users (UID >= 1000)
awk -F: '$3 >= 1000 && $3 < 65534 { print $1 }' /etc/passwd | while read -r user; do
  apply_policy "$user"
done

Grupphantering i bulk

Grupper är den primära mekanismen för att styra åtkomst till resurser. Ett provisioneringsskript måste säkerställa att nödvändiga grupper finns innan användare läggs till i dem — useradd -G nonexistent misslyckas annars.

Använd groupadd idempotent genom att kontrollera avslutskoden: den returnerar 9 om gruppen redan finns. Idiomet getent group <name> är ett portabelt och lättläst alternativ till att söka med grep i /etc/group.

gpasswd -a lägger till en användare i en grupp utan att ersätta befintliga medlemskap (till skillnad från usermod -G, som ersätter listan över kompletterande grupper).

#!/usr/bin/env bash
# ensure_groups.sh — create groups if missing, then add users
set -euo pipefail

[[ $EUID -ne 0 ]] && { echo "Must run as root"; exit 1; }

REQUIRED_GROUPS=(developers ops security auditors)

for grp in "${REQUIRED_GROUPS[@]}"; do
  if getent group "$grp" &>/dev/null; then
    echo "[SKIP] Group $grp already exists"
  else
    groupadd "$grp"
    echo "[OK] Created group $grp"
  fi
done

# Safely add a user to a group (append, don't replace)
add_to_group() {
  local user="$1" group="$2"
  gpasswd -a "$user" "$group" 2>/dev/null && echo "[OK] $user -> $group"
}

Fullständigt bulkprovisioneringsskript

Så här sätts allt ihop: ett enda skript läser en CSV-fil, skapar användare och grupper, anger lösenordsprinciper, loggar varje åtgärd och hanterar fel på ett smidigt sätt utan att stoppa hela batchkörningen.

Viktiga designbeslut i skriptet nedan:

  • En LOG_FILE med tidsstämplar fångar alla åtgärder för granskning
  • Fel för enskilda användare loggas men avbryter inte loopen (|| log_error)
  • Skriptet är idempotent — säkert att köra igen efter delvisa fel
  • All utskrift går både till terminalen och loggfilen via tee
#!/usr/bin/env bash
# bulk_provision.sh — production user provisioning
set -uo pipefail

[[ $EUID -ne 0 ]] && { echo "Must run as root"; exit 1; }

USER_FILE="${1:-users.csv}"
LOG_FILE="/var/log/user_provision_$(date +%F).log"

log()  { echo "[$(date '+%F %T')] $*" | tee -a "$LOG_FILE"; }
err()  { log "ERROR: $*"; }

log "=== Provisioning started from $USER_FILE ==="

tail -n +2 "$USER_FILE" | while IFS=',' read -r username fullname group shell; do
  username="${username// /}"
  [[ -z "$username" || "$username" == \#* ]] && continue
  group="${group:-staff}"
  shell="${shell:-/bin/bash}"

  # Ensure group exists
  getent group "$group" &>/dev/null || groupadd "$group"

  # Create user idempotently
  if id "$username" &>/dev/null; then
    log "[SKIP] $username exists"
  else
    useradd -m -s "$shell" -c "$fullname" -G "$group" "$username" || { err "useradd failed for $username"; continue; }
    TMP="$(tr -dc 'A-Za-z0-9' < /dev/urandom | head -c 14)"
    echo "${username}:${TMP}" | chpasswd
    chage -M 90 -m 1 -W 14 -I 30 -d 0 "$username"
    log "[OK] $username created (group=$group) tmp_pass=$TMP"
  fi
done

log "=== Provisioning complete ==="

Hantera sudoers-drop-in-fragment

Det är riskabelt att redigera /etc/sudoers direkt — ett syntaxfel kan låsa ute alla från sudo. Det säkra tillvägagångssättet är att använda drop-in-filer i /etc/sudoers.d/, där varje fil valideras med visudo -c -f innan den placeras på rätt plats.

God praxis för sudoers-fragment:

  • Döp filer efter teamet eller rollen som de ger behörighet till (till exempel 10-developers, 20-ops)
  • Använd gruppbaserade regler (%developers ALL=(ALL) NOPASSWD: /usr/bin/systemctl) i stället för rader per användare
  • Ange alltid behörigheterna till 0440 och ägarskapet till root:root
  • Validera med visudo -c — kommandot returnerar en icke-nollkod vid alla syntaxfel
#!/usr/bin/env bash
# write_sudoers_fragment.sh
set -euo pipefail

[[ $EUID -ne 0 ]] && { echo "Must run as root"; exit 1; }

FRAGMENT_NAME="${1:-10-developers}"
SUDOERS_DIR="/etc/sudoers.d"
TMP_FILE="$(mktemp)"

# Write the fragment to a temp file first
cat > "$TMP_FILE" << 'EOF'
# Developers: restart services and view journals without full root
%developers ALL=(ALL) NOPASSWD: /usr/bin/systemctl restart *, /usr/bin/journalctl
%ops        ALL=(ALL) NOPASSWD: ALL
EOF

# Validate BEFORE installing
if visudo -c -f "$TMP_FILE"; then
  install -m 0440 -o root -g root "$TMP_FILE" "${SUDOERS_DIR}/${FRAGMENT_NAME}"
  echo "[OK] Installed ${SUDOERS_DIR}/${FRAGMENT_NAME}"
else
  echo "[ERROR] sudoers syntax check failed — fragment NOT installed"
  rm -f "$TMP_FILE"
  exit 1
fi
rm -f "$TMP_FILE"

Granska befintliga konton

Efter provisioneringen (och enligt ett regelbundet schema) bör Ni granska användardatabasen för avvikelser:

  • Konton med UID 0 — alla konton med UID 0 utom root är ett kritiskt säkerhetsfynd
  • Konton utan lösenord — poster med ett tomt lösenordsfält eller fältet ! i /etc/shadow
  • Utgångna konton som fortfarande är aktiva — utdata från chage -l kan tolkas i bulk
  • Användare med skal men utan hemkatalog — en felkonfiguration som hindrar inloggning

Det är enkelt att skapa en strukturerad rapport och skicka den till säkerhetsteamet med mail eller genom att lägga till den i en övervakad loggsökväg.

#!/usr/bin/env bash
# audit_users.sh — produce a security-relevant user report
set -uo pipefail

REPORT="/var/log/user_audit_$(date +%F).txt"

echo "=== User Audit Report $(date) ===" > "$REPORT"

echo "" >> "$REPORT"
echo "--- Accounts with UID 0 (should be root only) ---" >> "$REPORT"
awk -F: '$3 == 0 { print $1 }' /etc/passwd >> "$REPORT"

echo "" >> "$REPORT"
echo "--- Accounts with empty password field ---" >> "$REPORT"
awk -F: '($2 == "" || $2 == "!") && $3 >= 1000 { print $1 }' /etc/shadow 2>/dev/null >> "$REPORT" || echo "  (requires root)" >> "$REPORT"

echo "" >> "$REPORT"
echo "--- Normal users (UID 1000-60000) ---" >> "$REPORT"
awk -F: '$3 >= 1000 && $3 < 60000 { printf "%-20s uid=%-6s shell=%s\n", $1, $3, $7 }' /etc/passwd >> "$REPORT"

cat "$REPORT"
echo "Report saved to $REPORT"

Låsa, låsa upp och ta bort konton

Avslut av användarkonton är lika viktigt som onboarding. När en användare slutar är rätt ordningsföljd:

  1. Lås kontot omedelbart (usermod -L) — kommandot lägger till ! framför hashvärdet för shadow-lösenordet och förhindrar inloggning utan att data tas bort
  2. Återkalla sudo-behörighet — ta bort användarens sudoers-fragment om ett sådant finns
  3. Överför ägarskapet för användarens filer till en chef eller ett arkivkonto
  4. Arkivera hemkatalogen som ett tar-arkiv innan den tas bort
  5. Ta bort kontot med userdel -r — kommandot tar bort hemkatalogen och e-postkön

usermod -U låser upp ett konto (tar bort prefixet !) och är användbart vid tillfällig avstängning.

#!/usr/bin/env bash
# offboard_user.sh — lock, archive, then optionally delete
set -euo pipefail

[[ $EUID -ne 0 ]] && { echo "Must run as root"; exit 1; }

USERNAME="$1"
ARCHIVE_DIR="/srv/archived-homes"
mkdir -p "$ARCHIVE_DIR"

# 1. Lock account
usermod -L "$USERNAME"
echo "[OK] Account $USERNAME locked"

# 2. Remove sudoers fragment if present
SUDOERS_FILE="/etc/sudoers.d/${USERNAME}"
[[ -f "$SUDOERS_FILE" ]] && rm -f "$SUDOERS_FILE" && echo "[OK] Removed sudoers fragment"

# 3. Archive home directory
HOME_DIR="$(getent passwd "$USERNAME" | cut -d: -f6)"
if [[ -d "$HOME_DIR" ]]; then
  tar -czf "${ARCHIVE_DIR}/${USERNAME}_$(date +%F).tar.gz" -C "$(dirname "$HOME_DIR")" "$(basename "$HOME_DIR")"
  echo "[OK] Home archived to ${ARCHIVE_DIR}/${USERNAME}_$(date +%F).tar.gz"
fi

echo "[NOTICE] Review archive, then run: userdel -r $USERNAME"

Testa skriptet med ett dry-run-läge

Provisioneringsskript i produktion måste kunna testas utan sidoeffekter. Implementera ett dry-run-läge med en DRY_RUN-flagga som ersätter alla kommandon som ändrar systemet med echo-stubbar.

Mönstret är enkelt: definiera en hjälpfunktion run() som antingen kör eller skriver ut kommandot beroende på flaggan. Det innebär att:

  • Varje kodväg körs under testningen
  • Utdata visar exakt vad som skulle hända vid en riktig körning
  • CI-pipelines kan validera logiken utan root-åtkomst

Komplettera dry-run med ett särskilt prefix för testanvändare (till exempel test_), så blir det enkelt att städa efter integrationstester.

#!/usr/bin/env bash
# provision_with_dryrun.sh
set -euo pipefail

DRY_RUN="${DRY_RUN:-false}"

# Wrapper: execute or echo
run() {
  if [[ "$DRY_RUN" == "true" ]]; then
    echo "[DRY-RUN] $*"
  else
    "$@"
  fi
}

create_user() {
  local user="$1" group="$2"
  if id "$user" &>/dev/null; then
    echo "[SKIP] $user exists"
    return
  fi
  run useradd -m -s /bin/bash -G "$group" "$user"
  run chage -M 90 -m 1 -W 14 -d 0 "$user"
  echo "[OK] $user provisioned (dry=$DRY_RUN)"
}

# Test run
DRY_RUN=true create_user testuser developers
echo "---"
create_user realuser developers 2>/dev/null || true

Vilket kommando ska Ni använda för att lägga till en användare i en kompletterande grupp utan att ta bort befintliga gruppmedlemskap?

I ett bulkprovisioneringsskript behöver Ni tilldela en befintlig användare gruppen auditors. Användaren är redan medlem i developers och staff. Vilket kommando bevarar alla befintliga medlemskap samtidigt som det nya läggs till?

Lektionsrepetition: automatisera provisionering av användare och grupper

I den här lektionen har Ni byggt en komplett verktygslåda för användarprovisionering av produktionskvalitet. Här är de viktigaste slutsatserna:

  • Tolka indata defensivt — använd IFS/read-loopar, hoppa över tomma rader och kommentarsrader och validera fält innan något systemanrop görs
  • Grunderna i useradd — använd alltid -m (hem), -s (skal), -c (kommentar) och -G (grupper); kontrollera förekomsten med id först för idempotens
  • Lösenord — ange dem via standardindata till chpasswd för att hålla klartexten borta från processargumenten; tvinga fram byte vid första inloggningen med chage -d 0
  • chage för principer — standardisera maximal ålder (-M), varningsdagar (-W) och inaktivitetslåsning (-I) för alla konton som inte är systemkonton
  • Gruppmedlemskap — använd gpasswd -a eller usermod -aG (med flaggan -a) för att lägga till medlemskap i stället för att ersätta dem
  • Sudoers-drop-ins — skriv till /etc/sudoers.d/, validera med visudo -c -f före installation och ange behörigheterna 0440 root:root
  • Avslut av konton — lås (usermod -L), arkivera hemkatalogen och ta sedan bort kontot; hoppa aldrig över arkiveringssteget
  • Dry-run-läge — omslut kommandon som ändrar systemet med en hjälpfunktion run(), så att pipelines kan verifiera logiken utan sidoeffekter som kräver root

Genom att kombinera dessa mönster får Ni ett repeterbart, granskningsbart och säkert automatiseringslager för Linux-identitetshantering i alla skalor.

Gratis att börja

Lär dig DevOps-bootcamp med en AI-lärare – gratis

Skriv och kör riktig kod i webbläsaren, få omedelbar hjälp av en AI-lärare dygnet runt och fortsätt där du slutade – på webben eller i appen.

Kurser
142
Lektioner
568

Vanliga frågor

Är lektionen ”Automatisera skapande av användare och grupper” gratis?

Ja – du kan läsa vilka 3 lektioner som helst i lärvägen DevOps-bootcamp, inklusive ”Automatisera skapande av användare och grupper”, kostnadsfritt i sin helhet här på webben. Därefter låser CoddyKit PRO upp alla lektioner, plus interaktiv övning med en inbyggd kodredigerare och en AI-lärare dygnet runt. Kursen i DevOps-bootcamp innehåller totalt 4 lektioner.

Vad lär jag mig i ”Automatisera skapande av användare och grupper”?

Skapa, ändra och granska konton i bulk med useradd, chage och hantering av sudoers-fragment. Ni övar på DevOps-bootcamp med praktisk kod som körs direkt i webbläsaren, medan en AI-handledare som är tillgänglig dygnet runt svarar på Era frågor under lektionen.

Behöver jag någon erfarenhet för att börja lära mig DevOps-bootcamp?

Du behöver inga förkunskaper. Utbildningen i DevOps-bootcamp på CoddyKit är upplagd för allt från nybörjare till avancerade elever, så att du kan börja här eller från början och gå fram i din egen takt. Detta är lektion 1 av 4.

Hur lång tid tar lektionen ”Automatisera skapande av användare och grupper”?

De flesta CoddyKit-lektioner tar cirka 5–10 minuter. Varje lektion är kort och interaktiv, så att du gör stadiga framsteg och kan fortsätta precis där du slutade – på webben eller i appen.

Kan jag skriva och köra kod i den här DevOps-bootcamp-lektionen?

Ja. Varje DevOps-bootcamp-lektion innehåller en inbyggd kodredigerare, så att du kan skriva och köra riktig kod direkt i webbläsaren och få omedelbar AI-feedback – utan lokal installation.

Alla lektioner i den här kursen

  1. Automatisera skapande av användare och grupper
  2. Styra systemd-tjänster och skriva unit-filer
  3. Automatisera diskar, filsystem och monteringar
  4. Skapa skript för systemhälsa och aviseringar
← Tillbaka till DevOps-bootcamp