Skip to content

Instantly share code, notes, and snippets.

@clemenko
Created July 1, 2025 20:35
Show Gist options
  • Save clemenko/c1ffb30f73e7e64b7297fe11c681b554 to your computer and use it in GitHub Desktop.
Save clemenko/c1ffb30f73e7e64b7297fe11c681b554 to your computer and use it in GitHub Desktop.
#!/usr/bin/env bash
# harvester automation
export RED='\x1b[0;31m'
export GREEN='\x1b[32m'
export BLUE='\x1b[34m'
export YELLOW='\x1b[33m'
export NO_COLOR='\x1b[0m'
# variables
vip=192.168.1.12
longPassword=Pa22word1234#
shortPassword=Pa22word
# key pair
keypair="ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA26evmemRbhTtjV9szD9SwcFW9VOD38jDuJmyYYdqoqIltDkpUqDa/V1jxLSyrizhOHrlJtUOj790cxrvInaBNP7nHIO+GwC9VH8wFi4KG/TFj3K8SfNZ24QoUY12rLiHR6hRxcT4aUGnqFHGv2WTqsW2sxz03z+W1qeMqWYJOUfkqKKs2jiz42U+0Kp9BxsFBlai/WAXrQsYC8CcpQSRKdggOMQf04CqqhXzt5Q4Cmago+Fr7HcvEnPDAaNcVtfS5DYLERcX2OVgWT3RBWhDIjD8vYCMBBCy2QUrc4ZhKZfkF9aemjnKLfLcbdpMfb+r7NwJsVQSPKcjYAJOckE8RQ== [email protected]"
# set functions for debugging/logging
function info { echo -e "$GREEN[info]$NO_COLOR $1" ; }
function warn { echo -e "$YELLOW[warn]$NO_COLOR $1" ; }
function fatal { echo -e "$RED[error]$NO_COLOR $1" ; exit 1 ; }
function info_ok { echo -e "$GREEN" "ok" "$NO_COLOR" ; }
#better error checking
command -v curl >/dev/null 2>&1 || { fatal "Curl was not found. Please install" ; }
command -v jq >/dev/null 2>&1 || { fatal "Jq was not found. Please install" ; }
command -v kubectl >/dev/null 2>&1 || { fatal "Kubectl was not found. Please install" ; }
info " - waiting for harvester "
until curl -skf -m 1 --output /dev/null https://$vip/v3-public ; do echo -n "." ; sleep 10 ; done
info_ok
info " - setting long password"
token=$(curl -sk -X POST https://$vip/v3-public/localProviders/local?action=login -H 'content-type: application/json' -d '{"username":"admin","password":"admin"}' | jq -r .token)
curl -sk https://$vip/v3/users?action=changepassword -H 'content-type: application/json' -H "Authorization: Bearer $token" -d '{"currentPassword":"admin","newPassword":"'$longPassword'"}'
info_ok
# reauthenticate
api_token=$(curl -sk https://$vip/v3/token -H 'content-type: application/json' -H "Authorization: Bearer $token" -d '{"type":"token","description":"automation"}' | jq -r .token)
info " - getting kubeconfig"
curl -sk https://$vip/v1/management.cattle.io.clusters/local?action=generateKubeconfig -H "Authorization: Bearer $api_token" -X POST -H 'content-type: application/json' | jq -r .config | sed -e '/certificate-authority-data/,18d' -e '/server/ a\'$'\n'' insecure-skip-tls-verify: true' > $vip.yaml
export KUBECONFIG=$vip.yaml
info_ok
# load password length, image, network, keypair and template
info " - configuring password length, images, network, and keypair"
cat <<EOF | kubectl apply -f - > /dev/null 2>&1
apiVersion: management.cattle.io/v3
kind: Setting
metadata:
name: password-min-length
namespace: cattle-system
value: "8"
---
apiVersion: harvesterhci.io/v1beta1
kind: VirtualMachineImage
metadata:
name: rocky95
annotations:
harvesterhci.io/storageClassName: harvester-longhorn
labels:
harvesterhci.io/image-type: raw_qcow2
harvesterhci.io/os-type: rocky
namespace: default
spec:
displayName: rocky10
retry: 3
sourceType: download
storageClassParameters:
migratable: 'true'
numberOfReplicas: '3'
staleReplicaTimeout: '30'
url: https://dl.rockylinux.org/pub/rocky/10/images/x86_64/Rocky-10-GenericCloud-Base.latest.x86_64.qcow2
---
apiVersion: harvesterhci.io/v1beta1
kind: VirtualMachineImage
metadata:
name: plucky
annotations:
harvesterhci.io/storageClassName: harvester-longhorn
labels:
harvesterhci.io/image-type: raw_qcow2
harvesterhci.io/os-type: ubuntu
namespace: default
spec:
displayName: plucky
retry: 3
sourceType: download
storageClassParameters:
migratable: 'true'
numberOfReplicas: '3'
staleReplicaTimeout: '30'
url: https://cloud-images.ubuntu.com/minimal/releases/plucky/release/ubuntu-25.04-minimal-cloudimg-amd64.img
---
apiVersion: harvesterhci.io/v1beta1
kind: KeyPair
metadata:
name: keypair
namespace: default
spec:
publicKey: $keypair
---
apiVersion: v1
kind: ConfigMap
metadata:
labels:
harvesterhci.io/cloud-init-template: user
name: default
namespace: default
data:
cloudInit: |-
#cloud-config
disable_root: false
packages:
- vim
- sudo
- bind-utils
- qemu-guest-agent
- htop
runcmd:
- sysctl -w net.ipv6.conf.all.disable_ipv6=1
- systemctl restart qemu-guest-agent
- yum install -y epel-release && yum install -y htop jq
ssh_pwauth: True
users:
- name: root
hashed_passwd: \$6\$911qHLlKBOcS6/n/\$G4fpeL4JJsrYAfORGf5nRzwSm0YBnIwm1FTyLx365chA.hvX7Yy9yeAEBEXhJ72sa1PgN8YT7sOnRJ4Max6Nr0
lock_passwd: false
shell: /bin/bash
ssh_authorized_keys:
- ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA26evmemRbhTtjV9szD9SwcFW9VOD38jDuJmyYYdqoqIltDkpUqDa/V1jxLSyrizhOHrlJtUOj790cxrvInaBNP7nHIO+GwC9VH8wFi4KG/TFj3K8SfNZ24QoUY12rLiHR6hRxcT4aUGnqFHGv2WTqsW2sxz03z+W1qeMqWYJOUfkqKKs2jiz42U+0Kp9BxsFBlai/WAXrQsYC8CcpQSRKdggOMQf04CqqhXzt5Q4Cmago+Fr7HcvEnPDAaNcVtfS5DYLERcX2OVgWT3RBWhDIjD8vYCMBBCy2QUrc4ZhKZfkF9aemjnKLfLcbdpMfb+r7NwJsVQSPKcjYAJOckE8RQ== [email protected]
---
apiVersion: k8s.cni.cncf.io/v1
kind: NetworkAttachmentDefinition
metadata:
annotations:
network.harvesterhci.io/route: '{"mode":"auto","serverIPAddr":"","cidr":"","gateway":""}'
name: vlan1
namespace: default
spec:
config: '{"cniVersion":"0.3.1","name":"vlan1","type":"bridge","bridge":"mgmt-br","promiscMode":true,"ipam":{}}'
EOF
# delete if you want the long password
# password shortener - one last time
token=$(curl -sk -X POST https://$vip/v3-public/localProviders/local?action=login -H 'content-type: application/json' -d '{"username":"admin","password":"'$longPassword'"}' | jq -r .token)
api_token=$(curl -sk https://$vip/v3/token -H 'content-type: application/json' -H "Authorization: Bearer $token" -d '{"type":"token","description":"automation"}' | jq -r .token)
curl -sk https://$vip/v3/users?action=changepassword -H 'content-type: application/json' -H "Authorization: Bearer $token" -d '{"currentPassword":"'$longPassword'","newPassword":"'$shortPassword'"}'
info Complete
schemeversion: 1
serverurl: ""
token: ilikechicken
os:
afterinstallchrootcommands: []
sshauthorizedkeys: []
writefiles: []
hostname: slim
modules:
- kvm
- vhost_net
- nvme
sysctls:
kernel.printk: "4 4 1 7"
kernel.kptr_restrict: "1"
ntpservers:
- 0.suse.pool.ntp.org
dnsnameservers:
- 192.168.8.2
- 8.8.8.8
wifi: []
password: Pa22word
environment: {}
labels:
topology.kubernetes.io/zone: zone1
persistentstatepaths: []
externalstorage:
enabled: false
multipathconfig: []
additionalkernelarguments: ""
ssh_authorized_keys:
- "ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA26evmemRbhTtjV9szD9SwcFW9VOD38jDuJmyYYdqoqIltDkpUqDa/V1jxLSyrizhOHrlJtUOj790cxrvInaBNP7nHIO+GwC9VH8wFi4KG/TFj3K8SfNZ24QoUY12rLiHR6hRxcT4aUGnqFHGv2WTqsW2sxz03z+W1qeMqWYJOUfkqKKs2jiz42U+0Kp9BxsFBlai/WAXrQsYC8CcpQSRKdggOMQf04CqqhXzt5Q4Cmago+Fr7HcvEnPDAaNcVtfS5DYLERcX2OVgWT3RBWhDIjD8vYCMBBCy2QUrc4ZhKZfkF9aemjnKLfLcbdpMfb+r7NwJsVQSPKcjYAJOckE8RQ== [email protected]"
install:
automatic: true
skipchecks: true
mode: create
managementinterface:
interfaces:
- name: enp1s0
hwaddr: ""
method: static
ip: 192.168.1.12
subnetmask: 255.255.255.0
gateway: 192.168.1.1
defaultroute: true
bondoptions:
miimon: "100"
mode: active-backup
mtu: 0
vlanid: 0
vip: 192.168.1.14
viphwaddr: ""
vipmode: static
clusterdns: ""
clusterpodcidr: ""
clusterservicecidr: ""
forceefi: false
device: /dev/nvme0n1
configurl: ""
silent: false
isourl: http://192.168.1.13/harvester/harvester-v1.5.1-amd64.iso
poweroff: false
noformat: false
debug: false
tty: tty1
forcegpt: true
role: default
withnetimages: false
wipealldisks: false
wipediskslist: []
forcembr: false
datadisk: ""
webhooks: []
addons:
harvester_vm_import_controller:
enabled: false
values_content: ""
harvester_pcidevices_controller:
enabled: false
values_content: ""
rancher_monitoring:
enabled: false
values_content: ""
rancher_logging:
enabled: false
values_content: ""
harvester_seeder:
enabled: false
values_content: ""
harvester:
storage_class:
replica_count: 1
longhorn:
defaultsettings:
guaranteedEngineManagerCPU: 2
guaranteedReplicaManagerCPU: 2
persistentpartitionsize: 150Gi
system_settings:
auto-disk-provision-paths: ""
#containerd-registry: '{"Mirrors": {"*": {"Endpoints": ["https://myregistry.local:5000"]}}, "Configs": {"myregistry.local:5000": {"Auth": {"Username": "testuser", "Password": "testpassword"}, "TLS": {"InsecureSkipVerify": false}}}}'
ui-source: bundled
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment