3
2
Fork
You've already forked lab-toolbox
1
Toolbox to easily create VMs with libvirt / virt-manager
  • Python 100%
2026年06月05日 09:34:11 +02:00
.gitignore chore: ignore env files 2026年03月03日 20:13:02 +01:00
create_vm.py chore: fix linting 2026年06月05日 09:11:21 +02:00
LICENSE docs: License - MIT for simplicity - just don't sue me 2026年04月28日 11:07:06 +02:00
README.md docs: License - MIT for simplicity - just don't sue me 2026年04月28日 11:07:06 +02:00

RHEL VM Creation Script

Automated RHEL VM creation tool using KVM/QEMU with cloud-init configuration. This script streamlines the process of creating virtual machines from RHEL template images with automatic user configuration, SSH key setup, and customizable resources.

Features

  • Automatic image discovery - Finds the latest RHEL image from _RAW/<Date>/ directories
  • Cloud-init integration - Automated user creation, SSH key setup, and system configuration
  • User convenience - Uses your current username and SSH public key automatically
  • Secure password handling - Interactive password prompt (no hardcoded defaults); supports SSH key-only mode
  • Resource customization - Configure memory, vCPUs, and network settings
  • Multiple RHEL versions - Supports RHEL 8, 9, and 10
  • Dry-run mode - Preview what will be created without making changes
  • Optional package installation - Control whether packages are installed during boot

Requirements

Hardware

  • A CPU with hardware virtualisation support (Intel VT-x or AMD-V)

Verify it is enabled:

grep -Ec '(vmx|svm)' /proc/cpuinfo
# Output > 0 means virtualisation is available

Host OS

The script is designed for Fedora / RHEL / CentOS Stream hosts. The commands below use dnf.

Software dependencies

Tool Package Required Purpose
python3 python3 Yes Runs the script (uses stdlib only, no pip dependencies)
openssl openssl Yes Hashes the VM user password with SHA-512 crypt
virt-install virt-install Yes Creates and registers the VM with libvirt
virsh libvirt-client Yes VM management (start, stop, console, IP lookup)
qemu-kvm qemu-kvm Yes Kernel-based virtualisation backend
libvirtd libvirt Yes Virtualisation daemon
qemu-img qemu-img Optional Resizes the disk image when --disk-size is used
virt-manager virt-manager Optional GUI for managing VMs

Installation (Fedora / RHEL)

# Core requirements
sudo dnf install -y python3 openssl qemu-kvm libvirt libvirt-client virt-install
# Optional: disk resize support
sudo dnf install -y qemu-img
# Optional: graphical VM manager
sudo dnf install -y virt-manager
# Enable and start the virtualisation daemon
sudo systemctl enable --now libvirtd

Verify the setup:

# Check libvirtd is running
sudo systemctl status libvirtd
# Check virt-install is available
virt-install --version
# Check openssl supports SHA-512 crypt (should print a 6ドル$ hash)
echo test | openssl passwd -6 -stdin

RHEL template images

Place downloaded RHEL KVM guest images in _RAW/<YYYY-MM-DD>/:

_RAW/
└── 2026年01月02日/
 ├── rhel-8.10-x86_64-kvm.qcow2
 ├── rhel-9.7-x86_64-kvm.qcow2
 └── rhel-10.1-x86_64-kvm.qcow2

Images can be downloaded from Red Hat Customer Portal (requires a Red Hat account or active subscription).

SSH key

An SSH public key in ~/.ssh/ is strongly recommended (ed25519, RSA, or ECDSA). Without one, a console password is required to access the VM. Generate a key if needed:

ssh-keygen -t ed25519 -C "your_email@example.com"

Quick Start

Create a basic RHEL 10 VM:

./create_vm.py --rhel 10 --hostname rhel10-lab01

