update the README to reflect recent changes
add script to automatically add SSH key pair to main.tf files, for deployments that do require it
This commit is contained in:
332
README.md
332
README.md
@@ -11,16 +11,16 @@ The folder *multiple* contains two subfolders, one with shared modules and the o
|
|||||||
The idea is to reuse modules across multiple virtual machines and operating systems.
|
The idea is to reuse modules across multiple virtual machines and operating systems.
|
||||||
|
|
||||||
```
|
```
|
||||||
./multiple:
|
.:
|
||||||
environments shared_modules
|
environments shared_modules
|
||||||
|
|
||||||
./multiple/environments:
|
./environments:
|
||||||
cloud_init.yaml ubuntu-cloud-server-2404-bios
|
cloud_init.yaml ubuntu-cloud-server-2404-bios
|
||||||
|
|
||||||
./multiple/environments/ubuntu-cloud-server-2404-bios:
|
./environments/ubuntu-cloud-server-2404-bios:
|
||||||
ubuntu-cloud-server-2404-bios.tf
|
ubuntu-cloud-server-2404-bios.tf
|
||||||
|
|
||||||
./multiple/shared_modules:
|
./shared_modules:
|
||||||
cloud-init.tf domain.tf network.tf outputs.tf pool.tf provider.tf variables.tf volume.tf
|
cloud-init.tf domain.tf network.tf outputs.tf pool.tf provider.tf variables.tf volume.tf
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -29,320 +29,92 @@ cloud-init.tf domain.tf network.tf outputs.tf pool.tf provider.tf variable
|
|||||||
- [QEMU](https://www.qemu.org/)
|
- [QEMU](https://www.qemu.org/)
|
||||||
- [libvirt](https://libvirt.org/)
|
- [libvirt](https://libvirt.org/)
|
||||||
- [Terraform provider for Libvirt](https://github.com/dmacvicar/terraform-provider-libvirt)
|
- [Terraform provider for Libvirt](https://github.com/dmacvicar/terraform-provider-libvirt)
|
||||||
|
- An SSH key pair to connect to machines that are deployed using cloud-init
|
||||||
|
|
||||||
## Assumptions
|
## Assumptions
|
||||||
|
|
||||||
Your Linux x86_64-based machine has at least 4 GB of available memory and 2 CPUs.
|
- Your Linux x86_64-based machine has at least 4 GB of available memory and 2 CPUs
|
||||||
|
|
||||||
## How to use it
|
## How to use it
|
||||||
|
|
||||||
- Clone this repository
|
- Clone this repository
|
||||||
- Go to folder *example*
|
- Run the following to generate a public key pair
|
||||||
- Execute the following commands, which will download and install the required Terraform provider if not already present
|
|
||||||
|
|
||||||
```
|
```
|
||||||
$ terraform init
|
$ ssh-keygen -t rsa -b 4096 -f ~/.ssh/terraform_key -C "terraform-deployment"
|
||||||
|
|
||||||
Initializing the backend...
|
|
||||||
Initializing provider plugins...
|
|
||||||
- Reusing previous version of dmacvicar/libvirt from the dependency lock file
|
|
||||||
- Using previously-installed dmacvicar/libvirt v0.8.3
|
|
||||||
|
|
||||||
Terraform has been successfully initialized!
|
|
||||||
|
|
||||||
[...]
|
|
||||||
```
|
```
|
||||||
|
|
||||||
- The following command will plan the deployment, describing actions that will be taken when applied
|
- Make the script executable
|
||||||
|
```
|
||||||
|
$ chmod +x update_ssh_keys.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
- Run the script (it will use terraform_key by default), and will update all `main.tf` file so that they use the previously generated key:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ ./update_ssh_keys.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
> Alternatively, you can use your own public key and update it manually in the `main.tf` deployment file
|
||||||
|
|
||||||
|
- Navigate to one of the available deployment
|
||||||
|
|
||||||
|
```
|
||||||
|
$ cd environments/ubuntu-cloud-server-2404-bios/
|
||||||
|
```
|
||||||
|
|
||||||
|
- Initialize your terraform environment
|
||||||
|
|
||||||
|
```
|
||||||
|
$ terraform init
|
||||||
|
```
|
||||||
|
- Plan the deployment
|
||||||
|
|
||||||
```
|
```
|
||||||
$ terraform plan
|
$ terraform plan
|
||||||
|
|
||||||
[...]
|
|
||||||
|
|
||||||
Terraform will perform the following actions
|
|
||||||
|
|
||||||
# A cloud-init ISO disk is created, which provides pre-configured settings and scripts that are applied to a cloud-native disk image during its initial boot. Without it, no user would be created and it would not be possible to log into the virtual machine
|
|
||||||
|
|
||||||
+ resource "libvirt_cloudinit_disk" "commoninit" {
|
|
||||||
+ name = "commoninit.iso"
|
|
||||||
+ pool = "ubuntu-bios"
|
|
||||||
|
|
||||||
[...]
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
# The libvirt domain or virtual machine will be created
|
|
||||||
|
|
||||||
+ resource "libvirt_domain" "domain" {
|
|
||||||
+ cloudinit = (known after apply)
|
|
||||||
|
|
||||||
[...]
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
# Here, a libvirt pool to store the virtual machine disk image will be created. It should be possible to use the default one
|
|
||||||
|
|
||||||
+ resource "libvirt_pool" "ubuntu-bios" {
|
|
||||||
|
|
||||||
[...]
|
|
||||||
|
|
||||||
+ name = "ubuntu-bios"
|
|
||||||
+ type = "dir"
|
|
||||||
|
|
||||||
+ target {
|
|
||||||
+ path = "/tmp/ubuntu-bios"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# A qcow2 disk volume will be created and stored in the previously created pool, based on a Ubuntu noble (24.04) hosted cloud image
|
|
||||||
|
|
||||||
+ resource "libvirt_volume" "ubuntu-qcow2" {
|
|
||||||
+ format = "qcow2"
|
|
||||||
|
|
||||||
[...]
|
|
||||||
|
|
||||||
# The plan summaries the action to be taken, which in this case is about creating resources
|
|
||||||
|
|
||||||
Plan: 4 to add, 0 to change, 0 to destroy.
|
|
||||||
```
|
```
|
||||||
|
|
||||||
- The last command will carry out the plan
|
- Deploy
|
||||||
|
|
||||||
```
|
```
|
||||||
$ terraform apply
|
$ terraform deploy
|
||||||
|
|
||||||
[...]
|
|
||||||
|
|
||||||
Do you want to perform these actions?
|
|
||||||
Terraform will perform the actions described above.
|
|
||||||
Only 'yes' will be accepted to approve.
|
|
||||||
|
|
||||||
Enter a value: yes
|
|
||||||
|
|
||||||
# The actions are carried out
|
|
||||||
libvirt_pool.ubuntu-bios: Creating...
|
|
||||||
libvirt_pool.ubuntu-bios: Creation complete after 0s
|
|
||||||
|
|
||||||
[...]
|
|
||||||
|
|
||||||
Apply complete! Resources: 4 added, 0 changed, 0 destroyed.
|
|
||||||
```
|
```
|
||||||
|
|
||||||
- Identify the created machine
|
- Identify the name of the machine, which requires elevated privileges
|
||||||
|
|
||||||
```
|
```
|
||||||
$ sudo virsh list --all
|
# virsh list --all
|
||||||
|
Id Name State
|
||||||
Id Name State
|
--------------------------------------------
|
||||||
---------------------------------------------
|
2 u24-bios-0 running
|
||||||
10 ubuntu-cloud-server-2404-0 running
|
|
||||||
```
|
```
|
||||||
|
|
||||||
- Determine its IP address
|
- Fetch IP address
|
||||||
|
|
||||||
```
|
```
|
||||||
$ sudo virsh domifaddr ubuntu-cloud-server-2404-0
|
# virsh domifaddr u24-bios-0
|
||||||
|
```
|
||||||
|
|
||||||
|
- Connect to the machine with the user `groot`
|
||||||
|
|
||||||
|
```
|
||||||
|
$ ssh groot@10.17.3.107
|
||||||
```
|
```
|
||||||
|
|
||||||
```
|
```
|
||||||
Name MAC address Protocol Address
|
groot@ubuntu:~$
|
||||||
----------------------------------------------------------------
|
|
||||||
vnet3 52:54:00:e2:51:c0 ipv4 192.168.122.24/24
|
|
||||||
```
|
```
|
||||||
|
|
||||||
- Connect to the machine
|
- Logout
|
||||||
|
|
||||||
```
|
```
|
||||||
$ ssh root@192.168.122.24
|
$ exit
|
||||||
|
|
||||||
[...]
|
|
||||||
|
|
||||||
# Use the password defined in the cloud-init.cfg file
|
|
||||||
|
|
||||||
root@192.168.122.24's password:
|
|
||||||
|
|
||||||
Welcome to Ubuntu 24.04.3 LTS (GNU/Linux 6.8.0-71-generic x86_64)
|
|
||||||
[...]
|
|
||||||
System information as of Tue Aug 26 10:40:49 UTC 2025
|
|
||||||
|
|
||||||
System load: 0.0 Processes: 113
|
|
||||||
Usage of /: 67.4% of 2.35GB Users logged in: 0
|
|
||||||
Memory usage: 5% IPv4 address for ens3: 192.168.122.24
|
|
||||||
Swap usage: 0%
|
|
||||||
```
|
```
|
||||||
|
|
||||||
- Exit the virtual machine
|
- Destroy the machine
|
||||||
|
|
||||||
```
|
|
||||||
root@ubuntu$ exit
|
|
||||||
```
|
|
||||||
|
|
||||||
- To destroy the virtual machine, execute the following command
|
|
||||||
|
|
||||||
```
|
```
|
||||||
$ terraform destroy
|
$ terraform destroy
|
||||||
|
|
||||||
[...]
|
|
||||||
|
|
||||||
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
|
|
||||||
- destroy
|
|
||||||
|
|
||||||
Terraform will perform the following actions:
|
|
||||||
|
|
||||||
# libvirt_cloudinit_disk.commoninit will be destroyed
|
|
||||||
- resource "libvirt_cloudinit_disk" "commoninit" {
|
|
||||||
|
|
||||||
[...]
|
|
||||||
|
|
||||||
# libvirt_domain.domain[0] will be destroyed
|
|
||||||
- resource "libvirt_domain" "domain" {
|
|
||||||
- arch = "x86_64" -> null
|
|
||||||
|
|
||||||
[...]
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# libvirt_pool.ubuntu2 will be destroyed
|
|
||||||
- resource "libvirt_pool" "ubuntu2" {
|
|
||||||
- allocation = 798310400 -> null
|
|
||||||
- available = 16019255296 -> null
|
|
||||||
|
|
||||||
[...]
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# libvirt_volume.ubuntu-qcow2 will be destroyed
|
|
||||||
- resource "libvirt_volume" "ubuntu-qcow2" {
|
|
||||||
|
|
||||||
[...]
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
Plan: 0 to add, 0 to change, 4 to destroy.
|
|
||||||
|
|
||||||
Do you really want to destroy all resources?
|
|
||||||
Terraform will destroy all your managed infrastructure, as shown above.
|
|
||||||
There is no undo. Only 'yes' will be accepted to confirm.
|
|
||||||
|
|
||||||
Enter a value: yes
|
|
||||||
|
|
||||||
libvirt_domain.domain[0]: Destroying... [id=611d5ede-e4b4-4ca5-ad83-83030942a6b5]
|
|
||||||
libvirt_domain.domain[0]: Destruction complete after 0s
|
|
||||||
libvirt_cloudinit_disk.commoninit: Destroying... [id=/tmp/cluster_storage2/commoninit.iso;5f4e08ef-ad51-484f-a9f2-c926f582974a]
|
|
||||||
libvirt_volume.ubuntu-qcow2: Destroying... [id=/tmp/cluster_storage2/ubuntu-qcow2]
|
|
||||||
libvirt_cloudinit_disk.commoninit: Destruction complete after 0s
|
|
||||||
libvirt_volume.ubuntu-qcow2: Destruction complete after 0s
|
|
||||||
libvirt_pool.ubuntu2: Destroying... [id=dbd62f8b-5d09-4e96-87e2-88e95c582896]
|
|
||||||
libvirt_pool.ubuntu2: Destruction complete after 0s
|
|
||||||
|
|
||||||
Destroy complete! Resources: 4 destroyed.
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Explanations
|
|
||||||
|
|
||||||
Let's take a look inside the *ubuntu-cloud-server-2404-bios* folder, which contains two files, *ubuntu-cloud-server-2404-bios.tf* and *cloud_init.cfg*
|
|
||||||
|
|
||||||
The first file *ubuntu-cloud-server-2404-bios.tf* contains the main configuration for the Terraform deployment.
|
|
||||||
|
|
||||||
- It starts by defining the required Terraform version and provider
|
|
||||||
|
|
||||||
```
|
|
||||||
terraform {
|
|
||||||
required_version = ">= 0.13"
|
|
||||||
required_providers {
|
|
||||||
libvirt = {
|
|
||||||
source = "dmacvicar/libvirt"
|
|
||||||
version = "0.8.3"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
- The specific provider is defined here
|
|
||||||
|
|
||||||
```
|
|
||||||
provider "libvirt" {
|
|
||||||
uri = "qemu:///system"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
> The [connection URI](https://libvirt.org/uri.html#qemu-qemu-and-kvm-uris) of the libvirt instance can be defined. One could for instance specific a libvirt instance that is hosted remotely
|
|
||||||
|
|
||||||
- A libvirt pool, to store the virtual machine image, is created:
|
|
||||||
|
|
||||||
```
|
|
||||||
resource "libvirt_pool" "ubuntu-bios" {
|
|
||||||
name = "ubuntu-bios"
|
|
||||||
type = "dir"
|
|
||||||
target {
|
|
||||||
path = "/tmp/ubuntu-bios"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
- The cloud-init user data will be fetched from a specific file whose path has to be declared:
|
|
||||||
|
|
||||||
```
|
|
||||||
data "template_file" "user_data" {
|
|
||||||
template = file("${path.module}/cloud_init.cfg")
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
- The ISO cloud-init disk will be created:
|
|
||||||
|
|
||||||
```
|
|
||||||
resource "libvirt_cloudinit_disk" "commoninit" {
|
|
||||||
name = "commoninit.iso"
|
|
||||||
user_data = data.template_file.user_data.rendered
|
|
||||||
pool = libvirt_pool.ubuntu-bios.name
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
- Perhaps the most important, the domain will be created:
|
|
||||||
|
|
||||||
> Values can be adjusted, such as memory or vCPU counts. In the examples, multiple virtio-based device hardware are created, such as a virtio-gpu.
|
|
||||||
|
|
||||||
```
|
|
||||||
resource "libvirt_domain" "domain" {
|
|
||||||
count = 1
|
|
||||||
name = "ubuntu-cloud-server-2404-${count.index}"
|
|
||||||
memory = "4092"
|
|
||||||
vcpu = 2
|
|
||||||
cloudinit = libvirt_cloudinit_disk.commoninit.id
|
|
||||||
|
|
||||||
cpu {
|
|
||||||
mode = "host-model"
|
|
||||||
}
|
|
||||||
|
|
||||||
disk {
|
|
||||||
volume_id = libvirt_volume.ubuntu-qcow2.id
|
|
||||||
}
|
|
||||||
|
|
||||||
console {
|
|
||||||
type = "pty"
|
|
||||||
target_port = "0"
|
|
||||||
target_type = "virtio"
|
|
||||||
}
|
|
||||||
|
|
||||||
video {
|
|
||||||
type = "virtio"
|
|
||||||
}
|
|
||||||
|
|
||||||
tpm {
|
|
||||||
backend_type = "emulator"
|
|
||||||
backend_version = "2.0"
|
|
||||||
}
|
|
||||||
|
|
||||||
network_interface {
|
|
||||||
network_name = "default"
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Resources
|
## Resources
|
||||||
|
|||||||
80
update_ssh_keys.sh
Executable file
80
update_ssh_keys.sh
Executable file
@@ -0,0 +1,80 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Script to automatically update SSH keys in all main.tf files
|
||||||
|
# This script looks for terraform_key (or terraform_key.pub) in ~/.ssh directory
|
||||||
|
|
||||||
|
# Function to display usage
|
||||||
|
usage() {
|
||||||
|
echo "Usage: $0 [ssh_key_name]"
|
||||||
|
echo " ssh_key_name: Name of the SSH key pair (default: terraform_key)"
|
||||||
|
echo ""
|
||||||
|
echo "Example:"
|
||||||
|
echo " $0 # Uses default 'terraform_key'"
|
||||||
|
echo " $0 my_custom_key # Uses 'my_custom_key' and 'my_custom_key.pub'"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Set the SSH key name (default to terraform_key)
|
||||||
|
SSH_KEY_NAME="${1:-terraform_key}"
|
||||||
|
|
||||||
|
# Expand the home directory properly
|
||||||
|
HOME_DIR="${HOME:-/home/$(whoami)}"
|
||||||
|
SSH_KEY_PATH="$HOME_DIR/.ssh/$SSH_KEY_NAME"
|
||||||
|
SSH_KEY_PUB_PATH="$HOME_DIR/.ssh/$SSH_KEY_NAME.pub"
|
||||||
|
|
||||||
|
# Check if SSH key exists
|
||||||
|
if [ ! -f "$SSH_KEY_PATH" ] && [ ! -f "$SSH_KEY_PUB_PATH" ]; then
|
||||||
|
echo "Error: SSH key '$SSH_KEY_NAME' not found in $HOME_DIR/.ssh/"
|
||||||
|
echo "Please generate your SSH key first:"
|
||||||
|
echo " ssh-keygen -t rsa -b 4096 -f $HOME_DIR/.ssh/$SSH_KEY_NAME"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check if public key exists specifically (required for reading)
|
||||||
|
if [ ! -f "$SSH_KEY_PUB_PATH" ]; then
|
||||||
|
echo "Error: SSH public key '$SSH_KEY_NAME.pub' not found in $HOME_DIR/.ssh/"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Get the public key content (remove any trailing whitespace)
|
||||||
|
PUBLIC_KEY=$(cat "$SSH_KEY_PUB_PATH" | tr -d '\n')
|
||||||
|
|
||||||
|
# Validate that we got a valid SSH key
|
||||||
|
if [[ ! "$PUBLIC_KEY" =~ ^ssh-[a-z]+[[:space:]]+[A-Za-z0-9+/]*[=]{0,3} ]]; then
|
||||||
|
echo "Error: Invalid SSH public key format detected"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Found SSH public key:"
|
||||||
|
echo "$PUBLIC_KEY"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Find all main.tf files and update them
|
||||||
|
MAIN_TF_FILES=$(find . -name "main.tf" -type f)
|
||||||
|
|
||||||
|
if [ -z "$MAIN_TF_FILES" ]; then
|
||||||
|
echo "No main.tf files found!"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Updating SSH key in the following files:"
|
||||||
|
echo "$MAIN_TF_FILES"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Replace the ssh_key line in all main.tf files using # as delimiter
|
||||||
|
echo "Replacing SSH key in all main.tf files..."
|
||||||
|
for file in $MAIN_TF_FILES; do
|
||||||
|
sed -i "s#ssh_key = \".*\"#ssh_key = \"$PUBLIC_KEY\"#g" "$file"
|
||||||
|
done
|
||||||
|
|
||||||
|
# Verify the replacement worked
|
||||||
|
echo ""
|
||||||
|
echo "Verification:"
|
||||||
|
for file in $MAIN_TF_FILES; do
|
||||||
|
echo "File: $file"
|
||||||
|
grep "ssh_key =" "$file" | head -1
|
||||||
|
done
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "SSH key has been successfully updated in all main.tf files!"
|
||||||
|
echo "Backup files are saved with timestamp suffixes."
|
||||||
Reference in New Issue
Block a user