Skip to content

Shell Scripts ​

Bash/shell scripts for automation, backup operations, and system administration tasks.

Prerequisites ​

  • Bash 4.0+ or Zsh
  • curl for downloading scripts
  • kubectl (for Kubernetes scripts)
  • jq (optional, for JSON processing)

Quick Start ​

sh
# Run directly (one-time use)
curl -s https://raw.githubusercontent.com/this-is-tobi/tools/main/shell/<script_name> | bash -s -- --help

# Download and run (repeated use)
curl -fsSL https://raw.githubusercontent.com/this-is-tobi/tools/main/shell/<script_name> -o script.sh
chmod +x script.sh
./script.sh --help

Available Scripts ​

ScriptDescription
backup-kube-mariadb.shbackup / restore mariadb database from / to a kubernetes pod.
backup-kube-pg.shbackup / restore postgres database from / to a kubernetes pod.
backup-kube-qdrant.shbackup / restore qdrant collection snapshots from / to a kubernetes pod.
backup-kube-vault.shbackup / restore vault raft cluster from / to a kubernetes pod.
clone-subdir.shclone a subdirectory from a git repository.
compose-to-matrix.shparse docker-compose file to create github matrix.
delete-ghcr-image.shdelete image and subsequent manifests from ghcr.
eol-infos.shget package end of life infos.
export-argocd-resources.shexport ready-to-apply argocd resources.
export-kube-resources.shexport ready-to-apply kubernetes resources.
github-create-app.shcreate a github application.
github-create-ruleset.shcreate a github rulesets for a given repository.
github-configure-repo.shconfigure github repository security best practices.
helm-template.shgenerate helm template.
init-env-files.shinit '.env' and '.yaml' example files by copying them without 'example'.
keycloak-add-clients.shadd keycloak clients for a given keycloak realm.
keycloak-add-users.shadd keycloak users for a given keycloak realm.
keycloak-check-tac.shcheck how many users have accepted terms and conditions in a keycloak realm.
keycloak-extract-users.shextract all users from a keycloak realm to a CSV file.
keycloak-get-token.shdisplay keycloak token for the given infos.
keycloak-list-users.shlist keycloak users for a given keycloak realm.
keycloak-required-tac.shadd terms and conditions required action to all realm users.
kube-generate-token.shgenerate a kubernetes token / kubeconfig with a given service account / RBAC.
manage-etc-hosts.shadd or update host ip adress in /etc/hosts.
monitor-kube-cnpg.shgenerate and print cnpg monitoring report.
monitor-kube-qdrant.shgenerate and print qdrant monitoring report.
monitor-kube-redis.shgenerate and print redis monitoring report.
monitor-kube-vault.shgenerate and print vault monitoring report.
purge-ghcr-tags.shpurge ghcr tags older than a given date.
setup-ai-agent.shbootstrap Claude Code / Copilot instructions, skills, agents, and safely run rtk init -g --copilot.
trivy-report.shparse trivy json reports to create a markdown summary.
update-zsh-completions.shupdate zsh-completions sources.

TIP

Using a script directly from a curl command:

sh
curl -s https://raw.githubusercontent.com/this-is-tobi/tools/main/shell/<script_name> | bash -s -- -h

Usage Examples ​

Backup Operations ​

The backup scripts never put a password or token on a command line (where ps and the Kubernetes API audit log would show it): it reaches the pod on stdin, or the local client through its usual environment variable. Pass it through that variable rather than the -p / -a flag, which stays visible in the local process list. The *_forward modes forward a random free local port, so a database running on your machine is never touched, and every failure (dump, copy, port forward) makes the script exit non-zero instead of reporting success.

ScriptSecret variableFlag
backup-kube-pg.shPGPASSWORD-p
backup-kube-mariadb.shMYSQL_PWD-p
backup-kube-vault.shVAULT_TOKEN-p
backup-kube-qdrant.shQDRANT_API_KEY-a

PostgreSQL:

sh
read -rs PGPASSWORD && export PGPASSWORD   # prompts without echo, stays out of the shell history
./backup-kube-pg.sh -m dump -n my-namespace -t my-postgres-pod -d mydatabase -u myuser -o ./backups
./backup-kube-pg.sh -m restore_forward -n my-namespace -t svc/my-postgres -d mydatabase -u myuser -f ./backups/20240101T1200-mydatabase.dump -z

MariaDB:

sh
read -rs MYSQL_PWD && export MYSQL_PWD
./backup-kube-mariadb.sh -m dump_forward -n my-namespace -t my-mariadb-pod -d mydatabase -o ./backups

Vault:

sh
read -rs VAULT_TOKEN && export VAULT_TOKEN
./backup-kube-vault.sh -m dump -n vault -t vault-0 -o ./backups
./backup-kube-vault.sh -m restore_forward -n vault -t vault-0 -f ./backups/20240101T1200-vault.snap

Qdrant (snapshots go through the HTTP API over a port forward, the former dump / restore modes are gone):

sh
read -rs QDRANT_API_KEY && export QDRANT_API_KEY
./backup-kube-qdrant.sh -m dump_forward -n my-namespace -t qdrant-0 -o ./backups
./backup-kube-qdrant.sh -m restore_forward -n my-namespace -t qdrant-0 -f ./backups/20240101T1200-qdrant

Monitoring ​

sh
# PostgreSQL (CloudNativePG) cluster report
./monitor-kube-cnpg.sh -n database-namespace -c my-cluster

# Vault health check
./monitor-kube-vault.sh -n vault -v vault

# Redis cluster status
./monitor-kube-redis.sh -n redis-namespace -r my-redis

# Qdrant cluster status
./monitor-kube-qdrant.sh -n qdrant-namespace -q my-qdrant

Resource Management ​