This will:

  1. Find the latest RHEL 10 image
  2. Create a rhel10-lab01/ directory
  3. Prompt you to choose SSH key-only login or set a console password
  4. Generate cloud-init configuration files
  5. Copy the disk image
  6. Create and start the VM

Command-Line Options

Required Arguments

Option Short Description
--rhel -r RHEL version: 8, 9, or 10
--hostname -n Hostname for the VM (can be FQDN or short name)

Image Selection (Optional)

Option Default Description
--minor latest Minor version to use (e.g., --minor 0 for 10.0)
--image auto Path to specific qcow2 image file (overrides --rhel and --minor)

Optional Arguments

Option Short Default Description
--memory -m 4096 Memory in MB
--vcpus -c 2 Number of vCPUs
--disk-size -d template size Disk size in GB
--network default Network to connect to
--timezone system timezone Timezone for the VM
--ca-url none URL to download custom CA certificate
--ssh-key auto-detect Path to SSH public key file to add to the VM
--install-packages false Install packages during cloud-init (requires repo access)
--dry-run false Show what would be done without creating the VM

Usage Examples

Basic VM Creation

./create_vm.py --rhel 10 --hostname rhel10-lab01

VM with FQDN

./create_vm.py --rhel 9 --hostname rhel9-web01.example.com

Custom Resources

./create_vm.py --rhel 10 --hostname rhel10-db01 --memory 8192 --vcpus 4

Specific Minor Version

# Use RHEL 10.0 instead of the latest 10.x
./create_vm.py --rhel 10 --minor 0 --hostname rhel10-lab01
# Use RHEL 9.4 specifically
./create_vm.py --rhel 9 --minor 4 --hostname rhel9-web01

Specific Image File

# Use a specific qcow2 image directly
./create_vm.py --rhel 10 --image /path/to/custom-rhel10.qcow2 --hostname rhel10-custom

Custom Disk Size

# 50 GB disk
./create_vm.py --rhel 10 --hostname rhel10-lab01 --disk-size 50
# 100 GB disk with more resources
./create_vm.py --rhel 10 --hostname rhel10-db01 --memory 8192 --vcpus 4 --disk-size 100

Note: If --disk-size is not specified, the VM uses the original template image size.

Custom Network

./create_vm.py --rhel 8 --hostname rhel8-test --network bridge=br0

Custom Timezone

./create_vm.py --rhel 10 --hostname rhel10-lab01 --timezone America/New_York
./create_vm.py --rhel 9 --hostname rhel9-web01 --timezone Asia/Tokyo
./create_vm.py --rhel 10 --hostname rhel10-db01 --timezone UTC

Custom CA Certificate

# Install a custom root CA certificate on first boot
./create_vm.py --rhel 10 --hostname rhel10-lab01 --ca-url https://pki.example.com/root-ca.crt

The certificate will be downloaded to /etc/pki/ca-trust/source/anchors/ and update-ca-trust will be run automatically during cloud-init.

Custom SSH Key

# Use a specific SSH key instead of the auto-detected one
./create_vm.py --rhel 10 --hostname rhel10-lab01 --ssh-key ~/.ssh/id_ed25519.pub
# Useful when your default key differs from what you want on the VM
./create_vm.py --rhel 9 --hostname rhel9-web01 --ssh-key /path/to/team-key.pub

Note: When --ssh-key is specified, auto-detection of keys from ~/.ssh/ is skipped entirely.

With Package Installation

./create_vm.py --rhel 10 --hostname rhel10-lab01 --install-packages

Note: Package installation requires repository access. Without --install-packages, packages can be installed manually after boot to avoid cloud-init failures.

Dry Run Example

Test without creating the VM:

./create_vm.py --rhel 10 --hostname rhel10-lab01 --memory 8192 --vcpus 4 --dry-run

Output:

