├── main ├── roles │ ├── hyperv │ │ ├── vars │ │ │ └── main.yml │ │ └── tasks │ │ │ └── main.yml │ ├── create_vhdx │ │ ├── files │ │ │ └── packer │ │ │ │ ├── linux │ │ │ │ ├── common │ │ │ │ │ ├── vmware.sh │ │ │ │ │ ├── motd.sh │ │ │ │ │ ├── virtualbox.sh │ │ │ │ │ ├── vagrant.sh │ │ │ │ │ └── chef.sh │ │ │ │ └── ubuntu │ │ │ │ │ ├── inventory.ini │ │ │ │ │ ├── hyperv-xenial.sh │ │ │ │ │ ├── cloud-init.sh │ │ │ │ │ ├── http │ │ │ │ │ ├── 14.04 │ │ │ │ │ │ ├── preseed.cfg │ │ │ │ │ │ └── preseed-cloud-init.cfg │ │ │ │ │ ├── 17.10 │ │ │ │ │ │ ├── preseed-cloud-init.cfg │ │ │ │ │ │ └── preseed.cfg │ │ │ │ │ ├── 18.04 │ │ │ │ │ │ ├── preseed.cfg │ │ │ │ │ │ └── preseed-cloud-init.cfg │ │ │ │ │ ├── 16.04 │ │ │ │ │ │ ├── preseed.cfg │ │ │ │ │ │ └── preseed-cloud-init.cfg │ │ │ │ │ └── base │ │ │ │ │ │ ├── preseed.cfg │ │ │ │ │ │ └── preseed-cloud-init.cfg │ │ │ │ │ ├── cleanup.sh │ │ │ │ │ ├── update.sh │ │ │ │ │ ├── hyperv-gen2.sh │ │ │ │ │ ├── network.sh │ │ │ │ │ └── kubernetes_install.yml │ │ │ │ └── hyperv-ubuntu-18.04.json │ │ ├── vars │ │ │ └── main.yml │ │ └── tasks │ │ │ └── main.yml │ ├── create_vm │ │ ├── files │ │ │ ├── CreateFolder │ │ │ │ └── localhost.mof │ │ │ └── config_vm.ps1 │ │ ├── vars │ │ │ └── main.yml │ │ └── tasks │ │ │ └── main.yml │ ├── join_cluster │ │ └── tasks │ │ │ └── main.yml │ ├── kube │ │ └── tasks │ │ │ └── main.yml │ └── kube_master │ │ └── tasks │ │ └── main.yml ├── group_vars │ ├── kube.yml │ ├── main.yml │ └── hyperv.yml └── playbook.yml ├── .vscode └── settings.json ├── misc ├── host_preparation │ ├── 02_enable_wsl.ps1 │ ├── 03_install_ubuntu.ps1 │ └── 01_config_winrm_ansible.ps1 ├── wsl_preparation │ └── 04_configure.sh └── Create windows 2019 VM │ └── new-windows2019.ps1 ├── .gitignore ├── CODE_OF_CONDUCT.md ├── inventory ├── hosts.yml └── kube.yml ├── .pre-commit-config.yaml ├── LICENSE ├── readme.md └── CONTRIBUTING.md /main/roles/hyperv/vars/main.yml: -------------------------------------------------------------------------------- 1 | hyperv_switch: "Packer" -------------------------------------------------------------------------------- /main/roles/create_vhdx/files/packer/linux/common/vmware.sh: -------------------------------------------------------------------------------- 1 | apt-get install open-vm-tools -y 2 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "terminal.integrated.env.windows": "C:\\Windows\\System32\\bash.exe" 3 | } 4 | -------------------------------------------------------------------------------- /main/group_vars/kube.yml: -------------------------------------------------------------------------------- 1 | ansible_user: vagrant 2 | ansible_password: vagrant 3 | ansible_become_password: vagrant -------------------------------------------------------------------------------- /main/roles/create_vhdx/files/packer/linux/common/motd.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | echo 'Happy coding!' > /etc/motd.tail 4 | -------------------------------------------------------------------------------- /misc/host_preparation/02_enable_wsl.ps1: -------------------------------------------------------------------------------- 1 | Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Windows-Subsystem-Linux 2 | -------------------------------------------------------------------------------- /main/group_vars/main.yml: -------------------------------------------------------------------------------- 1 | root: "c:\\Kubernetes" 2 | hyperv_switch: "Packer" 3 | username: "etienne" 4 | password: "P@ssword1234!" 5 | output_dir: "{{ root }}\\Source_vhdx" -------------------------------------------------------------------------------- /main/roles/create_vm/files/CreateFolder/localhost.mof: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EtienneDeneuve/kubernetes_hyperv/HEAD/main/roles/create_vm/files/CreateFolder/localhost.mof -------------------------------------------------------------------------------- /main/roles/create_vhdx/vars/main.yml: -------------------------------------------------------------------------------- 1 | root: "c:\\Kubernetes" 2 | hyperv_switch: "Packer" 3 | username: "etienne" 4 | password: "P@ssword1234!" 5 | output_dir: "{{ root }}\\Source_vhdx" 6 | -------------------------------------------------------------------------------- /main/roles/create_vhdx/files/packer/linux/ubuntu/inventory.ini: -------------------------------------------------------------------------------- 1 | [local] 2 | localhost ansible_connection=local ansible_become=yes ansible_become_user=vagrant ansible_become_password=vagrant 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | part1/packer/output-hyperv-iso 2 | part1/packer/packer_cache 3 | part1/dsc/CreateFolder 4 | *.box 5 | *.vhd 6 | *.vhdx 7 | *.iso 8 | part1/dsc/CreateFolder 9 | part1/packer/output-hyperv-iso/* 10 | *.retry 11 | -------------------------------------------------------------------------------- /main/roles/create_vhdx/files/packer/linux/ubuntu/hyperv-xenial.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # install Microsoft best practices 4 | apt-get -y install linux-virtual-lts-xenial linux-tools-virtual-lts-xenial linux-cloud-tools-virtual-lts-xenial 5 | -------------------------------------------------------------------------------- /misc/host_preparation/03_install_ubuntu.ps1: -------------------------------------------------------------------------------- 1 | Invoke-WebRequest -Uri https://aka.ms/wsl-ubuntu-1604 -OutFile Ubuntu.zip -UseBasicParsing 2 | Expand-Archive .\Ubuntu.zip c:\Distros\Ubuntu 3 | Rename-Item c:\distros\ubuntu\ubuntu1804.exe c:\distros\ubuntu\ubuntu.exe 4 | -------------------------------------------------------------------------------- /main/roles/create_vhdx/files/packer/linux/common/virtualbox.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | mkdir -p /mnt/virtualbox 4 | mount -o loop /home/vagrant/VBoxGuest*.iso /mnt/virtualbox 5 | sh /mnt/virtualbox/VBoxLinuxAdditions.run 6 | umount /mnt/virtualbox 7 | rm -rf /home/vagrant/VBoxGuest*.iso 8 | -------------------------------------------------------------------------------- /main/roles/create_vhdx/files/packer/linux/ubuntu/cloud-init.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # install cloud-init 4 | apt-get -y install cloud-init 5 | 6 | #hostname will be managed by cloud-init, but the current value will not be removed 7 | HOSTNAME=`hostname` 8 | sed -i "/${HOSTNAME}/d" /etc/hosts 9 | -------------------------------------------------------------------------------- /main/roles/create_vhdx/files/packer/linux/ubuntu/http/14.04/preseed.cfg: -------------------------------------------------------------------------------- 1 | d-i preseed/include string ../base/preseed.cfg 2 | 3 | # Minimum packages (see postinstall.sh) 4 | d-i pkgsel/include string openssh-server ntp linux-tools-$(uname -r) linux-cloud-tools-$(uname -r) linux-cloud-tools-common 5 | -------------------------------------------------------------------------------- /misc/wsl_preparation/04_configure.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | apt-get -y install apt-transport-https curl software-properties-common ca-certificates 3 | apt-add-repository --yes --update ppa:ansible/ansible 4 | apt-get -y install ansible python-pip 5 | pip install pip --upgrade 6 | pip install pywinrm 7 | -------------------------------------------------------------------------------- /main/roles/create_vhdx/files/packer/linux/ubuntu/http/17.10/preseed-cloud-init.cfg: -------------------------------------------------------------------------------- 1 | d-i preseed/include string ../base/preseed-cloud-init.cfg 2 | 3 | # Minimum packages (see postinstall.sh) 4 | d-i pkgsel/include string openssh-server ntp linux-tools-virtual-lts-xenial linux-cloud-tools-virtual-lts-xenial 5 | -------------------------------------------------------------------------------- /main/group_vars/hyperv.yml: -------------------------------------------------------------------------------- 1 | ansible_connection: winrm 2 | ansible_winrm_transport: basic 3 | ansible_user: administrator 4 | ansible_password: P@ssword1234! 5 | ansible_winrm_server_cert_validation: ignore 6 | ansible_become_method: runas 7 | ansible_become_user: administrator 8 | ansible_become_password: P@ssword1234! -------------------------------------------------------------------------------- /main/roles/create_vhdx/files/packer/linux/ubuntu/http/14.04/preseed-cloud-init.cfg: -------------------------------------------------------------------------------- 1 | d-i preseed/include string ../base/preseed-cloud-init.cfg 2 | 3 | # Minimum packages (see postinstall.sh) 4 | d-i pkgsel/include string openssh-server ntp linux-tools-$(uname -r) linux-cloud-tools-$(uname -r) linux-cloud-tools-common 5 | -------------------------------------------------------------------------------- /main/roles/create_vm/vars/main.yml: -------------------------------------------------------------------------------- 1 | root: "c:\\Kubernetes" 2 | # hyperv_switch: "packer-hyperv-iso" 3 | hyperv_switch: "Packer" 4 | username: "etienne" 5 | password: "P@ssword1234!" 6 | output_dir: "{{ root }}\\Source_vhdx" 7 | nodes_name: 8 | - master-01 9 | - master-02 10 | - worker-01 11 | - worker-02 12 | -------------------------------------------------------------------------------- /main/roles/create_vhdx/files/packer/linux/ubuntu/cleanup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Clean up 4 | purge-old-kernels 5 | apt-get -y remove dkms 6 | apt-get -y autoremove --purge 7 | apt-get -y clean 8 | 9 | # Remove temporary files 10 | rm -rf /tmp/* 11 | 12 | # Zero out free space 13 | dd if=/dev/zero of=/EMPTY bs=1M 14 | rm -f /EMPTY 15 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. 2 | -------------------------------------------------------------------------------- /main/roles/create_vhdx/files/packer/linux/ubuntu/http/18.04/preseed.cfg: -------------------------------------------------------------------------------- 1 | d-i preseed/include string ../base/preseed.cfg 2 | 3 | # Minimum packages (see postinstall.sh) 4 | 5 | d-i pkgsel/include string openssh-server ntp 6 | 7 | d-i preseed/late_command string in-target apt-get install -y --install-recommends linux-virtual linux-tools-virtual linux-cloud-tools-virtual; 8 | -------------------------------------------------------------------------------- /main/roles/create_vhdx/files/packer/linux/ubuntu/http/17.10/preseed.cfg: -------------------------------------------------------------------------------- 1 | d-i preseed/include string ../base/preseed.cfg 2 | 3 | # Minimum packages (see postinstall.sh) 4 | 5 | d-i pkgsel/include string openssh-server ntp 6 | 7 | d-i preseed/late_command string in-target apt-get install -y --install-recommends linux-image-virtual linux-tools-virtual linux-cloud-tools-virtual; 8 | -------------------------------------------------------------------------------- /main/roles/create_vhdx/files/packer/linux/ubuntu/http/16.04/preseed.cfg: -------------------------------------------------------------------------------- 1 | d-i preseed/include string ../base/preseed.cfg 2 | 3 | # Minimum packages (see postinstall.sh) 4 | 5 | d-i pkgsel/include string openssh-server ntp 6 | 7 | d-i preseed/late_command string in-target apt-get install -y --install-recommends linux-virtual-lts-xenial linux-tools-virtual-lts-xenial linux-cloud-tools-virtual-lts-xenial; 8 | -------------------------------------------------------------------------------- /main/roles/create_vhdx/files/packer/linux/ubuntu/http/16.04/preseed-cloud-init.cfg: -------------------------------------------------------------------------------- 1 | d-i preseed/include string ../base/preseed-cloud-init.cfg 2 | 3 | # Minimum packages (see postinstall.sh) 4 | 5 | d-i pkgsel/include string openssh-server ntp 6 | 7 | d-i preseed/late_command string in-target apt-get install -y --install-recommends linux-virtual-lts-xenial linux-tools-virtual-lts-xenial linux-cloud-tools-virtual-lts-xenial; 8 | -------------------------------------------------------------------------------- /main/roles/create_vhdx/files/packer/linux/ubuntu/update.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Update the box 4 | apt-get -y update 5 | apt-get -y upgrade 6 | 7 | # Install dependencies 8 | apt-get -y install dkms 9 | apt-get -y install nfs-common 10 | apt-get -y install byobu 11 | apt-get -y install software-properties-common 12 | apt-get -y install aptitude 13 | apt-add-repository --yes --update ppa:ansible/ansible 14 | apt-get -y install ansible 15 | -------------------------------------------------------------------------------- /inventory/hosts.yml: -------------------------------------------------------------------------------- 1 | all: 2 | hosts: 3 | hyperv: 4 | ansible_host: 172.20.10.4 5 | children: 6 | kube: 7 | children: 8 | master: 9 | hosts: 10 | k8smaster: 11 | ansible_host: 192.168.1.235 12 | nodes: 13 | hosts: 14 | k8snode1: 15 | ansible_host: 192.168.1.234 16 | k8snode2: 17 | ansible_host: 192.168.1.236 18 | -------------------------------------------------------------------------------- /main/roles/hyperv/tasks/main.yml: -------------------------------------------------------------------------------- 1 | 2 | - name: Ensure Hyper V is installed 3 | win_feature: 4 | name: Hyper-V 5 | state: present 6 | include_sub_features: yes 7 | include_management_tools: yes 8 | 9 | - name: "Adding xHyperV module using DSC" 10 | win_psmodule: 11 | name: xHyper-V 12 | state: present 13 | become: True 14 | 15 | - name: Create Default Switch 16 | win_shell: New-VMSwitch -Name {{ Packer }} -AllowManagementOS $true -NetAdapterName Ethernet -------------------------------------------------------------------------------- /main/roles/create_vhdx/files/packer/linux/common/vagrant.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Store build time 4 | date > /etc/vagrant_box_build_time 5 | 6 | # Set up sudo 7 | echo 'vagrant ALL=NOPASSWD:ALL' > /etc/sudoers.d/vagrant 8 | 9 | # Install vagrant key 10 | mkdir -pm 700 /home/vagrant/.ssh 11 | wget --no-check-certificate https://raw.github.com/mitchellh/vagrant/master/keys/vagrant.pub -O /home/vagrant/.ssh/authorized_keys 12 | chmod 0600 /home/vagrant/.ssh/authorized_keys 13 | chown -R vagrant:vagrant /home/vagrant/.ssh 14 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | repos: 3 | - repo: https://github.com/pre-commit/pre-commit-hooks 4 | rev: v1.3.0 5 | hooks: 6 | - id: trailing-whitespace 7 | - id: check-yaml 8 | - id: check-xml 9 | - id: check-json 10 | - id: end-of-file-fixer 11 | - id: trailing-whitespace 12 | - id: check-case-conflict 13 | - id: check-merge-conflict 14 | - repo: https://github.com/willthames/ansible-lint.git 15 | rev: v3.5.0rc1 16 | hooks: 17 | - id: ansible-lint 18 | files: \.(yaml|yml)$ 19 | -------------------------------------------------------------------------------- /main/roles/create_vhdx/files/packer/linux/ubuntu/hyperv-gen2.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | #disable on hyperv (quiet mode has problems with hyper-v graphics) 4 | #sed -i 's/GRUB_CMDLINE_LINUX_DEFAULT=\"quiet\"/GRUB_CMDLINE_LINUX_DEFAULT=\"\"/g' /etc/default/grub 5 | #update-grub 6 | 7 | # gen 2 EFI fix - see https://docs.microsoft.com/en-us/windows-server/virtualization/hyper-v/supported-ubuntu-virtual-machines-on-hyper-v 8 | cp -r /boot/efi/EFI/ubuntu/ /boot/efi/EFI/boot 9 | mv /boot/efi/EFI/boot/shimx64.efi /boot/efi/EFI/boot/bootx64.efi 10 | -------------------------------------------------------------------------------- /inventory/kube.yml: -------------------------------------------------------------------------------- 1 | all: 2 | children: 3 | kube: 4 | children: 5 | master: 6 | hosts: 7 | k8smaster: 8 | ansible_host: 192.168.1.235 9 | ansible_user: vagrant 10 | ansible_password: vagrant 11 | ansible_become_password: vagrant 12 | nodes: 13 | hosts: 14 | k8snode1: 15 | ansible_host: 192.168.1.234 16 | ansible_user: vagrant 17 | ansible_password: vagrant 18 | ansible_become_password: vagrant 19 | k8snode2: 20 | ansible_host: 192.168.1.236 21 | ansible_user: vagrant 22 | ansible_password: vagrant 23 | ansible_become_password: vagrant 24 | -------------------------------------------------------------------------------- /misc/Create windows 2019 VM/new-windows2019.ps1: -------------------------------------------------------------------------------- 1 | 2 | $iso_path = "C:\Users\etienne_deneuve\Downloads\en_windows_server_version_1903_x64_dvd_58ddff4b.iso" 3 | $root_vm_path = "C:\Users\etienne_deneuve\Documents\VM\windows2019.vhdx" 4 | 5 | $vm_settings = @{ 6 | "Name" = "Windows 2019" 7 | "MemoryStartupBytes" = 2GB 8 | "Generation" = 2 9 | "SwitchName" = "External - Wire" 10 | } 11 | 12 | if (Test-Path($root_vm_path) ) { 13 | $vm_settings.Add("VHDPath", $root_vm_path) 14 | 15 | } 16 | else { 17 | $vm_settings.Add("NewVHDPath", $root_vm_path) 18 | $vm_settings.Add("NewVHDSizeBytes", 127GB) 19 | } 20 | 21 | $vm = New-VM @vm_settings 22 | $DVD = Add-VMDvdDrive -VMName $VM.VMName -Path $iso_path -Passthru 23 | Set-VMFirmware -VM $VM -FirstBootDevice $DVD 24 | Set-VMProcessor -ExposeVirtualizationExtensions $true -VM $vm 25 | -------------------------------------------------------------------------------- /main/roles/create_vm/tasks/main.yml: -------------------------------------------------------------------------------- 1 | - name: "create folders" 2 | win_file: 3 | path: "{{ root }}\\{{ item }}" 4 | state: directory 5 | force: no 6 | with_items: nodes_name 7 | 8 | - name: "copy dsc script" 9 | win_copy: 10 | src: "{{role_path}}/files/config_vm.ps1" 11 | dest: '{{ root }}\\config_vm.ps1' 12 | force: yes 13 | 14 | - name: "copy vhd in folders" 15 | win_copy: 16 | src: "{{ output_dir }}\\Virtual Hard Disks\\ubuntu-bionic.vhdx" 17 | dest: "{{ root }}\\{{ item }}\\{{ item }}.vhdx" 18 | remote_src: yes 19 | force: no 20 | with_items: nodes_name 21 | 22 | - name: "set facts for nodes" 23 | set_fact: 24 | 25 | - name: "Launch dsc configuration script" 26 | win_shell: .\\config_vm.ps1 -vmswitch {{ hyperv_switch}} -rootpath {{ root }} -vms @({{ nodes_name|join (',') }}) 27 | become: 28 | args: 29 | chdir: "{{ root }}" 30 | tags: 31 | - skip_ansible_lint 32 | -------------------------------------------------------------------------------- /main/roles/create_vhdx/files/packer/linux/ubuntu/network.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -eux 2 | 3 | ubuntu_version="`lsb_release -r | awk '{print $2}'`"; 4 | major_version="`echo $ubuntu_version | awk -F. '{print $1}'`"; 5 | 6 | if [ "$major_version" -le "15" ] && [ "$ubuntu_version" != "15.10" ]; then 7 | echo "Disabling automatic udev rules for network interfaces in Ubuntu" 8 | # Disable automatic udev rules for network interfaces in Ubuntu, 9 | # source: http://6.ptmc.org/164/ 10 | rm -f /etc/udev/rules.d/70-persistent-net.rules; 11 | mkdir -p /etc/udev/rules.d/70-persistent-net.rules; 12 | rm -f /lib/udev/rules.d/75-persistent-net-generator.rules; 13 | rm -rf /dev/.udev/ /var/lib/dhcp3/* /var/lib/dhcp/*; 14 | fi 15 | 16 | # Adding a 2 sec delay to the interface up, to make the dhclient happy 17 | echo "pre-up sleep 2" >> /etc/network/interfaces 18 | 19 | # Disable DNS reverse lookup 20 | echo "UseDNS no" >> /etc/ssh/sshd_config 21 | -------------------------------------------------------------------------------- /main/roles/create_vhdx/files/packer/linux/ubuntu/http/18.04/preseed-cloud-init.cfg: -------------------------------------------------------------------------------- 1 | d-i preseed/include string ../base/preseed-cloud-init.cfg 2 | 3 | # Minimum packages (see postinstall.sh) 4 | 5 | d-i pkgsel/include string openssh-server ntp 6 | 7 | d-i preseed/late_command string in-target apt-get install -y --install-recommends linux-virtual linux-tools-virtual linux-cloud-tools-virtual; 8 | 9 | d-i pkgsel/include string curl openssh-server sudo sed linux-tools-$(uname -r) linux-cloud-tools-$(uname -r) linux-cloud-tools-common 10 | 11 | d-i preseed/late_command string \ 12 | sed -i -e "s/.*PermitRootLogin.*/PermitRootLogin yes/g" /target/etc/ssh/sshd_config ; \ 13 | dmesg | grep "Hypervisor detected: Microsoft HyperV" ; \ 14 | if [ $? -eq 0 ]; then \ 15 | chroot /target /bin/bash -c 'service ssh stop ; echo "deb http://deb.debian.org/debian jessie main" >> /etc/apt/sources.list ; apt-get update ; apt-get install hyperv-daemons' ; \ 16 | eject /dev/cdrom ; \ 17 | fi 18 | -------------------------------------------------------------------------------- /main/roles/join_cluster/tasks/main.yml: -------------------------------------------------------------------------------- 1 | 2 | - name: check if node is already part of cluster 3 | shell: kubectl get nodes | grep {{ inventory_hostname }} 4 | register: nodeclustered 5 | hosts: master 6 | tags: 7 | - skip_ansible_lint 8 | 9 | - name: set fact with stdout 10 | set_fact: 11 | clusterup: "{{ nodeclustered.stdout is search('Ready')}}" 12 | 13 | - name: 14 | debug: 15 | msg: "[Worker] K8S_TOKEN_HOLDER K8S token is {{ hostvars['K8S_TOKEN_HOLDER']['token'] }}" 16 | when: (clusterup|bool == false) 17 | 18 | - name: 19 | debug: 20 | msg: "[Worker] K8S_TOKEN_HOLDER K8S Hash is {{ hostvars['K8S_TOKEN_HOLDER']['hash'] }}" 21 | when: (clusterup|bool == false) 22 | 23 | - name: 24 | debug: 25 | msg: "[Worker] K8S_TOKEN_HOLDER K8S Hash is {{ hostvars['K8S_TOKEN_HOLDER']['masterip'] }}" 26 | when: (clusterup|bool == false) 27 | 28 | - name: join cluster 29 | shell: "kubeadm join {{ hostvars['K8S_TOKEN_HOLDER']['masterip'] }}:6443 --token {{ hostvars['K8S_TOKEN_HOLDER']['token'] }} --discovery-token-ca-cert-hash {{ hostvars['K8S_TOKEN_HOLDER']['hash'] }}" 30 | when: (clusterup|bool == false) 31 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Etienne Deneuve 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /main/roles/create_vm/files/config_vm.ps1: -------------------------------------------------------------------------------- 1 | param( 2 | $vms, 3 | $prefix, 4 | $vswitch, 5 | $rootpath 6 | ) 7 | 8 | Configuration CreateVM 9 | { 10 | param( 11 | $rootpath, 12 | $prefix, 13 | $vswitch, 14 | $vms 15 | ) 16 | Import-DscResource -module xHyper-V 17 | node "localhost" 18 | { 19 | foreach ($vm in $vms) { 20 | xVMHyperV $vm { 21 | Name = $($prefix + $vm) 22 | SwitchName = $vswitch 23 | VhdPath = Join-Path $rootpath "$($vm).vhdx" 24 | SecureBoot = $false 25 | EnableGuestService = $true 26 | ProcessorCount = 2 27 | Generation = 2 28 | MaximumMemory = 2GB 29 | MinimumMemory = 512MB 30 | RestartIfNeeded = $true 31 | State = "Running" 32 | WaitForIP = $true 33 | } 34 | } 35 | 36 | } 37 | } 38 | 39 | CreateVM -rootpath $rootpath -vms $vms -vswitch $vswitch | out-null 40 | Start-DscConfiguration -ComputerName localhost -Path $pwd\CreateFolder -Wait -Verbose -f 41 | -------------------------------------------------------------------------------- /main/roles/create_vhdx/files/packer/linux/common/chef.sh: -------------------------------------------------------------------------------- 1 | # Default to Omni Bus install 2 | if [ -z "$CHEF_INSTALLMETHOD" ]; then 3 | export CHEF_INSTALLMETHOD="omnibus" 4 | fi 5 | 6 | # Installing chef 7 | case $CHEF_INSTALLMETHOD in 8 | "gems") 9 | # Using gems 10 | if [ -z "$CHEF_VERSION" ]; then 11 | # Default to latest 12 | gem install chef --no-ri --no-rdoc 13 | else 14 | gem install chef --no-ri --no-rdoc --version $CHEF_VERSION 15 | fi 16 | ;; 17 | 18 | "omnibus") 19 | # Using omnibus 20 | if [ -z "$CHEF_VERSION" ]; then 21 | # Default to latest 22 | wget -O - http://opscode.com/chef/install.sh | sudo bash -s 23 | else 24 | wget -O - http://opscode.com/chef/install.sh | sudo bash -s -- -v $CHEF_VERSION 25 | fi 26 | ;; 27 | 28 | "package") 29 | # Using packages 30 | apt-get install -y debconf-utils 31 | echo "chef chef/chef_server_url string $CHEF_SERVER_URL" | debconf-set-selections 32 | if [ -z "$CHEF_VERSION" ]; then 33 | # Default to latest 34 | apt-get install -y chef 35 | else 36 | apt-get install -y chef=$CHEF_VERSION 37 | fi 38 | ;; 39 | 40 | *) 41 | echo "Unsupported method for installing chef" 42 | exit -1 43 | ;; 44 | esac 45 | -------------------------------------------------------------------------------- /main/roles/create_vhdx/files/packer/linux/ubuntu/kubernetes_install.yml: -------------------------------------------------------------------------------- 1 | - hosts: local 2 | connection: local 3 | tasks: 4 | - name: Install prerequisites 5 | apt: 6 | name: ['apt-transport-https', 'curl', 'software-properties-common', 'ca-certificates'] 7 | state: present 8 | update_cache: yes 9 | become: True 10 | - name: Add Apt signing key for Kubernetes repo from Google 11 | apt_key: 12 | url: https://packages.cloud.google.com/apt/doc/apt-key.gpg 13 | state: present 14 | become: True 15 | - name: Add Apt signing key for docker repo 16 | apt_key: 17 | url: https://download.docker.com/linux/ubuntu/gpg 18 | state: present 19 | become: True 20 | - name: Add an Apt repository for docker 21 | apt_repository: 22 | repo: deb [arch=amd64] https://download.docker.com/linux/ubuntu bionic stable 23 | state: present 24 | filename: docker 25 | become: True 26 | - name: Add an Apt repository for kubernetes 27 | apt_repository: 28 | repo: deb https://apt.kubernetes.io/ kubernetes-xenial main 29 | state: present 30 | filename: kubernetes 31 | become: True 32 | - name: Install stuff 33 | apt: 34 | name: ['kubelet', 'kubeadm', 'kubectl', 'docker-ce'] 35 | state: present 36 | update_cache: yes 37 | become: True 38 | -------------------------------------------------------------------------------- /main/roles/kube/tasks/main.yml: -------------------------------------------------------------------------------- 1 | - name: set timezone to Europe/Paris 2 | timezone: 3 | name: Europe/Paris 4 | 5 | - name: install ntpdate 6 | apt: 7 | name: ['ntpdate'] 8 | update_cache: no 9 | state: present 10 | become: yes 11 | 12 | - name: force time sync 13 | shell: ntpdate time.windows.com 14 | become: yes 15 | tags: 16 | - skip_ansible_lint 17 | 18 | - name: Set hostname to ensure they are unique 19 | hostname: 20 | name: "{{ inventory_hostname }}" 21 | 22 | - name: Disable swap 23 | shell: swapoff -a 24 | tags: 25 | - skip_ansible_lint 26 | 27 | - name: Disable swap permanently 28 | replace: 29 | path: /etc/fstab 30 | regexp: '^(\s*)([^#\n]+\s+)(\w+\s+)swap(\s+.*)$' 31 | replace: '#\1\2\3swap\4' 32 | backup: yes 33 | become: yes 34 | 35 | - name: Add Apt signing key for Kubernetes repo from Google 36 | apt_key: 37 | url: "{{ item }}" 38 | state: present 39 | become: True 40 | with_items: 41 | - https://packages.cloud.google.com/apt/doc/apt-key.gpg 42 | - https://download.docker.com/linux/ubuntu/gpg 43 | 44 | - name: Add an Apt repository for Docker & Kubernetes 45 | apt_repository: 46 | repo: "{{ item.repo }}" 47 | state: present 48 | filename: "{{ item.filename }}" 49 | become: True 50 | with_items: 51 | - { filename: docker, repo: "deb [arch=amd64] https://download.docker.com/linux/ubuntu bionic stable" } 52 | - { filename: kubernetes, repo: "deb https://apt.kubernetes.io/ kubernetes-xenial main" } 53 | 54 | - name: Install packages 55 | apt: 56 | name: ['apt-transport-https', 'curl', 'software-properties-common', 'ca-certificates', 'kubelet', 'kubeadm', 'kubectl', 'docker-ce'] 57 | state: present 58 | update_cache: yes 59 | become: True 60 | -------------------------------------------------------------------------------- /main/roles/create_vhdx/tasks/main.yml: -------------------------------------------------------------------------------- 1 | - name: Install packer 2 | win_chocolatey: 3 | name: packer 4 | state: present 5 | 6 | - name: Set an environment variable for all users 7 | win_environment: 8 | state: present 9 | name: PACKER_LOG 10 | value: 1 11 | level: machine 12 | 13 | 14 | - name: Set an environment variable for all users 15 | win_environment: 16 | state: present 17 | name: PACKER_LOG_PATH 18 | value: packerlog.txt 19 | level: machine 20 | 21 | - name: "Create folder for packer files" 22 | win_file: 23 | path: '{{ root }}\\packer' 24 | state: directory 25 | force: no 26 | 27 | - name: "copy files & folders" 28 | win_copy: 29 | src: "{{role_path}}/files/packer/" 30 | dest: '{{ root }}\\packer' 31 | force: yes 32 | 33 | - name: "Check if image exists" 34 | win_stat: 35 | path: "{{ output_dir }}\\Virtual Hard Disks\\ubuntu-bionic.vhdx" 36 | register: stat_image 37 | 38 | - name: "Packer - validate server image" 39 | win_command: "packer validate -var 'hyperv_switchname={{ hyperv_switch }}' -var 'username={{ username }}' -var 'password={{ password }}' -var 'output_dir={{ output_dir }}' .\\hyperv-ubuntu-18.04.json" 40 | args: 41 | chdir: "{{ root }}\\packer" 42 | retries: 2 43 | delay: 60 44 | become: True 45 | when: stat_image.stat.exists == false 46 | tags: 47 | - skip_ansible_lint 48 | 49 | - name: "Packer - build server image" 50 | win_command: "packer -machine-readable build -force -var 'hyperv_switchname={{ hyperv_switch }}' -var 'username={{ username }}' -var 'password={{ password }}' -var 'output_dir={{ output_dir }}' .\\hyperv-ubuntu-18.04.json" 51 | args: 52 | chdir: "{{ root }}\\packer" 53 | retries: 2 54 | delay: 60 55 | become: True 56 | when: stat_image.stat.exists == false 57 | tags: 58 | - skip_ansible_lint 59 | -------------------------------------------------------------------------------- /main/playbook.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - hosts: hyperv 3 | become: yes 4 | roles: 5 | - hyperv 6 | - create_vhdx 7 | - create_vm 8 | 9 | - hosts: kube 10 | become: yes 11 | roles: 12 | - kube 13 | 14 | - hosts: master 15 | become: yes 16 | roles: 17 | - kube_master 18 | 19 | - hosts: master 20 | become: no 21 | tasks: 22 | - name: check if node is already part of cluster 23 | shell: kubectl get nodes | grep k8snode1 24 | register: node01clustered 25 | tags: 26 | - skip_ansible_lint 27 | 28 | - name: check if node is already part of cluster 29 | shell: kubectl get nodes | grep k8snode2 30 | register: node02clustered 31 | tags: 32 | - skip_ansible_lint 33 | 34 | - name: set fact 35 | set_fact: 36 | node02clustered: "{{ node02clustered }}" 37 | node01clustered: "{{ node01clustered }}" 38 | 39 | - name: "Add host to share between playbooks" 40 | add_host: 41 | name: "K8S_CLUSTERED_HOLDER" 42 | node1: "{{ node01clustered.stdout }}" 43 | node2: "{{ node02clustered.stdout }}" 44 | 45 | - hosts: nodes 46 | become: no 47 | tasks: 48 | - name: set fact with stdout 49 | set_fact: 50 | cluster01up: "{{ hostvars['K8S_CLUSTERED_HOLDER']['node1'] is search('Ready')}}" 51 | cluster02up: "{{ hostvars['K8S_CLUSTERED_HOLDER']['node2'] is search('Ready')}}" 52 | 53 | - name: join cluster 54 | shell: "kubeadm join {{ hostvars['K8S_TOKEN_HOLDER']['masterip'] }}:6443 --token {{ hostvars['K8S_TOKEN_HOLDER']['token'] }} --discovery-token-ca-cert-hash {{ hostvars['K8S_TOKEN_HOLDER']['hash'] }}" 55 | when: (cluster01up|bool == false) and ( inventory_hostname == 'k8snode1' ) 56 | tags: 57 | - skip_ansible_lint 58 | 59 | - name: join cluster 60 | shell: "kubeadm join {{ hostvars['K8S_TOKEN_HOLDER']['masterip'] }}:6443 --token {{ hostvars['K8S_TOKEN_HOLDER']['token'] }} --discovery-token-ca-cert-hash {{ hostvars['K8S_TOKEN_HOLDER']['hash'] }}" 61 | when: (cluster02up|bool == false) and ( inventory_hostname == 'k8snode2' ) 62 | tags: 63 | - skip_ansible_lint 64 | -------------------------------------------------------------------------------- /main/roles/kube_master/tasks/main.yml: -------------------------------------------------------------------------------- 1 | 2 | - name: Checking cluster state 3 | shell: "kubectl get nodes | grep {{ inventory_hostname }}" 4 | register: stnodes 5 | become: False 6 | tags: 7 | - skip_ansible_lint 8 | 9 | - name: set fact with stdout 10 | set_fact: 11 | clusterup: "{{ stnodes.stdout is search('Ready')}}" 12 | 13 | - name: Init cluster 14 | shell: "kubeadm init --pod-network-cidr=10.244.0.0/16" 15 | register: kubeadmoutput 16 | when: (clusterup|bool == false) 17 | tags: 18 | - skip_ansible_lint 19 | 20 | - name: Kubeadm token creation for cluster join 21 | shell: kubeadm token create --print-join-command 22 | register: results 23 | when: (clusterup|bool == false) 24 | tags: 25 | - skip_ansible_lint 26 | 27 | - name: Extract token from output 28 | set_fact: 29 | token: "{{ results.stdout | regex_search(regexp, '\\2') | first }}" 30 | vars: 31 | regexp: '([^\s]+\s){4}([^\s]+)' 32 | when: (clusterup|bool == false) 33 | 34 | - name: Extract hash from output 35 | set_fact: 36 | hash: "{{ results.stdout | regex_search(regexp, '\\2') | first }}" 37 | vars: 38 | regexp: '([^\s]+\s){6}([^\s]+)' 39 | when: (clusterup|bool == false) 40 | 41 | - name: set ip of master 42 | set_fact: 43 | masterip: "{{ ansible_eth0.ipv4.address }}" 44 | when: (clusterup|bool == false) 45 | 46 | - name: "Add K8S Token and Hash to dummy host" 47 | add_host: 48 | name: "K8S_TOKEN_HOLDER" 49 | token: "{{ token }}" 50 | hash: "{{ hash }}" 51 | masterip: "{{ ansible_eth0.ipv4.address }}" 52 | when: (clusterup|bool == false) 53 | 54 | - name: "create .kube folder" 55 | file: 56 | path: $HOME/.kube 57 | state: directory 58 | mode: 0755 59 | when: (clusterup|bool == false) 60 | 61 | - name: "copy /etc/kubernetes/admin.conf" 62 | copy: 63 | src: /etc/kubernetes/admin.conf 64 | dest: $HOME/.kube/config 65 | remote_src: yes 66 | owner: vagrant 67 | group: vagrant 68 | become: yes 69 | when: (clusterup|bool == false) 70 | 71 | - name: apply flannel 72 | shell: | 73 | kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml 74 | kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/master/Documentation/k8s-manifests/kube-flannel-rbac.yml 75 | when: (clusterup|bool == false) 76 | tags: 77 | - skip_ansible_lint 78 | -------------------------------------------------------------------------------- /main/roles/create_vhdx/files/packer/hyperv-ubuntu-18.04.json: -------------------------------------------------------------------------------- 1 | { 2 | "variables": { 3 | "vm_name": "ubuntu-bionic", 4 | "cpu": "1", 5 | "ram_size": "2048", 6 | "disk_size": "21440", 7 | "iso_url": "http://cdimage.ubuntu.com/ubuntu/releases/bionic/release/ubuntu-18.04.2-server-amd64.iso", 8 | "iso_checksum_type": "sha1", 9 | "iso_checksum": "57c3a25f2c5fd61596ce39afbff44dd49b0089a2", 10 | "hyperv_switchname": "{{env `hyperv_switchname`}}", 11 | "username": "vagrant", 12 | "password": "vagrant", 13 | "output_dir": "C:\\Kubernetes\\Source_vhdx" 14 | }, 15 | "builders": [ 16 | { 17 | "vm_name": "{{user `vm_name`}}", 18 | "type": "hyperv-iso", 19 | "disk_size": "{{user `disk_size`}}", 20 | "guest_additions_mode": "disable", 21 | "iso_url": "{{user `iso_url`}}", 22 | "iso_checksum_type": "{{user `iso_checksum_type`}}", 23 | "iso_checksum": "{{user `iso_checksum`}}", 24 | "communicator": "ssh", 25 | "ssh_username": "{{user `username`}}", 26 | "ssh_password": "{{user `password`}}", 27 | "ssh_timeout": "4h", 28 | "http_directory": "./linux/ubuntu/http/", 29 | "boot_wait": "5s", 30 | "skip_export": true, 31 | "output_directory": "{{user `output_dir` }}", 32 | "headless": true, 33 | "boot_command": [ 34 | "", 35 | "set gfxpayload=1024x768", 36 | "linux /install/vmlinuz ", 37 | "preseed/url=http://{{.HTTPIP}}:{{.HTTPPort}}/18.04/preseed.cfg ", 38 | "debian-installer=en_US.UTF-8 auto=true locale=en_US.UTF-8 kbd-chooser/method=us ", 39 | "hostname={{.Name}} ", 40 | "fb=false debconf/frontend=noninteractive ", 41 | "keyboard-configuration/modelcode=SKIP keyboard-configuration/layout=USA ", 42 | "keyboard-configuration/variant=USA console-setup/ask_detect=false ", 43 | "initrd /install/initrd.gz", 44 | "boot" 45 | ], 46 | "shutdown_command": "echo 'vagrant' | sudo -S -E shutdown -P now", 47 | "memory": "{{user `ram_size`}}", 48 | "cpus": "{{user `cpu`}}", 49 | "generation": 2, 50 | "switch_name": "{{user `hyperv_switchname`}}", 51 | "enable_secure_boot": false 52 | } 53 | ], 54 | "provisioners": [ 55 | { 56 | "type": "shell", 57 | "execute_command": "echo 'vagrant' | sudo -S -E sh {{.Path}}", 58 | "scripts": [ 59 | "./linux/ubuntu/update.sh", 60 | "./linux/ubuntu/network.sh", 61 | "./linux/common/motd.sh" 62 | ] 63 | }, 64 | { 65 | "type": "shell", 66 | "execute_command": "echo 'vagrant' | sudo -S -E sh {{.Path}}", 67 | "scripts": [ 68 | "./linux/ubuntu/cleanup.sh" 69 | ] 70 | } 71 | ] 72 | } 73 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | 2 | [![HitCount](http://hits.dwyl.io/etiennedeneuve/kubernetes_hyperv.svg)](http://hits.dwyl.io/etiennedeneuve/kubernetes_hyperv) 3 | 4 | # Create a Kubernetes Cluster on Hyper V 5 | 6 | ## Latest changes 7 | 8 | - You don't need WSL in 2019 VM, you need just to configure WinRM on the Hyper-V and add the IP in inventory\host, and WSL a mac or linux for launching the playbook 9 | - Added Group Vars in Part 5, so you don't need to change multiple stuff 10 | - I'm working on the project to add a way to change number of each type of Kubernetes nodes - WIP 11 | 12 | ## Prerequisites 13 | 14 | You need a Windows Server 2019 VM with nested virtualization enabled at least 8 Go of Ram in the VM: 15 | [Setup Guide for Hyper V](https://docs.microsoft.com/fr-fr/virtualization/hyper-v-on-windows/user-guide/nested-virtualization) 16 | 17 | ## TL&DR 18 | 19 | On the 2019 VM host : 20 | 21 | 1. ~~Install [WSL](#WSL)~~ 22 | 1. ~~[Hyper V](#Hyper-V)~~ 23 | 2. Activate [WinRM](#Configure-WinRM-for-Ansible) 24 | 3. ~~Install [packer](https://packer.io/downloads).~~ 25 | 26 | In WSL: 27 | 28 | 1. Install the [dependencies](#Install-Ansible-in-WSL) 29 | 1. Clone the project ``git clone https://github.com/EtienneDeneuve/kubernetes_hyperv/kubernetes_hyperv.git`` 30 | 1. Update the credential for hyperv in ``inventory/hosts.yml`` 31 | 2. Launch the playbook in ``main`` : ``ansible-playbook -i inventory/hosts.yml main/playbook.yml`` 32 | 3. SSH into the master node and start playing with your Kubernetes Cluster ! 33 | 34 | ## TODO 35 | 36 | - [ ] Add Tests with [Molecule](https://molecule.readthedocs.io/en/latest/) 37 | - [ ] Add Tests with [Pester](https://github.com/pester/Pester) 38 | - [ ] Add Tests for Packer image generation 39 | - [ ] Configure CI/CD for test 40 | - [ ] Add others distro for base os in Kubernetes 41 | - [ ] Hardening and avoid snowflake 42 | - [ ] Create box with Vagrant for Hyper V VM 43 | 44 | ## WSL installation 45 | 46 | For Windows 10 47 | 48 | ```Powershell 49 | Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Windows-Subsystem-Linux 50 | ``` 51 | 52 | Then for Windows 10 and Windows Server 2019 53 | 54 | ```Powershell 55 | Invoke-WebRequest -Uri https://aka.ms/wsl-ubuntu-1604 -OutFile Ubuntu.zip -UseBasicParsing 56 | Expand-Archive .\Ubuntu.zip c:\Distros\Ubuntu 57 | Rename-Item c:\distros\ubuntu\ubuntu1804.exe c:\distros\ubuntu\ubuntu.exe 58 | ``` 59 | 60 | ## Configure WinRM for Ansible in Windows 2019 VM 61 | 62 | Launch this script : 63 | [ConfigureRemotingForAnsible](https://github.com/ansible/ansible/blob/devel/examples/scripts/ConfigureRemotingForAnsible.ps1 64 | ) 65 | or use the one in misc folder : [here](webcast\misc\host_preparation\01_config_winrm_ansible.ps1) 66 | 67 | ## Install Ansible in WSL 68 | 69 | Launch the ``04_configure.sh`` script in ``misc\wsl_preparation``: 70 | sudo .\04_configure.sh 71 | 72 | ## Code of Conduct 73 | This project has adopted the [Microsoft Open Source Code of 74 | Conduct](https://opensource.microsoft.com/codeofconduct/). 75 | For more information see the [Code of Conduct 76 | FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or 77 | contact [opencode@microsoft.com](mailto:opencode@microsoft.com) 78 | with any additional questions or comments. 79 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to Kubernetes HyperV 2 | 3 | Welcome, and thank you for your interest in contributing to Kubernetes HyperV! 4 | 5 | There are many ways in which you can contribute, beyond writing code. The goal of this document is to provide a high-level overview of how you can get involved. 6 | 7 | ## Asking Questions 8 | 9 | Have a question? Open an issue and explain what do expect. 10 | 11 | The active community will be eager to assist you. Your well-worded question will serve as a resource to others searching for help. 12 | 13 | ## Providing Feedback 14 | 15 | Your comments and feedback are welcome, and the development team is available via a handful of different channels. 16 | 17 | ## Reporting Issues 18 | 19 | Have you identified a reproducible problem in Kubernetes HyperV? Have a feature request? We want to hear about it! Here's how you can make reporting your issue as effective as possible. 20 | 21 | ### Look For an Existing Issue 22 | 23 | Before you create a new issue, please do a search in [open issues](https://github.com/etiennedeneuve/kubernetes_hyperv/issues) to see if the issue or feature request has already been filed. 24 | 25 | Be sure to scan through the [most popular](https://github.com/etiennedeneuve/kubernetes_hyperv/issues?q=is%3Aopen+is%3Aissue+label%3Afeature-request+sort%3Areactions-%2B1-desc) feature requests. 26 | 27 | If you find your issue already exists, make relevant comments and add your [reaction](https://github.com/blog/2119-add-reactions-to-pull-requests-issues-and-comments). Use a reaction in place of a "+1" comment: 28 | 29 | * 👍 - upvote 30 | * 👎 - downvote 31 | 32 | 33 | If you cannot find an existing issue that describes your bug or feature, create a new issue using the guidelines below. 34 | 35 | ### Writing Good Bug Reports and Feature Requests 36 | 37 | File a single issue per problem and feature request. Do not enumerate multiple bugs or feature requests in the same issue. 38 | 39 | Do not add your issue as a comment to an existing issue unless it's for the identical input. Many issues look similar, but have different causes. 40 | 41 | The more information you can provide, the more likely someone will be successful at reproducing the issue and finding a fix. 42 | 43 | The built-in tool for reporting an issue, which you can access by using `Report Issue` in VS Code's Help menu, can help streamline this process by automatically providing the version of VS Code, all your installed extensions, and your system info. Additionally, the tool will search among existing issues to see if a similar issue already exists. 44 | 45 | Please include the following with each issue: 46 | 47 | * Version of Ansible, Packer, Hyper V, Windows Server 2019, WSL 48 | 49 | * Reproducible steps (1... 2... 3...) that cause the issue 50 | 51 | * What you expected to see, versus what you actually saw 52 | 53 | * Images, animations, or a link to a video showing the issue occurring 54 | 55 | * A code snippet that demonstrates the issue or a link to a code repository the developers can easily pull down to recreate the issue locally 56 | 57 | * **Note:** Because the developers need to copy and paste the code snippet, including a code snippet as a media file (i.e. .gif) is not sufficient. 58 | 59 | ### Final Checklist 60 | 61 | Please remember to do the following: 62 | 63 | * [ ] Search the issue repository to ensure your report is a new issue 64 | 65 | * [ ] Recreate the issue after disabling all extensions 66 | 67 | * [ ] Simplify your code around the issue to better isolate the problem 68 | 69 | Don't feel bad if the developers can't reproduce the issue right away. They will simply ask for more information! 70 | 71 | ### Follow Your Issue 72 | 73 | Once submitted, your report will go into the [issue tracking](https://github.com/etiennedeneuve/kubernetes_hyperv/wiki/Issue-Tracking) workflow. Be sure to understand what will happen next, so you know what to expect, and how to continue to assist throughout the process. 74 | 75 | ## Contributing Fixes 76 | 77 | If you are interested in writing code to fix issues, 78 | please see [How to Contribute](https://github.com/etiennedeneuve/kubernetes_hyperv/wiki/How-to-Contribute) in the wiki. 79 | 80 | # Thank You! 81 | 82 | Your contributions to open source, large or small, make great projects like this possible. Thank you for taking the time to contribute. 83 | -------------------------------------------------------------------------------- /main/roles/create_vhdx/files/packer/linux/ubuntu/http/base/preseed.cfg: -------------------------------------------------------------------------------- 1 | ## Options to set on the command line 2 | d-i debian-installer/locale string en_US.utf8 3 | d-i console-setup/ask_detect boolean false 4 | d-i console-setup/layout string us 5 | 6 | d-i netcfg/get_hostname string nl-ams-basebox3 7 | d-i netcfg/get_domain string unassigned-domain 8 | 9 | d-i time/zone string UTC 10 | d-i clock-setup/utc-auto boolean true 11 | d-i clock-setup/utc boolean true 12 | 13 | d-i kbd-chooser/method select American English 14 | 15 | d-i netcfg/wireless_wep string 16 | 17 | d-i base-installer/kernel/override-image string linux-server 18 | 19 | d-i debconf debconf/frontend select Noninteractive 20 | 21 | d-i pkgsel/install-language-support boolean false 22 | tasksel tasksel/first multiselect standard, ubuntu-server 23 | 24 | ### Partitioning 25 | ## Partitioning example 26 | # If the system has free space you can choose to only partition that space. 27 | # This is only honoured if partman-auto/method (below) is not set. 28 | # Alternatives: custom, some_device, some_device_crypto, some_device_lvm. 29 | #d-i partman-auto/init_automatically_partition select biggest_free 30 | 31 | # Alternatively, you may specify a disk to partition. If the system has only 32 | # one disk the installer will default to using that, but otherwise the device 33 | # name must be given in traditional, non-devfs format (so e.g. /dev/hda or 34 | # /dev/sda, and not e.g. /dev/discs/disc0/disc). 35 | # For example, to use the first SCSI/SATA hard disk: 36 | #d-i partman-auto/disk string /dev/sda 37 | # In addition, you'll need to specify the method to use. 38 | # The presently available methods are: 39 | # - regular: use the usual partition types for your architecture 40 | # - lvm: use LVM to partition the disk 41 | # - crypto: use LVM within an encrypted partition 42 | d-i partman-auto/method string lvm 43 | 44 | # If one of the disks that are going to be automatically partitioned 45 | # contains an old LVM configuration, the user will normally receive a 46 | # warning. This can be preseeded away... 47 | d-i partman-lvm/device_remove_lvm boolean true 48 | # The same applies to pre-existing software RAID array: 49 | d-i partman-md/device_remove_md boolean true 50 | # And the same goes for the confirmation to write the lvm partitions. 51 | d-i partman-lvm/confirm boolean true 52 | 53 | # For LVM partitioning, you can select how much of the volume group to use 54 | # for logical volumes. 55 | d-i partman-auto-lvm/guided_size string max 56 | #d-i partman-auto-lvm/guided_size string 10GB 57 | #d-i partman-auto-lvm/guided_size string 50% 58 | 59 | # You can choose one of the three predefined partitioning recipes: 60 | # - atomic: all files in one partition 61 | # - home: separate /home partition 62 | # - multi: separate /home, /usr, /var, and /tmp partitions 63 | d-i partman-auto/choose_recipe select atomic 64 | 65 | # Or provide a recipe of your own... 66 | # If you have a way to get a recipe file into the d-i environment, you can 67 | # just point at it. 68 | #d-i partman-auto/expert_recipe_file string /hd-media/recipe 69 | 70 | # If not, you can put an entire recipe into the preconfiguration file in one 71 | # (logical) line. This example creates a small /boot partition, suitable 72 | # swap, and uses the rest of the space for the root partition: 73 | #d-i partman-auto/expert_recipe string \ 74 | # boot-root :: \ 75 | # 40 50 100 ext3 \ 76 | # $primary{ } $bootable{ } \ 77 | # method{ format } format{ } \ 78 | # use_filesystem{ } filesystem{ ext3 } \ 79 | # mountpoint{ /boot } \ 80 | # . \ 81 | # 500 10000 1000000000 ext3 \ 82 | # method{ format } format{ } \ 83 | # use_filesystem{ } filesystem{ ext3 } \ 84 | # mountpoint{ / } \ 85 | # . \ 86 | # 64 512 300% linux-swap \ 87 | # method{ swap } format{ } \ 88 | # . 89 | 90 | # If you just want to change the default filesystem from ext3 to something 91 | # else, you can do that without providing a full recipe. 92 | #d-i partman/default_filesystem string ext4 93 | 94 | # The full recipe format is documented in the file partman-auto-recipe.txt 95 | # included in the 'debian-installer' package or available from D-I source 96 | # repository. This also documents how to specify settings such as file 97 | # system labels, volume group names and which physical devices to include 98 | # in a volume group. 99 | 100 | # This makes partman automatically partition without confirmation, provided 101 | # that you told it what to do using one of the methods above. 102 | d-i partman-partitioning/confirm_write_new_label boolean true 103 | d-i partman/choose_partition select finish 104 | d-i partman/confirm boolean true 105 | d-i partman/confirm_nooverwrite boolean true 106 | 107 | ## Partitioning using RAID 108 | # The method should be set to "raid". 109 | #d-i partman-auto/method string raid 110 | # Specify the disks to be partitioned. They will all get the same layout, 111 | # so this will only work if the disks are the same size. 112 | #d-i partman-auto/disk string /dev/sda /dev/sdb 113 | 114 | # Next you need to specify the physical partitions that will be used. 115 | #d-i partman-auto/expert_recipe string \ 116 | # multiraid :: \ 117 | # 1000 5000 4000 raid \ 118 | # $primary{ } method{ raid } \ 119 | # . \ 120 | # 64 512 300% raid \ 121 | # method{ raid } \ 122 | # . \ 123 | # 500 10000 1000000000 raid \ 124 | # method{ raid } \ 125 | # . 126 | 127 | # Last you need to specify how the previously defined partitions will be 128 | # used in the RAID setup. Remember to use the correct partition numbers 129 | # for logical partitions. RAID levels 0, 1, 5, 6 and 10 are supported; 130 | # devices are separated using "#". 131 | # Parameters are: 132 | # \ 133 | # 134 | 135 | #d-i partman-auto-raid/recipe string \ 136 | # 1 2 0 ext3 / \ 137 | # /dev/sda1#/dev/sdb1 \ 138 | # . \ 139 | # 1 2 0 swap - \ 140 | # /dev/sda5#/dev/sdb5 \ 141 | # . \ 142 | # 0 2 0 ext3 /home \ 143 | # /dev/sda6#/dev/sdb6 \ 144 | # . 145 | 146 | # For additional information see the file partman-auto-raid-recipe.txt 147 | # included in the 'debian-installer' package or available from D-I source 148 | # repository. 149 | 150 | # This makes partman automatically partition without confirmation. 151 | d-i partman-md/confirm boolean true 152 | d-i partman-partitioning/confirm_write_new_label boolean true 153 | d-i partman/choose_partition select finish 154 | d-i partman/confirm boolean true 155 | d-i partman/confirm_nooverwrite boolean true 156 | 157 | d-i partman-md/confirm_nooverwrite boolean true 158 | d-i partman-lvm/confirm_nooverwrite boolean true 159 | 160 | ## Controlling how partitions are mounted 161 | # The default is to mount by UUID, but you can also choose "traditional" to 162 | # use traditional device names, or "label" to try filesystem labels before 163 | # falling back to UUIDs. 164 | #d-i partman/mount_style select uuid 165 | 166 | d-i partman-partitioning/no_bootable_gpt_biosgrub boolean false 167 | d-i partman-partitioning/no_bootable_gpt_efi boolean false 168 | d-i partman-efi/non_efi_system boolean true 169 | 170 | # Default user 171 | d-i passwd/user-fullname string vagrant 172 | d-i passwd/username string vagrant 173 | d-i passwd/user-password password vagrant 174 | d-i passwd/user-password-again password vagrant 175 | d-i user-setup/encrypt-home boolean false 176 | d-i user-setup/allow-password-weak boolean true 177 | 178 | # Upgrade packages after debootstrap? (none, safe-upgrade, full-upgrade) 179 | # (note: set to none for speed) 180 | d-i pkgsel/upgrade select none 181 | 182 | d-i grub-installer/only_debian boolean true 183 | d-i grub-installer/with_other_os boolean true 184 | d-i grub-installer/timeout string 0 185 | d-i grub-installer/bootdev string (default 186 | 187 | d-i finish-install/reboot_in_progress note 188 | 189 | d-i pkgsel/update-policy select none 190 | 191 | choose-mirror-bin mirror/http/proxy string 192 | -------------------------------------------------------------------------------- /main/roles/create_vhdx/files/packer/linux/ubuntu/http/base/preseed-cloud-init.cfg: -------------------------------------------------------------------------------- 1 | ## Options to set on the command line 2 | d-i debian-installer/locale string en_US.utf8 3 | d-i console-setup/ask_detect boolean false 4 | d-i console-setup/layout string us 5 | 6 | d-i netcfg/get_hostname string nl-ams-basebox3 7 | d-i netcfg/get_domain string unassigned-domain 8 | 9 | d-i time/zone string UTC 10 | d-i clock-setup/utc-auto boolean true 11 | d-i clock-setup/utc boolean true 12 | 13 | d-i kbd-chooser/method select American English 14 | 15 | d-i netcfg/wireless_wep string 16 | 17 | d-i base-installer/kernel/override-image string linux-server 18 | 19 | d-i debconf debconf/frontend select Noninteractive 20 | 21 | d-i pkgsel/install-language-support boolean false 22 | tasksel tasksel/first multiselect standard, ubuntu-server 23 | 24 | ### Partitioning 25 | ## Partitioning example 26 | # If the system has free space you can choose to only partition that space. 27 | # This is only honoured if partman-auto/method (below) is not set. 28 | # Alternatives: custom, some_device, some_device_crypto, some_device_lvm. 29 | #d-i partman-auto/init_automatically_partition select biggest_free 30 | 31 | # Alternatively, you may specify a disk to partition. If the system has only 32 | # one disk the installer will default to using that, but otherwise the device 33 | # name must be given in traditional, non-devfs format (so e.g. /dev/hda or 34 | # /dev/sda, and not e.g. /dev/discs/disc0/disc). 35 | # For example, to use the first SCSI/SATA hard disk: 36 | #d-i partman-auto/disk string /dev/sda 37 | # In addition, you'll need to specify the method to use. 38 | # The presently available methods are: 39 | # - regular: use the usual partition types for your architecture 40 | # - lvm: use LVM to partition the disk 41 | # - crypto: use LVM within an encrypted partition 42 | d-i partman-auto/method string regular 43 | # If one of the disks that are going to be automatically partitioned 44 | # contains an old LVM configuration, the user will normally receive a 45 | # warning. This can be preseeded away... 46 | d-i partman-lvm/device_remove_lvm boolean true 47 | # The same applies to pre-existing software RAID array: 48 | d-i partman-md/device_remove_md boolean true 49 | # And the same goes for the confirmation to write the lvm partitions. 50 | d-i partman-lvm/confirm boolean true 51 | 52 | # For LVM partitioning, you can select how much of the volume group to use 53 | # for logical volumes. 54 | d-i partman-auto-lvm/guided_size string max 55 | #d-i partman-auto-lvm/guided_size string 10GB 56 | #d-i partman-auto-lvm/guided_size string 50% 57 | 58 | # You can choose one of the three predefined partitioning recipes: 59 | # - atomic: all files in one partition 60 | # - home: separate /home partition 61 | # - multi: separate /home, /usr, /var, and /tmp partitions 62 | #d-i partman-auto/choose_recipe select atomic 63 | 64 | # Or provide a recipe of your own... 65 | # If you have a way to get a recipe file into the d-i environment, you can 66 | # just point at it. 67 | #d-i partman-auto/expert_recipe_file string /hd-media/recipe 68 | 69 | # If not, you can put an entire recipe into the preconfiguration file in one 70 | # (logical) line. This example creates a small /boot partition, suitable 71 | # swap, and uses the rest of the space for the root partition: 72 | #d-i partman-auto/expert_recipe string \ 73 | # boot-root :: \ 74 | # 40 50 100 ext3 \ 75 | # $primary{ } $bootable{ } \ 76 | # method{ format } format{ } \ 77 | # use_filesystem{ } filesystem{ ext3 } \ 78 | # mountpoint{ /boot } \ 79 | # . \ 80 | # 500 10000 1000000000 ext3 \ 81 | # method{ format } format{ } \ 82 | # use_filesystem{ } filesystem{ ext3 } \ 83 | # mountpoint{ / } \ 84 | # . \ 85 | # 64 512 300% linux-swap \ 86 | # method{ swap } format{ } \ 87 | # . 88 | d-i partman-auto/expert_recipe string \ 89 | efi-root :: \ 90 | 60 70 96 \ 91 | $iflabel{ gpt } \ 92 | $reusemethod{ } \ 93 | method{ efi } \ 94 | format{ } . \ 95 | \ 96 | 538 512 1075 ext4 \ 97 | method{ format } \ 98 | format{ } \ 99 | use_filesystem{ } \ 100 | filesystem{ ext4 } \ 101 | mountpoint{ /boot } . \ 102 | \ 103 | 500 10000 -1 $default_filesystem \ 104 | $lvmok{ } \ 105 | method{ format } \ 106 | format{ } \ 107 | use_filesystem{ } \ 108 | $default_filesystem{ } \ 109 | mountpoint{ / } . \ 110 | 111 | 112 | # If you just want to change the default filesystem from ext3 to something 113 | # else, you can do that without providing a full recipe. 114 | d-i partman/default_filesystem string ext4 115 | 116 | # The full recipe format is documented in the file partman-auto-recipe.txt 117 | # included in the 'debian-installer' package or available from D-I source 118 | # repository. This also documents how to specify settings such as file 119 | # system labels, volume group names and which physical devices to include 120 | # in a volume group. 121 | 122 | # This makes partman automatically partition without confirmation, provided 123 | # that you told it what to do using one of the methods above. 124 | d-i partman-partitioning/confirm_write_new_label boolean true 125 | d-i partman/choose_partition select finish 126 | d-i partman/confirm boolean true 127 | d-i partman/confirm_nooverwrite boolean true 128 | d-i partman-basicfilesystems/no_swap boolean false 129 | ## Partitioning using RAID 130 | # The method should be set to "raid". 131 | #d-i partman-auto/method string raid 132 | # Specify the disks to be partitioned. They will all get the same layout, 133 | # so this will only work if the disks are the same size. 134 | #d-i partman-auto/disk string /dev/sda /dev/sdb 135 | 136 | # Next you need to specify the physical partitions that will be used. 137 | #d-i partman-auto/expert_recipe string \ 138 | # multiraid :: \ 139 | # 1000 5000 4000 raid \ 140 | # $primary{ } method{ raid } \ 141 | # . \ 142 | # 64 512 300% raid \ 143 | # method{ raid } \ 144 | # . \ 145 | # 500 10000 1000000000 raid \ 146 | # method{ raid } \ 147 | # . 148 | 149 | # Last you need to specify how the previously defined partitions will be 150 | # used in the RAID setup. Remember to use the correct partition numbers 151 | # for logical partitions. RAID levels 0, 1, 5, 6 and 10 are supported; 152 | # devices are separated using "#". 153 | # Parameters are: 154 | # \ 155 | # 156 | 157 | #d-i partman-auto-raid/recipe string \ 158 | # 1 2 0 ext3 / \ 159 | # /dev/sda1#/dev/sdb1 \ 160 | # . \ 161 | # 1 2 0 swap - \ 162 | # /dev/sda5#/dev/sdb5 \ 163 | # . \ 164 | # 0 2 0 ext3 /home \ 165 | # /dev/sda6#/dev/sdb6 \ 166 | # . 167 | 168 | # For additional information see the file partman-auto-raid-recipe.txt 169 | # included in the 'debian-installer' package or available from D-I source 170 | # repository. 171 | 172 | # This makes partman automatically partition without confirmation. 173 | d-i partman-md/confirm boolean true 174 | d-i partman-partitioning/confirm_write_new_label boolean true 175 | d-i partman/choose_partition select finish 176 | d-i partman/confirm boolean true 177 | d-i partman/confirm_nooverwrite boolean true 178 | 179 | d-i partman-md/confirm_nooverwrite boolean true 180 | d-i partman-lvm/confirm_nooverwrite boolean true 181 | 182 | ## Controlling how partitions are mounted 183 | # The default is to mount by UUID, but you can also choose "traditional" to 184 | # use traditional device names, or "label" to try filesystem labels before 185 | # falling back to UUIDs. 186 | #d-i partman/mount_style select uuid 187 | 188 | d-i partman-partitioning/no_bootable_gpt_biosgrub boolean true 189 | d-i partman-partitioning/no_bootable_gpt_efi boolean false 190 | d-i partman-efi/non_efi_system boolean false 191 | 192 | # Default user 193 | d-i passwd/user-fullname string ubuntu 194 | d-i passwd/username string ubuntu 195 | d-i passwd/user-password password ubuntu 196 | d-i passwd/user-password-again password ubuntu 197 | d-i user-setup/encrypt-home boolean false 198 | d-i user-setup/allow-password-weak boolean true 199 | 200 | # Upgrade packages after debootstrap? (none, safe-upgrade, full-upgrade) 201 | # (note: set to none for speed) 202 | d-i pkgsel/upgrade select none 203 | 204 | d-i grub-installer/only_debian boolean true 205 | d-i grub-installer/with_other_os boolean true 206 | d-i finish-install/reboot_in_progress note 207 | 208 | d-i pkgsel/update-policy select none 209 | 210 | choose-mirror-bin mirror/http/proxy string 211 | -------------------------------------------------------------------------------- /misc/host_preparation/01_config_winrm_ansible.ps1: -------------------------------------------------------------------------------- 1 | #Requires -Version 3.0 2 | 3 | # Configure a Windows host for remote management with Ansible 4 | # ----------------------------------------------------------- 5 | # 6 | # This script checks the current WinRM (PS Remoting) configuration and makes 7 | # the necessary changes to allow Ansible to connect, authenticate and 8 | # execute PowerShell commands. 9 | # 10 | # All events are logged to the Windows EventLog, useful for unattended runs. 11 | # 12 | # Use option -Verbose in order to see the verbose output messages. 13 | # 14 | # Use option -CertValidityDays to specify how long this certificate is valid 15 | # starting from today. So you would specify -CertValidityDays 3650 to get 16 | # a 10-year valid certificate. 17 | # 18 | # Use option -ForceNewSSLCert if the system has been SysPreped and a new 19 | # SSL Certificate must be forced on the WinRM Listener when re-running this 20 | # script. This is necessary when a new SID and CN name is created. 21 | # 22 | # Use option -EnableCredSSP to enable CredSSP as an authentication option. 23 | # 24 | # Use option -DisableBasicAuth to disable basic authentication. 25 | # 26 | # Use option -SkipNetworkProfileCheck to skip the network profile check. 27 | # Without specifying this the script will only run if the device's interfaces 28 | # are in DOMAIN or PRIVATE zones. Provide this switch if you want to enable 29 | # WinRM on a device with an interface in PUBLIC zone. 30 | # 31 | # Use option -SubjectName to specify the CN name of the certificate. This 32 | # defaults to the system's hostname and generally should not be specified. 33 | 34 | # Written by Trond Hindenes 35 | # Updated by Chris Church 36 | # Updated by Michael Crilly 37 | # Updated by Anton Ouzounov 38 | # Updated by Nicolas Simond 39 | # Updated by Dag Wieërs 40 | # Updated by Jordan Borean 41 | # Updated by Erwan Quélin 42 | # Updated by David Norman 43 | # 44 | # Version 1.0 - 2014-07-06 45 | # Version 1.1 - 2014-11-11 46 | # Version 1.2 - 2015-05-15 47 | # Version 1.3 - 2016-04-04 48 | # Version 1.4 - 2017-01-05 49 | # Version 1.5 - 2017-02-09 50 | # Version 1.6 - 2017-04-18 51 | # Version 1.7 - 2017-11-23 52 | # Version 1.8 - 2018-02-23 53 | # Version 1.9 - 2018-09-21 54 | 55 | # Support -Verbose option 56 | [CmdletBinding()] 57 | 58 | Param ( 59 | [string]$SubjectName = $env:COMPUTERNAME, 60 | [int]$CertValidityDays = 1095, 61 | [switch]$SkipNetworkProfileCheck, 62 | $CreateSelfSignedCert = $true, 63 | [switch]$ForceNewSSLCert, 64 | [switch]$GlobalHttpFirewallAccess, 65 | [switch]$DisableBasicAuth = $false, 66 | [switch]$EnableCredSSP 67 | ) 68 | 69 | Function Write-Log 70 | { 71 | $Message = $args[0] 72 | Write-EventLog -LogName Application -Source $EventSource -EntryType Information -EventId 1 -Message $Message 73 | } 74 | 75 | Function Write-VerboseLog 76 | { 77 | $Message = $args[0] 78 | Write-Verbose $Message 79 | Write-Log $Message 80 | } 81 | 82 | Function Write-HostLog 83 | { 84 | $Message = $args[0] 85 | Write-Output $Message 86 | Write-Log $Message 87 | } 88 | 89 | Function New-LegacySelfSignedCert 90 | { 91 | Param ( 92 | [string]$SubjectName, 93 | [int]$ValidDays = 1095 94 | ) 95 | 96 | $hostnonFQDN = $env:computerName 97 | $hostFQDN = [System.Net.Dns]::GetHostByName(($env:computerName)).Hostname 98 | $SignatureAlgorithm = "SHA256" 99 | 100 | $name = New-Object -COM "X509Enrollment.CX500DistinguishedName.1" 101 | $name.Encode("CN=$SubjectName", 0) 102 | 103 | $key = New-Object -COM "X509Enrollment.CX509PrivateKey.1" 104 | $key.ProviderName = "Microsoft Enhanced RSA and AES Cryptographic Provider" 105 | $key.KeySpec = 1 106 | $key.Length = 4096 107 | $key.SecurityDescriptor = "D:PAI(A;;0xd01f01ff;;;SY)(A;;0xd01f01ff;;;BA)(A;;0x80120089;;;NS)" 108 | $key.MachineContext = 1 109 | $key.Create() 110 | 111 | $serverauthoid = New-Object -COM "X509Enrollment.CObjectId.1" 112 | $serverauthoid.InitializeFromValue("1.3.6.1.5.5.7.3.1") 113 | $ekuoids = New-Object -COM "X509Enrollment.CObjectIds.1" 114 | $ekuoids.Add($serverauthoid) 115 | $ekuext = New-Object -COM "X509Enrollment.CX509ExtensionEnhancedKeyUsage.1" 116 | $ekuext.InitializeEncode($ekuoids) 117 | 118 | $cert = New-Object -COM "X509Enrollment.CX509CertificateRequestCertificate.1" 119 | $cert.InitializeFromPrivateKey(2, $key, "") 120 | $cert.Subject = $name 121 | $cert.Issuer = $cert.Subject 122 | $cert.NotBefore = (Get-Date).AddDays(-1) 123 | $cert.NotAfter = $cert.NotBefore.AddDays($ValidDays) 124 | 125 | $SigOID = New-Object -ComObject X509Enrollment.CObjectId 126 | $SigOID.InitializeFromValue(([Security.Cryptography.Oid]$SignatureAlgorithm).Value) 127 | 128 | [string[]] $AlternativeName += $hostnonFQDN 129 | $AlternativeName += $hostFQDN 130 | $IAlternativeNames = New-Object -ComObject X509Enrollment.CAlternativeNames 131 | 132 | foreach ($AN in $AlternativeName) 133 | { 134 | $AltName = New-Object -ComObject X509Enrollment.CAlternativeName 135 | $AltName.InitializeFromString(0x3,$AN) 136 | $IAlternativeNames.Add($AltName) 137 | } 138 | 139 | $SubjectAlternativeName = New-Object -ComObject X509Enrollment.CX509ExtensionAlternativeNames 140 | $SubjectAlternativeName.InitializeEncode($IAlternativeNames) 141 | 142 | [String[]]$KeyUsage = ("DigitalSignature", "KeyEncipherment") 143 | $KeyUsageObj = New-Object -ComObject X509Enrollment.CX509ExtensionKeyUsage 144 | $KeyUsageObj.InitializeEncode([int][Security.Cryptography.X509Certificates.X509KeyUsageFlags]($KeyUsage)) 145 | $KeyUsageObj.Critical = $true 146 | 147 | $cert.X509Extensions.Add($KeyUsageObj) 148 | $cert.X509Extensions.Add($ekuext) 149 | $cert.SignatureInformation.HashAlgorithm = $SigOID 150 | $CERT.X509Extensions.Add($SubjectAlternativeName) 151 | $cert.Encode() 152 | 153 | $enrollment = New-Object -COM "X509Enrollment.CX509Enrollment.1" 154 | $enrollment.InitializeFromRequest($cert) 155 | $certdata = $enrollment.CreateRequest(0) 156 | $enrollment.InstallResponse(2, $certdata, 0, "") 157 | 158 | # extract/return the thumbprint from the generated cert 159 | $parsed_cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2 160 | $parsed_cert.Import([System.Text.Encoding]::UTF8.GetBytes($certdata)) 161 | 162 | return $parsed_cert.Thumbprint 163 | } 164 | 165 | Function Enable-GlobalHttpFirewallAccess 166 | { 167 | Write-Verbose "Forcing global HTTP firewall access" 168 | # this is a fairly naive implementation; could be more sophisticated about rule matching/collapsing 169 | $fw = New-Object -ComObject HNetCfg.FWPolicy2 170 | 171 | # try to find/enable the default rule first 172 | $add_rule = $false 173 | $matching_rules = $fw.Rules | ? { $_.Name -eq "Windows Remote Management (HTTP-In)" } 174 | $rule = $null 175 | If ($matching_rules) { 176 | If ($matching_rules -isnot [Array]) { 177 | Write-Verbose "Editing existing single HTTP firewall rule" 178 | $rule = $matching_rules 179 | } 180 | Else { 181 | # try to find one with the All or Public profile first 182 | Write-Verbose "Found multiple existing HTTP firewall rules..." 183 | $rule = $matching_rules | % { $_.Profiles -band 4 }[0] 184 | 185 | If (-not $rule -or $rule -is [Array]) { 186 | Write-Verbose "Editing an arbitrary single HTTP firewall rule (multiple existed)" 187 | # oh well, just pick the first one 188 | $rule = $matching_rules[0] 189 | } 190 | } 191 | } 192 | 193 | If (-not $rule) { 194 | Write-Verbose "Creating a new HTTP firewall rule" 195 | $rule = New-Object -ComObject HNetCfg.FWRule 196 | $rule.Name = "Windows Remote Management (HTTP-In)" 197 | $rule.Description = "Inbound rule for Windows Remote Management via WS-Management. [TCP 5985]" 198 | $add_rule = $true 199 | } 200 | 201 | $rule.Profiles = 0x7FFFFFFF 202 | $rule.Protocol = 6 203 | $rule.LocalPorts = 5985 204 | $rule.RemotePorts = "*" 205 | $rule.LocalAddresses = "*" 206 | $rule.RemoteAddresses = "*" 207 | $rule.Enabled = $true 208 | $rule.Direction = 1 209 | $rule.Action = 1 210 | $rule.Grouping = "Windows Remote Management" 211 | 212 | If ($add_rule) { 213 | $fw.Rules.Add($rule) 214 | } 215 | 216 | Write-Verbose "HTTP firewall rule $($rule.Name) updated" 217 | } 218 | 219 | # Setup error handling. 220 | Trap 221 | { 222 | $_ 223 | Exit 1 224 | } 225 | $ErrorActionPreference = "Stop" 226 | 227 | # Get the ID and security principal of the current user account 228 | $myWindowsID=[System.Security.Principal.WindowsIdentity]::GetCurrent() 229 | $myWindowsPrincipal=new-object System.Security.Principal.WindowsPrincipal($myWindowsID) 230 | 231 | # Get the security principal for the Administrator role 232 | $adminRole=[System.Security.Principal.WindowsBuiltInRole]::Administrator 233 | 234 | # Check to see if we are currently running "as Administrator" 235 | if (-Not $myWindowsPrincipal.IsInRole($adminRole)) 236 | { 237 | Write-Output "ERROR: You need elevated Administrator privileges in order to run this script." 238 | Write-Output " Start Windows PowerShell by using the Run as Administrator option." 239 | Exit 2 240 | } 241 | 242 | $EventSource = $MyInvocation.MyCommand.Name 243 | If (-Not $EventSource) 244 | { 245 | $EventSource = "Powershell CLI" 246 | } 247 | 248 | If ([System.Diagnostics.EventLog]::Exists('Application') -eq $False -or [System.Diagnostics.EventLog]::SourceExists($EventSource) -eq $False) 249 | { 250 | New-EventLog -LogName Application -Source $EventSource 251 | } 252 | 253 | # Detect PowerShell version. 254 | If ($PSVersionTable.PSVersion.Major -lt 3) 255 | { 256 | Write-Log "PowerShell version 3 or higher is required." 257 | Throw "PowerShell version 3 or higher is required." 258 | } 259 | 260 | # Find and start the WinRM service. 261 | Write-Verbose "Verifying WinRM service." 262 | If (!(Get-Service "WinRM")) 263 | { 264 | Write-Log "Unable to find the WinRM service." 265 | Throw "Unable to find the WinRM service." 266 | } 267 | ElseIf ((Get-Service "WinRM").Status -ne "Running") 268 | { 269 | Write-Verbose "Setting WinRM service to start automatically on boot." 270 | Set-Service -Name "WinRM" -StartupType Automatic 271 | Write-Log "Set WinRM service to start automatically on boot." 272 | Write-Verbose "Starting WinRM service." 273 | Start-Service -Name "WinRM" -ErrorAction Stop 274 | Write-Log "Started WinRM service." 275 | 276 | } 277 | 278 | # WinRM should be running; check that we have a PS session config. 279 | If (!(Get-PSSessionConfiguration -Verbose:$false) -or (!(Get-ChildItem WSMan:\localhost\Listener))) 280 | { 281 | If ($SkipNetworkProfileCheck) { 282 | Write-Verbose "Enabling PS Remoting without checking Network profile." 283 | Enable-PSRemoting -SkipNetworkProfileCheck -Force -ErrorAction Stop 284 | Write-Log "Enabled PS Remoting without checking Network profile." 285 | } 286 | Else { 287 | Write-Verbose "Enabling PS Remoting." 288 | Enable-PSRemoting -Force -ErrorAction Stop 289 | Write-Log "Enabled PS Remoting." 290 | } 291 | } 292 | Else 293 | { 294 | Write-Verbose "PS Remoting is already enabled." 295 | } 296 | 297 | # Ensure LocalAccountTokenFilterPolicy is set to 1 298 | # https://github.com/ansible/ansible/issues/42978 299 | $token_path = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System" 300 | $token_prop_name = "LocalAccountTokenFilterPolicy" 301 | $token_key = Get-Item -Path $token_path 302 | $token_value = $token_key.GetValue($token_prop_name, $null) 303 | if ($token_value -ne 1) { 304 | Write-Verbose "Setting LocalAccountTOkenFilterPolicy to 1" 305 | if ($null -ne $token_value) { 306 | Remove-ItemProperty -Path $token_path -Name $token_prop_name 307 | } 308 | New-ItemProperty -Path $token_path -Name $token_prop_name -Value 1 -PropertyType DWORD > $null 309 | } 310 | 311 | # Make sure there is a SSL listener. 312 | $listeners = Get-ChildItem WSMan:\localhost\Listener 313 | If (!($listeners | Where {$_.Keys -like "TRANSPORT=HTTPS"})) 314 | { 315 | # We cannot use New-SelfSignedCertificate on 2012R2 and earlier 316 | $thumbprint = New-LegacySelfSignedCert -SubjectName $SubjectName -ValidDays $CertValidityDays 317 | Write-HostLog "Self-signed SSL certificate generated; thumbprint: $thumbprint" 318 | 319 | # Create the hashtables of settings to be used. 320 | $valueset = @{ 321 | Hostname = $SubjectName 322 | CertificateThumbprint = $thumbprint 323 | } 324 | 325 | $selectorset = @{ 326 | Transport = "HTTPS" 327 | Address = "*" 328 | } 329 | 330 | Write-Verbose "Enabling SSL listener." 331 | New-WSManInstance -ResourceURI 'winrm/config/Listener' -SelectorSet $selectorset -ValueSet $valueset 332 | Write-Log "Enabled SSL listener." 333 | } 334 | Else 335 | { 336 | Write-Verbose "SSL listener is already active." 337 | 338 | # Force a new SSL cert on Listener if the $ForceNewSSLCert 339 | If ($ForceNewSSLCert) 340 | { 341 | 342 | # We cannot use New-SelfSignedCertificate on 2012R2 and earlier 343 | $thumbprint = New-LegacySelfSignedCert -SubjectName $SubjectName -ValidDays $CertValidityDays 344 | Write-HostLog "Self-signed SSL certificate generated; thumbprint: $thumbprint" 345 | 346 | $valueset = @{ 347 | CertificateThumbprint = $thumbprint 348 | Hostname = $SubjectName 349 | } 350 | 351 | # Delete the listener for SSL 352 | $selectorset = @{ 353 | Address = "*" 354 | Transport = "HTTPS" 355 | } 356 | Remove-WSManInstance -ResourceURI 'winrm/config/Listener' -SelectorSet $selectorset 357 | 358 | # Add new Listener with new SSL cert 359 | New-WSManInstance -ResourceURI 'winrm/config/Listener' -SelectorSet $selectorset -ValueSet $valueset 360 | } 361 | } 362 | 363 | # Check for basic authentication. 364 | $basicAuthSetting = Get-ChildItem WSMan:\localhost\Service\Auth | Where-Object {$_.Name -eq "Basic"} 365 | 366 | If ($DisableBasicAuth) 367 | { 368 | If (($basicAuthSetting.Value) -eq $true) 369 | { 370 | Write-Verbose "Disabling basic auth support." 371 | Set-Item -Path "WSMan:\localhost\Service\Auth\Basic" -Value $false 372 | Write-Log "Disabled basic auth support." 373 | } 374 | Else 375 | { 376 | Write-Verbose "Basic auth is already disabled." 377 | } 378 | } 379 | Else 380 | { 381 | If (($basicAuthSetting.Value) -eq $false) 382 | { 383 | Write-Verbose "Enabling basic auth support." 384 | Set-Item -Path "WSMan:\localhost\Service\Auth\Basic" -Value $true 385 | Write-Log "Enabled basic auth support." 386 | } 387 | Else 388 | { 389 | Write-Verbose "Basic auth is already enabled." 390 | } 391 | } 392 | 393 | # If EnableCredSSP if set to true 394 | If ($EnableCredSSP) 395 | { 396 | # Check for CredSSP authentication 397 | $credsspAuthSetting = Get-ChildItem WSMan:\localhost\Service\Auth | Where {$_.Name -eq "CredSSP"} 398 | If (($credsspAuthSetting.Value) -eq $false) 399 | { 400 | Write-Verbose "Enabling CredSSP auth support." 401 | Enable-WSManCredSSP -role server -Force 402 | Write-Log "Enabled CredSSP auth support." 403 | } 404 | } 405 | 406 | If ($GlobalHttpFirewallAccess) { 407 | Enable-GlobalHttpFirewallAccess 408 | } 409 | 410 | # Configure firewall to allow WinRM HTTPS connections. 411 | $fwtest1 = netsh advfirewall firewall show rule name="Allow WinRM HTTPS" 412 | $fwtest2 = netsh advfirewall firewall show rule name="Allow WinRM HTTPS" profile=any 413 | If ($fwtest1.count -lt 5) 414 | { 415 | Write-Verbose "Adding firewall rule to allow WinRM HTTPS." 416 | netsh advfirewall firewall add rule profile=any name="Allow WinRM HTTPS" dir=in localport=5986 protocol=TCP action=allow 417 | Write-Log "Added firewall rule to allow WinRM HTTPS." 418 | } 419 | ElseIf (($fwtest1.count -ge 5) -and ($fwtest2.count -lt 5)) 420 | { 421 | Write-Verbose "Updating firewall rule to allow WinRM HTTPS for any profile." 422 | netsh advfirewall firewall set rule name="Allow WinRM HTTPS" new profile=any 423 | Write-Log "Updated firewall rule to allow WinRM HTTPS for any profile." 424 | } 425 | Else 426 | { 427 | Write-Verbose "Firewall rule already exists to allow WinRM HTTPS." 428 | } 429 | 430 | # Test a remoting connection to localhost, which should work. 431 | $httpResult = Invoke-Command -ComputerName "localhost" -ScriptBlock {$env:COMPUTERNAME} -ErrorVariable httpError -ErrorAction SilentlyContinue 432 | $httpsOptions = New-PSSessionOption -SkipCACheck -SkipCNCheck -SkipRevocationCheck 433 | 434 | $httpsResult = New-PSSession -UseSSL -ComputerName "localhost" -SessionOption $httpsOptions -ErrorVariable httpsError -ErrorAction SilentlyContinue 435 | 436 | If ($httpResult -and $httpsResult) 437 | { 438 | Write-Verbose "HTTP: Enabled | HTTPS: Enabled" 439 | } 440 | ElseIf ($httpsResult -and !$httpResult) 441 | { 442 | Write-Verbose "HTTP: Disabled | HTTPS: Enabled" 443 | } 444 | ElseIf ($httpResult -and !$httpsResult) 445 | { 446 | Write-Verbose "HTTP: Enabled | HTTPS: Disabled" 447 | } 448 | Else 449 | { 450 | Write-Log "Unable to establish an HTTP or HTTPS remoting session." 451 | Throw "Unable to establish an HTTP or HTTPS remoting session." 452 | } 453 | Write-VerboseLog "PS Remoting has been successfully configured for Ansible." 454 | --------------------------------------------------------------------------------