sh
# Export Kubernetes resources (files are created readable by you only: they can hold secrets)
./export-kube-resources.sh -k all -n my-namespace -o ./exported

# Export ArgoCD applications
./export-argocd-resources.sh -k all -n argocd -o ./argocd-backup

# Generate a service account token (24h by default, read-only role without access to secrets)
./kube-generate-token.sh -n default -s my-service-account

# Write a kubeconfig file (mode 600); add -S to also grant read access to secrets
./kube-generate-token.sh -n default -s my-service-account -d 8h -f kubeconfig -o ~/.kube/config.d/my-service-account

kube-generate-token.sh prints only the token or kubeconfig on stdout, progress goes to stderr.

Helm Chart Scaffolding ​

sh
# Generate a chart with a base service and two more; pin the template with HELM_CHARTS_REF=<branch|tag|sha>
./helm-template.sh -c my-chart -s api -a worker -a client -o ./my-chart

GitHub Repository Settings ​

The GitHub scripts read the token from GH_TOKEN, then GITHUB_TOKEN, then gh auth token. The -t flag still works but exposes the token in the process list and the shell history.

sh
# Merge strategy, Dependabot alerts, secret scanning, Actions policy (all actions allowed,
# pinned to a full commit SHA), read-only workflow token, tag protection for every tag
./github-configure-repo.sh -o owner -r repo -T '*'

# Only allow GitHub-owned actions and some patterns instead
./github-configure-repo.sh -o owner -r repo -a selected -L 'docker/*,owner/github-workflows/*'

# Classic branch protection on 'main'
./github-create-ruleset.sh -o owner -r repo -b main

# Repository ruleset on the default branch: required checks, pull requests without
# mandatory approval, repository admins may bypass
./github-create-ruleset.sh -o owner -r repo -m ruleset -c 'ci / lint,ci / test' -A 0 -a false

# Register a GitHub App (manifest flow: the browser opens, paste back the address GitHub redirects to)
./github-create-app.sh -n my-app -o owner -p '{"contents":"read"}' -w https://example.com/webhook -e push

github-configure-repo.sh leaves Dependabot security pull requests off by default (-x true turns them on when Renovate does not handle vulnerabilities). With -H true (the default), every action and reusable workflow must be referenced by a full commit SHA; it is not documented whether GitHub also enforces this for actions called from inside a composite action, so turn it off with -H false if a run fails on such an action. Both scripts update existing rulesets in place, so they can be run again.

GitHub Container Registry ​

sh
# Delete the version carrying exactly this tag and the untagged manifests it references
# (-n: dry run; -f: also when the version carries other tags, such as 'latest')
./delete-ghcr-image.sh -o owner -i image-name -t tag -n

# Purge versions older than a date (dry run first). Release tags ('latest', '1.2.3',
# 'api-v1.2.3', ...), what they reference and their signatures are kept unless -a is given
./purge-ghcr-tags.sh -u owner -i image-name -d 2024-01-01 -n

Both scripts need docker buildx to list the manifests an image references (docker login ghcr.io for private images) and follow the API pagination.

Keycloak Management ​

The admin password is read from KC_PASSWORD (and the client secret of keycloak-get-token.sh from KC_CLIENT_SECRET); -p and -s still work but expose the value in the process list and the shell history. Passwords and tokens are handed to curl through stdin or a file descriptor, never as arguments.

sh
read -rsp 'Password: ' KC_PASSWORD && export KC_PASSWORD

# Get access token
read -rsp 'Client secret: ' KC_CLIENT_SECRET && export KC_CLIENT_SECRET
./keycloak-get-token.sh -k https://keycloak.example.com -r my-realm -i client-id -u admin

# List users
./keycloak-list-users.sh -k https://keycloak.example.com -r my-realm -u admin

# Extract users to CSV (file created with mode 600, formula-like cells neutralized)
./keycloak-extract-users.sh -k https://keycloak.example.com -r my-realm -u admin -b 100 -o users.csv

# Add users or clients: inline JSON, '@file' or '-' (stdin) when they carry credentials
./keycloak-add-users.sh -k https://keycloak.example.com -r my-realm -u admin -d @users.json
./keycloak-add-clients.sh -k https://keycloak.example.com -r my-realm -u admin -c @clients.json

# Check terms acceptance
./keycloak-check-tac.sh -k https://keycloak.example.com -r my-realm -u admin

Development Tools ​

sh
# Clone a subdirectory (or a single file): './output/dir' is created, nothing else is written
./clone-subdir.sh -u https://github.com/owner/repo -b main -s path/to/dir -o ./output

# Generate GitHub Actions matrix
./compose-to-matrix.sh -f docker-compose.yml -n owner/repo -t 1.0.0 -a

# Map a host name in /etc/hosts (uses sudo only when needed; -f targets another file)
./manage-etc-hosts.sh -i 192.168.1.100 -n example.local

Troubleshooting ​

Permission Errors ​

sh
# Make script executable
chmod +x script.sh

# Some scripts need sudo
sudo ./script.sh

Kubernetes Connection ​

sh
# Verify kubectl config
kubectl config current-context
kubectl cluster-info

# Check permissions
kubectl auth can-i get pods --all-namespaces

GitHub Token Issues ​

sh
# Reuse the gh CLI session (nothing to export)
gh auth login

# Or set the token in the environment without leaving it in the shell history
read -rs GH_TOKEN && export GH_TOKEN

# Required access: repository admin (configure-repo, create-ruleset),
# read:packages + delete:packages (ghcr scripts)

Best Practices ​

  • Always test with --help first
  • Log output for audit trails: ./script.sh 2>&1 | tee log.txt
  • Use absolute paths in cron jobs
  • Never hardcode credentials
  • Store tokens securely