============================================================
RHEL VM Creation Script
============================================================
RHEL Version: 10
VM Name: rhel10-lab01
Hostname: rhel10-lab01
Memory: 8192 MB
vCPUs: 4
Network: default
============================================================
Step 1: Locating source disk image...
Found: _RAW/2026-01-02/rhel-10.1-x86_64-kvm.qcow2
Step 2: Creating VM directory...
Created: /home/user/VirtualMachines/rhel10-lab01
Step 3: Creating cloud-init configuration files...
Created: rhel10-lab01/user-data.yaml
Created: rhel10-lab01/meta-data.yaml
Step 4: Copying disk image...
Copying disk image...
 From: _RAW/2026-01-02/rhel-10.1-x86_64-kvm.qcow2
 To: rhel10-lab01/rhel-10.1-x86_64-kvm.qcow2
Disk image copied successfully
DRY RUN MODE - VM creation skipped
To create the VM manually, run:
sudo virt-install --name rhel10-lab01 --memory 8192 --vcpus 4 \
 --disk path=rhel10-lab01/rhel-10.1-x86_64-kvm.qcow2 --import --os-variant rhel10.0 \
 --network network=default --graphics spice \
 --cloud-init user-data=rhel10-lab01/user-data.yaml,meta-data=rhel10-lab01/meta-data.yaml

What Gets Created

For a VM named rhel10-lab01, the script creates:

rhel10-lab01/
├── rhel-10.1-x86_64-kvm.qcow2 # Disk image (copy from _RAW)
├── user-data.yaml # Cloud-init user configuration
└── meta-data.yaml # Cloud-init metadata

Authentication modes

When the script runs, it prompts you to choose how the VM user can log in:

Option 1 — SSH key only (recommended)

An SSH key is configured. Choose login method:
 1) SSH key only (recommended — no console password set)
 2) SSH key + console password
Choice [1/2, default 1]:
  • Password login is disabled; the account is locked
  • ssh_pwauth is set to False in cloud-init
  • Console (virsh console) requires an alternative method to recover access (e.g. re-running the script or editing the image offline)

Option 2 — SSH key + console password

  • You are prompted to enter and confirm a password (minimum 8 characters, no newlines)
  • The password is hashed with SHA-512 crypt (openssl passwd -6) before being written to user-data.yaml — the plaintext password is never stored anywhere
  • Both SSH key and password authentication are enabled

If no SSH key is found, a password is always required.

user-data.yaml Configuration

The generated cloud-init configuration includes:

  • User account: Your current username (from $USER)
  • Password: Entered interactively and stored as a SHA-512 hash; or account locked (SSH key-only mode)
  • SSH key: Key specified via --ssh-key, or auto-detected from ~/.ssh/
  • Sudo access: Passwordless sudo enabled
  • Timezone: System timezone (customizable with --timezone)
  • SSH access: Key-based always; password authentication only if a password was set
  • Root account: Disabled for security
  • Packages: Optional (disabled by default to avoid cloud-init failures)

Accessing Your VM

SSH Access (Primary Method)

# Get VM IP address
sudo virsh domifaddr rhel10-lab01
# Connect via SSH (uses your SSH key automatically)
ssh user@<vm-ip>

Console Access (Fallback)

# Connect to VM console
sudo virsh console rhel10-lab01

Login with the username printed at the end of the script and the password you entered during setup. If you chose SSH key-only mode, console password login is disabled — use SSH instead.

To exit the console, press Ctrl+]

VM Management

List VMs

# List all VMs
sudo virsh list --all
# List running VMs only
sudo virsh list

Control VMs

# Start a VM
sudo virsh start rhel10-lab01
# Stop a VM gracefully
sudo virsh shutdown rhel10-lab01
# Force stop a VM
sudo virsh destroy rhel10-lab01
# Delete a VM (keeps disk files)
sudo virsh undefine rhel10-lab01
# Delete a VM and its disk
sudo virsh undefine rhel10-lab01 --remove-all-storage

VM Information

# Get VM info
sudo virsh dominfo rhel10-lab01
# Get VM IP address
sudo virsh domifaddr rhel10-lab01
# View VM console output
sudo virsh console rhel10-lab01

