├── ssh ├── .gitignore ├── hyperv ├── .gitignore ├── destroy_azure.sh ├── FirstLogonCommands.xml ├── variables.tf ├── README.md ├── packer-build.ps1 ├── init_azure.sh ├── windows.tf └── provision.ps1 ├── images └── packer_builder.png ├── DEBUG.md ├── Vagrantfile ├── packer-build.sh ├── README.md ├── .github └── workflows │ └── build.yml ├── scripts └── provision-virtualbox+vmware-builder.sh ├── upload.sh └── machine.sh /ssh: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .vagrant/ 2 | *.jpg 3 | resources/ 4 | arm/ 5 | work/ 6 | -------------------------------------------------------------------------------- /hyperv/.gitignore: -------------------------------------------------------------------------------- 1 | terraform.tf* 2 | .terraform/ 3 | .terraform.tfstate.lock.info 4 | packer-build.log -------------------------------------------------------------------------------- /images/packer_builder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StefanScherer/packer-builder/HEAD/images/packer_builder.png -------------------------------------------------------------------------------- /DEBUG.md: -------------------------------------------------------------------------------- 1 | # Debugging packer builds 2 | 3 | ## hyperv 4 | 5 | Fetch the IP address of the Azure VM. Then adjust the login password 6 | trough SSH. 7 | 8 | ``` 9 | ssh packer@1.2.3.4 net user packer TheNewPassword 10 | ``` 11 | 12 | Now you can RDP into the machine with user packer and TheNewPassword 13 | and open the Hyper-V Manager and connect to the Hyper-V VM. 14 | -------------------------------------------------------------------------------- /hyperv/destroy_azure.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | resource_group=$(grep -A1 resource_group variables.tf | grep -v resource_group | awk '{gsub(/"/, "", $3); print $3}') 4 | 5 | aadClientName="Terraform-$resource_group" 6 | echo "Deleting service principal $aadClientName" 7 | az ad app delete --id http://$aadClientName 8 | 9 | echo "Deleting resource group $resource_group" 10 | az group delete -n $resource_group 11 | 12 | -------------------------------------------------------------------------------- /hyperv/FirstLogonCommands.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | cmd /c "copy C:\AzureData\CustomData.bin C:\provision.ps1"CopyScript 5 | 11 6 | 7 | 8 | powershell.exe -sta -ExecutionPolicy Unrestricted -file C:\provision.ps1RunScript 10 | 12 11 | 12 | 13 | -------------------------------------------------------------------------------- /Vagrantfile: -------------------------------------------------------------------------------- 1 | # -*- mode: ruby -*- 2 | # vi: set ft=ruby : 3 | 4 | Vagrant.configure("2") do |config| 5 | 6 | config.vm.define "vmware", primary: true do |cfg| 7 | cfg.vm.box = "bento/ubuntu-20.04" 8 | cfg.vm.synced_folder "/Users/stefan/packer_cache", "/home/vagrant/packer_cache" 9 | 10 | cfg.vm.provision "shell", path: "scripts/provision-virtualbox+vmware-builder.sh" 11 | cfg.vm.provider "vmware_desktop" do |v, override| 12 | v.vmx["memsize"] = "6196" 13 | v.vmx["numvcpus"] = "4" 14 | v.vmx["vhv.enable"] = "TRUE" 15 | v.gui = true 16 | end 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /hyperv/variables.tf: -------------------------------------------------------------------------------- 1 | # Settings 2 | 3 | variable "resource_group" { 4 | default = "packer-builder" 5 | } 6 | 7 | variable "location" { 8 | default = "westeurope" 9 | } 10 | 11 | variable "name" { 12 | default = "cirlce1" 13 | } 14 | 15 | variable "admin_username" { 16 | default = "packer" 17 | } 18 | 19 | variable "vm_size" { 20 | default = "Standard_D2s_v3" 21 | } 22 | 23 | variable "ssh" { 24 | default = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDm3bZwzpnjklKH7D/w7a83pzqdvB340dBhQuOmpLszs0H+Js505zZWX3nb0OSFMJ6LjPHZBL9OTj6B+sWxj95QrUTonHso2Jb2bhe8UYwmqvKpmMHIjIjl4UV73tN4VJSWtBaxNdG7KYnpv0dyZu1JitR72+qRUohbzkUbVLQUQ/LE8LJI5ob0VK9EdFXl249gSgYOp4G6Tocy5aVYJAR80bC7Ujrn4tLB0dzBERNQjPrR5JHQ7OlFcaI0ho9zVy+GziU++vNols/Dl0TkvHJKxNE1XQTWAHj1YjVzTMfDlc2M9uoeY/GLXxlkPMskoKbopB1ZvAFGLP2ldUfQ7NA3" 25 | } 26 | -------------------------------------------------------------------------------- /packer-build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | FILE=$1 3 | HYPERVISOR=$2 4 | GITHUB_URL=$3 5 | ISO_URL=$4 6 | 7 | log=packer-build.log 8 | echo $0 $* >> $log 9 | 10 | if [ ! -d work ]; then 11 | echo "Cloning $GITHUB_URL" >> $log 12 | git clone $GITHUB_URL work 13 | fi 14 | cd work 15 | touch $log 16 | git checkout -- *.json 17 | git pull 18 | rm -f *.box 19 | rm -rf output* 20 | 21 | isoflag="" 22 | if [ -z "${ISO_URL}" ]; then 23 | echo "Use default ISO." >> $log 24 | else 25 | echo "Use local ISO." >> $log 26 | if [ ! -e local.iso ]; then 27 | echo "Downloading ISO ..." >> $log 28 | curl -Lo local.iso $ISO_URL 29 | fi 30 | isoflag="--var iso_url=./local.iso" 31 | fi 32 | 33 | only=--only=${HYPERVISOR}-iso 34 | hypervisor1=${HYPERVISOR%+*} 35 | hypervisor2=${HYPERVISOR#*+} 36 | if [ "$hypervisor1" != "$hypervisor2" ]; then 37 | only="--only=${hypervisor1}-iso --only=${hypervisor2}-iso" 38 | fi 39 | echo Running packer build $only $isoflag --var headless=true "${FILE}.json" >> $log 40 | echo "" >> $log 41 | ls -l local.iso >> $log 42 | echo "" >> $log 43 | packer build $only $isoflag --var headless=true "${FILE}.json" | tee -a $log 44 | 45 | if [ ! -e ../packer-upload-and-destroy.sh ]; then 46 | sleep 30 47 | fi 48 | 49 | if [ -e ../packer-upload-and-destroy.sh ]; then 50 | ../packer-upload-and-destroy.sh | tee -a $log 51 | fi 52 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Packer Builder 2 | 3 | [![CircleCI](https://circleci.com/gh/StefanScherer/packer-builder.svg?style=svg)](https://circleci.com/gh/StefanScherer/packer-builder) 4 | 5 | A Packer builder CI pipeline for VirtualBox, VMware and Hyper-V Vagrant boxes. 6 | 7 | ![Packer Builder CI pipeline](images/packer_builder.png) 8 | 9 | ## Packet.net 10 | 11 | Create a baremetal [packet.net](https://packet.net) machine and run Packer there. 12 | 13 | First you need the `packet` golang cli: 14 | 15 | ```bash 16 | go get -u github.com/ebsarr/packet 17 | ``` 18 | 19 | Then use the script `machine.sh` to make things even simpler: 20 | 21 | ```bash 22 | ./machine.sh create p1 23 | ``` 24 | 25 | And then build a VMware VM 26 | 27 | ```bash 28 | ./build.sh p1 windows_2019_docker vmware|virtualbox 29 | ``` 30 | 31 | Afterwards remove the baremetal machine again 32 | 33 | ```bash 34 | ./machine.sh delete p1 35 | ``` 36 | 37 | There are several other commands in `machine.sh`. Have a look at the usage. 38 | 39 | ## Azure 40 | 41 | See [hyperv](hyperv/README.md) sub folder. 42 | 43 | ## Vagrant 44 | 45 | Create a local VM and run Packer there. This is used to test the 46 | provision script. 47 | 48 | ```bash 49 | vagrant up 50 | vagrant ssh 51 | packer build ... 52 | ``` 53 | 54 | ## Configuration 55 | 56 | ### CircleCI Environment Variables 57 | 58 | * AZURE_STORAGE_ACCESS_KEY 59 | * AZURE_STORAGE_ACCOUNT 60 | * AZURE_STORAGE_CONTAINER 61 | * PACKET_APIKEY 62 | * VAGRANT_CLOUD_TOKEN 63 | * VAGRANT_CLOUD_USER 64 | * windows_server_xxx_docker 65 | -------------------------------------------------------------------------------- /hyperv/README.md: -------------------------------------------------------------------------------- 1 | # Packer Hyper-V builder in Azure 2 | 3 | This is a Terraform template to spin up a VM in Azure that has nested Hyper-V 4 | activated and tools like Git, Packer and Vagrant installed. 5 | 6 | Now you are able to build Vagrant base boxes for Hyper-V in the Cloud with Packer. 7 | 8 | ## Initialize Azure and CircleCI 9 | 10 | You need a personal API key for CircleCI. Set the environment variable `CIRCLECI_TOKEN` 11 | and run the script 12 | 13 | ``` 14 | ./init_azure.sh 15 | ``` 16 | 17 | It will create a resource group and service principal and set `ARM_SUBSCRIPTION_ID`, `ARM_CLIENT_ID`, `ARM_CLIENT_SECRET` and `ARM_TENANT_ID` in CircleCI. 18 | 19 | ## Destroy Azure 20 | 21 | If you want to clean up your Azure subscription run 22 | 23 | ``` 24 | ./destroy_azure.sh 25 | ``` 26 | 27 | It will remove the resource group and service princial and app from Azure. 28 | 29 | ## Spin up the Azure VM with Terraform from local machine 30 | 31 | ### Install Terraform 32 | 33 | ``` 34 | brew install terraform 35 | ``` 36 | 37 | ### Configure 38 | 39 | Adjust the file `variables.tf` to your needs to choose 40 | 41 | * location / region 42 | * resource group name 43 | * DNS prefix and suffix 44 | * size of the VM's, default is `Standard_E2s_v3` which is needed for nested virtualization 45 | * username and password 46 | 47 | ### Secrets 48 | 49 | For Terraform you will need these environment variables 50 | 51 | ``` 52 | export ARM_SUBSCRIPTION_ID="uuid" 53 | export ARM_CLIENT_ID="uuid" 54 | export ARM_CLIENT_SECRET="secret" 55 | export ARM_TENANT_ID="uuid" 56 | ``` 57 | 58 | ### Plan 59 | 60 | ```bash 61 | terraform plan 62 | ``` 63 | 64 | ### Create / Apply 65 | 66 | Create the Azure VM with. After 5 minutes the VM should be up and running, and the provision.ps1 script will run inside the VM to install Packer, Vagrant, Hyper-V and then reboots the VM and adds the internal virtual switch 'packer-hyperv-iso' and DHCP server. 67 | 68 | ```bash 69 | terraform apply 70 | ``` 71 | 72 | If you want more than one Packer VM, then use eg. `terraform apply -var name=circle123`. 73 | -------------------------------------------------------------------------------- /hyperv/packer-build.ps1: -------------------------------------------------------------------------------- 1 | param ([String] $FILE, [String] $HYPERVISOR, [String] $GITHUB_URL, [String] $ISO_URL) 2 | 3 | if (!(Test-Path d:/work)) { 4 | Write-Host "Cloning $GITHUB_URL" 5 | git clone $GITHUB_URL d:/work 6 | } 7 | 8 | if ($HYPERVISOR -eq "azure") { 9 | $HYPERVISOR="hyperv" 10 | } 11 | 12 | cd d:/work 13 | 14 | git checkout -- *.json 15 | git pull 16 | Remove-Item *.box 17 | Remove-Item -recurse output* 18 | $isoflag="" 19 | 20 | # Allow me (user of that GitHub repo) to SSH into the machine. 21 | $keyPath = "~\.ssh\authorized_keys" 22 | if (!(Test-Path ~\.ssh)) { 23 | New-Item -Type Directory ~\.ssh > $null 24 | } 25 | $githubKeysUrl = $GITHUB_URL -replace "\/[^\/]+$", ".keys" 26 | $githubSshKey = $(curl.exe $githubKeysUrl) 27 | $githubSshKey | Out-File $keyPath -Append -Encoding Ascii 28 | 29 | $log = "packer-build.log" 30 | $null | Out-File -Encoding Ascii $log 31 | 32 | if ( "$ISO_URL" -eq "" ) { 33 | Write-Host "Use default ISO." >> $log 34 | } else { 35 | Write-Host "Use local ISO." >> $log 36 | if (!(Test-Path local.iso)) { 37 | curl.exe -Lo local.iso $ISO_URL 38 | } 39 | $isoflag="--var iso_url=./local.iso" 40 | } 41 | $only="--only=$HYPERVISOR-iso" 42 | 43 | Write-Host "Running packer build $only --var headless=true ${FILE}.json" 44 | 45 | # Use a CMD.exe script to have real pipes that do not buffer long-running packer builds 46 | @" 47 | rem use Azure temporary storage drive 48 | set TEMP=D:\temp 49 | if not exist %TEMP% ( 50 | mkdir %TEMP% 51 | ) 52 | set PACKER_TMP_DIR=%TEMP% 53 | 54 | powershell -File make_unattend_iso.ps1 55 | 56 | packer build $only $isoflag --var headless=true $FILE.json | "C:\Program Files\Git\usr\bin\tee.exe" -a $log 57 | 58 | if not exist %USERPROFILE%\packer-upload-and-destroy.ps1 ( 59 | ping 127.0.0.1 -n 30 > nul 60 | ) 61 | 62 | if exist %USERPROFILE%\packer-upload-and-destroy.ps1 ( 63 | powershell -file %USERPROFILE%\packer-upload-and-destroy.ps1 | "C:\Program Files\Git\usr\bin\tee.exe" -a $log 64 | ) 65 | 66 | ping 127.0.0.1 -n 6 > nul 67 | 68 | taskkill /F /IM tail.exe 69 | "@ | Out-File -Encoding Ascii packer-build.bat 70 | 71 | Start-Process cmd.exe -ArgumentList "/C", "packer-build.bat" 72 | -------------------------------------------------------------------------------- /hyperv/init_azure.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | for i in CIRCLECI_TOKEN 5 | do 6 | if [ -z "${!i}" ]; then 7 | echo "Environment variable $i must be set" 8 | exit 5 9 | fi 10 | done 11 | 12 | location=$(grep -A1 location variables.tf | grep -v location | awk '{gsub(/"/, "", $3); print $3}') 13 | resource_group=$(grep -A1 resource_group variables.tf | grep -v resource_group | awk '{gsub(/"/, "", $3); print $3}') 14 | 15 | echo "Creating resource group $resource_group" 16 | scope=$(az group create -n "$resource_group" -l "$location" | jq -r .id) 17 | 18 | aadClientName="Terraform-$resource_group" 19 | ARM_CLIENT_SECRET=$(pwgen 30 1) 20 | 21 | echo "Creating service principal $aadClientName" 22 | 23 | ARM_SUBSCRIPTION_ID=$(az account show | jq -r .id) 24 | ARM_TENANT_ID=$(az account show | jq -r .tenantId) 25 | 26 | set +e 27 | aadClientId=$(az ad sp show --id "http://$aadClientName" | jq -r .id) 28 | if [ "$aadClientId" != "" ]; then 29 | echo "Deleting old service principal $aadClientName" 30 | az ad sp delete --id "http://$aadClientName" 31 | fi 32 | set -e 33 | ARM_CLIENT_ID=$(az ad sp create-for-rbac -n "$aadClientName" --password "$ARM_CLIENT_SECRET" --role contributor --scopes "$scope" | jq -r .appId) 34 | 35 | org=$(git remote get-url origin | sed 's/.*github.com://' | sed 's/.*github.com\///' | sed 's/\.git$//' | sed 's/\/.*$//' ) 36 | repo=$(git remote get-url origin | sed 's/.*github.com://' | sed 's/.*github.com\///' | sed 's/\.git$//' | sed 's/.*\///' ) 37 | 38 | circlevars=" 39 | ARM_SUBSCRIPTION_ID 40 | ARM_CLIENT_ID 41 | ARM_CLIENT_SECRET 42 | ARM_TENANT_ID 43 | " 44 | 45 | echo "Deleting CircleCI environment variables for $org/$repo" 46 | for i in $circlevars 47 | do 48 | curl -X DELETE -sS --fail --header "Content-Type: application/json" "https://circleci.com/api/v1.1/project/github/$org/$repo/envvar/$i?circle-token=$CIRCLECI_TOKEN" >/dev/null || true 49 | done 50 | 51 | echo "Setting CircleCI environment variables for $org/$repo" 52 | for i in $circlevars 53 | do 54 | curl -X POST -sS --fail --header "Content-Type: application/json" -d "{\"name\":\"${i}\", \"value\":\"${!i}\"}" "https://circleci.com/api/v1.1/project/github/$org/$repo/envvar?circle-token=$CIRCLECI_TOKEN" >/dev/null 55 | done 56 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | 8 | jobs: 9 | 10 | build: 11 | runs-on: ubuntu-latest 12 | env: 13 | PACKER_TEMPLATE: windows_10 14 | PACKET_PLAN: baremetal_1 15 | HYPERVISOR: virtualbox+vmware 16 | PACKET_APIKEY: ${{ secrets.PACKET_APIKEY }} 17 | steps: 18 | - uses: actions/checkout@v2 19 | - name: Install SSH key 20 | uses: shimataro/ssh-key-action@v2 21 | with: 22 | key: ${{ secrets.SSH_KEY }} 23 | known_hosts: unnecessary 24 | - name: Install tools 25 | run: | 26 | docker create --name packet ebsarr/packet 27 | docker cp packet:/usr/local/bin/packet ./packet 28 | sudo mv ./packet /usr/local/bin/packet 29 | packet -v 30 | jq --version 31 | curl --version 32 | - name: Provision a packer build machine 33 | run: ./machine.sh create github${GITHUB_RUN_NUMBER} ${HYPERVISOR} 34 | - name: Run packer build 35 | run: ./build.sh github${GITHUB_RUN_NUMBER} ${PACKER_TEMPLATE} ${HYPERVISOR} 36 | env: 37 | AZURE_STORAGE_ACCOUNT: ${{ secrets.AZURE_STORAGE_ACCOUNT }} 38 | AZURE_STORAGE_ACCESS_KEY: ${{ secrets.AZURE_STORAGE_ACCESS_KEY }} 39 | AZURE_STORAGE_CONTAINER: ${{ secrets.AZURE_STORAGE_CONTAINER }} 40 | ARM_SUBSCRIPTION_ID: ${{ secrets.ARM_SUBSCRIPTION_ID }} 41 | ARM_CLIENT_ID: ${{ secrets.ARM_CLIENT_ID }} 42 | ARM_CLIENT_SECRET: ${{ secrets.ARM_CLIENT_SECRET }} 43 | ARM_TENANT_ID: ${{ secrets.ARM_TENANT_ID }} 44 | 45 | # upload: 46 | # runs-on: ubuntu-latest 47 | # env: 48 | # PACKER_TEMPLATE: windows_10 49 | # HYPERVISOR: virtualbox+vmware 50 | # container: 51 | # image: mcr.microsoft.com/azure-cli:2.28.0 52 | # steps: 53 | # - uses: actions/checkout@v2 54 | # - name: Install tools 55 | # run: | 56 | # apk update && apk add jq curl 57 | # az --version 58 | # jq --version 59 | # curl --version 60 | # - name: Upload to Vagrant Cloud 61 | # run: ./upload.sh ${PACKER_TEMPLATE} ${HYPERVISOR} 62 | # env: 63 | # AZURE_STORAGE_ACCOUNT: ${{ secrets.AZURE_STORAGE_ACCOUNT }} 64 | # AZURE_STORAGE_ACCESS_KEY: ${{ secrets.AZURE_STORAGE_ACCESS_KEY }} 65 | # AZURE_STORAGE_CONTAINER: ${{ secrets.AZURE_STORAGE_CONTAINER }} 66 | # ARM_SUBSCRIPTION_ID: ${{ secrets.ARM_SUBSCRIPTION_ID }} 67 | # ARM_CLIENT_ID: ${{ secrets.ARM_CLIENT_ID }} 68 | # ARM_CLIENT_SECRET: ${{ secrets.ARM_CLIENT_SECRET }} 69 | # ARM_TENANT_ID: ${{ secrets.ARM_TENANT_ID }} 70 | # VAGRANT_CLOUD_TOKEN: ${{ secrets.VAGRANT_CLOUD_TOKEN }} 71 | # VAGRANT_CLOUD_USER: ${{ secrets.VAGRANT_CLOUD_USER }} 72 | -------------------------------------------------------------------------------- /scripts/provision-virtualbox+vmware-builder.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -x 3 | 4 | echo "Running provision-virtualbox+vmware-builder.sh" 5 | 6 | PACKER_VERSION=1.7.2 7 | VMWARE_VERSION=16.1.1-17801498 8 | VIRTUALBOX_VERSION=6.1 9 | TRIAL1=504RK 10 | TRIAL2=GV011 11 | TRIAL3=M88X2 12 | TRIAL4=0CAK6 13 | TRIAL5=2H2Q6 14 | 15 | PACKER_URL=https://releases.hashicorp.com/packer/${PACKER_VERSION}/packer_${PACKER_VERSION}_linux_amd64.zip 16 | VMWARE_URL=http://download3.vmware.com/software/wkst/file/VMware-Workstation-Full-${VMWARE_VERSION}.x86_64.bundle 17 | 18 | # Install Virtualbox 19 | echo "deb http://download.virtualbox.org/virtualbox/debian $(lsb_release -cs) contrib" >> /etc/apt/sources.list 20 | wget -q https://www.virtualbox.org/download/oracle_vbox_2016.asc -O- | sudo apt-key add - 21 | wget -q https://www.virtualbox.org/download/oracle_vbox.asc -O- | sudo apt-key add - 22 | DEBIAN_FRONTEND=noninteractive apt-get update 23 | sudo DEBIAN_FRONTEND=noninteractive apt-get install -qq git unzip curl dkms build-essential \ 24 | "linux-headers-$(uname -r)" x11-common x11-xserver-utils libxtst6 libxinerama1 psmisc 25 | 26 | sudo DEBIAN_FRONTEND=noninteractive apt-get install -y virtualbox-${VIRTUALBOX_VERSION} 27 | 28 | # Install VirtualBox extension pack 29 | vbox=$(VBoxManage --version) 30 | vboxversion=${vbox%r*} 31 | vboxrevision=${vbox#*r} 32 | wget https://download.virtualbox.org/virtualbox/${vboxversion}/Oracle_VM_VirtualBox_Extension_Pack-${vboxversion}-${vboxrevision}.vbox-extpack 33 | yes | VBoxManage extpack install Oracle_VM_VirtualBox_Extension_Pack-${vboxversion}-${vboxrevision}.vbox-extpack 34 | rm Oracle_VM_VirtualBox_Extension_Pack-${vboxversion}-${vboxrevision}.vbox-extpack 35 | 36 | # install packer 37 | mkdir /opt/packer 38 | pushd /opt/packer 39 | echo "Downloading packer ${PACKER_VERSION} ...." 40 | curl -L -o ${PACKER_VERSION}_linux_amd64.zip ${PACKER_URL} 41 | echo "Installing packer ${PACKER_VERSION} ..." 42 | unzip ${PACKER_VERSION}_linux_amd64.zip 43 | rm ${PACKER_VERSION}_linux_amd64.zip 44 | pushd /usr/bin 45 | ln -s /opt/packer/* . 46 | popd 47 | popd 48 | 49 | rmmod kvm_intel kvm 50 | 51 | echo "Downloading VMware Workstation ${VMWARE_VERSION} ..." 52 | curl -o VMware-Workstation.bundle ${VMWARE_URL} 53 | echo "Installing VMware Workstation ${VMWARE_VERSION} ..." 54 | sh ./VMware-Workstation.bundle --console --required --eulas-agreed --set-setting vmware-workstation serialNumber $TRIAL1-$TRIAL2-$TRIAL3-$TRIAL4-$TRIAL5 55 | rm ./VMware-Workstation.bundle 56 | 57 | echo "Installing Azure cli ..." 58 | sudo apt-get install -y ca-certificates curl apt-transport-https lsb-release gnupg 59 | curl -sL https://packages.microsoft.com/keys/microsoft.asc | \ 60 | gpg --dearmor | \ 61 | sudo tee /etc/apt/trusted.gpg.d/microsoft.asc.gpg > /dev/null 62 | AZ_REPO=$(lsb_release -cs) 63 | echo "deb [arch=amd64] https://packages.microsoft.com/repos/azure-cli/ $AZ_REPO main" | \ 64 | sudo tee /etc/apt/sources.list.d/azure-cli.list 65 | sudo apt-get update 66 | sudo apt-get install -y azure-cli 67 | -------------------------------------------------------------------------------- /upload.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | FILE=$1 5 | HYPERVISOR=$2 6 | 7 | if [ -z "${FILE}" ] || [ "${FILE}" == "--help" ] || [ -z "${HYPERVISOR}" ]; then 8 | echo "Usage: $0 template hypervisor" 9 | echo "$0 windows_2019_docker vmware" 10 | exit 1 11 | fi 12 | 13 | function upload { 14 | FILE=$1 15 | BOX_VERSION=$2 16 | HYPERVISOR=$3 17 | 18 | VAGRANT_PROVIDER="$HYPERVISOR" 19 | if [ "$HYPERVISOR" == "vmware" ]; then 20 | VAGRANT_PROVIDER="${HYPERVISOR}_desktop" 21 | fi 22 | 23 | echo "Create a new provider $VAGRANT_PROVIDER for version $BOX_VERSION" 24 | curl \ 25 | --header "Content-Type: application/json" \ 26 | --header "Authorization: Bearer $VAGRANT_CLOUD_TOKEN" \ 27 | "https://app.vagrantup.com/api/v1/box/$VAGRANT_CLOUD_USER/$FILE/version/$BOX_VERSION/providers" \ 28 | --data "{ \"provider\": { \"name\": \"$VAGRANT_PROVIDER\" } }" 29 | 30 | echo "Prepare the provider for upload/get an upload URL" 31 | response=$(curl \ 32 | --header "Authorization: Bearer $VAGRANT_CLOUD_TOKEN" \ 33 | "https://app.vagrantup.com/api/v1/box/$VAGRANT_CLOUD_USER/$FILE/version/$BOX_VERSION/provider/$VAGRANT_PROVIDER/upload") 34 | 35 | # Extract the upload URL from the response (requires the jq command) 36 | upload_path=$(echo "$response" | jq -r .upload_path) 37 | 38 | echo "Upload ${FILE}_${HYPERVISOR}.box to Vagrant Cloud" 39 | set +e 40 | output=$(curl $upload_path --request PUT --upload-file "${FILE}_${HYPERVISOR}.box") 41 | stat=$? 42 | if [ "$stat" == "52" ]; then 43 | echo "Got curl status 52, ignoring for now." 44 | fi 45 | set -e 46 | 47 | rm "${FILE}_${HYPERVISOR}.box" 48 | } 49 | 50 | function download { 51 | FILE=$1 52 | HYPERVISOR=$2 53 | echo "Download box ${FILE}_${HYPERVISOR}.box" 54 | today=$(date +%Y-%m-%d) 55 | az storage blob download --account-name "${AZURE_STORAGE_ACCOUNT}" --account-key "${AZURE_STORAGE_ACCESS_KEY}" --container-name "${AZURE_STORAGE_CONTAINER}" --name "${FILE}/$today/${FILE}_${HYPERVISOR}.box" --file "${FILE}_${HYPERVISOR}.box" || true 56 | 57 | if [ ! -e "${FILE}_${HYPERVISOR}.box" ]; then 58 | yesterday=$(date -d "yesterday 13:00" +%Y-%m-%d) 59 | az storage blob download --account-name "${AZURE_STORAGE_ACCOUNT}" --account-key "${AZURE_STORAGE_ACCESS_KEY}" --container-name "${AZURE_STORAGE_CONTAINER}" --name "${FILE}/$yesterday/${FILE}_${HYPERVISOR}.box" --file "${FILE}_${HYPERVISOR}.box" || true 60 | fi 61 | 62 | if [ ! -e "${FILE}_${HYPERVISOR}.box" ]; then 63 | twodaysago=$(date -d "2 days ago 13:00" +%Y-%m-%d) 64 | az storage blob download --account-name "${AZURE_STORAGE_ACCOUNT}" --account-key "${AZURE_STORAGE_ACCESS_KEY}" --container-name "${AZURE_STORAGE_CONTAINER}" --name "${FILE}/$twodaysago/${FILE}_${HYPERVISOR}.box" --file "${FILE}_${HYPERVISOR}.box" 65 | fi 66 | } 67 | 68 | BOX_VERSION=$(date +%Y.%m.%d) 69 | echo "Create a new version $BOX_VERSION for $FILE in Vagrant Cloud" 70 | curl \ 71 | --header "Content-Type: application/json" \ 72 | --header "Authorization: Bearer $VAGRANT_CLOUD_TOKEN" \ 73 | "https://app.vagrantup.com/api/v1/box/$VAGRANT_CLOUD_USER/$FILE/versions" \ 74 | --data "{ \"version\": { \"version\": \"$BOX_VERSION\" } }" 75 | 76 | hypervisor1=${HYPERVISOR%+*} 77 | hypervisor2=${HYPERVISOR#*+} 78 | 79 | echo "Login to Azure" 80 | export AZURE_STORAGE_ACCOUNT=$AZURE_STORAGE_ACCOUNT 81 | export AZURE_STORAGE_ACCESS_KEY=$AZURE_STORAGE_ACCESS_KEY 82 | 83 | export ARM_SUBSCRIPTION_ID=$ARM_SUBSCRIPTION_ID 84 | export ARM_CLIENT_ID=$ARM_CLIENT_ID 85 | export ARM_CLIENT_SECRET=$ARM_CLIENT_SECRET 86 | export ARM_TENANT_ID=$ARM_TENANT_ID 87 | 88 | az login --service-principal --username "${ARM_CLIENT_ID}" --password "${ARM_CLIENT_SECRET}" --tenant "${ARM_TENANT_ID}" >az-login.txt 89 | 90 | download "$FILE" "$hypervisor1" 91 | upload "$FILE" "$BOX_VERSION" "$hypervisor1" 92 | 93 | if [ "$hypervisor1" != "$hypervisor2" ]; then 94 | download "$FILE" "$hypervisor2" 95 | upload "$FILE" "$BOX_VERSION" "$hypervisor2" 96 | fi 97 | -------------------------------------------------------------------------------- /hyperv/windows.tf: -------------------------------------------------------------------------------- 1 | # Configure the Microsoft Azure Provider 2 | provider "azurerm" { 3 | version = "= 1.25.0" 4 | } 5 | 6 | provider "random" { 7 | version = "~> 1.1" 8 | } 9 | 10 | resource "random_string" "password" { 11 | length = 16 12 | special = false 13 | } 14 | 15 | # Create a storage account 16 | resource "azurerm_storage_account" "global" { 17 | account_tier = "Premium" # Only locally redundant 18 | account_replication_type = "LRS" 19 | location = "${var.location}" 20 | name = "${var.name}" 21 | resource_group_name = "${var.resource_group}" 22 | } 23 | 24 | resource "azurerm_virtual_network" "windows" { 25 | name = "virtnet-${var.name}" 26 | address_space = ["10.0.0.0/16"] 27 | location = "${var.location}" 28 | resource_group_name = "${var.resource_group}" 29 | } 30 | 31 | resource "azurerm_subnet" "windows" { 32 | name = "subnet-${var.name}" 33 | resource_group_name = "${var.resource_group}" 34 | virtual_network_name = "${azurerm_virtual_network.windows.name}" 35 | address_prefix = "10.0.2.0/24" 36 | } 37 | 38 | resource "azurerm_network_interface" "windows" { 39 | name = "nic-${var.name}" 40 | location = "${var.location}" 41 | resource_group_name = "${var.resource_group}" 42 | 43 | ip_configuration { 44 | name = "testconfiguration1" 45 | subnet_id = "${azurerm_subnet.windows.id}" 46 | public_ip_address_id = "${azurerm_public_ip.windows.id}" 47 | private_ip_address_allocation = "dynamic" 48 | } 49 | } 50 | 51 | resource "azurerm_public_ip" "windows" { 52 | idle_timeout_in_minutes = 30 53 | location = "${var.location}" 54 | name = "pubip-${var.name}" 55 | allocation_method = "Dynamic" 56 | resource_group_name = "${var.resource_group}" 57 | } 58 | 59 | resource "azurerm_storage_container" "windows" { 60 | container_access_type = "private" 61 | name = "windows-storage" 62 | resource_group_name = "${var.resource_group}" 63 | storage_account_name = "${azurerm_storage_account.global.name}" 64 | } 65 | 66 | resource "azurerm_virtual_machine" "windows" { 67 | name = "vm-${var.name}" 68 | location = "${var.location}" 69 | resource_group_name = "${var.resource_group}" 70 | network_interface_ids = ["${azurerm_network_interface.windows.id}"] 71 | vm_size = "${var.vm_size}" 72 | 73 | storage_image_reference { 74 | publisher = "MicrosoftWindowsServer" 75 | offer = "WindowsServer" 76 | sku = "2019-Datacenter" 77 | version = "latest" 78 | } 79 | 80 | storage_os_disk { 81 | name = "osdisk-${var.name}" 82 | vhd_uri = "${azurerm_storage_container.windows.id}/disk1.vhd" 83 | caching = "ReadWrite" 84 | create_option = "FromImage" 85 | } 86 | 87 | os_profile { 88 | computer_name = "${var.name}" 89 | admin_username = "${var.admin_username}" 90 | admin_password = "${random_string.password.result}" 91 | custom_data = "${base64encode("Param($Username=\"${var.admin_username}\", $Password=\"${random_string.password.result}\", $sshKey=\"${var.ssh}\") ${file("./provision.ps1")}")}" 92 | } 93 | 94 | os_profile_windows_config { 95 | provision_vm_agent = true 96 | enable_automatic_upgrades = true 97 | additional_unattend_config { 98 | pass = "oobeSystem" 99 | component = "Microsoft-Windows-Shell-Setup" 100 | setting_name = "AutoLogon" 101 | content = "${random_string.password.result}true1${var.admin_username}" 102 | } 103 | additional_unattend_config { 104 | pass = "oobeSystem" 105 | component = "Microsoft-Windows-Shell-Setup" 106 | setting_name = "FirstLogonCommands" 107 | content = "${file("./FirstLogonCommands.xml")}" 108 | } 109 | } 110 | 111 | tags { 112 | environment = "staging" 113 | } 114 | } 115 | 116 | output "ip" { 117 | value = "${azurerm_public_ip.windows.ip_address}" 118 | } 119 | -------------------------------------------------------------------------------- /machine.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | COMMAND=$1 4 | NAME=$2 5 | HYPERVISOR=$3 6 | 7 | OSTYPE=ubuntu_20_04 8 | PACKET_PLAN=${PACKET_PLAN:-baremetal_0} 9 | PROJECT=${PACKET_PROJECT:-packer} 10 | AZURE_PLAN=${AZURE_PLAN:-Standard_D2s_v3} 11 | 12 | if [ -z "${COMMAND}" ] || [ "${COMMAND}" == "--help" ] ; then 13 | echo "Usage:" 14 | echo " $0 cleanup delete old machines" 15 | echo " $0 create name type create a new machine with vmware|virtualbox" 16 | echo " $0 delete name delete a machine" 17 | echo " $0 ip name get IP address of a machine" 18 | echo " $0 list list all machines" 19 | echo " $0 provision name type provision the machine with vmware|virtualbox" 20 | echo " $0 ssh name ssh into a machine" 21 | echo " $0 start name start a machine" 22 | echo " $0 stop name stop a machine" 23 | exit 1 24 | fi 25 | 26 | TOKEN=${PACKET_APIKEY} 27 | 28 | if [ -z "${TOKEN}" ]; then 29 | TOKEN=$(pass packet_token) 30 | fi 31 | 32 | function create { 33 | NAME=$1 34 | PROJECTID=$2 35 | HYPERVISOR=$3 36 | 37 | if [ -z "${NAME}" ]; then 38 | echo "Usage: $0 name" 39 | exit 1 40 | fi 41 | 42 | # create project if it does not exist 43 | if [ -z "${PROJECTID}" ]; then 44 | PROJECTID=$(packet -k "${TOKEN}" \ 45 | admin create-project --name "${PROJECT}" | jq -r .id) 46 | fi 47 | 48 | for facility in ewr1 sjc1 dfw2 ams1 nrt1 49 | do 50 | echo "Creating baremetal machine in packet facility $facility" 51 | # create machine 52 | if packet -k "${TOKEN}" baremetal create-device \ 53 | --billing hourly \ 54 | --facility "${facility}" \ 55 | --os-type "${OSTYPE}" \ 56 | --plan "${PACKET_PLAN}" \ 57 | --project-id "${PROJECTID}" \ 58 | --hostname "${NAME}"; then 59 | break 60 | fi 61 | done 62 | 63 | provision "${NAME}" "${PROJECTID}" "${HYPERVISOR}" 64 | } 65 | 66 | function cmd { 67 | NAME=$1 68 | PROJECTID=$2 69 | CMD=$3 70 | 71 | if [ -z "${NAME}" ] || [ -z "${PROJECTID}" ] || [ -z "${CMD}" ]; then 72 | echo "Usage: $0 name id command" 73 | exit 1 74 | fi 75 | 76 | DEVICEID=$(packet -k "${TOKEN}" \ 77 | baremetal list-devices --project-id "${PROJECTID}" | jq -r ".[] | select(.hostname == \"${NAME}\") .id") 78 | 79 | packet -k "${TOKEN}" \ 80 | baremetal "${CMD}" --device-id "${DEVICEID}" 81 | } 82 | 83 | function start { 84 | echo "Starting $1" 85 | cmd "$1" "$2" poweron-device 86 | } 87 | 88 | function stop { 89 | echo "Stopping $1" 90 | cmd "$1" "$2" poweroff-device 91 | } 92 | 93 | function delete { 94 | echo "Deleting $1" 95 | cmd "$1" "$2" delete-device 96 | } 97 | 98 | function cleanup { 99 | machines=$(packet -k "${TOKEN}" baremetal list-devices --project-id "${PROJECTID}" | \ 100 | jq -r '.[] | .hostname') 101 | for m in $machines 102 | do 103 | circle_build_num=${m#*e} 104 | RESPONSE=$(curl \ 105 | --silent -S \ 106 | --fail \ 107 | "https://circleci.com/api/v1.1/project/github/${CIRCLE_PROJECT_USERNAME}/${CIRCLE_PROJECT_REPONAME}/${circle_build_num}") 108 | STATE=$(echo "${RESPONSE}" | jq -r .lifecycle) 109 | echo "Found machine $m, CircleCI build ${circle_build_num} is ${STATE}" 110 | 111 | if [ "${STATE}" == "finished" ]; then 112 | echo "Removing resources of finished CircleCI job $circle_build_num" 113 | delete "$m" "$2" 114 | fi 115 | done 116 | echo "Cleanup done." 117 | } 118 | 119 | function list { 120 | packet -k "${TOKEN}" baremetal list-devices --project-id "${PROJECTID}" | \ 121 | jq -r '.[] | .hostname + " " + .state' 122 | } 123 | 124 | function ip { 125 | packet -k "${TOKEN}" baremetal list-devices --project-id "${PROJECTID}" | \ 126 | jq -r ".[] | select(.hostname == \"${NAME}\") | .ip_addresses[] | select(.public == true) | select(.address_family == 4).address" | head -1 127 | } 128 | 129 | function provision { 130 | echo "Provisioning $1" 131 | IP=$(ip) 132 | ssh-keygen -R "${IP}" 133 | ssh-keyscan "${IP}" >>~/.ssh/known_hosts 134 | /usr/bin/ssh "root@${IP}" < "scripts/provision-${HYPERVISOR}-builder.sh" 135 | } 136 | 137 | function ssh { 138 | /usr/bin/ssh "root@$(ip)" 139 | } 140 | 141 | function azure_create { 142 | NAME=$1 143 | HYPERVISOR=$2 144 | 145 | cd hyperv 146 | terraform init -input=false 147 | echo "Running Terraform to build VM ${NAME}" 148 | terraform apply -input=false -auto-approve --var "name=${NAME}" --var "vm_size=${AZURE_PLAN}" 149 | 150 | echo "Refreshing Terraform state" 151 | terraform refresh -input=false | grep -vi password 152 | 153 | IP=$(terraform output ip) 154 | if [ -z "$IP" ]; then 155 | echo "Waiting for IP" 156 | sleep 60 157 | IP=$(terraform output ip) 158 | fi 159 | 160 | echo "IP address of Azure VM $NAME: $IP" 161 | 162 | echo "Wait until SSH is available" 163 | maxConnectionAttempts=30 164 | sleepSeconds=20 165 | index=1 166 | success=0 167 | 168 | while (( index <= maxConnectionAttempts )) 169 | do 170 | /usr/bin/ssh -o StrictHostKeyChecking=no "packer@$IP" ver 171 | case $? in 172 | (0) echo "${index}> Success"; ((success+=1));; 173 | (*) echo "${index} of ${maxConnectionAttempts}> SSH server not ready yet, waiting ${sleepSeconds} seconds..."; success=0 ;; 174 | esac 175 | if [ $success -eq 2 ]; then 176 | break 177 | fi 178 | sleep $sleepSeconds 179 | ((index+=1)) 180 | done 181 | set -e 182 | 183 | ssh-keygen -R "${IP}" 184 | ssh-keyscan "${IP}" >>~/.ssh/known_hosts 185 | } 186 | 187 | if [ "${HYPERVISOR}" == "hyperv" ]; then 188 | azure_create "${NAME}" "${HYPERVISOR}" 189 | elif [ "${HYPERVISOR}" == "azure" ]; then 190 | azure_create "${NAME}" "${HYPERVISOR}" 191 | else 192 | PROJECTID=$(packet -k "${TOKEN}" \ 193 | admin list-projects | jq -r ".[] | select(.name == \"${PROJECT}\") .id") 194 | 195 | "${COMMAND}" "${NAME}" "${PROJECTID}" "${HYPERVISOR}" 196 | fi 197 | -------------------------------------------------------------------------------- /hyperv/provision.ps1: -------------------------------------------------------------------------------- 1 | 2 | Start-Transcript -Path C:\provision.log -Append 3 | 4 | Function SetupPhase1 { 5 | Cscript $env:WinDir\System32\SCregEdit.wsf /AU 1 6 | Net stop wuauserv 7 | Net start wuauserv 8 | 9 | Set-MpPreference -DisableRealtimeMonitoring $true 10 | 11 | New-ItemProperty -Path HKCU:\Software\Microsoft\ServerManager -Name DoNotOpenServerManagerAtLogon -PropertyType DWORD -Value "1" -Force 12 | 13 | Write-Output "Installing Chocolatey" 14 | curl.exe -o install-chocolatey.ps1 https://chocolatey.org/install.ps1 15 | .\install-chocolatey.ps1 16 | choco feature disable --name showDownloadProgress 17 | choco install -y git 18 | choco install -y curl 19 | choco install -y packer -version 1.4.1 20 | # choco install -y vagrant -version 2.0.3 21 | choco install -y terraform -version 0.11.14 22 | choco install -y procexp 23 | choco install -y procmon 24 | choco install -y azure-cli 25 | 26 | Write-Output "Installing Hyper-V" 27 | Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Hyper-V -All -NoRestart 28 | Install-WindowsFeature Hyper-V-Tools 29 | Install-WindowsFeature Hyper-V-PowerShell 30 | 31 | #Write-Output Install all Windows Updates 32 | #Get-Content C:\windows\system32\en-us\WUA_SearchDownloadInstall.vbs | ForEach-Object { 33 | # $_ -replace 'confirm = msgbox.*$', 'confirm = vbNo' 34 | #} | Out-File $env:TEMP\WUA_SearchDownloadInstall.vbs 35 | #"a`na" | cscript $env:TEMP\WUA_SearchDownloadInstall.vbs 36 | 37 | Write-Output "Rebooting" 38 | Restart-Computer 39 | } 40 | 41 | Function SetupPhase2 { 42 | 43 | # Write-Output "Installing Vagrant plugins" 44 | # vagrant plugin install vagrant-reload 45 | 46 | Write-Output "Adding NAT" 47 | New-VMSwitch -SwitchName "packer-hyperv-iso" -SwitchType Internal 48 | New-NetIPAddress -IPAddress 192.168.0.1 -PrefixLength 24 -InterfaceIndex (Get-NetAdapter -name "vEthernet (packer-hyperv-iso)").ifIndex 49 | New-NetNat -Name MyNATnetwork -InternalIPInterfaceAddressPrefix 192.168.0.0/24 50 | 51 | Write-Output "Adding DHCP scope" 52 | Install-WindowsFeature DHCP -IncludeManagementTools 53 | Add-DhcpServerv4Scope -Name "Internal" -StartRange 192.168.0.10 -EndRange 192.168.0.250 -SubnetMask 255.255.255.0 -Description "Internal Network" 54 | Set-DhcpServerv4OptionValue -ScopeID 192.168.0 -DNSServer 8.8.8.8 -Router 192.168.0.1 55 | 56 | Write-Output "Allow Packer http server" 57 | New-NetFirewallRule -DisplayName "Allow Packer" -Direction Inbound -Program "C:\ProgramData\chocolatey\lib\packer\tools\packer.exe" -RemoteAddress LocalSubnet -Action Allow 58 | 59 | Write-Output "Disabling autologon" 60 | New-ItemProperty -Path "HKCU:\Software\Microsoft\Windows NT\CurrentVersion\Winlogon" -Name AutoAdminLogon -PropertyType DWORD -Value "0" -Force 61 | 62 | Write-Output "Downloading OpenSSH" 63 | curl.exe -o OpenSSH-Win64.zip -L https://github.com/PowerShell/Win32-OpenSSH/releases/download/v8.0.0.0p1-Beta/OpenSSH-Win64.zip 64 | 65 | Write-Output "Expanding OpenSSH" 66 | Expand-Archive OpenSSH-Win64.zip -DestinationPath 'C:\Program Files' 67 | Remove-Item -Force OpenSSH-Win64.zip 68 | 69 | Push-Location 'C:\Program Files\OpenSSH-Win64' 70 | 71 | Write-Output "Installing OpenSSH" 72 | & .\install-sshd.ps1 73 | 74 | if ( ! (Test-Path $env:ProgramData\ssh) ) { 75 | New-Item -Type Directory $env:ProgramData\ssh 76 | } 77 | 78 | Write-Output "Generating host keys" 79 | .\ssh-keygen.exe -A 80 | 81 | Write-Output "Fixing host file permissions" 82 | & .\FixHostFilePermissions.ps1 -Confirm:$false 83 | 84 | Write-Output "Fixing user file permissions" 85 | & .\FixUserFilePermissions.ps1 -Confirm:$false 86 | 87 | $newPath = 'C:\Program Files\OpenSSH-Win64;' + [Environment]::GetEnvironmentVariable("PATH", [EnvironmentVariableTarget]::Machine) 88 | [Environment]::SetEnvironmentVariable("PATH", $newPath, [EnvironmentVariableTarget]::Machine) 89 | 90 | Write-Output 'Editing OpenSSH config' 91 | $sshd_config = Get-Content sshd_config_default 92 | $sshd_config = $sshd_config -replace 'StrictModes yes', 'StrictModes no' 93 | $sshd_config = $sshd_config -replace '#PubkeyAuthentication yes', 'PubkeyAuthentication yes' 94 | $sshd_config = $sshd_config -replace 'Match Group administrators', '#Match Group administrators' 95 | $sshd_config = $sshd_config -replace 'AuthorizedKeysFile __PROGRAMDATA__/ssh/administrators_authorized_keys', '#AuthorizedKeysFile __PROGRAMDATA__/ssh/administrators_authorized_keys' 96 | Set-Content $env:ProgramData\ssh\sshd_config $sshd_config 97 | 98 | Write-Output "Adding public key to authorized_keys" 99 | $keyPath = "~\.ssh\authorized_keys" 100 | New-Item -Type Directory ~\.ssh > $null 101 | $sshKey | Out-File $keyPath -Encoding Ascii 102 | 103 | Import-Module .\OpenSSHUtils.psd1 -Force 104 | Repair-AuthorizedKeyPermission -FilePath $keyPath 105 | 106 | Pop-Location 107 | 108 | Write-Output "Setting sshd service startup type to 'Automatic'" 109 | Set-Service sshd -StartupType Automatic 110 | Set-Service ssh-agent -StartupType Automatic 111 | Write-Output "Setting sshd service restart behavior" 112 | sc.exe failure sshd reset= 86400 actions= restart/500 113 | 114 | Write-Output "Starting sshd service" 115 | Start-Service sshd 116 | Start-Service ssh-agent 117 | 118 | Write-Output "Opening firewall port 22" 119 | New-NetFirewallRule -Protocol TCP -LocalPort 22 -Direction Inbound -Action Allow -DisplayName SSH 120 | 121 | Write-Output "Removing scheduled job" 122 | Unregister-ScheduledJob -Name NewServerSetupResume -Force 123 | } 124 | 125 | if (!(Test-Path c:\ProgramData\chocolatey)) { 126 | $pwd = ConvertTo-SecureString -String $Password -AsPlainText -Force 127 | $cred = New-Object System.Management.Automation.PSCredential($Username, $pwd) 128 | $AtStartup = New-JobTrigger -AtStartup 129 | Register-ScheduledJob -Name NewServerSetupResume ` 130 | -Credential $cred ` 131 | -Trigger $AtStartup ` 132 | -ScriptBlock { c:\provision.ps1 } 133 | SetupPhase1 134 | } else { 135 | SetupPhase2 136 | } 137 | --------------------------------------------------------------------------------