diff --git a/.github/workflows/create-prerelase.yml b/.github/workflows/create-prerelase.yml index 2f1d668b..ce37d26c 100644 --- a/.github/workflows/create-prerelase.yml +++ b/.github/workflows/create-prerelase.yml @@ -8,6 +8,7 @@ on: push: paths: - 'Scripts/**' + - 'Tools/**' branches: [ dev ] jobs: @@ -16,22 +17,28 @@ jobs: if: "!contains(github.event.head_commit.message, '[no release]')" runs-on: windows-2019 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 with: - fetch-depth: 0 + fetch-depth: 1 - name: Build scripts id: build shell: powershell run: | - ./build.ps1 -Version prerelease + ./build.ps1 -Version dev $filename = "mslab_dev-$((Get-Date -Format "yyyyMMdd")).zip" mv ./Release.zip $filename - echo "::set-output name=filename::$filename" - - uses: "marvinpinto/action-automatic-releases@latest" + echo "filename=$filename" >> $env:GITHUB_OUTPUT + - name: Delete current dev prerelease + uses: cb80/delrel@latest with: - repo_token: "${{ secrets.GITHUB_TOKEN }}" - automatic_release_tag: "dev" + tag: dev + - name: Create new dev prerelease + uses: softprops/action-gh-release@v1 + with: + tag_name: dev + name: dev branch preview + generate_release_notes: true prerelease: true - title: "dev branch preview" files: | ${{ steps.build.outputs.filename }} + Output/Tools/*.ps1 diff --git a/.github/workflows/create-release.yml b/.github/workflows/create-release.yml index ea976540..63432c70 100644 --- a/.github/workflows/create-release.yml +++ b/.github/workflows/create-release.yml @@ -8,10 +8,12 @@ on: push: paths: - 'Scripts/**' + - 'Tools/**' branches: [ master ] jobs: new-version: + environment: release name: Bump version if: "!contains(github.event.head_commit.message, '[no release]')" runs-on: windows-2019 @@ -19,14 +21,15 @@ jobs: previous_tag: ${{ steps.bump.outputs.previous_tag }} new_tag: ${{ steps.bump.outputs.new_tag }} steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - id: bump name: Bump version run: | $today = Get-Date $newVersion = @($today.ToString("yy"), $today.ToString("MM"), "1") git fetch --tags - $hash = git rev-list --tags --topo-order --max-count=1 + # Get the latest tag that matches our versioning schema (starts with letter v) + $hash = git rev-list --tags=v* --topo-order --max-count=1 if($hash) { $currentTag = git describe --tags $hash $parts = $currentTag.Substring(1) -split '\.' @@ -35,72 +38,67 @@ jobs: $newTag = "v" + ($newVersion -join ".") git tag $newTag + if(-not $?) { + throw "Tagging of new release version failed!" + } + + git push origin $newTag + "New version: $newTag" - echo "::set-output name=previous_tag::$currentTag" - echo "::set-output name=new_tag::$newTag" - - - name: Push version tag - uses: ad-m/github-push-action@master - with: - github_token: ${{ secrets.GITHUB_TOKEN }} - tags: true + echo "previous_tag=$currentTag" >> $env:GITHUB_OUTPUT + echo "new_tag=$newTag" >> $env:GITHUB_OUTPUT new-release: name: Create release if: "!contains(github.event.head_commit.message, '[no release]')" - runs-on: windows-2019 + runs-on: self-hosted needs: new-version steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 with: fetch-depth: 0 - - uses: azure/login@v1 - with: - creds: ${{ secrets.AZURE_CREDENTIALS }} - enable-AzPSSession: true - - name: "Build scripts" - uses: azure/powershell@v1 + - name: Build and sign release scripts + shell: pwsh env: SIGN_SCRIPT_URI: ${{ secrets.SIGN_SCRIPT_URI }} - CLIENT_ID: ${{ secrets.CLIENT_ID }} # just to ofusctate it in the output - with: - azPSVersion: "latest" - inlineScript: | - ./build.ps1 -Version ${{ needs.new-version.outputs.new_tag }} -SignScripts $true -SignScriptUri $env:SIGN_SCRIPT_URI -ClientId $env:CLIENT_ID + CLIENT_ID: ${{ secrets.CLIENT_ID }} # just to obfusctate it in the output + run: | + ./build.ps1 -Version ${{ needs.new-version.outputs.new_tag }} -SignScripts $true -SignScriptUri $env:SIGN_SCRIPT_URI -ClientId $env:CLIENT_ID + Move-Item ./Release.zip mslab_${{ needs.new-version.outputs.new_tag }}.zip - name: Create changelog id: changelog shell: powershell run: | if("${{ needs.new-version.outputs.previous_tag }}" -ne "") { - $changelog = (& { git log ${{ needs.new-version.outputs.previous_tag }}..HEAD --pretty=format:'- %s (%h)' --abbrev-commit -- Scripts }) -join '%0D%0A' + $changelog = (& { git log ${{ needs.new-version.outputs.previous_tag }}..HEAD --pretty=format:'- %s (%h)' --abbrev-commit -- Scripts Tools }) -join "`n" "Changes for ${{ needs.new-version.outputs.previous_tag }} are:" $changelog } else { $changelog = "" } - echo "::set-output name=changelog::$changelog" - - - name: Create Github Release - id: create_release - uses: actions/create-release@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - tag_name: ${{ needs.new-version.outputs.new_tag }} # ${{ github.ref }} - release_name: Release ${{ needs.new-version.outputs.new_tag }} # ${{ github.ref }} - body: | - Changes in this version: - ${{ steps.changelog.outputs.changelog }} - draft: false - prerelease: false + + $changeLogContent = @" + :package: MSLab scripts are in **[mslab_${{ needs.new-version.outputs.new_tag }}.zip](${{ github.server_url }}/${{ github.repository }}/releases/download/${{ needs.new-version.outputs.new_tag }}/mslab_${{ needs.new-version.outputs.new_tag }}.zip)** file. + + :information_source: Remaining `.ps1` files in this release would be downloaded on-demand by MSLab scripts during deployment, only if needed. - - name: Upload ZIP to Release - id: upload-scripts - uses: actions/upload-release-asset@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + "@ + + if($changelog -ne "") { + $changeLogContent += @" + ## Changes in this version + $changelog + "@ + } + + Set-Content -Value $changeLogContent -Path .\changelog.md + - name: Create new release + uses: softprops/action-gh-release@v1 with: - upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: ./Release.zip - asset_name: mslab_${{ needs.new-version.outputs.new_tag }}.zip - asset_content_type: application/zip + tag_name: ${{ needs.new-version.outputs.new_tag }} # ${{ github.ref }} + name: MSLab ${{ needs.new-version.outputs.new_tag }} # ${{ github.ref }} + generate_release_notes: false + body_path: changelog.md + files: | + mslab_${{ needs.new-version.outputs.new_tag }}.zip + Output/Tools/*.ps1 diff --git a/Scripts/0_Shared.ps1 b/Scripts/0_Shared.ps1 index a7db48bf..3a4893db 100644 --- a/Scripts/0_Shared.ps1 +++ b/Scripts/0_Shared.ps1 @@ -155,7 +155,7 @@ function Get-PcSystemType { $aiPropertyCache = @{} -function New-TelemetryEvent { +function Initialize-TelemetryEvent { param( [Parameter(Mandatory = $true)] [string]$Event, @@ -303,12 +303,12 @@ function Send-TelemetryEvent { ) process { - $telemetryEvent = New-TelemetryEvent -Event $Event -Properties $Properties -Metrics $Metrics -NickName $NickName + $telemetryEvent = Initialize-TelemetryEvent -Event $Event -Properties $Properties -Metrics $Metrics -NickName $NickName Send-TelemetryObject -Data $telemetryEvent } } -function Send-TelemetryEvents { +function Send-TelemetryEvent { param( [Parameter(Mandatory = $true)] [array]$Events diff --git a/Scripts/1_Prereq.ps1 b/Scripts/1_Prereq.ps1 index a04140b1..eada06ad 100644 --- a/Scripts/1_Prereq.ps1 +++ b/Scripts/1_Prereq.ps1 @@ -4,11 +4,11 @@ If (-not $isAdmin) { Write-Host "-- Restarting as Administrator" -ForegroundColor Cyan ; Start-Sleep -Seconds 1 if($PSVersionTable.PSEdition -eq "Core") { - Start-Process pwsh.exe "-NoProfile -ExecutionPolicy Bypass -File `"$PSCommandPath`"" -Verb RunAs + Start-Process pwsh.exe "-NoProfile -ExecutionPolicy Bypass -File `"$PSCommandPath`"" -Verb RunAs } else { - Start-Process powershell.exe "-NoProfile -ExecutionPolicy Bypass -File `"$PSCommandPath`"" -Verb RunAs + Start-Process powershell.exe "-NoProfile -ExecutionPolicy Bypass -File `"$PSCommandPath`"" -Verb RunAs } - + exit } @@ -18,10 +18,10 @@ If (-not $isAdmin) { #region Functions . $PSScriptRoot\0_Shared.ps1 # [!build-include-inline] -function Get-WindowsBuildNumber { +function Get-WindowsBuildNumber { $os = Get-CimInstance -ClassName Win32_OperatingSystem - return [int]($os.BuildNumber) -} + return [int]($os.BuildNumber) +} #endregion #region Initialization @@ -83,58 +83,82 @@ function Get-WindowsBuildNumber { #region Download Scripts #add scripts for VMM - $Filenames="1_SQL_Install","2_ADK_Install","3_SCVMM_Install" - foreach ($Filename in $filenames){ - $Path="$PSScriptRoot\Temp\ToolsVHD\SCVMM\$Filename.ps1" - If (Test-Path -Path $Path){ + $filenames = "1_SQL_Install", "2_ADK_Install", "3_SCVMM_Install" + foreach ($filename in $filenames) { + $Path = "$PSScriptRoot\Temp\ToolsVHD\SCVMM\$filename.ps1" + if (Test-Path -Path $Path) { WriteSuccess "`t $Filename is present, skipping download" - }else{ - $FileContent=$null - $FileContent = (Invoke-WebRequest -UseBasicParsing -Uri "https://raw.githubusercontent.com/Microsoft/MSLab/master/Tools/$Filename.ps1").Content - if ($FileContent){ + } else { + $FileContent = $null + + try { + # try to download tagged version first + $FileContent = (Invoke-WebRequest -UseBasicParsing -Uri "https://raw.githubusercontent.com/microsoft/MSLab/$mslabVersion/Tools/$filename.ps1").Content + } catch { + WriteInfo "Download $filename failed with $($_.Exception.Message), trying master branch now" + # if that fails, try master branch + $FileContent = (Invoke-WebRequest -UseBasicParsing -Uri "https://raw.githubusercontent.com/microsoft/MSLab/master/Tools/$filename.ps1").Content + } + + if ($FileContent) { $script = New-Item $Path -type File -Force $FileContent=$FileContent -replace "PasswordGoesHere",$LabConfig.AdminPassword #only applies to 1_SQL_Install and 3_SCVMM_Install.ps1 $FileContent=$FileContent -replace "DomainNameGoesHere",$LabConfig.DomainNetbiosName #only applies to 1_SQL_Install and 3_SCVMM_Install.ps1 - Set-Content -path $script -value $FileContent - }else{ + Set-Content -Path $script -value $FileContent + } else { WriteErrorAndExit "Unable to download $Filename." } } } # add createparentdisks, DownloadLatestCU and PatchParentDisks scripts to Parent Disks folder - $FileNames = "CreateParentDisk", "DownloadLatestCUs", "PatchParentDisks", "CreateVMFleetDisk" + $fileNames = "CreateParentDisk", "DownloadLatestCUs", "PatchParentDisks", "CreateVMFleetDisk" if($LabConfig.Linux) { - $FileNames += "CreateLinuxParentDisk" + $fileNames += "CreateLinuxParentDisk" } - foreach ($filename in $filenames) { - $Path="$PSScriptRoot\ParentDisks\$FileName.ps1" - If (Test-Path -Path $Path) { - WriteSuccess "`t $Filename is present, skipping download" + foreach ($filename in $fileNames) { + $path = "$PSScriptRoot\ParentDisks\$FileName.ps1" + If (Test-Path -Path $path) { + WriteSuccess "`t $filename is present, skipping download" } else { $FileContent = $null - $FileContent = (Invoke-WebRequest -UseBasicParsing -Uri "https://raw.githubusercontent.com/Microsoft/MSLab/master/Tools/$FileName.ps1").Content - if ($FileContent) { - $script = New-Item "$PSScriptRoot\ParentDisks\$FileName.ps1" -type File -Force - Set-Content -path $script -value $FileContent - } else { - WriteErrorAndExit "Unable to download $Filename." + + try { + # try to download release version first + Invoke-WebRequest -UseBasicParsing -Uri "https://github.com/microsoft/MSLab/releases/download/$mslabVersion/$Filename.ps1" -OutFile $path + } catch { + WriteInfo "Download $filename failed with $($_.Exception.Message), trying master branch now" + + # if that fails, try master branch + $FileContent = (Invoke-WebRequest -UseBasicParsing -Uri "https://raw.githubusercontent.com/microsoft/MSLab/master/Tools/$FileName.ps1").Content + if ($FileContent) { + $script = New-Item $path -type File -Force + Set-Content -Path $script -value $FileContent + } else { + WriteErrorAndExit "Unable to download $Filename." + } } } } # Download convert-windowsimage into Temp -WriteInfoHighlighted "Testing Convert-windowsimage presence" -If ( Test-Path -Path "$PSScriptRoot\Temp\Convert-WindowsImage.ps1" ) { - WriteSuccess "`t Convert-windowsimage.ps1 is present, skipping download" -}else{ - WriteInfo "`t Downloading Convert-WindowsImage" - try { - Invoke-WebRequest -UseBasicParsing -Uri "https://raw.githubusercontent.com/microsoft/MSLab/master/Tools/Convert-WindowsImage.ps1" -OutFile "$PSScriptRoot\Temp\Convert-WindowsImage.ps1" - } catch { - WriteError "`t Failed to download Convert-WindowsImage.ps1!" + WriteInfoHighlighted "Testing Convert-windowsimage presence" + $convertWindowsImagePath = "$PSScriptRoot\Temp\Convert-WindowsImage.ps1" + If (Test-Path -Path $convertWindowsImagePath) { + WriteSuccess "`t Convert-windowsimage.ps1 is present, skipping download" + } else { + WriteInfo "`t Downloading Convert-WindowsImage" + try { + Invoke-WebRequest -UseBasicParsing -Uri "https://github.com/microsoft/MSLab/releases/download/$mslabVersion/Convert-WindowsImage.ps1" -OutFile $convertWindowsImagePath + } catch { + try { + WriteInfo "Download Convert-windowsimage.ps1 failed with $($_.Exception.Message), trying master branch now" + Invoke-WebRequest -UseBasicParsing -Uri "https://raw.githubusercontent.com/microsoft/MSLab/master/Tools/Convert-WindowsImage.ps1" -OutFile $convertWindowsImagePath + } catch { + WriteError "`t Failed to download Convert-WindowsImage.ps1!" + } + } } -} #endregion #region some tools to download @@ -142,7 +166,7 @@ If ( Test-Path -Path "$PSScriptRoot\Temp\Convert-WindowsImage.ps1" ) { WriteInfoHighlighted "Testing diskspd presence" If ( Test-Path -Path "$PSScriptRoot\Temp\ToolsVHD\DiskSpd\diskspd.exe" ) { WriteSuccess "`t Diskspd is present, skipping download" - }else{ + }else{ WriteInfo "`t Diskspd not there - Downloading diskspd" try { <# aka.ms/diskspd changed. Commented @@ -173,13 +197,13 @@ If ( Test-Path -Path "$PSScriptRoot\Temp\Convert-WindowsImage.ps1" ) { $modules=("ActiveDirectoryDsc","6.3.0"),("xDHCPServer","3.1.1"),("DnsServerDsc","3.0.0"),("NetworkingDSC","9.0.0"),("xPSDesiredStateConfiguration","9.1.0") foreach ($module in $modules){ - WriteInfoHighlighted "Testing if modules are present" + WriteInfoHighlighted "Testing if modules are present" $modulename=$module[0] $moduleversion=$module[1] if (!(Test-Path "$PSScriptRoot\Temp\DSC\$modulename\$Moduleversion")){ WriteInfo "`t Module $module not found... Downloading" - #Install NuGET package provider - if ((Get-PackageProvider -Name NuGet) -eq $null){ + #Install NuGET package provider + if ((Get-PackageProvider -Name NuGet) -eq $null){ Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Confirm:$false -Force } Find-DscResource -moduleName $modulename -RequiredVersion $moduleversion | Save-Module -Path "$PSScriptRoot\Temp\DSC" @@ -214,24 +238,24 @@ if($LabConfig.Linux -eq $true) { WriteInfo "`t Test Packer availability" # Packer - if (Get-Command "packer.exe" -ErrorAction SilentlyContinue) - { + if (Get-Command "packer.exe" -ErrorAction SilentlyContinue) + { WriteSuccess "`t Packer is in PATH." } else { WriteInfo "`t`t Downloading latest Packer binary" WriteInfo "`t`t Creating packer directory" - $linuxToolsDirPath = "$PSScriptRoot\LAB\bin" + $linuxToolsDirPath = "$PSScriptRoot\LAB\bin" New-Item $linuxToolsDirPath -ItemType Directory -Force | Out-Null - + if(-not (Test-Path (Join-Path $linuxToolsDirPath "packer.exe"))) { $packerReleaseInfo = Invoke-RestMethod -Uri "https://checkpoint-api.hashicorp.com/v1/check/packer" - $downloadUrl = "https://releases.hashicorp.com/packer/$($packerReleaseInfo.current_version)/packer_$($packerReleaseInfo.current_version)_windows_amd64.zip" - Start-BitsTransfer -Source $downloadUrl -Destination (Join-Path $linuxToolsDirPath "packer.zip") + $downloadUrl = "https://releases.hashicorp.com/packer/$($packerReleaseInfo.current_version)/packer_$($packerReleaseInfo.current_version)_windows_amd64.zip" + Start-BitsTransfer -Source $downloadUrl -Destination (Join-Path $linuxToolsDirPath "packer.zip") Expand-Archive -Path (Join-Path $linuxToolsDirPath "packer.zip") -DestinationPath $linuxToolsDirPath -Force - Remove-Item -Path (Join-Path $linuxToolsDirPath "packer.zip") + Remove-Item -Path (Join-Path $linuxToolsDirPath "packer.zip") } - + WriteInfo "`t`t Creating Packer firewall rule" $id = $PSScriptRoot -replace '[^a-zA-Z0-9]' $fwRule = Get-NetFirewallRule -Name "mslab-packer-$id" -ErrorAction SilentlyContinue @@ -244,7 +268,7 @@ if($LabConfig.Linux -eq $true) { WriteInfo "`t`t Downloading Packer templates" $packerTemplatesDirectory = "$PSScriptRoot\ParentDisks\PackerTemplates\" if (-not (Test-Path $packerTemplatesDirectory)) { - New-Item -Type Directory -Path $packerTemplatesDirectory + New-Item -Type Directory -Path $packerTemplatesDirectory } $templatesBase = "https://github.com/microsoft/mslab-templates/releases/latest/download/" @@ -285,11 +309,11 @@ if($LabConfig.Linux -eq $true) { if($comparison) { WriteError "`t SSH Keypair $($LabConfig.SshKeyPath) does not match." } - } - else + } + else { WriteInfo "`t`t Generating new SSH key pair" - $sshKeyDir = "$PSScriptRoot\LAB\.ssh" + $sshKeyDir = "$PSScriptRoot\LAB\.ssh" $key = "$sshKeyDir\lab_rsa" New-Item -ItemType Directory $sshKeyDir -ErrorAction SilentlyContinue | Out-Null ssh-keygen.exe -t rsa -b 4096 -C "$($LabConfig.DomainAdminName)" -f $key -q -N '""' @@ -302,11 +326,11 @@ if((Get-TelemetryLevel) -in $TelemetryEnabledLevels) { $metrics = @{ 'script.duration' = ((Get-Date) - $StartDateTime).TotalSeconds } - + Send-TelemetryEvent -Event "Prereq.End" -Metrics $metrics -NickName $LabConfig.TelemetryNickName | Out-Null } -# finishing +# finishing WriteInfo "Script finished at $(Get-date) and took $(((get-date) - $StartDateTime).TotalMinutes) Minutes" Stop-Transcript @@ -314,4 +338,4 @@ Stop-Transcript If (!$LabConfig.AutoClosePSWindows) { WriteSuccess "Press enter to continue..." Read-Host | Out-Null -} \ No newline at end of file +} diff --git a/Scripts/2_CreateParentDisks.ps1 b/Scripts/2_CreateParentDisks.ps1 index a1a55deb..50436dc5 100644 --- a/Scripts/2_CreateParentDisks.ps1 +++ b/Scripts/2_CreateParentDisks.ps1 @@ -1,4 +1,4 @@ -# Verify Running as Admin +# Verify Running as Admin $isAdmin = ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator") If (-not $isAdmin) { Write-Host "-- Restarting as Administrator" -ForegroundColor Cyan ; Start-Sleep -Seconds 1 diff --git a/Scripts/3_Deploy.ps1 b/Scripts/3_Deploy.ps1 index 0abf18f8..e36aa092 100644 --- a/Scripts/3_Deploy.ps1 +++ b/Scripts/3_Deploy.ps1 @@ -4,9 +4,9 @@ If (-not $isAdmin) { Write-Host "-- Restarting as Administrator" -ForegroundColor Cyan ; Start-Sleep -Seconds 1 if($PSVersionTable.PSEdition -eq "Core") { - Start-Process pwsh.exe "-NoProfile -ExecutionPolicy Bypass -File `"$PSCommandPath`"" -Verb RunAs + Start-Process pwsh.exe "-NoProfile -ExecutionPolicy Bypass -File `"$PSCommandPath`"" -Verb RunAs } else { - Start-Process powershell.exe "-NoProfile -ExecutionPolicy Bypass -File `"$PSCommandPath`"" -Verb RunAs + Start-Process powershell.exe "-NoProfile -ExecutionPolicy Bypass -File `"$PSCommandPath`"" -Verb RunAs } exit @@ -58,8 +58,8 @@ If (-not $isAdmin) { true - true - true + true + true $TimeZone @@ -74,7 +74,7 @@ If (-not $isAdmin) { $RunSynchronous - + @@ -82,11 +82,11 @@ If (-not $isAdmin) { Set-Content $unattendFile $fileContent #return the file object - $unattendFile + $unattendFile } Function CreateUnattendFileNoDjoin{ - #Create Unattend(without domain join) + #Create Unattend(without domain join) param ( [parameter(Mandatory=$true)] [string] @@ -136,8 +136,8 @@ If (-not $isAdmin) { true - true - true + true + true $TimeZone @@ -148,7 +148,7 @@ If (-not $isAdmin) { Set-Content $unattendFile $fileContent #return the file object - $unattendFile + $unattendFile } Function CreateUnattendFileWin2012{ @@ -210,8 +210,8 @@ If (-not $isAdmin) { true - true - true + true + true $TimeZone @@ -222,7 +222,7 @@ If (-not $isAdmin) { Set-Content $unattendFile $fileContent #return the file object - $unattendFile + $unattendFile } Function AdditionalLocalAccountXML{ @@ -251,10 +251,10 @@ If (-not $isAdmin) { "@ } - function Get-WindowsBuildNumber { + function Get-WindowsBuildNumber { $os = Get-CimInstance -ClassName Win32_OperatingSystem - return [int]($os.BuildNumber) - } + return [int]($os.BuildNumber) + } Function Set-VMNetworkConfiguration { #source:http://www.ravichaganti.com/blog/?p=2766 with some changes @@ -297,7 +297,7 @@ If (-not $isAdmin) { [Switch]$Dhcp ) - $VM = Get-CimInstance -Namespace "root\virtualization\v2" -ClassName "Msvm_ComputerSystem" | Where-Object ElementName -eq $NetworkAdapter.VMName + $VM = Get-CimInstance -Namespace "root\virtualization\v2" -ClassName "Msvm_ComputerSystem" | Where-Object ElementName -eq $NetworkAdapter.VMName $VMSettings = Get-CimAssociatedInstance -InputObject $vm -ResultClassName "Msvm_VirtualSystemSettingData" | Where-Object VirtualSystemType -EQ "Microsoft:Hyper-V:System:Realized" $VMNetAdapters = Get-CimAssociatedInstance -InputObject $VMSettings -ResultClassName "Msvm_SyntheticEthernetPortSettingData" @@ -339,7 +339,7 @@ If (-not $isAdmin) { } function WrapProcess{ - #Using this function you can run legacy program and search in output string + #Using this function you can run legacy program and search in output string #Example: WrapProcess -filename fltmc.exe -arguments "attach svhdxflt e:" -outputstring "Success" [CmdletBinding()] [Alias()] @@ -382,11 +382,11 @@ If (-not $isAdmin) { # test if process is still running if(!$process.HasExited){ do{ - Start-Sleep 1 + Start-Sleep 1 }until ($process.HasExited -eq $true) } - # get output + # get output $out = $process.StandardOutput.ReadToEnd() if ($out.Contains($outputstring)) { @@ -408,7 +408,7 @@ If (-not $isAdmin) { WriteInfoHighlighted "Creating VM $($VMConfig.VMName)" WriteInfo "`t Looking for Parent Disk" $serverparent = Get-ChildItem "$PSScriptRoot\ParentDisks\" -Recurse | Where-Object Name -eq $VMConfig.ParentVHD - + if ($serverparent -eq $null) { WriteErrorAndExit "Server parent disk $($VMConfig.ParentVHD) not found." }else{ @@ -423,7 +423,7 @@ If (-not $isAdmin) { } WriteInfo "`t Creating OS VHD" New-VHD -ParentPath $serverparent.fullname -Path $vhdpath - + $VMTemp = New-VM -Path "$LabFolder\VMs" -Name $VMname -Generation 2 -MemoryStartupBytes $VMConfig.MemoryStartupBytes -SwitchName $SwitchName -VHDPath $vhdPath #Set dynamic memory @@ -439,16 +439,16 @@ If (-not $isAdmin) { $VMTemp | Set-VM -AutomaticCheckpointsEnabled $False } $VMTemp | Set-VMFirmware -EnableSecureBoot Off - + # only Debian Buster supports Secure Boot #$vm | Set-VMFirmware -EnableSecureBoot On -SecureBootTemplateId "272e7447-90a4-4563-a4b9-8e4ab00526ce" # -SecureBootTemplate MicrosoftUEFICertificateAuthority - + Start-VM $VMTemp # wait for the IP address Write-Host "`t Waiting for network connectivity to the VM..." -NoNewLine $count = 0 - do { + do { $ip = $VMTemp | Get-VMNetworkAdapter | Select-Object -ExpandProperty IPAddresses Start-Sleep -Seconds 1 @@ -485,12 +485,12 @@ If (-not $isAdmin) { if(-not $VMConfig.LinuxDomainJoin -or $VMConfig.LinuxDomainJoin.ToLower() -eq "sssd") { WriteInfo "`t Creating AD Computer object..." Invoke-Command -VMGuid $DC.id -Credential $cred -ArgumentList $VMConfig.VMName,$path,$Labconfig -ScriptBlock { - param($Name,$path,$Labconfig); + param($Name,$path,$Labconfig); New-ADComputer -Name $Name -Path "OU=$($Labconfig.DefaultOUName),$($Labconfig.DN)" $password = ConvertTo-SecureString -String $Name -AsPlainText -Force Get-ADComputer -Identity $Name | Set-ADAccountPassword -NewPassword:$password -Reset:$true - } + } WriteInfo "`t Joining to AD..." $upn = ("$(($LabConfig.DomainAdminName).ToLower())@$($LabConfig.DomainName)") @@ -503,12 +503,12 @@ If (-not $isAdmin) { # Wait for vm to shut down $count = 0 - do { + do { $vm = $VMTemp | Get-VM Start-Sleep -Seconds 1 $count += 1 } while ($vm.State -ne "Off" -and $count -le 60) - + if($vm.State -ne "Off") { $VMTemp | Stop-VM } @@ -530,7 +530,7 @@ If (-not $isAdmin) { WriteInfoHighlighted "Creating VM $($VMConfig.VMName)" WriteInfo "`t Looking for Parent Disk" $serverparent=Get-ChildItem "$PSScriptRoot\ParentDisks\" -Recurse | Where-Object Name -eq $VMConfig.ParentVHD - + if ($serverparent -eq $null){ WriteErrorAndExit "Server parent disk $($VMConfig.ParentVHD) not found." }else{ @@ -554,7 +554,7 @@ If (-not $isAdmin) { if ($VMConfig.Generation -eq 1){ $VMTemp=New-VM -Name $VMname -VHDPath $vhdpath -MemoryStartupBytes $VMConfig.MemoryStartupBytes -path "$LabFolder\VMs" -SwitchName $SwitchName -Generation 1 }else{ - $VMTemp=New-VM -Name $VMname -VHDPath $vhdpath -MemoryStartupBytes $VMConfig.MemoryStartupBytes -path "$LabFolder\VMs" -SwitchName $SwitchName -Generation 2 + $VMTemp=New-VM -Name $VMname -VHDPath $vhdpath -MemoryStartupBytes $VMConfig.MemoryStartupBytes -path "$LabFolder\VMs" -SwitchName $SwitchName -Generation 2 } $VMTemp | Set-VMMemory -DynamicMemoryEnabled $true $VMTemp | Get-VMNetworkAdapter | Rename-VMNetworkAdapter -NewName Management1 @@ -805,7 +805,7 @@ If (-not $isAdmin) { if ($unattendFile){ WriteInfo "`t Adding unattend to VHD" Mount-WindowsImage -Path $mountdir -ImagePath $VHDPath -Index 1 - Use-WindowsUnattend -Path $mountdir -UnattendPath $unattendFile + Use-WindowsUnattend -Path $mountdir -UnattendPath $unattendFile #&"$PSScriptRoot\Tools\dism\dism" /mount-image /imagefile:$vhdpath /index:1 /MountDir:$mountdir #&"$PSScriptRoot\Tools\dism\dism" /image:$mountdir /Apply-Unattend:$unattendfile New-item -type directory "$mountdir\Windows\Panther" -ErrorAction Ignore @@ -863,7 +863,6 @@ If (-not $isAdmin) { } Send-TelemetryEvent -Event "Deploy.Start" -NickName $LabConfig.TelemetryNickName | Out-Null } - #endregion #region Set variables @@ -904,7 +903,7 @@ If (-not $isAdmin) { WriteInfo "`t Prefix used in lab is $($labconfig.prefix)" $SwitchName=($labconfig.prefix+$LabConfig.SwitchName) - WriteInfo "`t Switchname is $SwitchName" + WriteInfo "`t Switchname is $SwitchName" WriteInfo "`t Workdir is $PSScriptRoot" @@ -922,7 +921,7 @@ If (-not $isAdmin) { Get-CimInstance -ClassName "win32_processor" | ForEach-Object { $global:NumberOfLogicalProcessors += $_.NumberOfLogicalProcessors } #Calculate highest VLAN (for additional subnets) - [int]$HighestVLAN=$LabConfig.AllowedVLANs -split "," -split "-" | Select -Last 1 + [int]$HighestVLAN=$LabConfig.AllowedVLANs -split "," -split "-" | Select-Object -Last 1 #Grab defined Management Subnet IDs and ignore 0 $ManagementSubnetIDs=$labconfig.vms.ManagementSubnetID + $LabConfig.ManagementSubnetIDs | Select-Object -Unique | Sort-Object | Where-Object {$_ -ne 0} @@ -991,7 +990,7 @@ If (-not $isAdmin) { WriteInfo "`t Installing Failover Clustering Feature" $FC=Install-WindowsFeature Failover-Clustering If ($FC.Success -eq $True){ - WriteSuccess "`t`t Failover Clustering Feature installed with exit code: $($FC.ExitCode)" + WriteSuccess "`t`t Failover Clustering Feature installed with exit code: $($FC.ExitCode)" }else{ WriteError "`t`t Failover Clustering Feature was not installed with exit code: $($FC.ExitCode)" } @@ -1008,10 +1007,10 @@ If (-not $isAdmin) { } } - WriteInfoHighlighted "Adding svhdxflt to registry for autostart" + WriteInfoHighlighted "Adding svhdxflt to registry for autostart" if (!(Test-Path HKLM:\SYSTEM\CurrentControlSet\Services\svhdxflt\Parameters)){ New-Item HKLM:\SYSTEM\CurrentControlSet\Services\svhdxflt\Parameters - } + } New-ItemProperty -Path HKLM:\SYSTEM\CurrentControlSet\Services\svhdxflt\Parameters -Name AutoAttachOnNonCSVVolumes -PropertyType DWORD -Value 1 -force } @@ -1043,7 +1042,7 @@ If (-not $isAdmin) { WriteInfoHighlighted "Checking if volume filesystem is NTFS or ReFS" $driveletter=$PSScriptRoot -split ":" | Select-Object -First 1 if ($PSScriptRoot -like "c:\ClusterStorage*"){ - WriteSuccess "`t Volume Cluster Shared Volume. Mountdir will be $env:Temp\MSLabMountdir" + WriteSuccess "`t Volume Cluster Shared Volume. Mountdir will be $env:Temp\MSLabMountdir" $mountdir="$env:Temp\MSLabMountDir" $VolumeFileSystem="CSVFS" }else{ @@ -1068,7 +1067,7 @@ If (-not $isAdmin) { WriteInfoHighlighted "Creating Switch" WriteInfo "`t Checking if $SwitchName already exists..." - if (-not (Get-VMSwitch -Name $SwitchName -ErrorAction Ignore)){ + if (-not (Get-VMSwitch -Name $SwitchName -ErrorAction Ignore)){ WriteInfo "`t Creating $SwitchName..." if ($LabConfig.SwitchNICs){ #test if NICs are not already connected to another switch @@ -1144,7 +1143,7 @@ If (-not $isAdmin) { if ($TempNetAdapters.name.count -gt 1){ WriteInfo "`t More than 1 NIC detected" WriteInfoHighlighted "`t Please select NetAdapter you want to use for vSwitch" - $tempNetAdapter=get-netadapter | Where-Object Name -NotLike vEthernet* | Where-Object status -eq up | Out-GridView -OutputMode Single -Title "Please select adapter you want to use for External vSwitch" + $tempNetAdapter=get-netadapter | Where-Object Name -NotLike vEthernet* | Where-Object status -eq up | Out-GridView -OutputMode Single -Title "Please select adapter you want to use for External vSwitch" if (!$tempNetAdapter){ WriteErrorAndExit "You did not select any net adapter. Exitting." } @@ -1204,8 +1203,8 @@ If (-not $isAdmin) { $toolsVHD=New-VHD -Path "$PSScriptRoot\ParentDisks\tools.vhdx" -SizeBytes 30GB -Dynamic #mount and format VHD $VHDMount = Mount-VHD $toolsVHD.Path -Passthru - $vhddisk = $VHDMount| get-disk - $vhddisk | Initialize-Disk -PartitionStyle GPT -PassThru | New-Partition -UseMaximumSize -AssignDriveLetter |Format-Volume -FileSystem NTFS -AllocationUnitSize 8kb -NewFileSystemLabel ToolsDisk + $vhddisk = $VHDMount| get-disk + $vhddisk | Initialize-Disk -PartitionStyle GPT -PassThru | New-Partition -UseMaximumSize -AssignDriveLetter |Format-Volume -FileSystem NTFS -AllocationUnitSize 8kb -NewFileSystemLabel ToolsDisk #dismount VHD Dismount-VHD $vhddisk.Number }else{ @@ -1272,7 +1271,7 @@ If (-not $isAdmin) { $DC | Add-VMNetworkAdapter -SwitchName $SwitchName -Name $AdditionalNetworkConfig.NetName WriteInfo "`t`t Adding Adapter $($AdditionalNetworkConfig.NetName) with IP $($AdditionalNetworkConfig.NetAddress)$global:IP" $DC | Get-VMNetworkAdapter -Name $AdditionalNetworkConfig.NetName | Set-VMNetworkConfiguration -IPAddress "$($AdditionalNetworkConfig.NetAddress)$global:IP" -Subnet $AdditionalNetworkConfig.Subnet - if($AdditionalNetworkConfig.NetVLAN -ne 0){ + if($AdditionalNetworkConfig.NetVLAN -ne 0){ $DC | Get-VMNetworkAdapter -Name $AdditionalNetworkConfig.NetName | Set-VMNetworkAdapterVlan -VlanId $AdditionalNetworkConfig.NetVLAN -Access } } @@ -1388,7 +1387,7 @@ If (-not $isAdmin) { $IP="192.168.0.$startIP" WriteInfo "`t Configure static IP $IP on Internet NIC" Invoke-Command -VMGuid $DC.id -Credential $cred -ScriptBlock { - $NetAdapterName=(Get-NetAdapterAdvancedProperty | where displayvalue -eq Internet).Name + $NetAdapterName=(Get-NetAdapterAdvancedProperty | Where-Object displayvalue -eq Internet).Name New-NetIPAddress -InterfaceAlias $NetAdapterName -IPAddress $using:IP -PrefixLength 24 -DefaultGateway "192.168.0.1" } } @@ -1513,7 +1512,7 @@ If (-not $isAdmin) { WriteInfoHighlighted "Creating DSC config to configure DC as pull server" [DSCLocalConfigurationManager()] - Configuration PullClientConfig + Configuration PullClientConfig { param ( @@ -1536,7 +1535,7 @@ If (-not $isAdmin) { ActionAfterReboot = 'ContinueConfiguration' } - ConfigurationRepositoryWeb PullServerWeb { + ConfigurationRepositoryWeb PullServerWeb { ServerURL = "http://dc.$($DomainName):8080/PSDSCPullServer.svc" AllowUnsecureConnection = $true RegistrationKey = '14fc8e72-5036-4e79-9f89-5382160053aa' @@ -1573,7 +1572,7 @@ If (-not $isAdmin) { if(-not $VMConfig.MemoryStartupBytes) { $VMConfig.MemoryStartupBytes = 512MB } - + #create VM with Shared configuration if ($VMConfig.configuration -eq 'Shared'){ #create disks (if not already created) @@ -1581,11 +1580,11 @@ If (-not $isAdmin) { if (!(Test-Path -Path "$LABfolder\VMs\*$VMSet*.VHDS")){ $SharedSSDs=$null $SharedHDDs=$null - If (($VMConfig.SSDNumber -ge 1) -and ($VMConfig.SSDNumber -ne $null)){ + If (($VMConfig.SSDNumber -ge 1) -and ($VMConfig.SSDNumber -ne $null)){ $SharedSSDs= 1..$VMConfig.ssdnumber | ForEach-Object {New-vhd -Path "$LABfolder\VMs\SharedSSD-$VMSet-$_.VHDS" -Dynamic -Size $VMConfig.SSDSize} $SharedSSDs | ForEach-Object {WriteInfo "`t Disk SSD $($_.path) size $($_.size /1GB)GB created"} } - If (($VMConfig.HDDNumber -ge 1) -and ($VMConfig.HDDNumber -ne $null)){ + If (($VMConfig.HDDNumber -ge 1) -and ($VMConfig.HDDNumber -ne $null)){ $SharedHDDs= 1..$VMConfig.hddnumber | ForEach-Object {New-VHD -Path "$LABfolder\VMs\SharedHDD-$VMSet-$_.VHDS" -Dynamic -Size $VMConfig.HDDSize} $SharedHDDs | ForEach-Object {WriteInfo "`t Disk HDD $($_.path) size $($_.size /1GB)GB created"} } @@ -1630,7 +1629,7 @@ If (-not $isAdmin) { #Add disks #add "SSDs" - If (($VMConfig.SSDNumber -ge 1) -and ($VMConfig.SSDNumber -ne $null)){ + If (($VMConfig.SSDNumber -ge 1) -and ($VMConfig.SSDNumber -ne $null)){ $SSDs= 1..$VMConfig.SSDNumber | ForEach-Object { New-vhd -Path "$LabFolder\VMs\$VMname\Virtual Hard Disks\SSD-$_.VHDX" -Dynamic -Size $VMConfig.SSDSize} WriteInfoHighlighted "`t Adding Virtual SSD Disks" $SSDs | ForEach-Object { @@ -1651,7 +1650,7 @@ If (-not $isAdmin) { } } - #create VM with Replica configuration + #create VM with Replica configuration if ($VMConfig.configuration -eq 'Replica'){ #create shared drives if not already created $VMSet=$VMConfig.VMSet @@ -1668,7 +1667,7 @@ If (-not $isAdmin) { $createdVm = BuildVM -VMConfig $VMConfig -LabConfig $labconfig -LabFolder $LABfolder #Add disks - $VMname=$Labconfig.Prefix+$VMConfig.VMName + $VMname=$Labconfig.Prefix+$VMConfig.VMName WriteInfoHighlighted "`t Attaching Shared Disks..." #Add HDD $ReplicaHdd | ForEach-Object { @@ -1692,7 +1691,7 @@ If (-not $isAdmin) { } if((Test-Path -Path $createdVm.OSDiskPath) -and $VMConfig.configuration -ne "Linux") { $osInfo = Get-WindowsImage -ImagePath $createdVm.OSDiskPath -Index 1 - + $properties.'vm.os.installationType' = $osInfo.InstallationType $properties.'vm.os.editionId' = $osInfo.EditionId $properties.'vm.os.version' = $osInfo.Version @@ -1701,10 +1700,10 @@ If (-not $isAdmin) { $metrics = @{ 'vm.deploymentDuration' = ((Get-Date) - $vmProvisioningStartTime).TotalSeconds } - $vmInfo = New-TelemetryEvent -Event "Deploy.VM" -Properties $properties -Metrics $metrics -NickName $LabConfig.TelemetryNickName + $vmInfo = Initialize-TelemetryEvent -Event "Deploy.VM" -Properties $properties -Metrics $metrics -NickName $LabConfig.TelemetryNickName $vmDeploymentEvents += $vmInfo } - + $provisionedVMs += $createdVm.VM } } @@ -1712,7 +1711,7 @@ If (-not $isAdmin) { #endregion #region Finishing - WriteInfoHighlighted "Finishing..." + WriteInfoHighlighted "Finishing..." #a bit cleanup Remove-Item -Path "$PSScriptRoot\temp" -Force -Recurse @@ -1721,7 +1720,7 @@ If (-not $isAdmin) { WriteInfo "`t Setting MacSpoofing On and AllowTeaming On" Set-VMNetworkAdapter -VMName "$($labconfig.Prefix)*" -MacAddressSpoofing On -AllowTeaming On - #list VMs + #list VMs $AllVMs = Get-VM | Where-Object name -like "$($labconfig.Prefix)*" $AllVMs | ForEach-Object { WriteSuccess "Machine $($_.VMName) provisioned" } @@ -1732,7 +1731,7 @@ If (-not $isAdmin) { #Enable Guest services on all VMs if integration component if configured if ($labconfig.EnableGuestServiceInterface){ WriteInfo "`t Enabling Guest Service Interface" - $vms = Get-VM -VMName "$($labconfig.Prefix)*" | Where-Object {$_.state -eq "Running" -or $_.state -eq "Off"} + $vms = Get-VM -VMName "$($labconfig.Prefix)*" | Where-Object {$_.state -eq "Running" -or $_.state -eq "Off"} foreach ($vm in $vms) { $guestServiceId = 'Microsoft:{0}\6C09BB55-D683-4DA0-8931-C9BF705F6480' -f $vm.Id $guestService = $vm | Get-VMIntegrationService | Where-Object -FilterScript {$_.Id -eq $guestServiceId} @@ -1764,7 +1763,7 @@ If (-not $isAdmin) { <# 0 #> New-Object System.Management.Automation.Host.ChoiceDescription "&No", "No VM will be started." <# 1 #> New-Object System.Management.Automation.Host.ChoiceDescription "&All", "All VMs in the lab will be started." ) - + if($provisionedVMs.Count -gt 0) { <# 2 #> $options += New-Object System.Management.Automation.Host.ChoiceDescription "&Deployed only", "Only newly deployed VMs will be started." } @@ -1780,10 +1779,10 @@ If (-not $isAdmin) { $toStart = $provisionedVMs } } - + if(($toStart | Measure-Object).Count -gt 0) { WriteInfoHighlighted "Starting VMs" - $toStart | ForEach-Object { + $toStart | ForEach-Object { WriteInfo "`t $($_.Name)" Start-VM -VM $_ -WarningAction SilentlyContinue } @@ -1802,10 +1801,10 @@ If (-not $isAdmin) { 'lab.isncrementalDeployment' = $LABExists 'lab.autostartmode' = $startVMs } - $telemetryEvent = New-TelemetryEvent -Event "Deploy.End" -Metrics $metrics -Properties $properties -NickName $LabConfig.TelemetryNickName + $telemetryEvent = Initialize-TelemetryEvent -Event "Deploy.End" -Metrics $metrics -Properties $properties -NickName $LabConfig.TelemetryNickName $vmDeploymentEvents += $telemetryEvent - Send-TelemetryEvents -Events $vmDeploymentEvents | Out-Null + Send-TelemetryEvent -Events $vmDeploymentEvents | Out-Null } #write how much it took to deploy diff --git a/Scripts/Cleanup.ps1 b/Scripts/Cleanup.ps1 index ea5b5c08..e318a080 100644 --- a/Scripts/Cleanup.ps1 +++ b/Scripts/Cleanup.ps1 @@ -4,9 +4,9 @@ If (-not $isAdmin) { Write-Host "-- Restarting as Administrator" -ForegroundColor Cyan ; Start-Sleep -Seconds 1 if($PSVersionTable.PSEdition -eq "Core") { - Start-Process pwsh.exe "-NoProfile -ExecutionPolicy Bypass -File `"$PSCommandPath`"" -Verb RunAs + Start-Process pwsh.exe "-NoProfile -ExecutionPolicy Bypass -File `"$PSCommandPath`"" -Verb RunAs } else { - Start-Process powershell.exe "-NoProfile -ExecutionPolicy Bypass -File `"$PSCommandPath`"" -Verb RunAs + Start-Process powershell.exe "-NoProfile -ExecutionPolicy Bypass -File `"$PSCommandPath`"" -Verb RunAs } exit @@ -62,7 +62,7 @@ If (-not $isAdmin) { #just one more space WriteInfo "" - # This is only needed if you kill deployment script in middle when it mounts VHD into mountdir. + # This is only needed if you kill deployment script in middle when it mounts VHD into mountdir. if ((Get-ChildItem -Path "$PSScriptRoot\temp\mountdir" -ErrorAction SilentlyContinue)){ Dismount-WindowsImage -Path "$PSScriptRoot\temp\mountdir" -Discard -ErrorAction SilentlyContinue } @@ -105,18 +105,18 @@ If (-not $isAdmin) { WriteInfo "`t Removing vSwitch $($extvSwitch.Name)" $extvSwitch | Remove-VMSwitch -Force } - #Cleanup folders + #Cleanup folders if ((test-path "$PSScriptRoot\LAB\VMs") -or (test-path "$PSScriptRoot\temp") ){ WriteInfoHighlighted "Cleaning folders" "$PSScriptRoot\LAB\VMs","$PSScriptRoot\temp" | ForEach-Object { if ((test-path -Path $_)){ WriteInfo "`t Removing folder $_" remove-item $_ -Confirm:$False -Recurse - } + } } } - #Unzipping configuration files as VM was removed few lines ago-and it deletes vm configuration... + #Unzipping configuration files as VM was removed few lines ago-and it deletes vm configuration... $zipfile = "$PSScriptRoot\LAB\DC\Virtual Machines.zip" $zipoutput = "$PSScriptRoot\LAB\DC\" Microsoft.PowerShell.Archive\Expand-Archive -Path $zipfile -DestinationPath $zipoutput -Force @@ -131,7 +131,7 @@ If (-not $isAdmin) { Send-TelemetryEvent -Event "Cleanup" -Metrics $metrics -NickName $LabConfig.TelemetryNickName | Out-Null } - #finishing + #finishing WriteSuccess "Job Done! Press enter to continue ..." $exit=Read-Host }else { diff --git a/Scripts/LabConfig.ps1 b/Scripts/LabConfig.ps1 index cde81bfc..dfa3efaa 100644 --- a/Scripts/LabConfig.ps1 +++ b/Scripts/LabConfig.ps1 @@ -36,7 +36,7 @@ $LabConfig=@{ DomainAdminName='LabAdmin'; AdminPassword='LS1setup!'; Prefix = 'M ServerMSUsFolder=""; # (Optional) If configured, script will inject all MSU files found into server OS EnableGuestServiceInterface=$false; # (Optional) If True, then Guest Services integration component will be enabled on all VMs. DCVMProcessorCount=2; # (Optional) 2 is default. If specified more/less, processorcount will be modified. - DHCPscope="10.0.0.0"; # (Optional) 10.0.0.0 is configured if nothing is specified. Scope has to end with .0 (like 10.10.10.0). It's always /24 + DHCPscope="10.0.0.0"; # (Optional) 10.0.0.0 is configured if nothing is specified. Scope has to end with .0 (like 10.10.10.0). It's always /24 DCVMVersion="9.0"; # (Optional) Latest is used if nothing is specified. Make sure you use values like "8.0","8.3","9.0" TelemetryLevel=""; # (Optional) If configured, script will stop prompting you for telemetry. Values are "None","Basic","Full" TelemetryNickname=""; # (Optional) If configured, telemetry will be sent with NickName to correlate data to specified NickName. So when leaderboards will be published, MSLab users will be able to see their own stats @@ -53,7 +53,7 @@ $LabConfig=@{ DomainAdminName='LabAdmin'; AdminPassword='LS1setup!'; Prefix = 'M } # Specifying LabVMs - 1..4 | ForEach-Object { + 1..4 | ForEach-Object { $VMNames="S2D"; # Here you can bulk edit name of 4 VMs created. In this case will be s2d1,s2d2,s2d3,s2d4 created $LABConfig.VMs += @{ VMName = "$VMNames$_" ; @@ -68,7 +68,7 @@ $LabConfig=@{ DomainAdminName='LabAdmin'; AdminPassword='LS1setup!'; Prefix = 'M } #optional: (only if AdditionalNetworks are configured in $LabConfig.VMs) this is just an example. In this configuration its not needed. - $LABConfig.AdditionalNetworksConfig += @{ + $LABConfig.AdditionalNetworksConfig += @{ NetName = 'Storage1'; # Network Name NetAddress='172.16.1.'; # Network Addresses prefix. (starts with 1), therefore first VM with Additional network config will have IP 172.16.1.1 NetVLAN='1'; # VLAN tagging @@ -109,7 +109,7 @@ $LabConfig=@{ DomainAdminName='LabAdmin'; AdminPassword='LS1setup!'; Prefix = 'M 'SQL' - installs just SQL 'Prereqs' - installs ADK and SQL 'No' - No, or anything else, nothing is installed. - + *requires install files in toolsVHD\SCVMM\, or it will fail. You can download all tools here: SQL: http://www.microsoft.com/en-us/evalcenter/evaluate-sql-server-2016 SCVMM: http://www.microsoft.com/en-us/evalcenter/evaluate-system-center-technical-preview @@ -145,7 +145,7 @@ $LabConfig=@{ DomainAdminName='LabAdmin'; AdminPassword='LS1setup!'; Prefix = 'M It will add vNIC to DC and configure NAT with some Open DNS servers in DNS forwarder UseHostDnsAsForwarder (Optional) - If $true, local DNS servers will be used as DNS forwarders in DC when Internet is enabled. + If $true, local DNS servers will be used as DNS forwarders in DC when Internet is enabled. By default local host's DNS servers will be used as forwarders. CustomDnsForwarders (Optional) @@ -153,7 +153,7 @@ $LabConfig=@{ DomainAdminName='LabAdmin'; AdminPassword='LS1setup!'; Prefix = 'M If not defined at all, commonly known DNS servers will be used as a fallback: - Google DNS: 8.8.8.8 - Cloudfare: 1.1.1.1 - + DHCPscope (Optional) If configured, a custom DHCP scope will be used. Will always use a '/24'. Specify input like '10.1.0.0' or '192.168.0.0' @@ -196,11 +196,11 @@ $LabConfig=@{ DomainAdminName='LabAdmin'; AdminPassword='LS1setup!'; Prefix = 'M ManagementSubnetIDs Example: ManagementSubnetIDs=0..3 If configured, it will add another management subnet. For example if configured 0..3, it will add 3 more subnets 10.0.1.0/24 to 10.0.3.0/24 on VLANs that 11,12, and 13. (Because allowed VLANs are 1-10) - + Linux (optional) Example: Linux=$true If set to $true, additional prerequisities (SSH Client, SSH Key, Packer, Packer templates) required for building Linux images will be downloaded and configured. - + LinuxAdminName (optional) Example: LinuxAdminName="linuxadmin" If set, local user account with that name will be created in Linux image. If not, DomainAdminName will be used as a local account. @@ -227,7 +227,7 @@ $LabConfig=@{ DomainAdminName='LabAdmin'; AdminPassword='LS1setup!'; Prefix = 'M $LABConfig.VMs += @{ VMName = 'Management' ; Configuration = 'Simple' ; ParentVHD = 'Win10_G2.vhdx' ; MemoryStartupBytes= 1GB ; AddToolsVHD=$True } Multiple: 1..2 | ForEach-Object { $VMNames="Replica" ; $LABConfig.VMs += @{ VMName = "$VMNames$_" ; Configuration = 'Replica' ; ParentVHD = 'Win2016NanoHV_G2.vhdx' ; ReplicaHDDSize = 20GB ; ReplicaLogSize = 10GB ; MemoryStartupBytes= 2GB ; VMSet= 'ReplicaSet1' ; AdditionalNetworks = $True} } - + VMName (Mandatory) Can be whatever. This name will be used as name to djoin VM. @@ -247,7 +247,7 @@ $LabConfig=@{ DomainAdminName='LabAdmin'; AdminPassword='LS1setup!'; Prefix = 'M 'Win10_G2.vhdx' - Windows 10 if you selected to hydrate it with create client parent. AdditionalNetworks (Optional) - $True - Additional networks (configured in AdditonalNetworkConfig) are added + $True - Additional networks (configured in AdditonalNetworkConfig) are added AdditionalNetworkAdapters (Optional) - Hashtable or array if multiple network adapters should be connected to this virtual machine @{ @@ -257,7 +257,7 @@ $LabConfig=@{ DomainAdminName='LabAdmin'; AdminPassword='LS1setup!'; Prefix = 'M IpConfiguration (Optional) - DHCP or hastable with specific IP configuration @{ IpAddress (Mandatory) - Static IP Address that would be injected to the OS - Subnet (Mandatory) + Subnet (Mandatory) } } @@ -278,7 +278,7 @@ $LabConfig=@{ DomainAdminName='LabAdmin'; AdminPassword='LS1setup!'; Prefix = 'M MemoryMinimumBytes (Optional) Example: 1GB Minimum memory bytes, must be less or equal to MemoryStartupBytes - If not set, default is used. + If not set, default is used. StaticMemory (Optional) if $True, then static memory is configured @@ -344,7 +344,7 @@ $LabConfig=@{ DomainAdminName='LabAdmin'; AdminPassword='LS1setup!'; Prefix = 'M ManagementSubnetID (Optional) This will set Management NICs to defined subnet id by configuring native VLAN ID. Default is 0. If configured to 1, it will increase highest allowed VLAN by one and configure. - For example ManagementSubnetID=1, AllowedVlans=10, then ManagementSubnetID VLAN will be configured 11. + For example ManagementSubnetID=1, AllowedVlans=10, then ManagementSubnetID VLAN will be configured 11. #DisableTimeIC (Optional) Example DisableTimeIC=$true @@ -374,9 +374,9 @@ $LabConfig=@{ DomainAdminName='LabAdmin'; AdminPassword='LS1setup!'; Prefix = 'M <# Just some VMs $LabConfig.VMs = @( - @{ VMName = 'Simple1' ; Configuration = 'Simple' ; ParentVHD = 'Win2016NanoHV_G2.vhdx' ; MemoryStartupBytes= 512MB }, - @{ VMName = 'Simple2' ; Configuration = 'Simple' ; ParentVHD = 'Win2016NanoHV_G2.vhdx' ; MemoryStartupBytes= 512MB }, - @{ VMName = 'Simple3' ; Configuration = 'Simple' ; ParentVHD = 'Win2016NanoHV_G2.vhdx' ; MemoryStartupBytes= 512MB }, + @{ VMName = 'Simple1' ; Configuration = 'Simple' ; ParentVHD = 'Win2016NanoHV_G2.vhdx' ; MemoryStartupBytes= 512MB }, + @{ VMName = 'Simple2' ; Configuration = 'Simple' ; ParentVHD = 'Win2016NanoHV_G2.vhdx' ; MemoryStartupBytes= 512MB }, + @{ VMName = 'Simple3' ; Configuration = 'Simple' ; ParentVHD = 'Win2016NanoHV_G2.vhdx' ; MemoryStartupBytes= 512MB }, @{ VMName = 'Simple4' ; Configuration = 'Simple' ; ParentVHD = 'Win2016NanoHV_G2.vhdx' ; MemoryStartupBytes= 512MB } ) @@ -391,7 +391,7 @@ $LabConfig=@{ DomainAdminName='LabAdmin'; AdminPassword='LS1setup!'; Prefix = 'M 1..100 | ForEach-Object {"NanoServer$_"} | ForEach-Object { $LabConfig.VMs += @{ VMName = $_ ; Configuration = 'Simple' ; ParentVHD = 'Win2016NanoHV_G2.vhdx' ; MemoryStartupBytes= 512MB } } 1..100 | ForEach-Object {"Windows10_$_"} | ForEach-Object { $LabConfig.VMs += @{ VMName = $_ ; Configuration = 'Simple' ; ParentVHD = 'Win10_G2.vhdx' ; MemoryStartupBytes= 512MB ; AddToolsVHD=$True ; DisableWCF=$True } } - or Several different VMs + or Several different VMs * you need to provide your GPT VHD for win 2012 (like created with convertwindowsimage script) $LabConfig.VMs += @{ VMName = 'Win10' ; Configuration = 'Simple' ; ParentVHD = 'Win10_G2.vhdx' ; MemoryStartupBytes= 512MB ; DisableWCF=$True ; vTPM=$True ; EnableWinRM=$True } $LabConfig.VMs += @{ VMName = 'Win10_OOBE' ; Configuration = 'Simple' ; ParentVHD = 'Win10_G2.vhdx' ; MemoryStartupBytes= 512MB ; DisableWCF=$True ; vTPM=$True ; Unattend="None" } diff --git a/Tools/CreateParentDisk.ps1 b/Tools/CreateParentDisk.ps1 index cab66585..c1750e9d 100644 --- a/Tools/CreateParentDisk.ps1 +++ b/Tools/CreateParentDisk.ps1 @@ -39,14 +39,21 @@ If (-not $isAdmin) { #endregion - #region download convert-windowsimage if needed and load it + $mslabVersion = "dev" - if (!(Test-Path "$PSScriptRoot\Convert-WindowsImage.ps1")){ + #region download convert-windowsimage if needed and load it + $convertWindowsImagePath = "$PSScriptRoot\Convert-WindowsImage.ps1" + if (-not (Test-Path -Path $convertWindowsImagePath)) { WriteInfo "`t Downloading Convert-WindowsImage" try { - Invoke-WebRequest -UseBasicParsing -Uri "https://raw.githubusercontent.com/microsoft/MSLab/master/Tools/Convert-WindowsImage.ps1" -OutFile "$PSScriptRoot\Convert-WindowsImage.ps1" + Invoke-WebRequest -UseBasicParsing -Uri "https://github.com/microsoft/MSLab/releases/download/$mslabVersion/Convert-WindowsImage.ps1" -OutFile $convertWindowsImagePath } catch { - WriteErrorAndExit "`t Failed to download Convert-WindowsImage.ps1!" + try { + WriteInfo "Download Convert-windowsimage.ps1 from releases ($mslabVersion) failed with $($_.Exception.Message), trying master branch now" + Invoke-WebRequest -UseBasicParsing -Uri "https://raw.githubusercontent.com/microsoft/MSLab/master/Tools/Convert-WindowsImage.ps1" -OutFile $convertWindowsImagePath + } catch { + WriteError "`t Failed to download Convert-WindowsImage.ps1!" + } } } diff --git a/build.ps1 b/build.ps1 index f05f5f17..73be65f8 100644 --- a/build.ps1 +++ b/build.ps1 @@ -5,29 +5,53 @@ param( [string]$Version, [Parameter(Mandatory = $false, ParameterSetName = 'BuildAndSign')] [bool]$SignScripts = $false, - [Parameter(Mandatory = $true, ParameterSetName = 'BuildAndSign')] - [string]$SignScriptUri, - [Parameter(Mandatory = $true, ParameterSetName = 'BuildAndSign')] - [string]$ClientId + [Parameter(Mandatory = $true, ParameterSetName = 'BuildAndSign', HelpMessage = "Azure Blob URI to code signing script")] + [string]$SignScriptUri = "", + [Parameter(Mandatory = $true, ParameterSetName = 'BuildAndSign', HelpMessage = "Client ID of Code Signing App Registration")] + [string]$ClientId = "" ) -$baseDir = ".\Scripts\" -$outputDir = ".\Output" -$outputFile = "Release.zip" -[array]$ignoredFiles = "0_Shared.ps1" -[array]$ignoredFilesToSign = @() #"LabConfig.ps1" +#region Configuration +$toolsDir = ".\Tools\" +$scriptsDir = ".\Scripts\" +$outputBaseDir = ".\Output\" +$scriptsOutputDir = Join-Path $outputBaseDir "ScriptsCompiled" +$signedScriptsOutputDir = Join-Path $outputBaseDir "Scripts" +$toolsOutputDir = Join-Path $outputBaseDir "ToolsCompiled" +$signedToolsOutputDir = Join-Path $outputBaseDir "Tools" +$scriptsOutputFile = "Release.zip" + +# Files that would be skipped by Build function (no replacements) +[array]$scriptsBuildIgnoredFiles = "0_Shared.ps1" +[array]$toolsBuildIgnoredFiles = @() + +# Files that won't be signed after build function +[array]$scriptsIgnoredFilesToSign = @() #"LabConfig.ps1" +[array]$toolsIgnoredFilesToSign = @("1_SQL_Install.ps1", "2_ADK_Install.ps1", "3_SCVMM_Install.ps1") +#endregion + +#region Init +if($SignScripts) { + # Download signing script with Managed Identity + $response = Invoke-RestMethod -Uri "http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=https%3A%2F%2Fstorage.azure.com%2F" -Headers @{ "Metadata" = "true" } + Invoke-WebRequest -Headers @{ "x-ms-version" = "2017-11-09"; "Authorization" = "Bearer $($response.access_token)" } -Uri $SignScriptUri -OutFile .\sign.ps1 + + . .\sign.ps1 +} -if(Test-Path -Path $outputDir) { - Remove-Item -Path $outputDir -Recurse -Force +if(Test-Path -Path $outputBaseDir) { + Remove-Item -Path $outputBaseDir -Recurse -Force } +#endregion -$releaseDirectory = New-Item -ItemType "Directory" -Path ".\" -Name $outputDir -$files = Get-ChildItem -Path $baseDir -foreach($file in $files) { - if($file.Name -in $ignoredFiles) { - continue - } - $content = Get-Content -Path $file.FullName +#region Functions +function Build-File { + param ( + [string]$InputFilePath, + [string]$OutputFilePath + ) + + $content = Get-Content -Path $InputFilePath $output = $content | ForEach-Object { $line = $_ @@ -42,7 +66,7 @@ foreach($file in $files) { if($includeFile.StartsWith(".\")) { $includeFile = $includeFile.Substring(2) } - $includeFile = Join-Path -Path $baseDir -ChildPath $includeFile + $includeFile = Join-Path -Path $scriptsDir -ChildPath $includeFile if(-not (Test-Path -Path $includeFile)) { throw "Unable to include requested script ($includeFile)" } @@ -56,38 +80,86 @@ foreach($file in $files) { $line } - $outFile = Join-Path -Path $releaseDirectory -ChildPath $file.Name - Set-Content -Path $outFile -Value $output + + Set-Content -Path $OutputFilePath -Value $output } +#endregion -$outputFullPath = $releaseDirectory.FullName - -if($SignScripts) { - # Download signing script - Invoke-WebRequest -Uri $SignScriptUri -OutFile .\sign.ps1 +#region Build (and optionally sign) Scripts +if(Test-Path -Path $scriptsOutputDir) { + Remove-Item -Path $scriptsOutputDir -Recurse -Force +} - . .\sign.ps1 +if(Test-Path -Path $signedScriptsOutputDir) { + Remove-Item -Path $signedScriptsOutputDir -Recurse -Force +} - $signedOutputDir = "$($outputDir)\Signed" - if(Test-Path -Path $signedOutputDir) { - Remove-Item -Path $signedOutputDir -Recurse -Force +$scriptsReleaseDirectory = New-Item -ItemType "Directory" -Path ".\" -Name $scriptsOutputDir +$scriptsFiles = Get-ChildItem -Path $scriptsDir +foreach($file in $scriptsFiles) { + if($file.Name -in $scriptsBuildIgnoredFiles) { + continue } + + $outFile = Join-Path -Path $scriptsReleaseDirectory -ChildPath $file.Name + Build-File -InputFilePath $file.FullName -OutputFilePath $outFile +} - $signedReleaseDirectory = New-Item -ItemType "Directory" -Path ".\" -Name $signedOutputDir - $files = Get-ChildItem -Path $releaseDirectory -File | Where-Object Name -NotIn $ignoredFilesToSign +$scriptsSignedDirectory = New-Item -ItemType "Directory" -Path ".\" -Name $signedScriptsOutputDir +$scriptFiles = Get-ChildItem -Path $scriptsReleaseDirectory -File | Where-Object Name -NotIn $scriptsIgnoredFilesToSign +if($SignScripts) { # sign scripts - Invoke-CodeSign -Files $files -OutputPath $signedReleaseDirectory -ClientId $ClientId + Invoke-CodeSign -Files $scriptFiles -OutputPath $scriptsSignedDirectory -ClientId $ClientId +} else { + # if not signing, just copy files over as is + $scriptFiles | Select-Object -ExpandProperty FullName | Copy-Item -Destination $scriptsSignedDirectory +} + +$signedScriptFiles = Get-ChildItem -Path $scriptsSignedDirectory.FullName +if($scriptFiles.Length -ne $signedScriptFiles.Length) { + throw "Signing files failed (source count: $($scriptFiles.Length), signedCount: $($signedScriptFiles.Length))" +} +#endregion + +#region Build (and optionally sign) Tools +if(Test-Path -Path $toolsOutputDir) { + Remove-Item -Path $toolsOutputDir -Recurse -Force +} - $signedFiles = Get-ChildItem -Path $signedReleaseDirectory.FullName - if($files.Length -ne $signedFiles.Length) { - throw "Signing files failed (source count: $($files.Length), signedCount: $($signedFiles.Length))" +if(Test-Path -Path $signedToolsOutputDir) { + Remove-Item -Path $signedToolsOutputDir -Recurse -Force +} + +$toolsReleaseDirectory = New-Item -ItemType "Directory" -Path ".\" -Name $toolsOutputDir +$toolsFiles = Get-ChildItem -Path $toolsDir +foreach($file in $toolsFiles) { + if($file.Name -in $toolsBuildIgnoredFiles) { + continue } + + $outFile = Join-Path -Path $toolsReleaseDirectory -ChildPath $file.Name + Build-File -InputFilePath $file.FullName -OutputFilePath $outFile +} - # and copy scripts that are ignored from signing - Get-ChildItem -Path $releaseDirectory -File | Where-Object Name -In $ignoredFilesToSign | Copy-Item -Destination $signedReleaseDirectory.FullName +$toolsSignedDirectory = New-Item -ItemType "Directory" -Path ".\" -Name $signedToolsOutputDir +$toolsFiles = Get-ChildItem -Path $toolsReleaseDirectory -File | Where-Object Name -NotIn $toolsIgnoredFilesToSign + +if($SignScripts) { + # Sign scripts in Tools folder + Invoke-CodeSign -Files $toolsFiles -OutputPath $toolsSignedDirectory -ClientId $ClientId +} else { + # or just copy tools scripts over + $toolsFiles | Select-Object -ExpandProperty FullName | Copy-Item -Destination $toolsSignedDirectory +} - $outputFullPath = $signedReleaseDirectory.FullName +$signedToolsFiles = Get-ChildItem -Path $toolsSignedDirectory.FullName +if($toolsFiles.Length -ne $signedToolsFiles.Length) { + throw "Signing files failed (source count: $($toolsFiles.Length), signedCount: $($signedToolsFiles.Length))" } +#endregion -Compress-Archive -Path "$($outputFullPath)\*" -DestinationPath $outputFile -CompressionLevel Optimal -Force +#region Create Scripts release ZIP +$scriptsOutputFullPath = $scriptsSignedDirectory.FullName +Compress-Archive -Path "$($scriptsOutputFullPath)\*" -DestinationPath $scriptsOutputFile -CompressionLevel Optimal -Force +#endregion \ No newline at end of file