Directory Structure

VirtualMachines/
├── _RAW/ # Template images (gitignored)
│ └── 2026年01月02日/
│ ├── rhel-8.10-x86_64-kvm.qcow2
│ ├── rhel-9.7-x86_64-kvm.qcow2
│ └── rhel-10.1-x86_64-kvm.qcow2
├── rhel10-lab01/ # VM directory (gitignored)
│ ├── rhel-10.1-x86_64-kvm.qcow2
│ ├── user-data.yaml
│ └── meta-data.yaml
├── create_vm.py # Main script
├── .gitignore # Excludes sensitive files
└── README.md # This file

Troubleshooting

Cloud-init Failures

Problem: cloud-final.service fails during boot

Cause: Package installation fails due to missing repository access

Solution: Don't use --install-packages flag. Install packages manually after boot:

ssh user@<vm-ip>
sudo dnf install vim wget curl git

SSH Key Not Found

Problem: Warning about missing SSH public key

Solution: Either generate an SSH key pair, or specify a key path explicitly with --ssh-key:

# Generate ed25519 key (recommended)
ssh-keygen -t ed25519 -C "your_email@example.com"
# Or generate RSA key
ssh-keygen -t rsa -b 4096 -C "your_email@example.com"
# Or point to an existing key directly
./create_vm.py --rhel 10 --hostname rhel10-lab01 --ssh-key /path/to/key.pub

VM Already Exists

Problem: Directory already exists

Solution: The script will prompt you to overwrite. You can also manually remove the directory:

rm -rf rhel10-lab01/

Can't Find VM After Creation

Problem: VM not showing in virsh list

Solution: Check if it's stopped:

sudo virsh list --all
sudo virsh start rhel10-lab01

Network Issues

Problem: Can't get VM IP address

Solution: Check network and restart VM:

# Check network
sudo virsh net-list --all
sudo virsh net-start default
# Restart VM
sudo virsh destroy rhel10-lab01
sudo virsh start rhel10-lab01

Security Considerations

  • Passwords: No hardcoded defaults — you set the password interactively, or choose SSH key-only mode. Passwords are hashed with SHA-512 crypt before being written to cloud-init files; the plaintext is never stored
  • SSH Keys: Only your SSH key is added to the VM
  • Root Access: Root login is disabled by default
  • Firewall: Configure firewall rules as needed for your use case
  • Updates: Remember to apply security updates regularly

Version Control

The .gitignore file is configured to exclude:

  • Cloud-init files (contain passwords and SSH keys)
  • Disk images (large binary files)
  • VM directories
  • RAW image directories

Only the script and documentation are version controlled.

Manual Package Installation

If you didn't use --install-packages, install common packages after boot:

# Connect to VM
ssh user@<vm-ip>
# Install common utilities
sudo dnf install -y vim wget curl git
# Install development tools
sudo dnf groupinstall -y "Development Tools"
# Install specific packages as needed
sudo dnf install -y htop tmux tree

Advanced Usage

Using Different Networks

# Bridge network
./create_vm.py --rhel 10 --hostname rhel10-lab01 --network bridge=br0
# Custom network
./create_vm.py --rhel 10 --hostname rhel10-lab01 --network network=custom-net

Large VMs for Database Workloads

./create_vm.py --rhel 10 --hostname rhel10-db01 \
 --memory 16384 \
 --vcpus 8 \
 --disk-size 200

Development VMs

./create_vm.py --rhel 9 --hostname rhel9-dev01 \
 --memory 8192 \
 --vcpus 4

Help

View all available options:

./create_vm.py --help

License

This project is licensed under the MIT License.

Lab icons created by Nhor Phai - Flaticon

Contributing

To add features or fix issues:

  1. Test changes with --dry-run first
  2. Update this README if adding new options
  3. Ensure .gitignore excludes sensitive files