Compare commits
250 Commits
Windows10
...
585e39cb97
| Author | SHA1 | Date | |
|---|---|---|---|
| 585e39cb97 | |||
| 1cd7e1510f | |||
| 0534b031fa | |||
| c8509aa3d5 | |||
| 158af986c3 | |||
| 5e537966f6 | |||
| 3849b79493 | |||
| 5f1d1bfa8a | |||
| fe306bd845 | |||
| ccbd4ed984 | |||
| fdc5c44e6a | |||
| c57291af6d | |||
| d652cf0346 | |||
| a1b8837cc5 | |||
| 5b7b93dd30 | |||
| d89ccd57da | |||
| 53da641926 | |||
| a3e9bc659a | |||
| fc51bf3f94 | |||
| 3d96f8c13b | |||
| 488fe10e1e | |||
| 01e168a7f9 | |||
| c48f27c42e | |||
| 185b332764 | |||
| e89505bef6 | |||
| b763d2b562 | |||
| d607f615e9 | |||
| ed7a474dbb | |||
| 14c6720196 | |||
| 277c91eeba | |||
| c9f3c648b7 | |||
| 8e680c45be | |||
| 7440d5824c | |||
| 8e75925b52 | |||
| d6234321d9 | |||
| ea20b1290c | |||
| d986d5ab25 | |||
| c29728594c | |||
| 10d5d6f389 | |||
| 96dccef450 | |||
| 05e0c50217 | |||
| 261e91ee2e | |||
| 1746af9b9d | |||
| 7c2ff54019 | |||
| 193ce9a534 | |||
| 9e91bef7b7 | |||
| 05b30287bd | |||
| 54caff8fb6 | |||
| 2f976898eb | |||
| 44befeda4b | |||
| 81847d3b93 | |||
| 39cc83ac57 | |||
| 0263b2dfc4 | |||
| e7e3b69d95 | |||
| b6ac086a31 | |||
| 20ce62fb6d | |||
| 0918eb36fe | |||
| 93e7d4dc9b | |||
| 9a0a33816c | |||
| eeb1364f1b | |||
| f04095db8c | |||
| 2847542976 | |||
| 929186d123 | |||
| d6c885240a | |||
| 0b97ae2fc5 | |||
| abacbf90ce | |||
| 243cf426d7 | |||
| 64b7ea45c0 | |||
| 3a2dbe572e | |||
| 2be42989e5 | |||
| cb97703406 | |||
| 16df0b65fc | |||
| 847b255e3b | |||
| d005697438 | |||
| ab010643df | |||
| e17cd1b633 | |||
| 504764af10 | |||
| 405fb5938f | |||
| 77e0f7b7cb | |||
| 7fb0e80537 | |||
| fb8b9b735f | |||
| 86e99b1515 | |||
| 952e92082f | |||
| 33e0220e34 | |||
| a51d922f00 | |||
| b441717ee1 | |||
| b7da591571 | |||
| fa0fa30062 | |||
| dd4e79901e | |||
| 1fcaa4b212 | |||
| 164fd15c60 | |||
| 241551169a | |||
| a2b20f49cc | |||
| fe8765ded7 | |||
| caf45b5270 | |||
| 9ba2df08cd | |||
| 9458f49744 | |||
| 369aaaa0b5 | |||
| 6220e2a9aa | |||
| e3e46bae7d | |||
| 6e6e7900da | |||
| 6c329a36e9 | |||
| f33f2912f1 | |||
| 9541942c23 | |||
| 2f937aded7 | |||
| 95dea97382 | |||
| a840306245 | |||
| bbd103d527 | |||
| b2ceee8720 | |||
| d5c886f02b | |||
| 1d59cd4b3c | |||
| f2d9147291 | |||
| bc9f1c260f | |||
| 368f84769b | |||
| 51366476cc | |||
| dcbaf6b807 | |||
| 5dfc3a7813 | |||
| 0989d0c586 | |||
| 8a83f47572 | |||
| a3bbf88ce3 | |||
| 5e0cebf733 | |||
| 00e3266360 | |||
| c6a8f9f7bd | |||
| 4f1231f973 | |||
| 049bedbd8f | |||
| 0e7cfa0934 | |||
| 5435f73402 | |||
| 6917e0799a | |||
| 4616b9b070 | |||
| 8c741dc120 | |||
| 8cbfcb016b | |||
| 4ba7b590ba | |||
| 52660e1414 | |||
| 0ab6aaeaa5 | |||
| 02c26b2465 | |||
| 1842a08a39 | |||
| 0c01f024e9 | |||
| 40489ff373 | |||
| c491066384 | |||
| 4c054cc434 | |||
| dcbe6c397f | |||
| cb84a02b6f | |||
| 8f432d3353 | |||
| 1cdbcaccaf | |||
| f1c6161bcb | |||
| 123518a787 | |||
| 2ec6a756b7 | |||
| a1779be079 | |||
| 8ed9b2f754 | |||
| 72202d9f21 | |||
| 9eb5fbd0a3 | |||
| c58ede04c4 | |||
| 662e8984c3 | |||
| b7abf25907 | |||
| 18fa7742fa | |||
| f6993c2052 | |||
| 59d1730ca5 | |||
| b087203cfb | |||
| d39d594bf0 | |||
| 487239365e | |||
| 6ea03d152c | |||
| 01991435ae | |||
| a64b5b2325 | |||
| 38d7442025 | |||
| cf91519076 | |||
| bae044e145 | |||
| f39b4bbb62 | |||
| 9739c51100 | |||
| 0df98d4341 | |||
| fc23dc068d | |||
| 4d78d65ad8 | |||
| c1440d9dcd | |||
| 4a5f390ae1 | |||
| e0a5b5a5da | |||
| 081aaaaa19 | |||
| 2bd0f8df0a | |||
| 2c57dbcddc | |||
| 0f01e803f2 | |||
| bd82e13fc4 | |||
| 2f902fa27c | |||
| 37ecd4a1b9 | |||
| 8a8cb09984 | |||
| bc46b09708 | |||
| d508b28213 | |||
| 03ed59680f | |||
| 40ade38c78 | |||
| c89edd3ee9 | |||
| e5880c222e | |||
| 236db40016 | |||
| b340c777ba | |||
| 10624f8c90 | |||
| eca6ae515e | |||
| b6656c3d9c | |||
| 82b8eeca4f | |||
| 5af4d729ef | |||
| 73c266632c | |||
| cd448a0af3 | |||
| 6b1db0dd23 | |||
| f15485e7c2 | |||
| 4257849ca4 | |||
| 3426cd7ed3 | |||
| 226fa9859b | |||
| 7e36abe0cb | |||
| 1e470f38dd | |||
| 389c35bb05 | |||
| f40889370d | |||
| 990534618b | |||
| c0953acefe | |||
| b33280b443 | |||
| 9460bc9bd6 | |||
| 5f835960b9 | |||
| 35c2df5f4e | |||
| fcdc7d07ad | |||
| 998f5ef381 | |||
| a9041b19a4 | |||
| 0a2647d465 | |||
| 93cadce4fe | |||
| 4d7400ed43 | |||
| f8d0c1cdde | |||
| 4ea138fd69 | |||
| d5c3e2c0b6 | |||
| f33a99ad05 | |||
| d50bf402af | |||
| 8adbec57d5 | |||
| 06d0a44c4d | |||
| be9609875a | |||
| 6c27d2e74d | |||
| 346b2878a6 | |||
| 0ff31c104b | |||
| 4bf036c49d | |||
| f6a27db822 | |||
| b9fb3cdb30 | |||
| d95daa1861 | |||
| fcea2ef449 | |||
| d680b0dde4 | |||
| 03b5338f59 | |||
| b7d737e7df | |||
| c4389f4932 | |||
| bfa31fd7f7 | |||
| 94e1624180 | |||
| 64ecc33dcb | |||
| 1da7269d87 | |||
| 50bef57ff7 | |||
| 66e3c01863 | |||
| 7b525a6ea8 | |||
| 6ae9003fec | |||
| 7557a7bfca | |||
| 5501c640fc | |||
| 3f0a8ca3c4 | |||
| 04c9eb4f03 |
38
.drone.yml
38
.drone.yml
@@ -14,54 +14,56 @@ steps:
|
||||
- name: Debugging information
|
||||
image: bv11-cr01.bessems.eu/library/packer-extended
|
||||
commands:
|
||||
- yamllint --version
|
||||
- packer --version
|
||||
- pwsh --version
|
||||
- ansible --version
|
||||
- ovftool --version
|
||||
- name: Windows 10
|
||||
- packer --version
|
||||
- yamllint --version
|
||||
- name: Kubernetes Bootstrap Appliance
|
||||
image: bv11-cr01.bessems.eu/library/packer-extended
|
||||
pull: always
|
||||
commands:
|
||||
- sed -i -e "s/<<img-productkey>>/$${PRODUCTKEY}/" packer/preseed/Windows10/Autounattend.xml
|
||||
- |
|
||||
sed -i -e "s/<<img-password>>/$${WINRM_PASSWORD}/g" \
|
||||
packer/preseed/Windows10/Autounattend.xml \
|
||||
packer/preseed/Windows10/Sysprep_Unattend.xml
|
||||
sed -i -e "s/<<img-password>>/$${SSH_PASSWORD}/g" \
|
||||
packer/preseed/UbuntuServer22.04/user-data
|
||||
- |
|
||||
yamllint -d "{extends: relaxed, rules: {line-length: disable}}" scripts
|
||||
yamllint -d "{extends: relaxed, rules: {line-length: disable}}" \
|
||||
ansible \
|
||||
packer/preseed/UbuntuServer22.04/user-data \
|
||||
scripts
|
||||
- |
|
||||
ansible-galaxy install \
|
||||
-r ansible/requirements.yml
|
||||
- |
|
||||
packer init -upgrade \
|
||||
./packer
|
||||
- |
|
||||
packer validate \
|
||||
-var vm_name=$DRONE_BUILD_NUMBER-${DRONE_COMMIT_SHA:0:10} \
|
||||
-var vm_guestos=win10 \
|
||||
-var vm_guestos=k8sbootstrap \
|
||||
-var repo_username=$${REPO_USERNAME} \
|
||||
-var repo_password=$${REPO_PASSWORD} \
|
||||
-var vsphere_password=$${VSPHERE_PASSWORD} \
|
||||
-var winrm_password=$${WINRM_PASSWORD} \
|
||||
-var ssh_password=$${SSH_PASSWORD} \
|
||||
./packer
|
||||
- |
|
||||
packer build \
|
||||
-on-error=cleanup \
|
||||
-on-error=cleanup -timestamp-ui \
|
||||
-var vm_name=$DRONE_BUILD_NUMBER-${DRONE_COMMIT_SHA:0:10} \
|
||||
-var vm_guestos=win10 \
|
||||
-var vm_guestos=k8sbootstrap \
|
||||
-var repo_username=$${REPO_USERNAME} \
|
||||
-var repo_password=$${REPO_PASSWORD} \
|
||||
-var vsphere_password=$${VSPHERE_PASSWORD} \
|
||||
-var winrm_password=$${WINRM_PASSWORD} \
|
||||
-var ssh_password=$${SSH_PASSWORD} \
|
||||
./packer
|
||||
environment:
|
||||
VSPHERE_PASSWORD:
|
||||
from_secret: vsphere_password
|
||||
WINRM_PASSWORD:
|
||||
from_secret: winrm_password
|
||||
SSH_PASSWORD:
|
||||
from_secret: ssh_password
|
||||
REPO_USERNAME:
|
||||
from_secret: repo_username
|
||||
REPO_PASSWORD:
|
||||
from_secret: repo_password
|
||||
PRODUCTKEY:
|
||||
from_secret: prodkey_win10
|
||||
# PACKER_LOG: 1
|
||||
volumes:
|
||||
- name: output
|
||||
|
||||
@@ -1 +1 @@
|
||||
# Packer.Images [](https://ci.spamasaurus.com/djpbessems/Packer.Images)
|
||||
# Packer.Images [](https://ci.spamasaurus.com/djpbessems/Packer.Images)
|
||||
|
||||
3
ansible/ansible.cfg
Normal file
3
ansible/ansible.cfg
Normal file
@@ -0,0 +1,3 @@
|
||||
[defaults]
|
||||
deprecation_warnings = False
|
||||
remote_tmp = /tmp/.ansible-${USER}/tmp
|
||||
12
ansible/playbook.yml
Normal file
12
ansible/playbook.yml
Normal file
@@ -0,0 +1,12 @@
|
||||
---
|
||||
- hosts: all
|
||||
gather_facts: false
|
||||
vars_files:
|
||||
- metacluster.yml
|
||||
- workloadcluster.yml
|
||||
become: true
|
||||
roles:
|
||||
- os
|
||||
- firstboot
|
||||
- appliance
|
||||
- metacluster
|
||||
4
ansible/requirements.yml
Normal file
4
ansible/requirements.yml
Normal file
@@ -0,0 +1,4 @@
|
||||
collections:
|
||||
- ansible.utils
|
||||
- community.general
|
||||
- kubernetes.core
|
||||
@@ -0,0 +1,27 @@
|
||||
---
|
||||
- name: Initialize tempfolder
|
||||
ansible.builtin.tempfile:
|
||||
state: directory
|
||||
register: archive
|
||||
|
||||
- name: Download & extract archived static binary
|
||||
ansible.builtin.unarchive:
|
||||
src: "{{ item.url }}"
|
||||
dest: "{{ archive.path }}"
|
||||
remote_src: yes
|
||||
extra_opts: "{{ item.extra_opts | default(omit) }}"
|
||||
|
||||
- name: Install extracted binary
|
||||
ansible.builtin.copy:
|
||||
src: "{{ archive.path }}/{{ item.filename }}"
|
||||
dest: /usr/local/bin/{{ item.filename }}
|
||||
remote_src: yes
|
||||
owner: root
|
||||
group: root
|
||||
mode: 0755
|
||||
|
||||
- name: Cleanup tempfolder
|
||||
ansible.builtin.file:
|
||||
path: "{{ archive.path }}"
|
||||
state: absent
|
||||
when: archive.path is defined
|
||||
53
ansible/roles/appliance/tasks/dependencies.yml
Normal file
53
ansible/roles/appliance/tasks/dependencies.yml
Normal file
@@ -0,0 +1,53 @@
|
||||
# - name: Create folder structure(s)
|
||||
# ansible.builtin.file:
|
||||
# path: "{{ item }}"
|
||||
# state: directory
|
||||
# loop:
|
||||
# - /foo
|
||||
|
||||
- name: Download & install static binaries
|
||||
ansible.builtin.get_url:
|
||||
url: "{{ item.url }}"
|
||||
url_username: "{{ item.username | default(omit) }}"
|
||||
url_password: "{{ item.password | default(omit) }}"
|
||||
dest: /usr/local/bin/{{ item.filename }}
|
||||
owner: root
|
||||
group: root
|
||||
mode: 0755
|
||||
loop: "{{ dependencies.static_binaries | selectattr('archive', 'undefined') }}"
|
||||
loop_control:
|
||||
label: "{{ item.filename }}"
|
||||
|
||||
- name: Download, extract & install archived static binaries
|
||||
include_tasks: dependencies.archive_compressed.yml
|
||||
loop: "{{ dependencies.static_binaries | rejectattr('archive', 'undefined') | selectattr('archive', 'equalto', 'compressed') }}"
|
||||
loop_control:
|
||||
label: "{{ item.filename }}"
|
||||
|
||||
- name: Install ansible-galaxy collections
|
||||
ansible.builtin.shell:
|
||||
cmd: ansible-galaxy collection install {{ item }}
|
||||
loop: "{{ dependencies.ansible_galaxy_collections }}"
|
||||
|
||||
- name: Install distro packages
|
||||
ansible.builtin.apt:
|
||||
pkg: "{{ dependencies.packages.apt }}"
|
||||
state: latest
|
||||
update_cache: yes
|
||||
install_recommends: no
|
||||
|
||||
- name: Upgrade all packages
|
||||
ansible.builtin.apt:
|
||||
name: '*'
|
||||
state: latest
|
||||
update_cache: yes
|
||||
|
||||
- name: Install additional python packages
|
||||
ansible.builtin.pip:
|
||||
name: "{{ dependencies.packages.pip }}"
|
||||
state: latest
|
||||
|
||||
- name: Cleanup apt cache
|
||||
ansible.builtin.apt:
|
||||
autoremove: yes
|
||||
purge: yes
|
||||
2
ansible/roles/appliance/tasks/main.yml
Normal file
2
ansible/roles/appliance/tasks/main.yml
Normal file
@@ -0,0 +1,2 @@
|
||||
- name: Install & configure dependencies
|
||||
import_tasks: dependencies.yml
|
||||
24
ansible/roles/firstboot/files/ansible_payload/playbook.yml
Normal file
24
ansible/roles/firstboot/files/ansible_payload/playbook.yml
Normal file
@@ -0,0 +1,24 @@
|
||||
---
|
||||
- hosts: 127.0.0.1
|
||||
connection: local
|
||||
gather_facts: false
|
||||
vars_files:
|
||||
- metacluster.yml
|
||||
# become: true
|
||||
roles:
|
||||
- vapp
|
||||
- network
|
||||
- users
|
||||
- disks
|
||||
- metacluster
|
||||
- workloadcluster
|
||||
- tty
|
||||
- cleanup
|
||||
handlers:
|
||||
- name: Apply manifests
|
||||
kubernetes.core.k8s:
|
||||
src: "{{ item }}"
|
||||
state: present
|
||||
kubeconfig: "{{ kubeconfig.path }}"
|
||||
with_fileglob: /var/lib/rancher/k3s/server/manifests/*.yaml
|
||||
ignore_errors: yes
|
||||
@@ -0,0 +1,8 @@
|
||||
- name: Disable crontab job
|
||||
ansible.builtin.cron:
|
||||
name: firstboot
|
||||
state: absent
|
||||
|
||||
- name: Reboot host
|
||||
ansible.builtin.shell:
|
||||
cmd: /usr/sbin/reboot now
|
||||
@@ -0,0 +1,24 @@
|
||||
- name: Create volume group
|
||||
community.general.lvg:
|
||||
vg: longhorn_vg
|
||||
pvs:
|
||||
- /dev/sdb
|
||||
pvresize: yes
|
||||
|
||||
- name: Create logical volume
|
||||
community.general.lvol:
|
||||
vg: longhorn_vg
|
||||
lv: longhorn_lv
|
||||
size: 100%VG
|
||||
|
||||
- name: Create filesystem
|
||||
community.general.filesystem:
|
||||
dev: /dev/mapper/longhorn_vg-longhorn_lv
|
||||
fstype: ext4
|
||||
|
||||
- name: Mount dynamic disk
|
||||
ansible.posix.mount:
|
||||
path: /mnt/blockstorage
|
||||
src: /dev/mapper/longhorn_vg-longhorn_lv
|
||||
fstype: ext4
|
||||
state: mounted
|
||||
@@ -0,0 +1,398 @@
|
||||
- name: Gather service facts
|
||||
ansible.builtin.service_facts:
|
||||
# Module requires no attributes
|
||||
|
||||
- name: Install K3s
|
||||
ansible.builtin.command:
|
||||
cmd: ./install.sh
|
||||
chdir: /opt/metacluster/k3s
|
||||
environment:
|
||||
INSTALL_K3S_SKIP_DOWNLOAD: 'true'
|
||||
INSTALL_K3S_EXEC: 'server --cluster-init --disable local-storage'
|
||||
when: ansible_facts.services['k3s.service'] is undefined
|
||||
|
||||
- name: Ensure API availability
|
||||
ansible.utils.cli_parse:
|
||||
command: curl -k https://{{ vapp['guestinfo.ipaddress'] }}:6443/livez?verbose
|
||||
parser:
|
||||
name: ansible.utils.json
|
||||
set_fact: api_readycheck
|
||||
ignore_errors: yes
|
||||
until: api_readycheck.apiVersion is defined
|
||||
retries: 3
|
||||
delay: 30
|
||||
|
||||
- name: Install kubectl tab-completion
|
||||
ansible.builtin.shell:
|
||||
cmd: kubectl completion bash | tee /etc/bash_completion.d/kubectl
|
||||
|
||||
- name: Extract container images
|
||||
ansible.builtin.unarchive:
|
||||
src: /opt/metacluster/container-images/image-tarballs.tgz
|
||||
dest: /opt/metacluster/container-images
|
||||
list_files: yes
|
||||
register: imagetarballs
|
||||
|
||||
- name: Import container images
|
||||
ansible.builtin.command:
|
||||
cmd: k3s ctr image import {{ item }}
|
||||
chdir: /opt/metacluster/container-images
|
||||
loop: "{{ imagetarballs.files }}"
|
||||
|
||||
- name: Initialize tempfile
|
||||
ansible.builtin.tempfile:
|
||||
state: file
|
||||
register: kubeconfig
|
||||
|
||||
- name: Retrieve kubeconfig
|
||||
ansible.builtin.command:
|
||||
cmd: kubectl config view --raw
|
||||
register: kubectl_config
|
||||
|
||||
- name: Store kubeconfig in tempfile
|
||||
ansible.builtin.copy:
|
||||
dest: "{{ kubeconfig.path }}"
|
||||
content: "{{ kubectl_config.stdout }}"
|
||||
mode: 0600
|
||||
no_log: true
|
||||
|
||||
- name: Configure traefik dashboard ingress
|
||||
ansible.builtin.template:
|
||||
src: ingressroute.j2
|
||||
dest: /var/lib/rancher/k3s/server/manifests/{{ _template.name }}-manifest.yaml
|
||||
owner: root
|
||||
group: root
|
||||
mode: 0600
|
||||
vars:
|
||||
_template:
|
||||
name: traefik-dashboard
|
||||
namespace: kube-system
|
||||
config: |2
|
||||
entryPoints:
|
||||
- web
|
||||
- websecure
|
||||
routes:
|
||||
- kind: Rule
|
||||
match: Host(`ingress.{{ vapp['metacluster.fqdn'] }}`)
|
||||
services:
|
||||
- kind: TraefikService
|
||||
name: api@internal
|
||||
notify:
|
||||
- Apply manifests
|
||||
|
||||
- name: Trigger handlers
|
||||
ansible.builtin.meta: flush_handlers
|
||||
|
||||
- name: Create component entries in /etc/hosts
|
||||
ansible.builtin.lineinfile:
|
||||
path: /etc/hosts
|
||||
line: "{{ vapp['guestinfo.ipaddress'] }} {{ item + '.' + vapp['metacluster.fqdn'] }}"
|
||||
state: present
|
||||
loop:
|
||||
# TODO: Make this list dynamic
|
||||
- git
|
||||
- gitops
|
||||
- ingress
|
||||
- registry
|
||||
- storage
|
||||
|
||||
- name: Install longhorn chart
|
||||
kubernetes.core.helm:
|
||||
name: longhorn
|
||||
chart_ref: /opt/metacluster/helm-charts/longhorn
|
||||
release_namespace: longhorn-system
|
||||
create_namespace: yes
|
||||
wait: yes
|
||||
kubeconfig: "{{ kubeconfig.path }}"
|
||||
values: "{{ components.longhorn.chart_values }}"
|
||||
|
||||
- name: Install step-ca chart
|
||||
kubernetes.core.helm:
|
||||
name: step-certificates
|
||||
chart_ref: /opt/metacluster/helm-charts/step-certificates
|
||||
release_namespace: step-ca
|
||||
create_namespace: yes
|
||||
wait: yes
|
||||
kubeconfig: "{{ kubeconfig.path }}"
|
||||
values: "{{ components.stepcertificates.chart_values }}"
|
||||
|
||||
- name: Retrieve step-ca configuration
|
||||
kubernetes.core.k8s_log:
|
||||
kind: Job
|
||||
name: step-certificates
|
||||
namespace: step-ca
|
||||
kubeconfig: "{{ kubeconfig.path }}"
|
||||
register: stepca_bootstraplog
|
||||
|
||||
- name: Install root CA in system truststore
|
||||
ansible.builtin.shell:
|
||||
cmd: |
|
||||
step ca bootstrap \
|
||||
--ca-url={{ stepca_bootstraplog.log | regex_search('CA URL: (.+)', '\\1') | first }} \
|
||||
--fingerprint={{ stepca_bootstraplog.log | regex_search('CA Fingerprint: (.+)', '\\1') | first }} \
|
||||
--install
|
||||
|
||||
- name: Install harbor chart
|
||||
kubernetes.core.helm:
|
||||
name: harbor
|
||||
chart_ref: /opt/metacluster/helm-charts/harbor
|
||||
release_namespace: harbor
|
||||
create_namespace: yes
|
||||
wait: yes
|
||||
kubeconfig: "{{ kubeconfig.path }}"
|
||||
values: "{{ components.harbor.chart_values }}"
|
||||
|
||||
- name: Push images to registry
|
||||
ansible.builtin.shell:
|
||||
cmd: >-
|
||||
skopeo copy \
|
||||
--insecure-policy \
|
||||
--dest-tls-verify=false \
|
||||
--dest-creds admin:{{ vapp['guestinfo.rootpw'] }} \
|
||||
docker-archive:./{{ item }} \
|
||||
docker://registry.{{ vapp['metacluster.fqdn'] }}/library/$( \
|
||||
skopeo list-tags \
|
||||
--insecure-policy \
|
||||
docker-archive:./{{ item }} | \
|
||||
jq -r '.Tags[0]')
|
||||
chdir: /opt/metacluster/container-images/
|
||||
loop: "{{ imagetarballs.files }}"
|
||||
|
||||
- name: Configure K3s node for private registry
|
||||
ansible.builtin.template:
|
||||
dest: /etc/rancher/k3s/registries.yaml
|
||||
src: registries.j2
|
||||
notify:
|
||||
- Apply manifests
|
||||
|
||||
- name: Trigger handlers
|
||||
ansible.builtin.meta: flush_handlers
|
||||
|
||||
- block:
|
||||
|
||||
- name: Install gitea chart
|
||||
kubernetes.core.helm:
|
||||
name: gitea
|
||||
chart_ref: /opt/metacluster/helm-charts/gitea
|
||||
release_namespace: gitea
|
||||
create_namespace: yes
|
||||
wait: yes
|
||||
kubeconfig: "{{ kubeconfig.path }}"
|
||||
values: "{{ components.gitea.chart_values }}"
|
||||
|
||||
- name: Configure additional SSH ingress
|
||||
ansible.builtin.template:
|
||||
src: ingressroutetcp.j2
|
||||
dest: /var/lib/rancher/k3s/server/manifests/{{ _template.name }}-manifest.yaml
|
||||
owner: root
|
||||
group: root
|
||||
mode: 0600
|
||||
vars:
|
||||
_template:
|
||||
name: gitea-ssh
|
||||
namespace: gitea
|
||||
config: |2
|
||||
entryPoints:
|
||||
- ssh
|
||||
routes:
|
||||
- match: HostSNI(`*`)
|
||||
services:
|
||||
- name: gitea-ssh
|
||||
port: 22
|
||||
notify:
|
||||
- Apply manifests
|
||||
|
||||
- name: Trigger handlers
|
||||
ansible.builtin.meta: flush_handlers
|
||||
|
||||
- name: Ensure gitea API availability
|
||||
ansible.utils.cli_parse:
|
||||
# Available from Gitea 1.17.x
|
||||
# command: curl -k https://git.{{ vapp['metacluster.fqdn'] }}/api/healtz
|
||||
command: curl -k https://git.{{ vapp['metacluster.fqdn'] }}/api/v1/version
|
||||
parser:
|
||||
name: ansible.utils.json
|
||||
set_fact: api_readycheck
|
||||
ignore_errors:
|
||||
until: api_readycheck.version is defined
|
||||
retries: 3
|
||||
delay: 30
|
||||
|
||||
- name: Generate gitea API token
|
||||
ansible.builtin.uri:
|
||||
url: https://git.{{ vapp['metacluster.fqdn'] }}/api/v1/users/administrator/tokens
|
||||
method: POST
|
||||
user: administrator
|
||||
password: "{{ vapp['guestinfo.rootpw'] }}"
|
||||
force_basic_auth: yes
|
||||
body:
|
||||
name: token_init_{{ lookup('password', '/dev/null length=5 chars=ascii_letters,digits') }}
|
||||
register: gitea_api_token
|
||||
|
||||
- name: Retrieve existing gitea configuration
|
||||
ansible.builtin.uri:
|
||||
url: https://git.{{ vapp['metacluster.fqdn'] }}/api/v1/repos/search
|
||||
method: GET
|
||||
register: gitea_existing_config
|
||||
|
||||
- ansible.builtin.uri:
|
||||
url: "{{ item }}"
|
||||
method: GET
|
||||
headers:
|
||||
Authorization: token {{ gitea_api_token.json.sha1 }}
|
||||
register: gitea_existing_config_TST
|
||||
loop:
|
||||
# - https://git.{{ vapp['metacluster.fqdn'] }}/api/v1/orgs
|
||||
- https://git.{{ vapp['metacluster.fqdn'] }}/api/v1/repos/search
|
||||
- https://git.{{ vapp['metacluster.fqdn'] }}/api/v1/users/administrator/keys
|
||||
ignore_errors: yes
|
||||
- debug:
|
||||
var: gitea_existing_config_TST.results[].json.data
|
||||
|
||||
- block:
|
||||
|
||||
- name: Register SSH public key
|
||||
ansible.builtin.uri:
|
||||
url: https://git.{{ vapp['metacluster.fqdn'] }}/api/v1/user/keys
|
||||
method: POST
|
||||
headers:
|
||||
Authorization: token {{ gitea_api_token.json.sha1 }}
|
||||
body:
|
||||
key: "{{ gitops_sshkey.public_key }}"
|
||||
read_only: false
|
||||
title: GitOps
|
||||
|
||||
- name: Create organization(s)
|
||||
ansible.builtin.uri:
|
||||
url: https://git.{{ vapp['metacluster.fqdn'] }}/api/v1/orgs
|
||||
method: POST
|
||||
headers:
|
||||
Authorization: token {{ gitea_api_token.json.sha1 }}
|
||||
body: "{{ item }}"
|
||||
loop:
|
||||
- full_name: Meta-cluster
|
||||
description: Meta-cluster configuration items
|
||||
username: mc
|
||||
website: https://git.{{ vapp['metacluster.fqdn'] }}/mc
|
||||
location: '[...]'
|
||||
visibility: public
|
||||
- full_name: Workload-cluster
|
||||
description: Workload-cluster configuration items
|
||||
username: wl
|
||||
website: https://git.{{ vapp['metacluster.fqdn'] }}/wl
|
||||
location: '[...]'
|
||||
visibility: public
|
||||
loop_control:
|
||||
label: "{{ item.full_name }}"
|
||||
|
||||
- name: Create repositories
|
||||
ansible.builtin.uri:
|
||||
url: https://git.{{ vapp['metacluster.fqdn'] }}/api/v1/orgs/{{ item.organization }}/repos
|
||||
method: POST
|
||||
headers:
|
||||
Authorization: token {{ gitea_api_token.json.sha1 }}
|
||||
body: "{{ item.body }}"
|
||||
loop:
|
||||
- organization: mc
|
||||
body:
|
||||
name: GitOps.Config
|
||||
# auto_init: true
|
||||
# default_branch: main
|
||||
description: GitOps manifests
|
||||
- organization: wl
|
||||
body:
|
||||
name: Template.GitOps.Config
|
||||
# auto_init: true
|
||||
# default_branch: main
|
||||
description: GitOps manifests
|
||||
loop_control:
|
||||
label: "{{ item.organization + '/' + item.body.name }}"
|
||||
|
||||
- name: Rebase/Push source gitops repository
|
||||
ansible.builtin.shell:
|
||||
cmd: |
|
||||
git config --local http.sslVerify false
|
||||
git remote set-url origin https://administrator:{{ vapp['guestinfo.rootpw'] | urlencode }}@git.{{ vapp['metacluster.fqdn'] }}/mc/GitOps.Config.git
|
||||
git push
|
||||
chdir: /opt/metacluster/git-repositories/gitops
|
||||
|
||||
when: (gitea_existing_config.json is undefined) or (gitea_existing_config.json.data | length == 0)
|
||||
|
||||
module_defaults:
|
||||
ansible.builtin.uri:
|
||||
validate_certs: no
|
||||
status_code: [200, 201]
|
||||
body_format: json
|
||||
|
||||
- block:
|
||||
|
||||
- name: Install argo-cd chart
|
||||
kubernetes.core.helm:
|
||||
name: argo-cd
|
||||
chart_ref: /opt/metacluster/helm-charts/argo-cd
|
||||
release_namespace: argo-cd
|
||||
create_namespace: yes
|
||||
wait: yes
|
||||
kubeconfig: "{{ kubeconfig.path }}"
|
||||
values: "{{ components.argocd.chart_values }}"
|
||||
|
||||
- name: Ensure argo-cd API availability
|
||||
ansible.utils.cli_parse:
|
||||
command: curl -k https://gitops.{{ vapp['metacluster.fqdn'] }}/api/version
|
||||
parser:
|
||||
name: ansible.utils.json
|
||||
set_fact: api_readycheck
|
||||
ignore_errors:
|
||||
until: api_readycheck.Version is defined
|
||||
retries: 3
|
||||
delay: 30
|
||||
|
||||
- name: Generate argo-cd API token
|
||||
ansible.builtin.uri:
|
||||
url: https://gitops.{{ vapp['metacluster.fqdn'] }}/api/v1/session
|
||||
method: POST
|
||||
force_basic_auth: yes
|
||||
body:
|
||||
username: admin
|
||||
password: "{{ vapp['guestinfo.rootpw'] }}"
|
||||
register: argocd_api_token
|
||||
|
||||
# - name: Create umbrella application
|
||||
# ansible.builtin.template:
|
||||
#
|
||||
- name: Configure metacluster-gitops repository
|
||||
ansible.builtin.template:
|
||||
src: gitrepo.j2
|
||||
dest: /var/lib/rancher/k3s/server/manifests/{{ _template.name }}-manifest.yaml
|
||||
owner: root
|
||||
group: root
|
||||
mode: 0600
|
||||
vars:
|
||||
_template:
|
||||
name: argocd-gitrepo-metacluster
|
||||
namespace: argo-cd
|
||||
uid: "{{ lookup('ansible.builtin.password', '/dev/null length=5 chars=ascii_lowercase,digits seed=inventory_hostname') }}"
|
||||
privatekey: "{{ lookup('ansible.builtin.file', '~/.ssh/git_rsa_id') | indent(4, true) }}"
|
||||
notify:
|
||||
- Apply manifests
|
||||
|
||||
- name: Trigger handlers
|
||||
ansible.builtin.meta: flush_handlers
|
||||
|
||||
module_defaults:
|
||||
ansible.builtin.uri:
|
||||
validate_certs: no
|
||||
status_code: [200, 201]
|
||||
body_format: json
|
||||
|
||||
- name: Delete container image tarballs/archives
|
||||
ansible.builtin.file:
|
||||
path: "{{ item }}"
|
||||
state: absent
|
||||
with_fileglob: /opt/metacluster/container-images/*.tar
|
||||
|
||||
- name: Cleanup tempfile
|
||||
ansible.builtin.file:
|
||||
path: "{{ kubeconfig.path }}"
|
||||
state: absent
|
||||
when: kubeconfig.path is defined
|
||||
@@ -0,0 +1,12 @@
|
||||
- name: Set hostname
|
||||
ansible.builtin.hostname:
|
||||
name: "{{ vapp['guestinfo.hostname'] }}"
|
||||
|
||||
- name: Create netplan configuration file
|
||||
ansible.builtin.template:
|
||||
src: netplan.j2
|
||||
dest: /etc/netplan/00-installer-config.yaml
|
||||
|
||||
- name: Apply netplan configuration
|
||||
ansible.builtin.shell:
|
||||
cmd: /usr/sbin/netplan apply
|
||||
@@ -0,0 +1,10 @@
|
||||
network:
|
||||
version: 2
|
||||
ethernets:
|
||||
ens192:
|
||||
addresses:
|
||||
- {{ vapp['guestinfo.ipaddress'] }}/{{ vapp['guestinfo.prefixlength'] }}
|
||||
gateway4: {{ vapp['guestinfo.gateway'] }}
|
||||
nameservers:
|
||||
addresses:
|
||||
- {{ vapp['guestinfo.dnsserver'] }}
|
||||
@@ -0,0 +1,20 @@
|
||||
- name: Create folder structure(s)
|
||||
ansible.builtin.file:
|
||||
path: "{{ item }}"
|
||||
state: directory
|
||||
loop:
|
||||
- /opt/firstboot
|
||||
|
||||
- name: Save tty script file
|
||||
ansible.builtin.template:
|
||||
src: tty.j2
|
||||
dest: /opt/firstboot/tty.sh
|
||||
owner: root
|
||||
group: root
|
||||
mode: 0700
|
||||
|
||||
- name: Create @reboot crontab job
|
||||
ansible.builtin.cron:
|
||||
name: tty.consolemessage
|
||||
special_time: reboot
|
||||
job: /opt/firstboot/tty.sh
|
||||
@@ -0,0 +1,37 @@
|
||||
- name: Set root password
|
||||
ansible.builtin.user:
|
||||
name: root
|
||||
password: "{{ vapp['guestinfo.rootpw'] | password_hash('sha512', 65534 | random(seed=vapp['guestinfo.hostname']) | string) }}"
|
||||
generate_ssh_key: yes
|
||||
ssh_key_bits: 2048
|
||||
ssh_key_file: .ssh/id_rsa
|
||||
|
||||
- name: Save root SSH publickey
|
||||
ansible.builtin.lineinfile:
|
||||
path: /root/.ssh/authorized_keys
|
||||
line: "{{ vapp['guestinfo.rootsshkey'] }}"
|
||||
|
||||
- name: Disable SSH password authentication
|
||||
ansible.builtin.lineinfile:
|
||||
path: /etc/ssh/sshd_config
|
||||
regex: "{{ item.regex }}"
|
||||
line: "{{ item.line }}"
|
||||
state: "{{ item.state }}"
|
||||
loop:
|
||||
- regex: '^#PasswordAuthentication'
|
||||
line: 'PasswordAuthentication no'
|
||||
state: present
|
||||
- regex: '^PasswordAuthentication yes'
|
||||
line: 'PasswordAuthentication yes'
|
||||
state: absent
|
||||
|
||||
- name: Create dedicated SSH keypair
|
||||
community.crypto.openssh_keypair:
|
||||
path: /root/.ssh/git_rsa_id
|
||||
register: gitops_sshkey
|
||||
|
||||
- name: Delete 'ubuntu' user
|
||||
ansible.builtin.user:
|
||||
name: ubuntu
|
||||
state: absent
|
||||
remove: yes
|
||||
@@ -0,0 +1,38 @@
|
||||
- name: Store current ovfEnvironment
|
||||
ansible.builtin.shell:
|
||||
cmd: /usr/bin/vmtoolsd --cmd "info-get guestinfo.ovfEnv"
|
||||
register: ovfenv
|
||||
|
||||
- name: Parse XML for MoRef ID
|
||||
community.general.xml:
|
||||
xmlstring: "{{ ovfenv.stdout }}"
|
||||
namespaces:
|
||||
ns: http://schemas.dmtf.org/ovf/environment/1
|
||||
ve: http://www.vmware.com/schema/ovfenv
|
||||
xpath: /ns:Environment
|
||||
content: attribute
|
||||
register: environment_attribute
|
||||
|
||||
- name: Store MoRef ID
|
||||
ansible.builtin.set_fact:
|
||||
moref_id: "{{ ((environment_attribute.matches[0].values() | list)[0].values() | list)[1] }}"
|
||||
|
||||
- name: Parse XML for vApp properties
|
||||
community.general.xml:
|
||||
xmlstring: "{{ ovfenv.stdout }}"
|
||||
namespaces:
|
||||
ns: http://schemas.dmtf.org/ovf/environment/1
|
||||
xpath: /ns:Environment/ns:PropertySection/ns:Property
|
||||
content: attribute
|
||||
register: property_section
|
||||
|
||||
- name: Assign vApp properties to dictionary
|
||||
ansible.builtin.set_fact:
|
||||
vapp: >-
|
||||
{{ vapp | default({}) | combine({
|
||||
((item.values() | list)[0].values() | list)[0]:
|
||||
((item.values() | list)[0].values() | list)[1]})
|
||||
}}
|
||||
loop: "{{ property_section.matches }}"
|
||||
loop_control:
|
||||
label: "{{ ((item.values() | list)[0].values() | list)[0] }}"
|
||||
@@ -0,0 +1,106 @@
|
||||
- block:
|
||||
|
||||
- name: Gather hypervisor details
|
||||
ansible.builtin.shell:
|
||||
cmd: govc ls -L {{ item.moref }} | awk -F/ '{print ${{ item.part }}}'
|
||||
environment:
|
||||
GOVC_INSECURE: '1'
|
||||
GOVC_URL: "{{ vapp['hv.fqdn'] }}"
|
||||
GOVC_USERNAME: "{{ vapp['hv.username'] }}"
|
||||
GOVC_PASSWORD: "{{ vapp['hv.password'] }}"
|
||||
register: govc_inventory
|
||||
loop:
|
||||
- attribute: cluster
|
||||
moref: >-
|
||||
$(govc object.collect -json VirtualMachine:{{ moref_id }} | \
|
||||
jq -r '.[] | select(.Name == "runtime").Val.Host | .Type + ":" + .Value')
|
||||
part: (NF-1)
|
||||
- attribute: datacenter
|
||||
moref: VirtualMachine:{{ moref_id }}
|
||||
part: 2
|
||||
- attribute: datastore
|
||||
moref: >-
|
||||
$(govc object.collect -json VirtualMachine:{{ moref_id }} | \
|
||||
jq -r '.[] | select(.Name == "datastore").Val.ManagedObjectReference | .[].Type + ":" + .[].Value')
|
||||
part: NF
|
||||
- attribute: folder
|
||||
moref: >-
|
||||
$(govc object.collect -json VirtualMachine:{{ moref_id }} | \
|
||||
jq -r '.[] | select(.Name == "parent").Val | .Type + ":" + .Value')
|
||||
part: 0
|
||||
# - attribute: host
|
||||
# moref: >-
|
||||
# $(govc object.collect -json VirtualMachine:{{ moref_id }} | \
|
||||
# jq -r '.[] | select(.Name == "runtime").Val.Host | .Type + ":" + .Value')
|
||||
# part: NF
|
||||
- attribute: network
|
||||
moref: >-
|
||||
$(govc object.collect -json VirtualMachine:{{ moref_id }} | \
|
||||
jq -r '.[] | select(.Name == "network").Val.ManagedObjectReference | .[].Type + ":" + .[].Value')
|
||||
part: NF
|
||||
loop_control:
|
||||
label: "{{ item.attribute }}"
|
||||
|
||||
- name: Store hypervisor details in dictionary
|
||||
ansible.builtin.set_fact:
|
||||
vcenter_info: "{{ vcenter_info | default({}) | combine({ item.item.attribute : item.stdout }) }}"
|
||||
loop: "{{ govc_inventory.results }}"
|
||||
loop_control:
|
||||
label: "{{ item.item.attribute }}"
|
||||
|
||||
- block:
|
||||
|
||||
- name: Check for existing templates on hypervisor
|
||||
community.vmware.vmware_guest_info:
|
||||
name: "{{ (item | basename | split('.'))[:-1] | join('.') }}"
|
||||
register: existing_ova
|
||||
with_fileglob: /opt/workloadcluster/node-templates/*.ova
|
||||
ignore_errors: yes
|
||||
|
||||
- name: Parse OVA files for network mappings
|
||||
ansible.builtin.shell:
|
||||
cmd: govc import.spec -json {{ item }}
|
||||
environment:
|
||||
GOVC_INSECURE: '1'
|
||||
GOVC_URL: "{{ vapp['hv.fqdn'] }}"
|
||||
GOVC_USERNAME: "{{ vapp['hv.username'] }}"
|
||||
GOVC_PASSWORD: "{{ vapp['hv.password'] }}"
|
||||
register: ova_spec
|
||||
when: existing_ova.results[index] is failed
|
||||
with_fileglob: /opt/workloadcluster/node-templates/*.ova
|
||||
loop_control:
|
||||
index_var: index
|
||||
|
||||
- name: Deploy OVA templates on hypervisor
|
||||
community.vmware.vmware_deploy_ovf:
|
||||
cluster: "{{ vcenter_info.cluster }}"
|
||||
datastore: "{{ vcenter_info.datastore }}"
|
||||
folder: "{{ vcenter_info.folder }}"
|
||||
name: "{{ (item | basename | split('.'))[:-1] | join('.') }}"
|
||||
networks: "{u'{{ ova_spec.results[index].stdout | from_json | json_query('NetworkMapping[0].Name') }}':u'{{ vcenter_info.network }}'}"
|
||||
allow_duplicates: no
|
||||
power_on: false
|
||||
ovf: "{{ item }}"
|
||||
register: ova_deploy
|
||||
when: existing_ova.results[index] is failed
|
||||
with_fileglob: /opt/workloadcluster/node-templates/*.ova
|
||||
loop_control:
|
||||
index_var: index
|
||||
|
||||
- name: Mark deployed VM's as templates
|
||||
community.vmware.vmware_guest:
|
||||
name: "{{ item.instance.hw_name }}"
|
||||
is_template: yes
|
||||
when: ova_deploy.results[index] is not skipped
|
||||
loop: "{{ ova_deploy.results }}"
|
||||
loop_control:
|
||||
index_var: index
|
||||
label: "{{ item.item }}"
|
||||
|
||||
module_defaults:
|
||||
group/vmware:
|
||||
hostname: "{{ vapp['hv.fqdn'] }}"
|
||||
validate_certs: no
|
||||
username: "{{ vapp['hv.username'] }}"
|
||||
password: "{{ vapp['hv.password'] }}"
|
||||
datacenter: "{{ vcenter_info.datacenter }}"
|
||||
@@ -0,0 +1,13 @@
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: {{ _template.name }}-{{ _template.uid }}
|
||||
namespace: {{ _template.namespace }}
|
||||
labels:
|
||||
argocd.argoproj.io/secret-type: repository
|
||||
stringData:
|
||||
url: ssh://git@gitea-ssh.gitea.svc.cluster.local/mc/GitOps.Config.git
|
||||
name: {{ _template.name }}
|
||||
insecure: 'true'
|
||||
sshPrivateKey: |
|
||||
{{ _template.privatekey }}
|
||||
@@ -0,0 +1,7 @@
|
||||
apiVersion: traefik.containo.us/v1alpha1
|
||||
kind: IngressRoute
|
||||
metadata:
|
||||
name: {{ _template.name }}
|
||||
namespace: {{ _template.namespace }}
|
||||
spec:
|
||||
{{ _template.config }}
|
||||
@@ -0,0 +1,7 @@
|
||||
apiVersion: traefik.containo.us/v1alpha1
|
||||
kind: IngressRouteTCP
|
||||
metadata:
|
||||
name: {{ _template.name }}
|
||||
namespace: {{ _template.namespace }}
|
||||
spec:
|
||||
{{ _template.config }}
|
||||
@@ -0,0 +1,25 @@
|
||||
mirrors:
|
||||
cr.step.cm:
|
||||
endpoint:
|
||||
- https://registry.{{ vapp['metacluster.fqdn'] }}
|
||||
rewrite:
|
||||
"(.*)": "library/cr.step.sm/$1"
|
||||
docker.io:
|
||||
endpoint:
|
||||
- https://registry.{{ vapp['metacluster.fqdn'] }}
|
||||
rewrite:
|
||||
"(.*)": "library/docker.io/$1"
|
||||
ghcr.io:
|
||||
endpoint:
|
||||
- https://registry.{{ vapp['metacluster.fqdn'] }}
|
||||
rewrite:
|
||||
"(.*)": "library/ghcr.io/$1"
|
||||
quay.io:
|
||||
endpoint:
|
||||
- https://registry.{{ vapp['metacluster.fqdn'] }}
|
||||
rewrite:
|
||||
"(.*)": "library/quay.io/$1"
|
||||
configs:
|
||||
registry.{{ vapp['metacluster.fqdn'] }}:
|
||||
tls:
|
||||
insecure_skip_verify: true
|
||||
@@ -0,0 +1,42 @@
|
||||
#!/bin/bash
|
||||
|
||||
export TERM=linux
|
||||
|
||||
BGRN='\033[1;92m'
|
||||
BGRY='\033[1;30m'
|
||||
BBLU='\033[1;34m'
|
||||
BRED='\033[1;91m'
|
||||
BWHI='\033[1;97m'
|
||||
CBLA='\033[?16;0;30c' # Hide blinking cursor
|
||||
DFLT='\033[0m' # Reset colour
|
||||
LCLR='\033[K' # Clear to end of line
|
||||
PRST='\033[0;0H' # Reset cursor position
|
||||
|
||||
# COMPONENTS=('ca' 'ingress' 'storage' 'registry' 'git' 'gitops')
|
||||
COMPONENTS=('ca' 'storage' 'registry' 'git' 'gitops')
|
||||
FQDN='{{ vapp['metacluster.fqdn'] }}'
|
||||
IPADDRESS='{{ vapp['guestinfo.ipaddress'] }}'
|
||||
|
||||
# Waiting to allow boot sequence to finish; crude!
|
||||
sleep 30
|
||||
clear > /dev/tty1
|
||||
|
||||
while /bin/true; do
|
||||
echo -e "${PRST}" > /dev/tty1
|
||||
echo -e "\n\n\t${DFLT}To manage this appliance, please connect to one of the following:${LCLR}\n" > /dev/tty1
|
||||
|
||||
for c in "${COMPONENTS[@]}"; do
|
||||
STATUS=$(curl -ks "https://${c}.${FQDN}" -o /dev/null -w '%{http_code}')
|
||||
|
||||
if [[ "${STATUS}" -eq "200" ]]; then
|
||||
echo -e "\t [${BGRN}+${DFLT}] ${BBLU}https://${c}.${FQDN}${DFLT}${LCLR}" > /dev/tty1
|
||||
else
|
||||
echo -e "\t [${BRED}-${DFLT}] ${BBLU}https://${c}.${FQDN}${DFLT}${LCLR}" > /dev/tty1
|
||||
fi
|
||||
done
|
||||
|
||||
echo -e "\n\t${BGRY}Note that your DNS zone ${DFLT}must have${BGRY} respective records defined,\n\teach pointing to: ${DFLT}${IPADDRESS}${LCLR}" > /dev/tty1
|
||||
|
||||
echo -e "${CBLA}" > /dev/tty1
|
||||
sleep 1
|
||||
done
|
||||
26
ansible/roles/firstboot/tasks/main.yml
Normal file
26
ansible/roles/firstboot/tasks/main.yml
Normal file
@@ -0,0 +1,26 @@
|
||||
- name: Create destination folder
|
||||
ansible.builtin.file:
|
||||
path: /opt/firstboot
|
||||
state: directory
|
||||
|
||||
- name: Create firstboot script file
|
||||
ansible.builtin.template:
|
||||
src: firstboot.j2
|
||||
dest: /opt/firstboot/firstboot.sh
|
||||
owner: root
|
||||
group: root
|
||||
mode: o+x
|
||||
|
||||
- name: Create @reboot crontab job
|
||||
ansible.builtin.cron:
|
||||
name: firstboot
|
||||
special_time: reboot
|
||||
job: "/opt/firstboot/firstboot.sh >/dev/tty1 2>&1"
|
||||
|
||||
- name: Copy payload folder
|
||||
ansible.builtin.copy:
|
||||
src: ansible_payload/
|
||||
dest: /opt/firstboot/ansible/
|
||||
owner: root
|
||||
group: root
|
||||
mode: '0644'
|
||||
4
ansible/roles/firstboot/templates/firstboot.j2
Normal file
4
ansible/roles/firstboot/templates/firstboot.j2
Normal file
@@ -0,0 +1,4 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Apply firstboot configuration w/ ansible
|
||||
/usr/local/bin/ansible-playbook /opt/firstboot/ansible/playbook.yml | tee -a /var/log/firstboot.log > /dev/tty1 2>&1
|
||||
4
ansible/roles/metacluster/tasks/cleanup.yml
Normal file
4
ansible/roles/metacluster/tasks/cleanup.yml
Normal file
@@ -0,0 +1,4 @@
|
||||
- name: Zero-out disk
|
||||
ansible.builtin.shell:
|
||||
cmd: nice -n 10 dd bs=1M count=$(df -m . | awk '/[0-9]%/{print $(NF-2)}') if=/dev/zero of=./zero; sync; sync; rm -f ./zero
|
||||
chdir: /opt/metacluster
|
||||
75
ansible/roles/metacluster/tasks/components.yml
Normal file
75
ansible/roles/metacluster/tasks/components.yml
Normal file
@@ -0,0 +1,75 @@
|
||||
- name: Create folder structure(s)
|
||||
ansible.builtin.file:
|
||||
path: "{{ item }}"
|
||||
state: directory
|
||||
loop:
|
||||
- /opt/metacluster/helm-charts
|
||||
- /opt/metacluster/container-images
|
||||
|
||||
- name: Add helm repositories
|
||||
kubernetes.core.helm_repository:
|
||||
name: "{{ item.name }}"
|
||||
repo_url: "{{ item.url }}"
|
||||
state: present
|
||||
loop: "{{ platform.helm_repositories }}"
|
||||
|
||||
- name: Fetch helm charts
|
||||
ansible.builtin.command:
|
||||
cmd: helm fetch {{ item.value.helm.chart }} --untar --version {{ item.value.helm.version }}
|
||||
chdir: /opt/metacluster/helm-charts
|
||||
loop: "{{ lookup('ansible.builtin.dict', components) }}"
|
||||
loop_control:
|
||||
label: "{{ item.key }}"
|
||||
|
||||
- block:
|
||||
|
||||
- name: Aggregate chart_values into dict
|
||||
ansible.builtin.set_fact:
|
||||
chart_values: "{{ chart_values | default({}) | combine({ (item.key | regex_replace('[^A-Za-z0-9]', '')): { 'chart_values': (item.value.helm.chart_values | from_yaml) } }) }}"
|
||||
when: item.value.helm.chart_values is defined
|
||||
loop: "{{ lookup('ansible.builtin.dict', components) }}"
|
||||
loop_control:
|
||||
label: "{{ item.key }}"
|
||||
|
||||
- name: Write dict to vars_file
|
||||
ansible.builtin.copy:
|
||||
dest: /opt/firstboot/ansible/vars/metacluster.yml
|
||||
content: "{{ { 'components': chart_values } | to_nice_yaml(indent=2, width=4096) }}"
|
||||
|
||||
- name: Parse helm charts for container images
|
||||
ansible.builtin.shell:
|
||||
cmd: "{{ item.value.helm.parse_logic }}"
|
||||
chdir: /opt/metacluster/helm-charts/{{ item.key }}
|
||||
register: containerimages
|
||||
loop: "{{ lookup('ansible.builtin.dict', components) }}"
|
||||
loop_control:
|
||||
label: "{{ item.key }}"
|
||||
|
||||
- name: Pull and store containerimages
|
||||
ansible.builtin.shell:
|
||||
cmd: >-
|
||||
skopeo copy \
|
||||
--insecure-policy \
|
||||
--retry-times=5 \
|
||||
docker://{{ item }} \
|
||||
docker-archive:./{{ ( item | regex_findall('[^/:]+'))[-2] }}.tar:{{ item }}
|
||||
chdir: /opt/metacluster/container-images
|
||||
loop: "{{ ((containerimages.results | map(attribute='stdout_lines') | flatten) + dependencies.container_images) | unique }}"
|
||||
|
||||
# - name: Inject manifests
|
||||
# ansible.builtin.template:
|
||||
# src: "{{ item.type }}.j2"
|
||||
# dest: /var/lib/rancher/k3s/server/manifests/{{ item.name }}-manifest.yaml
|
||||
# owner: root
|
||||
# group: root
|
||||
# mode: 0600
|
||||
# loop: "{{ lookup('ansible.builtin.dict', components) | map(attribute='value.manifests') | list | select('defined') | flatten }}"
|
||||
# loop_control:
|
||||
# label: "{{ item.type + '/' + item.name }}"
|
||||
|
||||
- name: Compress tarballs
|
||||
community.general.archive:
|
||||
dest: /opt/metacluster/container-images/image-tarballs.tgz
|
||||
path: /opt/metacluster/container-images/*
|
||||
format: xz
|
||||
remove: yes
|
||||
40
ansible/roles/metacluster/tasks/k3s.yml
Normal file
40
ansible/roles/metacluster/tasks/k3s.yml
Normal file
@@ -0,0 +1,40 @@
|
||||
- name: Create folder structure(s)
|
||||
ansible.builtin.file:
|
||||
path: "{{ item }}"
|
||||
state: directory
|
||||
loop:
|
||||
- /var/lib/rancher/k3s/agent/images
|
||||
- /var/lib/rancher/k3s/server/manifests
|
||||
- /opt/metacluster/k3s
|
||||
|
||||
- name: Download & install K3s binary
|
||||
ansible.builtin.get_url:
|
||||
url: https://github.com/k3s-io/k3s/releases/download/{{ platform.k3s.version }}/k3s
|
||||
dest: /usr/local/bin/k3s
|
||||
owner: root
|
||||
group: root
|
||||
mode: 0755
|
||||
|
||||
- name: Download K3s images tarball
|
||||
ansible.builtin.get_url:
|
||||
url: https://github.com/k3s-io/k3s/releases/download/{{ platform.k3s.version }}/k3s-airgap-images-amd64.tar.gz
|
||||
dest: /var/lib/rancher/k3s/agent/images
|
||||
|
||||
- name: Download K3s install script
|
||||
ansible.builtin.get_url:
|
||||
url: https://get.k3s.io
|
||||
dest: /opt/metacluster/k3s/install.sh
|
||||
owner: root
|
||||
group: root
|
||||
mode: 0755
|
||||
|
||||
- name: Inject manifests
|
||||
ansible.builtin.template:
|
||||
src: helmchartconfig.j2
|
||||
dest: /var/lib/rancher/k3s/server/manifests/{{ item.name }}-config.yaml
|
||||
owner: root
|
||||
group: root
|
||||
mode: 0600
|
||||
loop: "{{ platform.packaged_components }}"
|
||||
loop_control:
|
||||
label: "{{ item.name }}"
|
||||
11
ansible/roles/metacluster/tasks/main.yml
Normal file
11
ansible/roles/metacluster/tasks/main.yml
Normal file
@@ -0,0 +1,11 @@
|
||||
- name: Pre-stage K3s components
|
||||
import_tasks: k3s.yml
|
||||
|
||||
- name: Pre-stage meta-cluster components
|
||||
import_tasks: components.yml
|
||||
|
||||
- name: Pre-stage meta-cluster configuration and workload-cluster components
|
||||
import_tasks: staging.yml
|
||||
|
||||
- name: Cleanup
|
||||
import_tasks: cleanup.yml
|
||||
21
ansible/roles/metacluster/tasks/staging.yml
Normal file
21
ansible/roles/metacluster/tasks/staging.yml
Normal file
@@ -0,0 +1,21 @@
|
||||
- name: Create folder structure(s)
|
||||
ansible.builtin.file:
|
||||
path: "{{ item }}"
|
||||
state: directory
|
||||
loop:
|
||||
- /opt/metacluster/git-repositories/gitops
|
||||
- /opt/workloadcluster/node-templates
|
||||
|
||||
- name: Clone git repository
|
||||
ansible.builtin.git:
|
||||
repo: "{{ platform.gitops.repository.uri }}"
|
||||
version: "{{ platform.gitops.repository.revision }}"
|
||||
dest: /opt/metacluster/git-repositories/gitops
|
||||
|
||||
- name: Download node-template images
|
||||
ansible.builtin.uri:
|
||||
url: "{{ item.url }}"
|
||||
dest: /opt/workloadcluster/node-templates/{{ downstream.node_templates.prefix }}{{ item.name }}
|
||||
loop: "{{ downstream.node_templates.images }}"
|
||||
loop_control:
|
||||
label: "{{ downstream.node_templates.prefix }}{{ item.name }}"
|
||||
8
ansible/roles/metacluster/templates/helmchartconfig.j2
Normal file
8
ansible/roles/metacluster/templates/helmchartconfig.j2
Normal file
@@ -0,0 +1,8 @@
|
||||
apiVersion: helm.cattle.io/v1
|
||||
kind: HelmChartConfig
|
||||
metadata:
|
||||
name: {{ item.name }}
|
||||
namespace: {{ item.namespace }}
|
||||
spec:
|
||||
valuesContent: |-
|
||||
{{ item.config }}
|
||||
13
ansible/roles/os/tasks/cloud-init.yml
Normal file
13
ansible/roles/os/tasks/cloud-init.yml
Normal file
@@ -0,0 +1,13 @@
|
||||
- name: Delete cloud-init package
|
||||
ansible.builtin.apt:
|
||||
name: cloud-init
|
||||
state: absent
|
||||
purge: yes
|
||||
|
||||
- name: Delete cloud-init files
|
||||
ansible.builtin.file:
|
||||
path: "{{ item }}"
|
||||
state: absent
|
||||
loop:
|
||||
- /etc/cloud
|
||||
- /var/lib/cloud
|
||||
5
ansible/roles/os/tasks/logging.yml
Normal file
5
ansible/roles/os/tasks/logging.yml
Normal file
@@ -0,0 +1,5 @@
|
||||
- name: Enable crontab logging
|
||||
ansible.builtin.lineinfile:
|
||||
path: /etc/rsyslog.d/50-default.conf
|
||||
regexp: '^#cron\.\*.*'
|
||||
line: "cron.*\t\t\t\t./var/log/cron.log"
|
||||
17
ansible/roles/os/tasks/main.yml
Normal file
17
ansible/roles/os/tasks/main.yml
Normal file
@@ -0,0 +1,17 @@
|
||||
- name: Disable tty logins
|
||||
import_tasks: tty.yml
|
||||
|
||||
- name: Remove snapd
|
||||
import_tasks: snapd.yml
|
||||
|
||||
- name: Remove cloud-init
|
||||
import_tasks: cloud-init.yml
|
||||
|
||||
- name: Configure default logging
|
||||
import_tasks: logging.yml
|
||||
|
||||
- name: Configure services
|
||||
import_tasks: services.yml
|
||||
|
||||
- name: Install packages
|
||||
import_tasks: packages.yml
|
||||
24
ansible/roles/os/tasks/packages.yml
Normal file
24
ansible/roles/os/tasks/packages.yml
Normal file
@@ -0,0 +1,24 @@
|
||||
- name: Install additional packages
|
||||
ansible.builtin.apt:
|
||||
pkg: "{{ packages.apt }}"
|
||||
state: latest
|
||||
update_cache: yes
|
||||
install_recommends: no
|
||||
|
||||
- name: Upgrade all packages
|
||||
ansible.builtin.apt:
|
||||
name: '*'
|
||||
state: latest
|
||||
update_cache: yes
|
||||
|
||||
- name: Install additional python packages
|
||||
ansible.builtin.pip:
|
||||
name: "{{ item }}"
|
||||
executable: pip3
|
||||
state: latest
|
||||
loop: "{{ packages.pip }}"
|
||||
|
||||
- name: Cleanup
|
||||
ansible.builtin.apt:
|
||||
autoremove: yes
|
||||
purge: yes
|
||||
5
ansible/roles/os/tasks/services.yml
Normal file
5
ansible/roles/os/tasks/services.yml
Normal file
@@ -0,0 +1,5 @@
|
||||
- name: Disable & mask networkd-wait-online
|
||||
ansible.builtin.systemd:
|
||||
name: systemd-networkd-wait-online
|
||||
enabled: no
|
||||
masked: yes
|
||||
19
ansible/roles/os/tasks/snapd.yml
Normal file
19
ansible/roles/os/tasks/snapd.yml
Normal file
@@ -0,0 +1,19 @@
|
||||
- name: Delete snapd package
|
||||
ansible.builtin.apt:
|
||||
name: snapd
|
||||
state: absent
|
||||
purge: yes
|
||||
|
||||
- name: Delete leftover files
|
||||
ansible.builtin.file:
|
||||
path: /root/snap
|
||||
state: absent
|
||||
|
||||
- name: Hold snapd package
|
||||
ansible.builtin.dpkg_selections:
|
||||
name: snapd
|
||||
selection: hold
|
||||
|
||||
- name: Reload systemd unit configurations
|
||||
ansible.builtin.systemd:
|
||||
daemon_reload: yes
|
||||
16
ansible/roles/os/tasks/tty.yml
Normal file
16
ansible/roles/os/tasks/tty.yml
Normal file
@@ -0,0 +1,16 @@
|
||||
- name: Disable extra tty
|
||||
ansible.builtin.lineinfile:
|
||||
path: /etc/systemd/logind.conf
|
||||
regexp: "{{ item.regexp }}"
|
||||
line: "{{ item.line }}"
|
||||
loop:
|
||||
- regexp: '^#NAutoVTs='
|
||||
line: 'NAutoVTs=1'
|
||||
- regexp: '^#ReserveVT='
|
||||
line: 'ReserveVT=11'
|
||||
|
||||
- name: Mask getty@tty1 service
|
||||
ansible.builtin.systemd:
|
||||
name: getty@tty1
|
||||
enabled: no
|
||||
masked: yes
|
||||
12
ansible/roles/os/vars/main.yml
Normal file
12
ansible/roles/os/vars/main.yml
Normal file
@@ -0,0 +1,12 @@
|
||||
packages:
|
||||
apt:
|
||||
- jq
|
||||
- python3-pip
|
||||
pip:
|
||||
- ansible-core
|
||||
- jinja2
|
||||
- lxml
|
||||
- markupsafe
|
||||
- pip
|
||||
- setuptools
|
||||
- wheel
|
||||
192
ansible/vars/metacluster.yml
Normal file
192
ansible/vars/metacluster.yml
Normal file
@@ -0,0 +1,192 @@
|
||||
platform:
|
||||
|
||||
k3s:
|
||||
version: v1.24.1+k3s1
|
||||
|
||||
gitops:
|
||||
repository:
|
||||
uri: https://code.spamasaurus.com/djpbessems/GitOps.MetaCluster.git
|
||||
# revision: v0.1.0
|
||||
revision: HEAD
|
||||
|
||||
packaged_components:
|
||||
- name: traefik
|
||||
namespace: kube-system
|
||||
config: |2
|
||||
additionalArguments:
|
||||
- "--certificatesResolvers.stepca.acme.caserver=https://step-certificates.step-ca.svc.cluster.local/acme/acme/directory"
|
||||
- "--certificatesResolvers.stepca.acme.email=admin"
|
||||
- "--certificatesResolvers.stepca.acme.httpChallenge=true"
|
||||
- "--certificatesResolvers.stepca.acme.httpChallenge.entryPoint=web"
|
||||
- "--certificatesResolvers.stepca.acme.storage=/data/acme.json"
|
||||
- "--certificatesResolvers.stepca.acme.tlsChallenge=true"
|
||||
globalArguments: []
|
||||
ingressRoute:
|
||||
dashboard:
|
||||
enabled: false
|
||||
ports:
|
||||
ssh:
|
||||
port: 8022
|
||||
protocol: TCP
|
||||
web:
|
||||
redirectTo: websecure
|
||||
|
||||
helm_repositories:
|
||||
- name: longhorn
|
||||
url: https://charts.longhorn.io
|
||||
- name: harbor
|
||||
url: https://helm.goharbor.io
|
||||
- name: gitea-charts
|
||||
url: https://dl.gitea.io/charts/
|
||||
- name: argo
|
||||
url: https://argoproj.github.io/argo-helm
|
||||
- name: sealed-secrets
|
||||
url: https://bitnami-labs.github.io/sealed-secrets
|
||||
- name: smallstep
|
||||
url: https://smallstep.github.io/helm-charts/
|
||||
|
||||
components:
|
||||
|
||||
longhorn:
|
||||
helm:
|
||||
version: 1.3.0
|
||||
chart: longhorn/longhorn
|
||||
parse_logic: cat values.yaml | yq eval '.. | select(has("repository")) | .repository + ":" + .tag'
|
||||
chart_values: !unsafe |
|
||||
defaultSettings:
|
||||
defaultDataPath: /mnt/blockstorage
|
||||
defaultReplicaCount: 1
|
||||
ingress:
|
||||
enabled: true
|
||||
host: storage.{{ vapp['metacluster.fqdn'] }}
|
||||
persistence:
|
||||
defaultClassReplicaCount: 1
|
||||
|
||||
step-certificates:
|
||||
helm:
|
||||
version: 1.18.2+20220324
|
||||
chart: smallstep/step-certificates
|
||||
parse_logic: helm template . | yq --no-doc eval '.. | .image? | select(.)' | sed '/:/!s/$/:latest/' | sort -u
|
||||
chart_values: !unsafe |
|
||||
inject:
|
||||
secrets:
|
||||
ca_password: "{{ vapp['guestinfo.rootpw'] | b64encode }}"
|
||||
provisioner_password: "{{ vapp['guestinfo.rootpw'] | b64encode }}"
|
||||
ingress:
|
||||
enabled: true
|
||||
hosts:
|
||||
- host: ca.{{ vapp['metacluster.fqdn'] }}
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
service:
|
||||
targetPort: 9000
|
||||
|
||||
harbor:
|
||||
helm:
|
||||
version: 1.9.1 # (= Harbor v2.5.1)
|
||||
chart: harbor/harbor
|
||||
parse_logic: helm template . | yq --no-doc eval '.. | .image? | select(.)' | sort -u | awk '!/ /'
|
||||
chart_values: !unsafe |
|
||||
expose:
|
||||
ingress:
|
||||
annotations: {}
|
||||
hosts:
|
||||
core: registry.{{ vapp['metacluster.fqdn'] }}
|
||||
tls:
|
||||
certSource: none
|
||||
enabled: false
|
||||
externalURL: https://registry.{{ vapp['metacluster.fqdn'] }}
|
||||
harborAdminPassword: "{{ vapp['guestinfo.rootpw'] }}"
|
||||
notary:
|
||||
enabled: false
|
||||
|
||||
gitea:
|
||||
helm:
|
||||
version: v5.0.9 # (= Gitea v1.16.8)
|
||||
chart: gitea-charts/gitea
|
||||
parse_logic: helm template . | yq --no-doc eval '.. | .image? | select(.)' | sort -u | sed '/:/!s/$/:latest/'
|
||||
chart_values: !unsafe |
|
||||
config:
|
||||
offlineMode: true
|
||||
gitea:
|
||||
admin:
|
||||
username: administrator
|
||||
password: "{{ vapp['guestinfo.rootpw'] }}"
|
||||
email: admin@{{ vapp['metacluster.fqdn'] }}
|
||||
image:
|
||||
pullPolicy: IfNotPresent
|
||||
ingress:
|
||||
enabled: true
|
||||
hosts:
|
||||
- host: git.{{ vapp['metacluster.fqdn'] }}
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
service:
|
||||
ssh:
|
||||
type: ClusterIP
|
||||
port: 22
|
||||
clusterIP:
|
||||
|
||||
argo-cd:
|
||||
helm:
|
||||
version: 4.9.7 # (= ArgoCD v2.4.2)
|
||||
chart: argo/argo-cd
|
||||
parse_logic: helm template . | yq --no-doc eval '.. | .image? | select(.)' | sort -u | awk '!/ /'
|
||||
chart_values: !unsafe |
|
||||
server:
|
||||
extraArgs:
|
||||
- --insecure
|
||||
ingress:
|
||||
enabled: true
|
||||
hosts:
|
||||
- gitops.{{ vapp['metacluster.fqdn'] }}
|
||||
configs:
|
||||
secret:
|
||||
argocdServerAdminPassword: "{{ vapp['guestinfo.rootpw'] | password_hash('bcrypt') }}"
|
||||
|
||||
sealed-secrets:
|
||||
helm:
|
||||
version: 2.4.0 # (= SealedSecrets v0.18.1)
|
||||
chart: sealed-secrets/sealed-secrets
|
||||
parse_logic: helm template . | yq --no-doc eval '.. | .image? | select(.)' | sort -u | awk '!/ /'
|
||||
|
||||
dependencies:
|
||||
|
||||
ansible_galaxy_collections:
|
||||
- ansible.posix
|
||||
- ansible.utils
|
||||
- community.crypto
|
||||
- community.general
|
||||
- community.vmware
|
||||
- kubernetes.core
|
||||
|
||||
container_images:
|
||||
- vmware/powerclicore:12.7
|
||||
|
||||
static_binaries:
|
||||
- filename: govc
|
||||
url: https://github.com/vmware/govmomi/releases/download/v0.29.0/govc_Linux_x86_64.tar.gz
|
||||
archive: compressed
|
||||
- filename: helm
|
||||
url: https://get.helm.sh/helm-v3.9.0-linux-amd64.tar.gz
|
||||
archive: compressed
|
||||
extra_opts: --strip-components=1
|
||||
- filename: skopeo
|
||||
url: https://code.spamasaurus.com/api/packages/djpbessems/generic/skopeo/v1.9.1/skopeo
|
||||
- filename: step
|
||||
url: https://dl.step.sm/gh-release/cli/gh-release-header/v0.21.0/step_linux_0.21.0_amd64.tar.gz
|
||||
archive: compressed
|
||||
extra_opts: --strip-components=2
|
||||
- filename: yq
|
||||
url: http://github.com/mikefarah/yq/releases/download/v4.25.3/yq_linux_amd64
|
||||
|
||||
packages:
|
||||
apt:
|
||||
- lvm2
|
||||
pip:
|
||||
- jmespath
|
||||
- kubernetes
|
||||
- passlib
|
||||
- pyvmomi
|
||||
7
ansible/vars/workloadcluster.yml
Normal file
7
ansible/vars/workloadcluster.yml
Normal file
@@ -0,0 +1,7 @@
|
||||
downstream:
|
||||
|
||||
node_templates:
|
||||
prefix: NodeTmpl_
|
||||
images:
|
||||
- url: https://stable.release.flatcar-linux.net/amd64-usr/3227.2.0/flatcar_production_vmware_ova.ova
|
||||
name: flatcar.ova
|
||||
@@ -1,16 +0,0 @@
|
||||
script = <<-EOH
|
||||
$nic = get-netadapter
|
||||
|
||||
Get-NetAdapterBinding –InterfaceAlias $nic.name –ComponentID ms_tcpip6
|
||||
EOH
|
||||
|
||||
control "ipv6" do
|
||||
title 'Disabled network protocol IPv6'
|
||||
desc '
|
||||
This test assures that IPv6 is disabled
|
||||
'
|
||||
|
||||
describe powershell(script) do
|
||||
its('stdout') { should match 'False' }
|
||||
end
|
||||
end
|
||||
@@ -1,29 +0,0 @@
|
||||
script = <<-EOH
|
||||
# Initialize variable to empty array
|
||||
$NonCompliantServices = @()
|
||||
|
||||
# Specify relevant services
|
||||
$Services = @(
|
||||
"wuauserv",
|
||||
"W3SVC",
|
||||
"XboxGipSvc",
|
||||
"XblGameSave"
|
||||
)
|
||||
|
||||
# Enumerate all services
|
||||
$NonCompliantServices += Get-Service $Services -ErrorAction 'SilentlyContinue' | Where-Object {$_.StartType -ne 'Disabled'}
|
||||
|
||||
# Output; 'True' or list of noncompliant services
|
||||
Write-Output ($True, $NonCompliantServices)[!($NonCompliantServices.Count -eq 0)]
|
||||
EOH
|
||||
|
||||
control "disabled_services" do
|
||||
title 'Disabled services'
|
||||
desc '
|
||||
This test assures that all unneeded services are set to "disabled".
|
||||
'
|
||||
|
||||
describe powershell(script) do
|
||||
its('stdout') { should match 'True' }
|
||||
end
|
||||
end
|
||||
@@ -1,29 +0,0 @@
|
||||
script = <<-EOH
|
||||
# Initialize variable to empty array
|
||||
$LogicalDisks = @()
|
||||
|
||||
# Enumerate all logicaldisks
|
||||
# DriveType:
|
||||
# Unknown (0)
|
||||
# No Root Directory (1)
|
||||
# Removable Disk (2)
|
||||
# Local Disk (3)
|
||||
# Network Drive (4)
|
||||
# Compact Disc (5)
|
||||
# RAM Disk (6)
|
||||
$LogicalDisks += Get-WmiObject -Class 'win32_logicaldisk' -Filter 'DriveType=3'
|
||||
|
||||
# Filter/Quantify
|
||||
($LogicalDisks.Count -eq 1) -and (($LogicalDisks | Where-Object {$_.DeviceID -ne 'C:'}).Count -eq 0)
|
||||
EOH
|
||||
|
||||
control "single_disk" do
|
||||
title 'Single Disk'
|
||||
desc '
|
||||
This test assures that only a single disk (C:) is available
|
||||
'
|
||||
|
||||
describe powershell(script) do
|
||||
its('stdout') { should match 'True' }
|
||||
end
|
||||
end
|
||||
@@ -1,54 +0,0 @@
|
||||
control "software_installed-7zip" do
|
||||
title 'Included Default Applications: 7-Zip'
|
||||
desc '
|
||||
This test assures that the software application "7-Zip" is installed.
|
||||
'
|
||||
|
||||
describe chocolatey_package('7zip.install') do
|
||||
it { should be_installed }
|
||||
end
|
||||
end
|
||||
|
||||
# control "software_installed-dotnetfx" do
|
||||
# title 'Included Default Applications: .NET'
|
||||
# desc '
|
||||
# This test assures that the software application ".NET" is installed.
|
||||
# '
|
||||
|
||||
# describe chocolatey_package('dotnetfx') do
|
||||
# it { should be_installed }
|
||||
# end
|
||||
# end
|
||||
|
||||
# control "software_installed-foxitreader" do
|
||||
# title 'Included Default Applications: Foxit Reader'
|
||||
# desc '
|
||||
# This test assures that the software application "Foxit Reader" is installed.
|
||||
# '
|
||||
|
||||
# describe chocolatey_package('foxitreader') do
|
||||
# it { should be_installed }
|
||||
# end
|
||||
# end
|
||||
|
||||
# control "software_installed-notepadplusplus" do
|
||||
# title 'Included Default Applications: Notepad++'
|
||||
# desc '
|
||||
# This test assures that the software application "Notepad++" is installed.
|
||||
# '
|
||||
|
||||
# describe chocolatey_package('notepadplusplus') do
|
||||
# it { should be_installed }
|
||||
# end
|
||||
# end
|
||||
|
||||
# control "software_installed-putty" do
|
||||
# title 'Included Default Applications: Putty'
|
||||
# desc '
|
||||
# This test assures that the software application "PuTTy" is installed.
|
||||
# '
|
||||
|
||||
# describe chocolatey_package('putty') do
|
||||
# it { should be_installed }
|
||||
# end
|
||||
# end
|
||||
@@ -1,10 +0,0 @@
|
||||
---
|
||||
name: Windows 10 IoT Enterprise
|
||||
title: Windows 10 IoT Enterprise InSpec Tests
|
||||
summary: Unit test for Windows 10 IoT Enterprise
|
||||
version: 1.0.0
|
||||
maintainer: https://code.spamasaurus.com/djpbessems
|
||||
copyright: https://code.spamasaurus.com/djpbessems
|
||||
license: Proprietary
|
||||
supports:
|
||||
- platform-family: windows
|
||||
4
packer/iso.auto.pkrvars.hcl
Normal file
4
packer/iso.auto.pkrvars.hcl
Normal file
@@ -0,0 +1,4 @@
|
||||
iso_url = "sn.itch.fyi/Repository/iso/Canonical/Ubuntu%20Server%2022.04/ubuntu-22.04-live-server-amd64.iso"
|
||||
iso_checksum = "sha256:84AEAF7823C8C61BAA0AE862D0A06B03409394800000B3235854A6B38EB4856F"
|
||||
// iso_url = "sn.itch.fyi/Repository/iso/Canonical/Ubuntu%20Server%2020.04/ubuntu-20.04.2-live-server-amd64.iso"
|
||||
// iso_checksum = "sha256:D1F2BF834BBE9BB43FAF16F9BE992A6F3935E65BE0EDECE1DEE2AA6EB1767423"
|
||||
101
packer/k8sbootstrap.pkr.hcl
Normal file
101
packer/k8sbootstrap.pkr.hcl
Normal file
@@ -0,0 +1,101 @@
|
||||
packer {
|
||||
required_plugins {
|
||||
}
|
||||
}
|
||||
|
||||
source "vsphere-iso" "k8sbootstrap" {
|
||||
vcenter_server = var.vcenter_server
|
||||
username = var.vsphere_username
|
||||
password = var.vsphere_password
|
||||
insecure_connection = "true"
|
||||
|
||||
vm_name = "${var.vm_guestos}-${var.vm_name}"
|
||||
datacenter = var.vsphere_datacenter
|
||||
cluster = var.vsphere_cluster
|
||||
host = var.vsphere_host
|
||||
folder = var.vsphere_folder
|
||||
datastore = var.vsphere_datastore
|
||||
|
||||
guest_os_type = "ubuntu64Guest"
|
||||
|
||||
boot_order = "disk,cdrom"
|
||||
boot_command = [
|
||||
"e<down><down><down><end>",
|
||||
" autoinstall ds=nocloud;",
|
||||
"<F10>"
|
||||
]
|
||||
boot_wait = "2s"
|
||||
|
||||
communicator = "ssh"
|
||||
ssh_username = "ubuntu"
|
||||
ssh_password = var.ssh_password
|
||||
ssh_timeout = "20m"
|
||||
ssh_handshake_attempts = "100"
|
||||
ssh_pty = true
|
||||
|
||||
CPUs = 2
|
||||
RAM = 8192
|
||||
|
||||
network_adapters {
|
||||
network = var.vsphere_network
|
||||
network_card = "vmxnet3"
|
||||
}
|
||||
storage {
|
||||
disk_size = 76800
|
||||
disk_thin_provisioned = true
|
||||
}
|
||||
disk_controller_type = ["pvscsi"]
|
||||
usb_controller = ["xhci"]
|
||||
|
||||
cd_files = [
|
||||
"packer/preseed/UbuntuServer22.04/user-data",
|
||||
"packer/preseed/UbuntuServer22.04/meta-data"
|
||||
]
|
||||
cd_label = "cidata"
|
||||
iso_url = local.iso_authenticatedurl
|
||||
iso_checksum = var.iso_checksum
|
||||
|
||||
shutdown_command = "echo '${var.ssh_password}' | sudo -S shutdown -P now"
|
||||
shutdown_timeout = "5m"
|
||||
|
||||
export {
|
||||
images = false
|
||||
output_directory = "/scratch/k8sbootstrap"
|
||||
}
|
||||
remove_cdrom = true
|
||||
}
|
||||
|
||||
build {
|
||||
sources = [
|
||||
"source.vsphere-iso.k8sbootstrap"
|
||||
]
|
||||
|
||||
provisioner "ansible" {
|
||||
pause_before = "2m30s"
|
||||
|
||||
playbook_file = "ansible/playbook.yml"
|
||||
user = "ubuntu"
|
||||
ansible_env_vars = [
|
||||
"ANSIBLE_CONFIG=ansible/ansible.cfg"
|
||||
]
|
||||
use_proxy = "false"
|
||||
extra_arguments = [
|
||||
"--extra-vars", "ansible_ssh_pass=${var.ssh_password}",
|
||||
"--extra-vars", "repo_username=${var.repo_username}",
|
||||
"--extra-vars", "repo_password=${var.repo_password}"
|
||||
]
|
||||
}
|
||||
|
||||
post-processor "shell-local" {
|
||||
inline = [
|
||||
"pwsh -command \"& scripts/Update-OvfConfiguration.ps1 \\",
|
||||
" -OVFFile '/scratch/k8sbootstrap/${var.vm_guestos}-${var.vm_name}.ovf' \\",
|
||||
" -Parameter @{'appliance.name'='${var.vm_guestos}';'appliance.version'='${var.vm_name}'}\"",
|
||||
"pwsh -file scripts/Update-Manifest.ps1 \\",
|
||||
" -ManifestFileName '/scratch/k8sbootstrap/${var.vm_guestos}-${var.vm_name}.mf'",
|
||||
"ovftool --acceptAllEulas --allowExtraConfig --overwrite \\",
|
||||
" '/scratch/k8sbootstrap/${var.vm_guestos}-${var.vm_name}.ovf' \\",
|
||||
" /output/Kubernetes.Bootstrap.Appliance.ova"
|
||||
]
|
||||
}
|
||||
}
|
||||
0
packer/preseed/UbuntuServer22.04/meta-data
Normal file
0
packer/preseed/UbuntuServer22.04/meta-data
Normal file
29
packer/preseed/UbuntuServer22.04/user-data
Normal file
29
packer/preseed/UbuntuServer22.04/user-data
Normal file
@@ -0,0 +1,29 @@
|
||||
#cloud-config
|
||||
autoinstall:
|
||||
version: 1
|
||||
locale: en_US
|
||||
keyboard:
|
||||
layout: en
|
||||
variant: us
|
||||
network:
|
||||
network:
|
||||
version: 2
|
||||
ethernets:
|
||||
ens192:
|
||||
dhcp4: true
|
||||
dhcp-identifier: mac
|
||||
storage:
|
||||
layout:
|
||||
name: direct
|
||||
identity:
|
||||
hostname: packer-template
|
||||
username: ubuntu
|
||||
# password: $6$ZThRyfmSMh9499ar$KSZus58U/l58Efci0tiJEqDKFCpoy.rv25JjGRv5.iL33AQLTY2aljumkGiDAiX6LsjzVsGTgH85Tx4S.aTfx0
|
||||
password: $6$rounds=4096$ZKfzRoaQOtc$M.fhOsI0gbLnJcCONXz/YkPfSoefP4i2/PQgzi2xHEi2x9CUhush.3VmYKL0XVr5JhoYvnLfFwqwR/1YYEqZy/
|
||||
ssh:
|
||||
install-server: yes
|
||||
allow-pw: true
|
||||
user-data:
|
||||
disable_root: false
|
||||
late-commands:
|
||||
- echo 'ubuntu ALL=(ALL) NOPASSWD:ALL' > /target/etc/sudoers.d/ubuntu
|
||||
@@ -1,159 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<unattend xmlns="urn:schemas-microsoft-com:unattend">
|
||||
<servicing/>
|
||||
<settings pass="windowsPE">
|
||||
<component xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" name="Microsoft-Windows-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS">
|
||||
<DiskConfiguration>
|
||||
<Disk wcm:action="add">
|
||||
<CreatePartitions>
|
||||
<CreatePartition wcm:action="add">
|
||||
<Order>1</Order>
|
||||
<Type>Primary</Type>
|
||||
<Extend>true</Extend>
|
||||
</CreatePartition>
|
||||
</CreatePartitions>
|
||||
<ModifyPartitions>
|
||||
<ModifyPartition wcm:action="add">
|
||||
<Extend>false</Extend>
|
||||
<Format>NTFS</Format>
|
||||
<Letter>C</Letter>
|
||||
<Order>1</Order>
|
||||
<PartitionID>1</PartitionID>
|
||||
<Label>Windows 10</Label>
|
||||
</ModifyPartition>
|
||||
</ModifyPartitions>
|
||||
<DiskID>0</DiskID>
|
||||
<WillWipeDisk>true</WillWipeDisk>
|
||||
</Disk>
|
||||
<WillShowUI>OnError</WillShowUI>
|
||||
</DiskConfiguration>
|
||||
<UserData>
|
||||
<AcceptEula>true</AcceptEula>
|
||||
<!-- <FullName>Spamasaurus Rex</FullName>
|
||||
<Organization>Spamasaurus Rex</Organization> -->
|
||||
<ProductKey>
|
||||
<Key><<img-productkey>></Key>
|
||||
<WillShowUI>Never</WillShowUI>
|
||||
</ProductKey>
|
||||
</UserData>
|
||||
<ImageInstall>
|
||||
<OSImage>
|
||||
<InstallTo>
|
||||
<DiskID>0</DiskID>
|
||||
<PartitionID>1</PartitionID>
|
||||
</InstallTo>
|
||||
<WillShowUI>OnError</WillShowUI>
|
||||
<InstallToAvailablePartition>false</InstallToAvailablePartition>
|
||||
<InstallFrom>
|
||||
<MetaData wcm:action="add">
|
||||
<Key>/IMAGE/INDEX</Key>
|
||||
<Value>3</Value>
|
||||
</MetaData>
|
||||
</InstallFrom>
|
||||
</OSImage>
|
||||
</ImageInstall>
|
||||
</component>
|
||||
<component xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" name="Microsoft-Windows-International-Core-WinPE" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS">
|
||||
<SetupUILanguage>
|
||||
<UILanguage>en-US</UILanguage>
|
||||
</SetupUILanguage>
|
||||
<InputLocale>en-US</InputLocale>
|
||||
<SystemLocale>en-US</SystemLocale>
|
||||
<UILanguage>en-US</UILanguage>
|
||||
<UILanguageFallback>en-US</UILanguageFallback>
|
||||
<UserLocale>en-US</UserLocale>
|
||||
</component>
|
||||
</settings>
|
||||
<settings pass="offlineServicing">
|
||||
<component name="Microsoft-Windows-LUA-Settings" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS">
|
||||
<EnableLUA>false</EnableLUA>
|
||||
</component>
|
||||
</settings>
|
||||
<settings pass="oobeSystem">
|
||||
<component name="Microsoft-Windows-International-Core" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<InputLocale>en-US</InputLocale>
|
||||
<SystemLocale>en-US</SystemLocale>
|
||||
<UILanguage>en-US</UILanguage>
|
||||
<UserLocale>en-US</UserLocale>
|
||||
</component>
|
||||
<component xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" name="Microsoft-Windows-Shell-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS">
|
||||
<UserAccounts>
|
||||
<AdministratorPassword>
|
||||
<Value><<img-password>></Value>
|
||||
<PlainText>true</PlainText>
|
||||
</AdministratorPassword>
|
||||
</UserAccounts>
|
||||
<OOBE>
|
||||
<HideEULAPage>true</HideEULAPage>
|
||||
<HideWirelessSetupInOOBE>true</HideWirelessSetupInOOBE>
|
||||
<NetworkLocation>Home</NetworkLocation>
|
||||
<ProtectYourPC>1</ProtectYourPC>
|
||||
</OOBE>
|
||||
<AutoLogon>
|
||||
<Password>
|
||||
<Value><<img-password>></Value>
|
||||
<PlainText>true</PlainText>
|
||||
</Password>
|
||||
<Username>administrator</Username>
|
||||
<Enabled>true</Enabled>
|
||||
</AutoLogon>
|
||||
<FirstLogonCommands>
|
||||
<SynchronousCommand wcm:action="add">
|
||||
<CommandLine>cmd.exe /c powershell -Command "Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Force"</CommandLine>
|
||||
<Description>Set execution policy 64bit</Description>
|
||||
<Order>1</Order>
|
||||
<RequiresUserInput>true</RequiresUserInput>
|
||||
</SynchronousCommand>
|
||||
<SynchronousCommand wcm:action="add">
|
||||
<CommandLine>C:\Windows\SysWOW64\cmd.exe /c powershell -Command "Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Force"</CommandLine>
|
||||
<Description>Set execution policy 32bit</Description>
|
||||
<Order>2</Order>
|
||||
<RequiresUserInput>true</RequiresUserInput>
|
||||
</SynchronousCommand>
|
||||
<SynchronousCommand wcm:action="add">
|
||||
<CommandLine>cmd.exe /c reg add "HKLM\System\CurrentControlSet\Control\Network\NewNetworkWindowOff"</CommandLine>
|
||||
<Description>Disable new network prompt</Description>
|
||||
<Order>3</Order>
|
||||
<RequiresUserInput>true</RequiresUserInput>
|
||||
</SynchronousCommand>
|
||||
<SynchronousCommand wcm:action="add">
|
||||
<CommandLine>cmd.exe /c C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -File a:\Set-NetworkProfile.ps1</CommandLine>
|
||||
<Description>Set network profile to private</Description>
|
||||
<Order>4</Order>
|
||||
<RequiresUserInput>true</RequiresUserInput>
|
||||
</SynchronousCommand>
|
||||
<SynchronousCommand wcm:action="add">
|
||||
<CommandLine>cmd.exe /c C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -File a:\Disable-WinRM.ps1</CommandLine>
|
||||
<Description>Disable WinRM</Description>
|
||||
<Order>5</Order>
|
||||
<RequiresUserInput>true</RequiresUserInput>
|
||||
</SynchronousCommand>
|
||||
<SynchronousCommand wcm:action="add">
|
||||
<CommandLine>cmd.exe /c a:\Install-VMwareTools.cmd</CommandLine>
|
||||
<Order>13</Order>
|
||||
<Description>Install VMware Tools</Description>
|
||||
</SynchronousCommand>
|
||||
<SynchronousCommand wcm:action="add">
|
||||
<CommandLine>cmd.exe /c C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -File a:\Enable-WinRM.ps1</CommandLine>
|
||||
<Description>Enable WinRM</Description>
|
||||
<Order>99</Order>
|
||||
</SynchronousCommand>
|
||||
</FirstLogonCommands>
|
||||
<ShowWindowsLive>false</ShowWindowsLive>
|
||||
</component>
|
||||
</settings>
|
||||
<settings pass="specialize">
|
||||
<component name="Microsoft-Windows-Shell-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS">
|
||||
<OEMInformation>
|
||||
<HelpCustomized>false</HelpCustomized>
|
||||
</OEMInformation>
|
||||
<!-- Rename computer here. -->
|
||||
<ComputerName>packer-template</ComputerName>
|
||||
<TimeZone>W. Europe Standard Time</TimeZone>
|
||||
<RegisteredOwner/>
|
||||
</component>
|
||||
<component xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" name="Microsoft-Windows-Security-SPP-UX" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS">
|
||||
<SkipAutoActivation>true</SkipAutoActivation>
|
||||
</component>
|
||||
</settings>
|
||||
</unattend>
|
||||
@@ -1,42 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<unattend xmlns="urn:schemas-microsoft-com:unattend">
|
||||
<settings pass="generalize">
|
||||
<component name="Microsoft-Windows-Security-SPP" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<SkipRearm>1</SkipRearm>
|
||||
</component>
|
||||
<component name="Microsoft-Windows-PnpSysprep" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<PersistAllDeviceInstalls>true</PersistAllDeviceInstalls>
|
||||
<DoNotCleanUpNonPresentDevices>true</DoNotCleanUpNonPresentDevices>
|
||||
</component>
|
||||
</settings>
|
||||
<settings pass="oobeSystem">
|
||||
<component name="Microsoft-Windows-International-Core" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<InputLocale>en-US</InputLocale>
|
||||
<SystemLocale>en-US</SystemLocale>
|
||||
<UILanguage>en-US</UILanguage>
|
||||
<UserLocale>en-US</UserLocale>
|
||||
</component>
|
||||
<component name="Microsoft-Windows-Shell-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<OOBE>
|
||||
<HideEULAPage>true</HideEULAPage>
|
||||
<HideLocalAccountScreen>true</HideLocalAccountScreen>
|
||||
<HideOEMRegistrationScreen>true</HideOEMRegistrationScreen>
|
||||
<HideOnlineAccountScreens>true</HideOnlineAccountScreens>
|
||||
<HideWirelessSetupInOOBE>true</HideWirelessSetupInOOBE>
|
||||
<NetworkLocation>Work</NetworkLocation>
|
||||
<ProtectYourPC>1</ProtectYourPC>
|
||||
<SkipMachineOOBE>true</SkipMachineOOBE>
|
||||
<SkipUserOOBE>true</SkipUserOOBE>
|
||||
</OOBE>
|
||||
<TimeZone>UTC</TimeZone>
|
||||
<UserAccounts>
|
||||
<AdministratorPassword>
|
||||
<Value><<img-password>></Value>
|
||||
<PlainText>true</PlainText>
|
||||
</AdministratorPassword>
|
||||
</UserAccounts>
|
||||
</component>
|
||||
</settings>
|
||||
<settings pass="specialize">
|
||||
</settings>
|
||||
</unattend>
|
||||
@@ -1,9 +1,12 @@
|
||||
variable "vcenter_server" {}
|
||||
variable "vsphere_username" {}
|
||||
variable "vsphere_password" {}
|
||||
variable "vsphere_password" {
|
||||
sensitive = true
|
||||
}
|
||||
|
||||
variable "vsphere_host" {}
|
||||
variable "vsphere_datacenter" {}
|
||||
variable "vsphere_cluster" {}
|
||||
|
||||
variable "vsphere_templatefolder" {}
|
||||
variable "vsphere_folder" {}
|
||||
@@ -12,7 +15,17 @@ variable "vsphere_network" {}
|
||||
|
||||
variable "vm_name" {}
|
||||
variable "vm_guestos" {}
|
||||
variable "winrm_password" {}
|
||||
variable "ssh_password" {
|
||||
sensitive = true
|
||||
}
|
||||
|
||||
variable "iso_url" {}
|
||||
variable "iso_checksum" {}
|
||||
variable "repo_username" {}
|
||||
variable "repo_password" {}
|
||||
variable "repo_password" {
|
||||
sensitive = true
|
||||
}
|
||||
local "iso_authenticatedurl" {
|
||||
expression = "https://${var.repo_username}:${var.repo_password}@${var.iso_url}"
|
||||
sensitive = true
|
||||
}
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
vcenter_server = "bv11-vc.bessems.lan"
|
||||
vsphere_username = "administrator@vsphere.local"
|
||||
vsphere_datacenter = "DeSchakel"
|
||||
vsphere_host = "bv11-esx.bessems.lan"
|
||||
vsphere_datastore = "Datastore01.SSD"
|
||||
vsphere_cluster = "Cluster.01"
|
||||
vsphere_host = "bv11-esx02.bessems.lan"
|
||||
vsphere_datastore = "NAS01.RAID5"
|
||||
vsphere_folder = "/Packer"
|
||||
vsphere_templatefolder = "/Templates"
|
||||
vsphere_network = "LAN"
|
||||
@@ -1,133 +0,0 @@
|
||||
packer {
|
||||
required_plugins {
|
||||
windows-update = {
|
||||
version = ">= 0.14.0"
|
||||
source = "github.com/rgl/windows-update"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
source "vsphere-iso" "win10" {
|
||||
vcenter_server = var.vcenter_server
|
||||
username = var.vsphere_username
|
||||
password = var.vsphere_password
|
||||
insecure_connection = "true"
|
||||
|
||||
vm_name = "${var.vm_guestos}-${var.vm_name}"
|
||||
datacenter = var.vsphere_datacenter
|
||||
host = var.vsphere_host
|
||||
folder = var.vsphere_folder
|
||||
datastore = var.vsphere_datastore
|
||||
|
||||
guest_os_type = "windows9_64Guest"
|
||||
|
||||
boot_order = "disk,cdrom"
|
||||
boot_command = [""]
|
||||
boot_wait = "5m"
|
||||
|
||||
communicator = "winrm"
|
||||
winrm_username = "administrator"
|
||||
winrm_password = var.winrm_password
|
||||
winrm_timeout = "10m"
|
||||
|
||||
CPUs = 2
|
||||
RAM = 8192
|
||||
|
||||
network_adapters {
|
||||
network = var.vsphere_network
|
||||
network_card = "vmxnet3"
|
||||
}
|
||||
storage {
|
||||
disk_size = 20480
|
||||
disk_thin_provisioned = true
|
||||
}
|
||||
disk_controller_type = ["lsilogic-sas"]
|
||||
usb_controller = ["xhci"]
|
||||
|
||||
floppy_files = [
|
||||
"packer/preseed/Windows10/Autounattend.xml",
|
||||
"packer/preseed/Windows10/Sysprep_Unattend.xml",
|
||||
"scripts/Set-NetworkProfile.ps1",
|
||||
"scripts/Disable-WinRM.ps1",
|
||||
"scripts/Enable-WinRM.ps1",
|
||||
"scripts/Install-VMwareTools.cmd"
|
||||
]
|
||||
iso_checksum = "sha256:8D1663B71280533824CF95C7AB48ADAF5A187C38FCFF5B16A569F903688916D0"
|
||||
iso_paths = [
|
||||
"ISO-files/VMware-tools-windows-11.3.5-18557794/VMware-tools-windows-11.3.5-18557794.iso"
|
||||
]
|
||||
iso_url = "https://${var.repo_username}:${var.repo_password}@sn.itch.fyi/Repository/iso/Microsoft/Windows%2010/20H2/en_windows_10_enterprise_20H2_x64.iso"
|
||||
|
||||
shutdown_command = "C:\\Windows\\System32\\Sysprep\\sysprep.exe /generalize /oobe /unattend:A:\\Sysprep_Unattend.xml"
|
||||
shutdown_timeout = "1h"
|
||||
|
||||
export {
|
||||
images = false
|
||||
output_directory = "/scratch/win10"
|
||||
}
|
||||
remove_cdrom = true
|
||||
}
|
||||
|
||||
build {
|
||||
sources = ["source.vsphere-iso.win10"]
|
||||
|
||||
provisioner "windows-update" {
|
||||
filters = [
|
||||
"exclude:$_.Title -like '*Preview*'",
|
||||
"include:$true"
|
||||
]
|
||||
}
|
||||
|
||||
provisioner "powershell" {
|
||||
inline = [
|
||||
"[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12",
|
||||
"Invoke-Expression ((New-Object Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))"
|
||||
]
|
||||
}
|
||||
|
||||
provisioner "powershell" {
|
||||
inline = [
|
||||
"choco config set --name=limit-output --value=LimitOutput",
|
||||
"choco install -y 7zip.install",
|
||||
"choco install -y sysinternals",
|
||||
"choco install -y firefox"
|
||||
]
|
||||
}
|
||||
|
||||
provisioner "windows-update" {
|
||||
filters = [
|
||||
"exclude:$_.Title -like '*Preview*'",
|
||||
"include:$true"
|
||||
]
|
||||
}
|
||||
|
||||
provisioner "powershell" {
|
||||
inline = [
|
||||
"New-Item -Path 'C:\\Payload\\Scripts' -ItemType 'Directory' -Force:$True -Confirm:$False"
|
||||
]
|
||||
}
|
||||
|
||||
provisioner "file" {
|
||||
destination = "C:\\Payload\\"
|
||||
source = "scripts/Windows10/payload/"
|
||||
}
|
||||
|
||||
provisioner "powershell" {
|
||||
scripts = [
|
||||
"scripts/Windows10/Register-ScheduledTask.ps1"
|
||||
]
|
||||
}
|
||||
|
||||
post-processor "shell-local" {
|
||||
inline = [
|
||||
"pwsh -command \"& scripts/Update-OvfConfiguration.ps1 \\",
|
||||
" -OVFFile '/scratch/win10/${var.vm_guestos}-${var.vm_name}.ovf' \\",
|
||||
" -Parameter @{'appliance.name'='${var.vm_guestos}';'appliance.version'='${var.vm_name}'}\"",
|
||||
"pwsh -file scripts/Update-Manifest.ps1 \\",
|
||||
" -ManifestFileName '/scratch/win10/${var.vm_guestos}-${var.vm_name}.mf'",
|
||||
"ovftool --acceptAllEulas --allowExtraConfig --overwrite \\",
|
||||
" '/scratch/win10/${var.vm_guestos}-${var.vm_name}.ovf' \\",
|
||||
" /output/Windows10.ova"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
netsh advfirewall firewall set rule name="Windows Remote Management (HTTP-In)" new enable=yes action=block
|
||||
netsh advfirewall firewall set rule group="Windows Remote Management" new enable=yes
|
||||
$winrmService = Get-Service -Name WinRM
|
||||
if ($winrmService.Status -eq "Running"){
|
||||
Disable-PSRemoting -Force
|
||||
}
|
||||
Stop-Service winrm
|
||||
Set-Service -Name winrm -StartupType Disabled
|
||||
@@ -1,18 +0,0 @@
|
||||
$NetworkListManager = [Activator]::CreateInstance([Type]::GetTypeFromCLSID([Guid]"{DCB00C01-570F-4A9B-8D69-199FDBA5723B}"))
|
||||
$Connections = $NetworkListManager.GetNetworkConnections()
|
||||
$Connections | ForEach-Object { $_.GetNetwork().SetCategory(1) }
|
||||
|
||||
Enable-PSRemoting -Force
|
||||
winrm quickconfig -q
|
||||
winrm quickconfig -transport:http
|
||||
winrm set winrm/config '@{MaxTimeoutms="1800000"}'
|
||||
winrm set winrm/config/winrs '@{MaxMemoryPerShellMB="800"}'
|
||||
winrm set winrm/config/service '@{AllowUnencrypted="true"}'
|
||||
winrm set winrm/config/service/auth '@{Basic="true"}'
|
||||
winrm set winrm/config/client/auth '@{Basic="true"}'
|
||||
winrm set winrm/config/listener?Address=*+Transport=HTTP '@{Port="5985"}'
|
||||
netsh advfirewall firewall set rule group="Windows Remote Administration" new enable=yes
|
||||
netsh advfirewall firewall set rule name="Windows Remote Management (HTTP-In)" new enable=yes action=allow
|
||||
netsh advfirewall firewall set rule name="Windows Remote Management (HTTP-In)" profile=public new remoteip=any
|
||||
Set-Service winrm -startuptype "auto"
|
||||
Restart-Service winrm
|
||||
@@ -1,2 +0,0 @@
|
||||
@rem Silent mode, basic UI, no reboot
|
||||
e:\setup64 /s /v "/qb REBOOT=R"
|
||||
@@ -1,73 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<BlockList>
|
||||
<!-- services to disable -->
|
||||
<Services>
|
||||
<Name>MVMCP2VAgent</Name>
|
||||
<Name>VMTools</Name>
|
||||
<Name> VMUpgradeHelper </Name>
|
||||
<Name> vmvss </Name>
|
||||
<Name>vmdesched</Name>
|
||||
<Name>Virtual Server</Name>
|
||||
<!-- Virtual Machine Helper -->
|
||||
<Name>vmh</Name>
|
||||
<!-- Xen-specific service -->
|
||||
<Name>xensvc</Name>
|
||||
</Services>
|
||||
<!-- drivers to disable -->
|
||||
<Drivers>
|
||||
<Name>vmx_svga</Name>
|
||||
<Name>vmmouse</Name>
|
||||
<Name>vmscsi</Name>
|
||||
<Name>amdpcn</Name>
|
||||
<Name>PCnet</Name>
|
||||
<Name>VMMEMCTL</Name>
|
||||
|
||||
<Name> pvscsi </Name>
|
||||
<Name> vmci </Name>
|
||||
<Name> vmmouse </Name>
|
||||
<Name> vmaudio </Name>
|
||||
<Name> vmrawdsk </Name>
|
||||
<Name> vmxnet </Name>
|
||||
<Name> vmxnet3ndis6 </Name>
|
||||
<Name> vm3dmp </Name>
|
||||
<Name> vmdebug </Name>
|
||||
<Name> vmxnet3ndis5 </Name>
|
||||
|
||||
|
||||
<Name>cirrus</Name>
|
||||
<!-- storage drivers -->
|
||||
<Name>buslogic</Name>
|
||||
<Name>symc810</Name>
|
||||
<Name>cpqarray</Name>
|
||||
<Name>pcntn4m</Name>
|
||||
<Name>cpqnf3</Name>
|
||||
<Name>MRaidNT</Name>
|
||||
<Name>Symc8XX</Name>
|
||||
<!-- VIA chipset drivers -->
|
||||
<Name>viaide</Name>
|
||||
<Name>VIAudio</Name>
|
||||
<Name>VIAPFD</Name>
|
||||
<Name>viafilter</Name>
|
||||
<Name>viaagp</Name>
|
||||
<Name>viaagp1</Name>
|
||||
<!-- network drivers: Intel(R) PRO/100 -->
|
||||
<Name>E100B</Name>
|
||||
<!-- tape drivers -->
|
||||
<Name>4mmdat</Name>
|
||||
<Name>4mmdat-SeSFT</Name>
|
||||
<Name>SCSIChanger</Name>
|
||||
|
||||
<!-- Virtual Machine Monitor -->
|
||||
<Name>vmm</Name>
|
||||
<!-- Xen-specific drivers -->
|
||||
<Name>xenevtchn</Name>
|
||||
<Name>xenvbd</Name>
|
||||
<Name>xennet</Name>
|
||||
</Drivers>
|
||||
<Programs>
|
||||
<Name>ProMON</Name>
|
||||
<Name>s3tray2</Name>
|
||||
<Name>VMwareTray</Name>
|
||||
<Name>VMwareUser</Name>
|
||||
</Programs>
|
||||
</BlockList>
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,23 +0,0 @@
|
||||
# You cannot enable Windows PowerShell Remoting on network connections that are set to Public
|
||||
# Spin through all the network locations and if they are set to Public, set them to Private
|
||||
# using the INetwork interface:
|
||||
# http://msdn.microsoft.com/en-us/library/windows/desktop/aa370750(v=vs.85).aspx
|
||||
# For more info, see:
|
||||
# http://blogs.msdn.com/b/powershell/archive/2009/04/03/setting-network-location-to-private.aspx
|
||||
|
||||
# Network location feature was only introduced in Windows Vista - no need to bother with this
|
||||
# if the operating system is older than Vista
|
||||
if([environment]::OSVersion.version.Major -lt 6) { return }
|
||||
|
||||
# You cannot change the network location if you are joined to a domain, so abort
|
||||
if(1,3,4,5 -contains (Get-WmiObject win32_computersystem).DomainRole) { return }
|
||||
|
||||
# Get network connections
|
||||
$networkListManager = [Activator]::CreateInstance([Type]::GetTypeFromCLSID([Guid]"{DCB00C01-570F-4A9B-8D69-199FDBA5723B}"))
|
||||
$connections = $networkListManager.GetNetworkConnections()
|
||||
|
||||
$connections |foreach {
|
||||
Write-Host $_.GetNetwork().GetName()"category was previously set to"$_.GetNetwork().GetCategory()
|
||||
$_.GetNetwork().SetCategory(1)
|
||||
Write-Host $_.GetNetwork().GetName()"changed to category"$_.GetNetwork().GetCategory()
|
||||
}
|
||||
@@ -47,7 +47,77 @@ $GetContentSplat = @{
|
||||
}
|
||||
$XML = [xml](Get-Content @GetContentSplat)
|
||||
$NS = [System.Xml.XmlNamespaceManager]$XML.NameTable
|
||||
[void]$NS.AddNamespace('Any', $XML.DocumentElement.xmlns)
|
||||
[void]$NS.AddNamespace('ns', $XML.DocumentElement.xmlns)
|
||||
[void]$NS.AddNamespace('ovf', $XML.DocumentElement.ovf)
|
||||
[void]$NS.AddNamespace('rasd', $XML.DocumentElement.rasd)
|
||||
[void]$NS.AddNamespace('vmw', $XML.DocumentElement.vmw)
|
||||
|
||||
# Create copy of existing 'Item/ResourceType'=17 (=Hard disk) node
|
||||
$XMLDiskTemplate = $XML.SelectSingleNode("//ns:VirtualHardwareSection/ns:Item/rasd:ResourceType[.='17']", $NS).ParentNode.CloneNode($True)
|
||||
|
||||
ForEach ($Disk in $OVFConfig.DynamicDisks) {
|
||||
# Determine next free available 'diskId'
|
||||
$XMLDisks = $XML.SelectNodes("//ns:DiskSection/ns:Disk[contains(@ovf:diskId,'vmdisk')]", $NS)
|
||||
$DiskId = 1
|
||||
While ($XMLDisks.DiskId -contains "vmdisk$($DiskId)") {
|
||||
$DiskId++
|
||||
}
|
||||
|
||||
# Add new 'Disk' node (under 'DiskSection')
|
||||
$XMLDisk = $XML.CreateElement('Disk', $XML.DocumentElement.xmlns)
|
||||
$PowersMap = @{
|
||||
KB = 10
|
||||
MB = 20
|
||||
GB = 30
|
||||
TB = 40
|
||||
PB = 50
|
||||
}
|
||||
If ($PowersMap.Keys -notcontains $Disk.UnitSize) {
|
||||
# Invalid UnitSize; skipping adding new disk
|
||||
Continue
|
||||
}
|
||||
|
||||
[void]$XMLDisk.SetAttribute('capacityAllocationUnits', $NS.LookupNamespace('ovf'), "byte * 2^$($PowersMap[$Disk.UnitSize])")
|
||||
[void]$XMLDisk.SetAttribute('format', $NS.LookupNamespace('ovf'), 'http://www.vmware.com/interfaces/specifications/vmdk.html#streamOptimized')
|
||||
[void]$XMLDisk.SetAttribute('diskId', $NS.LookupNamespace('ovf'), "vmdisk$($DiskId)")
|
||||
[void]$XMLDisk.SetAttribute('capacity', $NS.LookupNamespace('ovf'), '${{vmconfig.disksize.{0}}}' -f $DiskId)
|
||||
[void]$XMLDisk.SetAttribute('populatedSize', $NS.LookupNamespace('ovf'), 0)
|
||||
[void]$XML.SelectSingleNode('//ns:DiskSection', $NS).AppendChild($XMLDisk)
|
||||
|
||||
# Add new 'Item/ResourceType' node (under 'VirtualHardwareSection')
|
||||
$XMLDiskItem = $XMLDiskTemplate.CloneNode($True)
|
||||
$XMLDiskItem.SelectSingleNode('rasd:AddressOnParent', $NS).InnerText = ($DiskId - 1)
|
||||
$XMLDiskItem.SelectSingleNode('rasd:ElementName', $NS).InnerText = "Hard Disk $($DiskId)"
|
||||
$XMLDiskItem.SelectSingleNode('rasd:HostResource', $NS).InnerText = "ovf:/disk/vmdisk$($DiskId)"
|
||||
# Determine next free available and highest 'InstanceID'
|
||||
$InstanceIDs = $XML.SelectNodes('//ns:VirtualHardwareSection/ns:Item/rasd:InstanceID', $NS).InnerText
|
||||
$InstanceID = 1
|
||||
While ($InstanceIDs -contains $InstanceID) {
|
||||
$InstanceID++
|
||||
}
|
||||
$HighestInstanceID = ($InstanceIDs | Measure-Object -Maximum).Maximum
|
||||
$XMLDiskItem.SelectSingleNode('rasd:InstanceID', $NS).InnerText = $InstanceID
|
||||
[void]$XML.SelectSingleNode('//ns:VirtualHardwareSection', $NS).InsertAfter(
|
||||
$XMLDiskItem,
|
||||
$XML.SelectSingleNode("//ns:VirtualHardwareSection/ns:Item/rasd:InstanceID[.='$($HighestInstanceID)']", $NS).ParentNode
|
||||
)
|
||||
|
||||
$OVFConfig.PropertyCategories[0].ProductProperties += @{
|
||||
Key = "vmconfig.disksize.$($DiskId)"
|
||||
Type = If ([boolean]$Disk.Constraints.Minimum -or [boolean]$Disk.Constraints.Maximum) {
|
||||
"Int($($Disk.Constraints.Minimum)..$($Disk.Constraints.Maximum))"
|
||||
}
|
||||
Else {
|
||||
'Int'
|
||||
}
|
||||
Label = "Disk $($DiskId) size*"
|
||||
Description = "$($Disk.Description) (in $($Disk.UnitSize))".Trim()
|
||||
DefaultValue = "$($Disk.Constraints.Minimum)"
|
||||
Configurations = '*'
|
||||
UserConfigurable = 'true'
|
||||
}
|
||||
}
|
||||
Write-Host "Inserted $($OVFConfig.DynamicDisks.Count) new node(s) into 'DiskSection' and 'VirtualHardwareSection' respectively"
|
||||
|
||||
If ($OVFConfig.DeploymentConfigurations.Count -gt 0) {
|
||||
$XMLSection = $XML.CreateElement('DeploymentOptionSection', $XML.DocumentElement.xmlns)
|
||||
@@ -58,52 +128,72 @@ If ($OVFConfig.DeploymentConfigurations.Count -gt 0) {
|
||||
ForEach ($Configuration in $OVFConfig.DeploymentConfigurations) {
|
||||
$XMLConfig = $XML.CreateElement('Configuration', $XML.DocumentElement.xmlns)
|
||||
|
||||
$XMLConfigAttrId = $XML.CreateAttribute('id', $XML.DocumentElement.ovf)
|
||||
$XMLConfigAttrId.Value = $Configuration.Id
|
||||
[void]$XMLConfig.SetAttribute('id', $NS.LookupNamespace('ovf'), $Configuration.Id)
|
||||
|
||||
$XMLConfigLabel = $XML.CreateElement('Label', $XML.DocumentElement.xmlns)
|
||||
$XMLConfigLabel.InnerText = $Configuration.Label
|
||||
|
||||
$XMLConfigDescription = $XML.CreateElement('Description', $XML.DocumentElement.xmlns)
|
||||
$XMLConfigDescription.InnerText = $Configuration.Description
|
||||
|
||||
[void]$XMLConfig.Attributes.Append($XMLConfigAttrId)
|
||||
[void]$XMLConfig.AppendChild($XMLConfigLabel)
|
||||
[void]$XMLConfig.AppendChild($XMLConfigDescription)
|
||||
|
||||
[void]$XMLSection.AppendChild($XMLConfig)
|
||||
}
|
||||
[void]$XML.SelectSingleNode('//Any:Envelope', $NS).InsertAfter($XMLSection, $XML.SelectSingleNode('//Any:NetworkSection', $NS))
|
||||
[void]$XML.SelectSingleNode('//ns:Envelope', $NS).InsertAfter($XMLSection, $XML.SelectSingleNode('//ns:NetworkSection', $NS))
|
||||
Write-Host "Inserted 'DeploymentOptionSection' with $($Configuration.Count) nodes"
|
||||
|
||||
If ($OVFConfig.DeploymentConfigurations.Count -eq $OVFConfig.DeploymentConfigurations.Size.Count) {
|
||||
# Create copies of existing 'Item/ResourceType' nodes
|
||||
$XMLCPUTemplate = $XML.SelectSingleNode("//ns:VirtualHardwareSection/ns:Item/rasd:ResourceType[.='3']", $NS).ParentNode.CloneNode($True)
|
||||
$XMLMemoryTemplate = $XML.SelectSingleNode("//ns:VirtualHardwareSection/ns:Item/rasd:ResourceType[.='4']", $NS).ParentNode.CloneNode($True)
|
||||
# Delete existing nodes
|
||||
ForEach ($Node in $XML.SelectNodes("//ns:VirtualHardwareSection/ns:Item/rasd:ResourceType[.='3' or .='4']", $NS).ParentNode) {
|
||||
[void]$Node.ParentNode.RemoveChild($Node)
|
||||
}
|
||||
# Add adjusted 'Item/ResourceType' nodes
|
||||
ForEach ($Configuration in $OVFConfig.DeploymentConfigurations) {
|
||||
$XMLCPU = $XMLCPUTemplate.CloneNode($True)
|
||||
[void]$XMLCPU.SetAttribute('configuration', $NS.LookupNamespace('ovf'), $Configuration.Id)
|
||||
$XMLCPU.SelectSingleNode('rasd:ElementName', $NS).InnerText = '{0} virtual CPU(s)' -f $Configuration.Size.CPU
|
||||
$XMLCPU.SelectSingleNode('rasd:VirtualQuantity', $NS).InnerText = $Configuration.Size.CPU
|
||||
|
||||
$XMLMemory = $XMLMemoryTemplate.CloneNode($True)
|
||||
[void]$XMLMemory.SetAttribute('configuration', $NS.LookupNamespace('ovf'), $Configuration.Id)
|
||||
$XMLMemory.SelectSingleNode('rasd:ElementName', $NS).InnerText = '{0}MB of memory' -f $Configuration.Size.Memory
|
||||
$XMLMemory.SelectSingleNode('rasd:VirtualQuantity', $NS).InnerText = $Configuration.Size.Memory
|
||||
|
||||
[void]$XML.SelectSingleNode('//ns:VirtualHardwareSection', $NS).InsertAfter(
|
||||
$XMLCPU,
|
||||
$XML.SelectSingleNode('//ns:VirtualHardwareSection/ns:System', $NS)
|
||||
)
|
||||
[void]$XML.SelectSingleNode('//ns:VirtualHardwareSection', $NS).InsertAfter(
|
||||
$XMLMemory,
|
||||
$XML.SelectSingleNode('//ns:VirtualHardwareSection/ns:System', $NS)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$XMLAttrTransport = $XML.CreateAttribute('transport', $XML.DocumentElement.ovf)
|
||||
$XMLAttrTransport.Value = 'com.vmware.guestInfo'
|
||||
[void]$XML.SelectSingleNode('//Any:VirtualHardwareSection', $NS).Attributes.Append($XMLAttrTransport)
|
||||
[void]$XML.SelectSingleNode('//ns:VirtualHardwareSection', $NS).SetAttribute('transport', $NS.LookupNamespace('ovf'), 'com.vmware.guestInfo')
|
||||
ForEach ($ExtraConfig in $OVFConfig.AdvancedOptions) {
|
||||
$XMLExtraConfig = $XML.CreateElement('vmw:ExtraConfig', $XML.DocumentElement.vmw)
|
||||
|
||||
$XMLExtraConfigAttrRequired = $XML.CreateAttribute('required', $XML.DocumentElement.ovf)
|
||||
$XMLExtraConfigAttrRequired.Value = "$([boolean]$ExtraConfig.Required)".ToLower()
|
||||
$XMLExtraConfigAttrKey = $XML.CreateAttribute('key', $XML.DocumentElement.vmw)
|
||||
$XMLExtraConfigAttrKey.Value = $ExtraConfig.Key
|
||||
$XMLExtraConfigAttrValue = $XML.CreateAttribute('value', $XML.DocumentElement.vmw)
|
||||
$XMLExtraConfigAttrValue.Value = $ExtraConfig.Value
|
||||
[void]$XMLExtraConfig.SetAttribute('required', $NS.LookupNamespace('ovf'), "$([boolean]$ExtraConfig.Required)".ToLower())
|
||||
[void]$XMLExtraConfig.SetAttribute('key', $NS.LookupNamespace('vmw'), $ExtraConfig.Key)
|
||||
[void]$XMLExtraConfig.SetAttribute('value', $NS.LookupNamespace('vmw'), $ExtraConfig.Value)
|
||||
|
||||
[void]$XMLExtraConfig.Attributes.Append($XMLExtraConfigAttrRequired)
|
||||
[void]$XMLExtraConfig.Attributes.Append($XMLExtraConfigAttrKey)
|
||||
[void]$XMLExtraConfig.Attributes.Append($XMLExtraConfigAttrValue)
|
||||
[void]$XML.SelectSingleNode('//Any:VirtualHardwareSection', $NS).AppendChild($XMLExtraConfig)
|
||||
[void]$XML.SelectSingleNode('//ns:VirtualHardwareSection', $NS).AppendChild($XMLExtraConfig)
|
||||
}
|
||||
Write-Host "Added $($OVFConfig.AdvancedOptions.Count) 'vmw:ExtraConfig' nodes"
|
||||
Write-Host "Added $($OVFConfig.AdvancedOptions.Count) 'vmw:ExtraConfig' node(s)"
|
||||
|
||||
$XMLProductSection = $XML.SelectSingleNode('//Any:ProductSection', $NS)
|
||||
$XMLProductSection = $XML.SelectSingleNode('//ns:ProductSection', $NS)
|
||||
If ($XMLProductSection -eq $Null) {
|
||||
$XMLProductSection = $XML.CreateElement('ProductSection', $XML.DocumentElement.xmlns)
|
||||
[void]$XML.SelectSingleNode('//Any:VirtualSystem', $NS).AppendChild($XMLProductSection)
|
||||
[void]$XML.SelectSingleNode('//ns:VirtualSystem', $NS).AppendChild($XMLProductSection)
|
||||
Write-Host "Inserted 'ProductSection'"
|
||||
} Else {
|
||||
ForEach ($Child in $XMLProductSection.SelectNodes('//Any:ProductSection/child::*', $NS)) {
|
||||
ForEach ($Child in $XMLProductSection.SelectNodes('//ns:ProductSection/child::*', $NS)) {
|
||||
[void]$Child.ParentNode.RemoveChild($Child)
|
||||
}
|
||||
Write-Host "Destroyed pre-existing children in 'ProductSection'"
|
||||
@@ -124,15 +214,13 @@ ForEach ($Category in $OVFConfig.PropertyCategories) {
|
||||
ForEach ($Property in $Category.ProductProperties) {
|
||||
$XMLProperty = $XML.CreateElement('Property', $XML.DocumentElement.xmlns)
|
||||
|
||||
$XMLPropertyAttrKey = $XML.CreateAttribute('key', $XML.DocumentElement.ovf)
|
||||
$XMLPropertyAttrKey.Value = $Property.Key
|
||||
$XMLPropertyAttrType = $XML.CreateAttribute('type', $XML.DocumentElement.ovf)
|
||||
[void]$XMLProperty.SetAttribute('key', $NS.LookupNamespace('ovf'), $Property.Key)
|
||||
Switch -regex ($Property.Type) {
|
||||
'^boolean' {
|
||||
$XMLPropertyAttrType.Value = 'boolean'
|
||||
[void]$XMLProperty.SetAttribute('type', $NS.LookupNamespace('ovf'), 'boolean')
|
||||
}
|
||||
'^int' {
|
||||
$XMLPropertyAttrType.Value = 'uint8'
|
||||
[void]$XMLProperty.SetAttribute('type', $NS.LookupNamespace('ovf'), 'uint16')
|
||||
$Qualifiers = @()
|
||||
If ($Property.Type -match '^int\((\d*)\.\.(\d*)\)') {
|
||||
If ($Matches[1]) {
|
||||
@@ -141,23 +229,16 @@ ForEach ($Category in $OVFConfig.PropertyCategories) {
|
||||
If ($Matches[2]) {
|
||||
$Qualifiers += "MaxValue($($Matches[2]))"
|
||||
}
|
||||
$XMLPropertyAttrQualifiers = $XML.CreateAttribute('qualifiers', $XML.DocumentElement.ovf)
|
||||
$XMLPropertyAttrQualifiers.Value = $Qualifiers -join ' '
|
||||
[void]$XMLProperty.Attributes.Append($XMLPropertyAttrQualifiers)
|
||||
[void]$XMLProperty.SetAttribute('qualifiers', $NS.LookupNamespace('ovf'), $Qualifiers -join ' ')
|
||||
}
|
||||
}
|
||||
'^ip' {
|
||||
$XMLPropertyAttrType.Value = 'string'
|
||||
$XMLPropertyAttrQualifiers = $XML.CreateAttribute('qualifiers', $XML.DocumentElement.vmw)
|
||||
$XMLPropertyAttrQualifiers.Value = 'Ip'
|
||||
[void]$XMLProperty.Attributes.Append($XMLPropertyAttrQualifiers)
|
||||
[void]$XMLProperty.SetAttribute('type', $NS.LookupNamespace('ovf'), 'string')
|
||||
[void]$XMLProperty.SetAttribute('qualifiers', $NS.LookupNamespace('vmw'), 'Ip')
|
||||
}
|
||||
'^password' {
|
||||
$XMLPropertyAttrType.Value = 'string'
|
||||
$XMLPropertyAttrPassword = $XML.CreateAttribute('password', $XML.DocumentElement.ovf)
|
||||
$XMLPropertyAttrPassword.Value = 'true'
|
||||
[void]$XMLProperty.Attributes.Append($XMLPropertyAttrPassword)
|
||||
|
||||
[void]$XMLProperty.SetAttribute('type', $NS.LookupNamespace('ovf'), 'string')
|
||||
[void]$XMLProperty.SetAttribute('password', $NS.LookupNamespace('ovf'), 'true')
|
||||
$Qualifiers = @()
|
||||
If ($Property.Type -match '^password\((\d*)\.\.(\d*)\)') {
|
||||
If ($Matches[1]) {
|
||||
@@ -166,13 +247,11 @@ ForEach ($Category in $OVFConfig.PropertyCategories) {
|
||||
If ($Matches[2]) {
|
||||
$Qualifiers += "MaxLen($($Matches[2]))"
|
||||
}
|
||||
$XMLPropertyAttrQualifiers = $XML.CreateAttribute('qualifiers', $XML.DocumentElement.ovf)
|
||||
$XMLPropertyAttrQualifiers.Value = $Qualifiers -join ' '
|
||||
[void]$XMLProperty.Attributes.Append($XMLPropertyAttrQualifiers)
|
||||
[void]$XMLProperty.SetAttribute('qualifiers', $NS.LookupNamespace('ovf'), $Qualifiers -join ' ')
|
||||
}
|
||||
}
|
||||
'^string' {
|
||||
$XMLPropertyAttrType.Value = 'string'
|
||||
[void]$XMLProperty.SetAttribute('type', $NS.LookupNamespace('ovf'), 'string')
|
||||
$Qualifiers = @()
|
||||
If ($Property.Type -match '^string\((\d*)\.\.(\d*)\)') {
|
||||
If ($Matches[1]) {
|
||||
@@ -181,28 +260,19 @@ ForEach ($Category in $OVFConfig.PropertyCategories) {
|
||||
If ($Matches[2]) {
|
||||
$Qualifiers += "MaxLen($($Matches[2]))"
|
||||
}
|
||||
$XMLPropertyAttrQualifiers = $XML.CreateAttribute('qualifiers', $XML.DocumentElement.ovf)
|
||||
$XMLPropertyAttrQualifiers.Value = $Qualifiers -join ' '
|
||||
[void]$XMLProperty.Attributes.Append($XMLPropertyAttrQualifiers)
|
||||
[void]$XMLProperty.SetAttribute('qualifiers', $NS.LookupNamespace('ovf'), $Qualifiers -join ' ')
|
||||
} ElseIf ($Property.Type -match '^string\[(.*)\]') {
|
||||
$XMLPropertyAttrQualifiers = $XML.CreateAttribute('qualifiers', $XML.DocumentElement.ovf)
|
||||
$XMLPropertyAttrQualifiers.Value = "ValueMap{$($Matches[1] -replace '","', '", "')}"
|
||||
[void]$XMLProperty.Attributes.Append($XMLPropertyAttrQualifiers)
|
||||
[void]$XMLProperty.SetAttribute('qualifiers', $NS.LookupNamespace('ovf'), "ValueMap{$($Matches[1] -replace '","', '", "')}")
|
||||
}
|
||||
}
|
||||
}
|
||||
$XMLPropertyAttrUserConfigurable = $XML.CreateAttribute('userConfigurable', $XML.DocumentElement.ovf)
|
||||
$XMLPropertyAttrUserConfigurable.Value = "$([boolean]$Property.UserConfigurable)".ToLower()
|
||||
$XMLPropertyAttrValue = $XML.CreateAttribute('value', $XML.DocumentElement.ovf)
|
||||
[void]$XMLProperty.SetAttribute('userConfigurable', $NS.LookupNamespace('ovf'), "$([boolean]$Property.UserConfigurable)".ToLower())
|
||||
|
||||
If ($Property.Type -eq 'boolean') {
|
||||
$XMLPropertyAttrValue.Value = "$([boolean]$Property.DefaultValue)".ToLower()
|
||||
[void]$XMLProperty.SetAttribute('value', $NS.LookupNamespace('ovf'), "$([boolean]$Property.DefaultValue)".ToLower())
|
||||
} Else {
|
||||
$XMLPropertyAttrValue.Value = $Property.DefaultValue
|
||||
[void]$XMLProperty.SetAttribute('value', $NS.LookupNamespace('ovf'), $Property.DefaultValue)
|
||||
}
|
||||
[void]$XMLProperty.Attributes.Append($XMLPropertyAttrKey)
|
||||
[void]$XMLProperty.Attributes.Append($XMLPropertyAttrType)
|
||||
[void]$XMLProperty.Attributes.Append($XMLPropertyAttrUserConfigurable)
|
||||
[void]$XMLProperty.Attributes.Append($XMLPropertyAttrValue)
|
||||
|
||||
If ($Property.Label) {
|
||||
$XMLPropertyLabel = $XML.CreateElement('Label', $XML.DocumentElement.xmlns)
|
||||
@@ -216,30 +286,19 @@ ForEach ($Category in $OVFConfig.PropertyCategories) {
|
||||
}
|
||||
|
||||
If (($Property.Configurations.Count -eq 1) -and ($Property.Configurations -eq '*')) {
|
||||
$XMLPropertyAttrConfiguration = $XML.CreateAttribute('configuration', $XML.DocumentElement.ovf)
|
||||
$XMLPropertyAttrConfiguration.Value = $OVFConfig.DeploymentConfigurations.Id -join ' '
|
||||
[void]$XMLProperty.Attributes.Append($XMLPropertyAttrConfiguration)
|
||||
[void]$XMLProperty.SetAttribute('configuration', $NS.LookupNamespace('ovf'), $OVFConfig.DeploymentConfigurations.Id -join ' ')
|
||||
} ElseIf ($Property.Configurations.Count -gt 0) {
|
||||
$XMLPropertyAttrConfiguration = $XML.CreateAttribute('configuration', $XML.DocumentElement.ovf)
|
||||
$XMLPropertyAttrConfiguration.Value = $Property.Configurations -join ' '
|
||||
[void]$XMLProperty.Attributes.Append($XMLPropertyAttrConfiguration)
|
||||
[void]$XMLProperty.SetAttribute('configuration', $NS.LookupNamespace('ovf'), $Property.Configurations -join ' ')
|
||||
}
|
||||
|
||||
If ($Property.Value.Count -eq 1) {
|
||||
$XMLPropertyAttrValue = $XML.CreateAttribute('value', $XML.DocumentElement.ovf)
|
||||
$XMLPropertyAttrValue.Value = $Property.Value
|
||||
[void]$XMLProperty.Attributes.Append($XMLPropertyAttrValue)
|
||||
[void]$XMLProperty.SetAttribute('value', $NS.LookupNamespace('ovf'), $Property.Value)
|
||||
} ElseIf ($Property.Value.Count -gt 1) {
|
||||
ForEach ($Value in $Property.Value) {
|
||||
$XMLValue = $XML.CreateElement('Value', $XML.DocumentElement.xmlns)
|
||||
|
||||
$XMLValueAttrValue = $XML.CreateAttribute('value', $XML.DocumentElement.ovf)
|
||||
$XMLValueAttrValue.Value = $Value
|
||||
$XMLValueAttrConfiguration = $XML.CreateAttribute('configuration', $XML.DocumentElement.ovf)
|
||||
$XMLValueAttrConfiguration.Value = $Value
|
||||
|
||||
[void]$XMLValue.Attributes.Append($XMLValueAttrValue)
|
||||
[void]$XMLValue.Attributes.Append($XMLValueAttrConfiguration)
|
||||
[void]$XMLValue.SetAttribute('value', $NS.LookupNamespace('ovf'), $Value)
|
||||
[void]$XMLValue.SetAttribute('configuration', $NS.LookupNamespace('ovf'), $Value)
|
||||
|
||||
[void]$XMLProperty.AppendChild($XMLValue)
|
||||
}
|
||||
|
||||
@@ -1,46 +1,79 @@
|
||||
DeploymentConfigurations:
|
||||
- Id: domainmember
|
||||
Label: Domain member
|
||||
Description: Windows 10 client joined to an Active Directory domain
|
||||
- Id: standalone
|
||||
Label: Stand-alone
|
||||
Description: Stand-alone Windows 10 client
|
||||
|
||||
- Id: small
|
||||
Label: 'Ubuntu Server 22.04 [SMALL: 2 vCPU/4GB RAM]'
|
||||
Description: Ubuntu Server 22.04.x
|
||||
Size:
|
||||
CPU: 2
|
||||
Memory: 4096
|
||||
|
||||
- Id: medium
|
||||
Label: 'Ubuntu Server 22.04 [MEDIUM: 4 vCPU/8GB RAM]'
|
||||
Description: Ubuntu Server 22.04.x
|
||||
Size:
|
||||
CPU: 4
|
||||
Memory: 8192
|
||||
|
||||
DynamicDisks:
|
||||
|
||||
- Description: Longhorn persistent storage
|
||||
UnitSize: GB
|
||||
Constraints:
|
||||
Minimum: 100
|
||||
Maximum: ''
|
||||
|
||||
PropertyCategories:
|
||||
- Name: 0) Deployment information
|
||||
|
||||
# - Name: 0) Deployment information
|
||||
# ProductProperties:
|
||||
# - Key: deployment.type
|
||||
# Type: string
|
||||
# Value:
|
||||
# - small
|
||||
# - medium
|
||||
# UserConfigurable: false
|
||||
|
||||
- Name: 1) Kubernetes
|
||||
ProductProperties:
|
||||
- Key: deployment.type
|
||||
Type: string
|
||||
Value:
|
||||
- domainmember
|
||||
- standalone
|
||||
UserConfigurable: false
|
||||
- Name: 1) Operating System
|
||||
|
||||
- Key: metacluster.fqdn
|
||||
Type: string(1..)
|
||||
Label: Appliance FQDN*
|
||||
Description: 'Respective subdomains will be available for each component (e.g. storage.example.org); this address should already be configured as a wildcard record within your DNS zone.'
|
||||
DefaultValue: 'meta.k8s.cluster'
|
||||
Configurations: '*'
|
||||
UserConfigurable: true
|
||||
|
||||
- Name: 2) Operating System
|
||||
ProductProperties:
|
||||
|
||||
- Key: guestinfo.hostname
|
||||
Type: string(1..15)
|
||||
Label: Hostname*
|
||||
Description: '(max length: 15 characters)'
|
||||
Description: ''
|
||||
DefaultValue: 'META-NODE-{{ hostname.suffix }}'
|
||||
Configurations: '*'
|
||||
UserConfigurable: true
|
||||
|
||||
- Key: guestinfo.rootpw
|
||||
Type: password(7..)
|
||||
Label: Local root password*
|
||||
Description: ''
|
||||
DefaultValue: ''
|
||||
Configurations: '*'
|
||||
UserConfigurable: true
|
||||
- Key: guestinfo.administratorpw
|
||||
Type: password(7..)
|
||||
Label: Local administrator password*
|
||||
Description: Must meet password complexity rules
|
||||
DefaultValue: password
|
||||
Configurations:
|
||||
- standalone
|
||||
|
||||
- Key: guestinfo.rootsshkey
|
||||
Type: password(1..)
|
||||
Label: Local root SSH public key*
|
||||
Description: This line should start with 'ssh-rsa AAAAB3N'
|
||||
DefaultValue: ''
|
||||
Configurations: '*'
|
||||
UserConfigurable: true
|
||||
- Key: guestinfo.ntpserver
|
||||
Type: string(1..)
|
||||
Label: Time server*
|
||||
Description: A comma-separated list of timeservers
|
||||
DefaultValue: 0.pool.ntp.org,1.pool.ntp.org,2.pool.ntp.org
|
||||
Configurations:
|
||||
- standalone
|
||||
UserConfigurable: true
|
||||
- Name: 2) Networking
|
||||
|
||||
- Name: 3) Networking
|
||||
ProductProperties:
|
||||
|
||||
- Key: guestinfo.ipaddress
|
||||
Type: ip
|
||||
Label: IP Address*
|
||||
@@ -48,6 +81,7 @@ PropertyCategories:
|
||||
DefaultValue: ''
|
||||
Configurations: '*'
|
||||
UserConfigurable: true
|
||||
|
||||
- Key: guestinfo.prefixlength
|
||||
Type: int(8..32)
|
||||
Label: Subnet prefix length*
|
||||
@@ -55,6 +89,7 @@ PropertyCategories:
|
||||
DefaultValue: '24'
|
||||
Configurations: '*'
|
||||
UserConfigurable: true
|
||||
|
||||
- Key: guestinfo.dnsserver
|
||||
Type: ip
|
||||
Label: DNS server*
|
||||
@@ -62,6 +97,7 @@ PropertyCategories:
|
||||
DefaultValue: ''
|
||||
Configurations: '*'
|
||||
UserConfigurable: true
|
||||
|
||||
- Key: guestinfo.gateway
|
||||
Type: ip
|
||||
Label: Gateway*
|
||||
@@ -69,32 +105,42 @@ PropertyCategories:
|
||||
DefaultValue: ''
|
||||
Configurations: '*'
|
||||
UserConfigurable: true
|
||||
- Name: 3) Active Directory membership
|
||||
|
||||
- Key: guestinfo.ntpserver
|
||||
Type: string(1..)
|
||||
Label: Time server*
|
||||
Description: A comma-separated list of timeservers
|
||||
DefaultValue: 0.pool.ntp.org,1.pool.ntp.org,2.pool.ntp.org
|
||||
Configurations: '*'
|
||||
UserConfigurable: true
|
||||
|
||||
- Name: 4) Hypervisor
|
||||
ProductProperties:
|
||||
- Key: addsconfig.domainname
|
||||
|
||||
- Key: hv.fqdn
|
||||
Type: string(1..)
|
||||
Label: Domain name*
|
||||
Description: Must be able to be resolved through provided DNS server
|
||||
DefaultValue: example.org
|
||||
Configurations:
|
||||
- domainmember
|
||||
Label: vCenter FQDN/IP-address*
|
||||
Description: The address of the vCenter instance which this bootstrap appliance will interact with for provisioning new VM's.
|
||||
DefaultValue: ''
|
||||
Configurations: '*'
|
||||
UserConfigurable: true
|
||||
- Key: addsconfig.username
|
||||
|
||||
- Key: hv.username
|
||||
Type: string(1..)
|
||||
Label: Domain account username*
|
||||
Description: ''
|
||||
DefaultValue: username
|
||||
Configurations:
|
||||
- domainmember
|
||||
Label: vCenter username*
|
||||
Description: The username which this bootstrap appliance will authenticate with to the vCenter instance.
|
||||
DefaultValue: ''
|
||||
Configurations: '*'
|
||||
UserConfigurable: true
|
||||
- Key: addsconfig.password
|
||||
|
||||
- Key: hv.password
|
||||
Type: password(1..)
|
||||
Label: Domain account password*
|
||||
Description: ''
|
||||
DefaultValue: password
|
||||
Configurations:
|
||||
- domainmember
|
||||
Label: vCenter password ...*
|
||||
Description: The password which this bootstrap appliance will authenticate with to the vCenter instance.
|
||||
DefaultValue: ''
|
||||
Configurations: '*'
|
||||
UserConfigurable: true
|
||||
|
||||
AdvancedOptions:
|
||||
- Key: appliance.name
|
||||
Value: "{{ appliance.name }}"
|
||||
@@ -105,6 +151,9 @@ AdvancedOptions:
|
||||
|
||||
---
|
||||
Variables:
|
||||
- Name: hostname.suffix
|
||||
Expression: |
|
||||
(-join ((48..57) + (97..122) | Get-Random -Count 5 | % {[char]$_})).ToUpper()
|
||||
- Name: appliance.name
|
||||
Expression: |
|
||||
$Parameter['appliance.name']
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
[CmdletBinding()]
|
||||
Param(
|
||||
# No parameters
|
||||
)
|
||||
|
||||
# Create scheduled task
|
||||
& schtasks.exe /Create /TN 'FirstBoot' /SC ONSTART /RU SYSTEM /TR "powershell.exe -file C:\Payload\Apply-FirstBootConfig.ps1"
|
||||
@@ -1,244 +0,0 @@
|
||||
[CmdletBinding()]
|
||||
Param(
|
||||
# No parameters
|
||||
)
|
||||
|
||||
$SetLocationSplat = @{
|
||||
Path = $PSScriptRoot
|
||||
}
|
||||
Set-Location @SetLocationSplat
|
||||
|
||||
$NewEventLogSplat = @{
|
||||
LogName = 'Application'
|
||||
Source = 'FirstBoot'
|
||||
ErrorAction = 'SilentlyContinue'
|
||||
}
|
||||
New-EventLog @NewEventLogSplat
|
||||
$WriteEventLogSplat = @{
|
||||
LogName = 'Application'
|
||||
Source = 'FirstBoot'
|
||||
EntryType = 'Information'
|
||||
EventID = 1
|
||||
Message = "FirstBoot sequence initiated [working directory: '$PWD']"
|
||||
}
|
||||
Write-EventLog @WriteEventLogSplat
|
||||
|
||||
$VMwareToolsExecutable = "C:\Program Files\VMware\VMware Tools\vmtoolsd.exe"
|
||||
|
||||
[xml]$ovfEnv = & $VMwareToolsExecutable --cmd "info-get guestinfo.ovfEnv" | Out-String
|
||||
$ovfProperties = $ovfEnv.ChildNodes.NextSibling.PropertySection.Property
|
||||
|
||||
$ovfPropertyValues = @{}
|
||||
foreach ($ovfProperty in $ovfProperties) {
|
||||
$ovfPropertyValues[$ovfProperty.key] = $ovfProperty.Value
|
||||
}
|
||||
|
||||
# Check for mandatory values
|
||||
Switch ($ovfPropertyValues['deployment.type']) {
|
||||
'domainmember' {
|
||||
$MandatoryProperties, $MissingProperties = @('guestinfo.hostname', 'guestinfo.ipaddress', 'guestinfo.prefixlength', 'guestinfo.gateway', 'addsconfig.domainname', 'addsconfig.username', 'addsconfig.password'), @()
|
||||
}
|
||||
'standalone' {
|
||||
$MandatoryProperties, $MissingProperties = @('guestinfo.hostname', 'guestinfo.ipaddress', 'guestinfo.prefixlength', 'guestinfo.gateway', 'guestinfo.administratorpw', 'guestinfo.ntpserver'), @()
|
||||
}
|
||||
default {
|
||||
# Mandatory values missing, cannot provision.
|
||||
$WriteEventLogSplat = @{
|
||||
LogName = 'Application'
|
||||
Source = 'FirstBoot'
|
||||
EntryType = 'Error'
|
||||
EventID = 66
|
||||
Message = "Unexpected or no value set for property 'deployment.type', cannot provision."
|
||||
}
|
||||
Write-EventLog @WriteEventLogSplat
|
||||
& schtasks.exe /Change /TN 'FirstBoot' /DISABLE
|
||||
Stop-Computer -Force
|
||||
Exit
|
||||
}
|
||||
}
|
||||
ForEach ($Property in $MandatoryProperties) {
|
||||
If (!$ovfPropertyValues[$Property]) {
|
||||
$MissingProperties += $Property
|
||||
}
|
||||
}
|
||||
If ($MissingProperties.Length -gt 0) {
|
||||
# Mandatory values missing, cannot provision.
|
||||
$WriteEventLogSplat = @{
|
||||
LogName = 'Application'
|
||||
Source = 'FirstBoot'
|
||||
EntryType = 'Error'
|
||||
EventID = 66
|
||||
Message = "Missing values for mandatory properties $(($MissingProperties | ForEach-Object {"'{0}'" -f $_}) -join ', '), cannot provision."
|
||||
}
|
||||
Write-EventLog @WriteEventLogSplat
|
||||
& schtasks.exe /Change /TN 'FirstBoot' /DISABLE
|
||||
Stop-Computer -Force
|
||||
Exit
|
||||
}
|
||||
|
||||
# Set hostname and description
|
||||
If ($Env:ComputerName -ne $ovfPropertyValues['guestinfo.hostname']) {
|
||||
$RenameComputerSplat = @{
|
||||
NewName = $ovfPropertyValues['guestinfo.hostname']
|
||||
Force = $True
|
||||
Confirm = $False
|
||||
}
|
||||
Rename-Computer @RenameComputerSplat
|
||||
$SetCimInstanceSplat = @{
|
||||
InputObject = (Get-CimInstance -ClassName 'Win32_OperatingSystem')
|
||||
Property = @{
|
||||
Description = $ovfPropertyValues['guestinfo.hostname']
|
||||
}
|
||||
}
|
||||
Set-CimInstance @SetCimInstanceSplat
|
||||
|
||||
# Restart the computer to apply changes
|
||||
Restart-Computer -Force
|
||||
Exit
|
||||
}
|
||||
|
||||
# Configure network interface
|
||||
If ((Get-WmiObject -Class 'Win32_NetworkAdapterConfiguration').IPAddress -NotContains $ovfPropertyValues['guestinfo.ipaddress']) {
|
||||
$NewNetIPAddressSplat = @{
|
||||
InterfaceAlias = (Get-NetAdapter).Name
|
||||
AddressFamily = 'IPv4'
|
||||
IPAddress = $ovfPropertyValues['guestinfo.ipaddress']
|
||||
PrefixLength = $ovfPropertyValues['guestinfo.prefixlength']
|
||||
DefaultGateway = $ovfPropertyValues['guestinfo.gateway']
|
||||
}
|
||||
$IPAddress = New-NetIPAddress @NewNetIPAddressSplat
|
||||
|
||||
# Wait for network connection to become available
|
||||
$Timestamp, $TimeoutMinutes = (Get-Date), 5
|
||||
Do {
|
||||
If ($Timestamp.AddMinutes($TimeoutMinutes) -lt (Get-Date)) {
|
||||
$WriteEventLogSplat = @{
|
||||
LogName = 'Application'
|
||||
Source = 'FirstBoot'
|
||||
EntryType = 'Warning'
|
||||
EventID = 13
|
||||
Message = "Timeout after $($TimeoutMinutes) minutes waiting for network connection to become available."
|
||||
}
|
||||
Write-EventLog @WriteEventLogSplat
|
||||
Break
|
||||
}
|
||||
|
||||
Start-Sleep -Milliseconds 250
|
||||
|
||||
$GetNetIPAddressSplat = @{
|
||||
IPAddress = $ovfPropertyValues['guestinfo.ipaddress']
|
||||
InterfaceIndex = $IPAddress.InterfaceIndex
|
||||
AddressFamily = 'IPv4'
|
||||
ErrorAction = 'SilentlyContinue'
|
||||
}
|
||||
} Until ((Get-NetIPAddress @GetNetIPAddressSplat).AddressState -eq 'Preferred')
|
||||
|
||||
$OldErrorActionPreference, $ErrorActionPreference = $ErrorActionPreference, 'SilentlyContinue'
|
||||
$TestNetConnectionSplat = @{
|
||||
ComputerName = ([IPAddress]$ovfPropertyValues['guestinfo.dnsserver']).IPAddressToString
|
||||
InformationLevel = 'Quiet'
|
||||
}
|
||||
$SetDnsClientServerAddressSplat = @{
|
||||
InterfaceAlias = (Get-NetAdapter).Name
|
||||
ServerAddresses = If (
|
||||
[boolean]($ovfPropertyValues['guestinfo.dnsserver'] -as [IPaddress]) -and (Test-NetConnection @TestNetConnectionSplat)) {
|
||||
($ovfPropertyValues['guestinfo.dnsserver'])
|
||||
} else {
|
||||
('127.0.0.1')
|
||||
}
|
||||
Validate = $False
|
||||
}
|
||||
Set-DnsClientServerAddress @SetDnsClientServerAddressSplat
|
||||
$ErrorActionPreference, $OldErrorActionPreference = $OldErrorActionPreference, $NULL
|
||||
}
|
||||
|
||||
Switch ($ovfPropertyValues['deployment.type']) {
|
||||
'domainmember' {
|
||||
# Join Active Directory domain as member
|
||||
If (!(Get-WmiObject -Class Win32_ComputerSystem).PartOfDomain) {
|
||||
$AddComputerSplat = @{
|
||||
DomainName = $ovfPropertyValues['addsconfig.domainname']
|
||||
Credential = New-Object System.Management.Automation.PSCredential(
|
||||
$ovfPropertyValues['addsconfig.username'],
|
||||
(ConvertTo-SecureString $ovfPropertyValues['addsconfig.password'] -AsPlainText -Force)
|
||||
)
|
||||
# OUPath = $ovfPropertyValues['addsconfig.organizationalunit']
|
||||
Restart = $True
|
||||
Force = $True
|
||||
Confirm = $False
|
||||
}
|
||||
Add-Computer @AddComputerSplat
|
||||
|
||||
# Previous cmdlet performs a reboot on completion; so these are commented out
|
||||
# Restart-Computer -Force
|
||||
# Exit
|
||||
}
|
||||
}
|
||||
'standalone' {
|
||||
# Change password of built-in Administrator
|
||||
$BuiltinAdministrator = (Get-LocalUser | Where-Object {$_.SID -match '-500'})
|
||||
$ConvertToSecureStringSplat = @{
|
||||
String = $ovfPropertyValues['guestinfo.administratorpw']
|
||||
AsPlainText = $True
|
||||
Force = $True
|
||||
}
|
||||
$SetLocalUserSplat = @{
|
||||
InputObject = $BuiltinAdministrator
|
||||
Password = ConvertTo-SecureString @ConvertToSecureStringSplat
|
||||
PasswordNeverExpires = $True
|
||||
AccountNeverExpires = $True
|
||||
### This setting is not allowed on the last administrator
|
||||
# UserMayChangePassword = $False
|
||||
Confirm = $False
|
||||
}
|
||||
Set-LocalUser @SetLocalUserSplat
|
||||
|
||||
$EnableLocalUserSplat = @{
|
||||
InputObject = $BuiltinAdministrator
|
||||
Confirm = $False
|
||||
}
|
||||
Enable-LocalUser @EnableLocalUserSplat
|
||||
}
|
||||
}
|
||||
|
||||
# Iterate through and invoke all payload scripts
|
||||
#! TODO: add registry values to determine which scripts have already been invoked (in case of intermediate reboots)
|
||||
$GetItemSplat = @{
|
||||
Path = "$($PSScriptRoot)\Scripts\*.ps1"
|
||||
}
|
||||
ForEach ($Script in (Get-Item @GetItemSplat)) {
|
||||
Try {
|
||||
$WriteEventLogSplat = @{
|
||||
LogName = 'Application'
|
||||
Source = 'FirstBoot'
|
||||
EntryType = 'Information'
|
||||
EventID = 4
|
||||
Message = "Running script: '$($Script.FullName)'"
|
||||
}
|
||||
Write-EventLog @WriteEventLogSplat
|
||||
& $Script.FullName -Parameter $ovfPropertyValues
|
||||
}
|
||||
Catch {
|
||||
$WriteEventLogSplat = @{
|
||||
LogName = 'Application'
|
||||
Source = 'FirstBoot'
|
||||
EntryType = 'Error'
|
||||
EventID = 66
|
||||
Message = @"
|
||||
Error occurred while executing script '$($Script.Name)':
|
||||
$($_.Exception.Message)
|
||||
"@
|
||||
}
|
||||
Write-EventLog @WriteEventLogSplat
|
||||
}
|
||||
}
|
||||
|
||||
$WriteEventLogSplat = @{
|
||||
LogName = 'Application'
|
||||
Source = 'FirstBoot'
|
||||
EntryType = 'Information'
|
||||
EventID = 42
|
||||
Message = 'FirstBoot sequence applied and finished'
|
||||
}
|
||||
Write-EventLog @WriteEventLogSplat
|
||||
& schtasks.exe /Change /TN 'FirstBoot' /DISABLE
|
||||
Reference in New Issue
Block a user