diff --git a/Scenarios/AzSHCI Deployment 22H2 Edition/LabConfig.ps1 b/Scenarios/AzSHCI Deployment 22H2 Edition/LabConfig.ps1 index c3511b92..647a7953 100644 --- a/Scenarios/AzSHCI Deployment 22H2 Edition/LabConfig.ps1 +++ b/Scenarios/AzSHCI Deployment 22H2 Edition/LabConfig.ps1 @@ -1,6 +1,6 @@ $LabConfig=@{AllowedVLANs="1-10,711-719" ; DomainAdminName='LabAdmin'; AdminPassword='LS1setup!' ; DCEdition='4'; Internet=$true ; TelemetryLevel='Full' ; TelemetryNickname='' ; AdditionalNetworksConfig=@(); VMs=@()} -#Azure Stack HCI 21H2 +#Azure Stack HCI 22H2 1..4 | ForEach-Object {$LABConfig.VMs += @{ VMName = "AzSHCI$_" ; Configuration = 'S2D' ; ParentVHD = 'AzSHCI22H2_G2.vhdx' ; HDDNumber = 4 ; HDDSize= 2TB ; MemoryStartupBytes= 1GB; VMProcessorCount=4 ; vTPM=$true}} #Or with nested virtualization enabled #1..4 | ForEach-Object {$LABConfig.VMs += @{ VMName = "AzSHCI$_" ; Configuration = 'S2D' ; ParentVHD = 'AzSHCI22H2_G2.vhdx' ; HDDNumber = 4 ; HDDSize= 2TB ; MemoryStartupBytes= 4GB; VMProcessorCount=4 ; vTPM=$true ; NestedVirt=$true}} diff --git a/Scenarios/AzSHCI Deployment 22H2 Edition/Scenario.ps1 b/Scenarios/AzSHCI Deployment 22H2 Edition/Scenario.ps1 index 28cb7b8e..870f28b0 100644 --- a/Scenarios/AzSHCI Deployment 22H2 Edition/Scenario.ps1 +++ b/Scenarios/AzSHCI Deployment 22H2 Edition/Scenario.ps1 @@ -171,9 +171,9 @@ } } #define and install other features - $features="Failover-Clustering","RSAT-Clustering-PowerShell","Hyper-V-PowerShell","NetworkATC","NetworkHUD","Data-Center-Bridging","RSAT-DataCenterBridging-LLDP-Tools","FS-SMBBW" - #optional - affects perf even if not enabled on volumes as filter driver is attached (Bitlocker,SR,Dedup) - #$features+="RSAT-Feature-Tools-BitLocker","Storage-Replica","RSAT-Storage-Replica","FS-Data-Deduplication","System-Insights","RSAT-System-Insights" + $features="Failover-Clustering","RSAT-Clustering-PowerShell","Hyper-V-PowerShell","NetworkATC","NetworkHUD","Data-Center-Bridging","RSAT-DataCenterBridging-LLDP-Tools","FS-SMBBW","System-Insights","RSAT-System-Insights" + #optional - affects perf even if not enabled on volumes as filter driver is attached (SR,Dedup) and also Bitlocker, that affects a little bit + #$features+="Storage-Replica","RSAT-Storage-Replica","FS-Data-Deduplication","BitLocker","RSAT-Feature-Tools-BitLocker" Invoke-Command -ComputerName $servers -ScriptBlock {Install-WindowsFeature -Name $using:features} #endregion @@ -823,6 +823,10 @@ if ($NetATC){ #check number of live migrations get-vmhost -CimSession $Servers | Select-Object Name,MaximumVirtualMachineMigrations + + #check it in cluster (is only 1 - expected) + get-cluster -Name $ClusterName | Select-Object Name,MaximumParallelMigrations + #endregion #remove net intent global overrides if necessary diff --git a/Scenarios/AzSHCI and AVD/Scenario.ps1 b/Scenarios/AzSHCI and AVD/Scenario.ps1 index 2deab1c9..bc1b8a8b 100644 --- a/Scenarios/AzSHCI and AVD/Scenario.ps1 +++ b/Scenarios/AzSHCI and AVD/Scenario.ps1 @@ -392,7 +392,7 @@ #Copy agent and bootloader to VMs #create sessions $sessions=New-PSSession -ComputerName $VMs.VMName - #copy ARC agent + #copy AVD agent foreach ($session in $sessions){ Copy-Item -Path "$env:USERPROFILE\Downloads\AVDAgent.msi" -Destination "$env:USERPROFILE\Downloads\" -tosession $session -force Copy-Item -Path "$env:USERPROFILE\Downloads\AVDAgentBootloader.msi" -Destination "$env:USERPROFILE\Downloads\" -tosession $session -force diff --git a/Scenarios/AzSHCI and Kubernetes/Scenario.ps1 b/Scenarios/AzSHCI and Kubernetes/Scenario.ps1 index 52d38c87..8befe37c 100644 --- a/Scenarios/AzSHCI and Kubernetes/Scenario.ps1 +++ b/Scenarios/AzSHCI and Kubernetes/Scenario.ps1 @@ -276,16 +276,17 @@ Foreach ($PSSession in $PSSessions){ $VolumeName="AKS" $Servers=(Get-ClusterNode -Cluster $ClusterName).Name $DHCPServer="DC" - $DHCPScopeID="10.0.0.0" $VIPPoolStart="10.0.1.2" $VIPPoolEnd="10.0.1.100" + $VLANID=11 + $resourcegroupname="$ClusterName-rg" + + #if dhcp is disabled: $k8sNodeIpPoolStart="10.0.1.101" $k8sNodeIpPoolEnd="10.0.1.254" $IPAddressPrefix="10.0.1.0/24" $DNSServers="10.0.1.1" $Gateway="10.0.1.1" - $VLANID=11 - $resourcegroupname="$ClusterName-rg" #JaromirK note: it would be great if I could simply run "Initialize-AksHciNode -ComputerName $ClusterName". I could simply skip credssp. Same applies for AksHciConfig and AksHciRegistration @@ -323,11 +324,13 @@ Foreach ($PSSession in $PSSessions){ #configure aks #note: I'm assigning larger control plane VM than default as I saw IP disapperaring IP address if it was smaller in virtual environment (I tested manually incresed size to 8cores and 8GB RAM) Invoke-Command -ComputerName $servers[0] -Credential $Credentials -Authentication Credssp -ScriptBlock { + #install nuget first + Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force #DHCP - #$vnet = New-AksHciNetworkSetting -Name $using:vNetName -vSwitchName $using:vSwitchName -vippoolstart $using:vippoolstart -vippoolend $using:vippoolend + $vnet = New-AksHciNetworkSetting -Name $using:vNetName -vSwitchName $using:vSwitchName -vippoolstart $using:vippoolstart -vippoolend $using:vippoolend -vlanID $using:VLANID #Static - $vnet = New-AksHciNetworkSetting -Name $using:vNetName -ipAddressPrefix $using:IPAddressPrefix -vSwitchName $using:vSwitchName -vippoolstart $using:vippoolstart -vippoolend $using:vippoolend -k8sNodeIpPoolStart $using:k8sNodeIpPoolStart -k8sNodeIpPoolEnd $using:k8sNodeIpPoolEnd -vlanID $using:VLANID -DNSServers $using:DNSServers -gateway $Using:Gateway - Set-AksHciConfig -vnet $vnet -workingDir c:\clusterstorage\$using:VolumeName\ImagesStore -imageDir c:\clusterstorage\$using:VolumeName\Images -cloudConfigLocation c:\clusterstorage\$using:VolumeName\Config -ClusterRoleName "$($using:ClusterName)_AKS" -controlPlaneVmSize 'Standard_A4_v2' # Get-AksHciVmSize + #$vnet = New-AksHciNetworkSetting -Name $using:vNetName -ipAddressPrefix $using:IPAddressPrefix -vSwitchName $using:vSwitchName -vippoolstart $using:vippoolstart -vippoolend $using:vippoolend -k8sNodeIpPoolStart $using:k8sNodeIpPoolStart -k8sNodeIpPoolEnd $using:k8sNodeIpPoolEnd -vlanID $using:VLANID -DNSServers $using:DNSServers -gateway $Using:Gateway + Set-AksHciConfig -vnet $vnet -workingDir c:\clusterstorage\$using:VolumeName\WorkDir -imageDir c:\clusterstorage\$using:VolumeName\Images -cloudConfigLocation c:\clusterstorage\$using:VolumeName\Config -ClusterRoleName "$($using:ClusterName)_AKS" -controlPlaneVmSize 'Standard_A4_v2' # Get-AksHciVmSize } #validate config @@ -518,7 +521,7 @@ $password="" #if blank, password will be created #create credentials $ClientID=$sp.AppId $SecureSecret= ConvertTo-SecureString $password -AsPlainText -Force -$Credentials = New-Object System.Management.Automation.PSCredential ($ClientID , $SecureSecret) +$SPCredentials = New-Object System.Management.Automation.PSCredential ($ClientID , $SecureSecret) #register namespace Microsoft.KubernetesConfiguration and Microsoft.Kubernetes Register-AzResourceProvider -ProviderNamespace Microsoft.Kubernetes @@ -529,7 +532,7 @@ Invoke-Command -ComputerName $ClusterName -ScriptBlock { #Generate kubeconfig Get-AksHciCredential -Name $using:KubernetesClusterName -confirm:0 #onboard - Enable-AksHciArcConnection -Name $using:KubernetesClusterName -tenantId $using:tenantID -subscriptionId $using:subscriptionID -resourcegroup $using:resourcegroup -Location $using:location -credential $using:Credentials + Enable-AksHciArcConnection -Name $using:KubernetesClusterName -tenantId $using:tenantID -subscriptionId $using:subscriptionID -resourcegroup $using:resourcegroup -Location $using:location -credential $using:SPCredentials } diff --git a/Scenarios/AzSHCI and MDT/Scenario.ps1 b/Scenarios/AzSHCI and MDT/Scenario.ps1 index eaab84d9..c43aef70 100644 --- a/Scenarios/AzSHCI and MDT/Scenario.ps1 +++ b/Scenarios/AzSHCI and MDT/Scenario.ps1 @@ -1,244 +1,244 @@ #run all from DC or management machine (Windows 11 or Windows Server 2022) #region variables - $MDTServer="MDT" - $DeploymentShareLocation="D:\DeploymentShare" - $Connection="TCPIP" #or "NamedPipes" - $downloadfolder="$env:USERPROFILE\Downloads" - $WDSRoot="D:\RemoteInstall" - $DHCPServer="DC" - $ScopeID="10.0.0.0" - - $CredSSPUserName="CORP\LabAdmin" - $CredSSPPassword="LS1setup!" +$MDTServer="MDT" +$DeploymentShareLocation="D:\DeploymentShare" +$Connection="TCPIP" #or "NamedPipes" +$downloadfolder="$env:USERPROFILE\Downloads" +$WDSRoot="D:\RemoteInstall" +$DHCPServer="DC" +$ScopeID="10.0.0.0" + +$CredSSPUserName="CORP\LabAdmin" +$CredSSPPassword="LS1setup!" #endregion #region prereqs - #install management features (ADDS, DHCP,...) - $WindowsInstallationType=Get-ItemPropertyValue -Path 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\' -Name InstallationType - If ($WindowsInstallationType -like "Server*"){ - Install-WindowsFeature -Name "RSAT-AD-PowerShell","RSAT-ADDS","RSAT-DHCP","RSAT-DNS-Server","WDS-AdminPack" - }else{ - $Capabilities="Rsat.ServerManager.Tools~~~~0.0.1.0","Rsat.DHCP.Tools~~~~0.0.1.0","Rsat.ActiveDirectory.DS-LDS.Tools~~~~0.0.1.0","Rsat.Dns.Tools~~~~0.0.1.0" - foreach ($Capability in $Capabilities){ - Add-WindowsCapability -Name $Capability -Online - } +#install management features (ADDS, DHCP,...) +$WindowsInstallationType=Get-ItemPropertyValue -Path 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\' -Name InstallationType +If ($WindowsInstallationType -like "Server*"){ + Install-WindowsFeature -Name "RSAT-AD-PowerShell","RSAT-ADDS","RSAT-DHCP","RSAT-DNS-Server","WDS-AdminPack" +}else{ + $Capabilities="Rsat.ServerManager.Tools~~~~0.0.1.0","Rsat.DHCP.Tools~~~~0.0.1.0","Rsat.ActiveDirectory.DS-LDS.Tools~~~~0.0.1.0","Rsat.Dns.Tools~~~~0.0.1.0" + foreach ($Capability in $Capabilities){ + Add-WindowsCapability -Name $Capability -Online } +} - #download and install binaries - #Download files - $files=@() - #$Files+=@{Uri="https://go.microsoft.com/fwlink/?linkid=2026036" ; FileName="adksetup.exe" ; Description="Windows 10 ADK 1809"} - #$Files+=@{Uri="https://go.microsoft.com/fwlink/?linkid=2022233" ; FileName="adkwinpesetup.exe" ; Description="WindowsPE 1809"} - $Files+=@{Uri="https://go.microsoft.com/fwlink/?linkid=2165884" ; FileName="adksetup.exe" ; Description="Windows 11 21H2 ADK"} - $Files+=@{Uri="https://go.microsoft.com/fwlink/?linkid=2166133" ; FileName="adkwinpesetup.exe" ; Description="WindowsPE for Windows 11 21H2"} - $Files+=@{Uri="https://download.microsoft.com/download/3/3/9/339BE62D-B4B8-4956-B58D-73C4685FC492/MicrosoftDeploymentToolkit_x64.msi" ; FileName="MicrosoftDeploymentToolkit_x64.msi" ; Description="Microsoft Deployment Toolkit"} - #$Files+=@{Uri="https://software-download.microsoft.com/download/pr/AzureStackHCI_17784.1408_EN-US.iso" ; FileName="AzureStackHCI_17784.1408_EN-US.iso" ; Description="Azure Stack HCI ISO"} - #$Files+=@{Uri="https://software-static.download.prss.microsoft.com/sg/download/888969d5-f34g-4e03-ac9d-1f9786c66749/AzureStackHCI_20348.587_en-us.iso" ; FileName="AzureStackHCI_20348.587_en-us.iso" ; Description="Azure Stack HCI ISO"} - $Files+=@{Uri="https://software-static.download.prss.microsoft.com/dbazure/988969d5-f34g-4e03-ac9d-1f9786c66751/20349.1129.221007-2120.fe_release_hciv3_svc_refresh_SERVERAZURESTACKHCICOR_OEMRET_x64FRE_en-us.iso" ; FileName="AzureStackHCI_20349.1129_en-us.iso" ; Description="Azure Stack HCI ISO"} - $Files+=@{Uri="https://go.microsoft.com/fwlink/?linkid=866658" ; FileName="SQL2019-SSEI-Expr.exe" ; Description="SQL Express 2019"} - #$Files+=@{Uri="https://aka.ms/ssmsfullsetup" ; FileName="SSMS-Setup-ENU.exe" ; Description="SQL Management Studio"} - foreach ($file in $files){ - if (-not (Test-Path "$downloadfolder\$($file.filename)")){ - Start-BitsTransfer -Source $file.uri -Destination "$downloadfolder\$($file.filename)" -DisplayName "Downloading: $($file.filename)" - } +#download and install binaries + #Download files + $files=@() + #$Files+=@{Uri="https://go.microsoft.com/fwlink/?linkid=2026036" ; FileName="adksetup.exe" ; Description="Windows 10 ADK 1809"} + #$Files+=@{Uri="https://go.microsoft.com/fwlink/?linkid=2022233" ; FileName="adkwinpesetup.exe" ; Description="WindowsPE 1809"} + $Files+=@{Uri="https://go.microsoft.com/fwlink/?linkid=2196127" ; FileName="adksetup.exe" ; Description="Windows 11 22H2 ADK"} + $Files+=@{Uri="https://go.microsoft.com/fwlink/?linkid=2196224" ; FileName="adkwinpesetup.exe" ; Description="WindowsPE for Windows 11 22H2"} + $Files+=@{Uri="https://download.microsoft.com/download/3/3/9/339BE62D-B4B8-4956-B58D-73C4685FC492/MicrosoftDeploymentToolkit_x64.msi" ; FileName="MicrosoftDeploymentToolkit_x64.msi" ; Description="Microsoft Deployment Toolkit"} + #$Files+=@{Uri="https://software-download.microsoft.com/download/pr/AzureStackHCI_17784.1408_EN-US.iso" ; FileName="AzureStackHCI_17784.1408_EN-US.iso" ; Description="Azure Stack HCI ISO"} + #$Files+=@{Uri="https://software-static.download.prss.microsoft.com/sg/download/888969d5-f34g-4e03-ac9d-1f9786c66749/AzureStackHCI_20348.587_en-us.iso" ; FileName="AzureStackHCI_20348.587_en-us.iso" ; Description="Azure Stack HCI ISO"} + $Files+=@{Uri="https://software-static.download.prss.microsoft.com/dbazure/888969d5-f34g-4e03-ac9d-1f9786c66750/AzureStackHCI_20349.1607_en-us.iso" ; FileName="AzureStackHCI_20349.1607_en-us.iso" ; Description="Azure Stack HCI ISO"} + $Files+=@{Uri="https://go.microsoft.com/fwlink/?linkid=866658" ; FileName="SQL2019-SSEI-Expr.exe" ; Description="SQL Express 2019"} + #$Files+=@{Uri="https://aka.ms/ssmsfullsetup" ; FileName="SSMS-Setup-ENU.exe" ; Description="SQL Management Studio"} + foreach ($file in $files){ + if (-not (Test-Path "$downloadfolder\$($file.filename)")){ + Start-BitsTransfer -Source $file.uri -Destination "$downloadfolder\$($file.filename)" -DisplayName "Downloading: $($file.filename)" } + } - #install ADK - Start-Process -Wait -FilePath "$downloadfolder\adksetup.exe" -ArgumentList "/features OptionId.DeploymentTools OptionId.UserStateMigrationTool /quiet" - #install ADK WinPE - Start-Process -Wait -FilePath "$downloadfolder\adkwinpesetup.exe" -ArgumentList "/features OptionID.WindowsPreinstallationEnvironment /Quiet" - #install MDT locally - Start-Process -Wait -Filepath msiexec.exe -Argumentlist "/i $downloadfolder\MicrosoftDeploymentToolkit_x64.msi /q" - - #prepare MDT Server - #format and prepare "D" drive on MDT Server - Get-Disk -CimSession $MDTServer | Where-Object PartitionStyle -eq RAW | Initialize-Disk -PartitionStyle GPT -PassThru | New-Partition -UseMaximumSize -AssignDriveLetter | Format-Volume -Filesystem NTFS -AllocationUnitSize 8kb -NewFileSystemLabel "Storage" - #enable SMB FIrewall rule - Enable-NetFirewallRUle -CimSession $MDTServer -Name FPS-SMB-In-TCP - #copy binaries - New-Item -Path "\\$MDTServer\d$\" -Name INstall -ItemType Directory - Copy-Item -Path "$env:UserProfile\Downloads\*" -Destination "\\$MDTServer\d$\Install" -Recurse - - #Install MDT and Prereqs to MDT Server - Invoke-Command -ComputerName $MDTServer -ScriptBlock { - $downloadfolder="d:\Install" - #install ADK - Start-Process -Wait -FilePath "$downloadfolder\adksetup.exe" -ArgumentList "/features OptionId.DeploymentTools OptionId.UserStateMigrationTool /quiet" - #install ADK WinPE - Start-Process -Wait -FilePath "$downloadfolder\adkwinpesetup.exe" -ArgumentList "/features OptionID.WindowsPreinstallationEnvironment /Quiet" - #install MDT - Start-Process -Wait -Filepath msiexec.exe -Argumentlist "/i $downloadfolder\MicrosoftDeploymentToolkit_x64.msi /q" - } + #install ADK + Start-Process -Wait -FilePath "$downloadfolder\adksetup.exe" -ArgumentList "/features OptionId.DeploymentTools OptionId.UserStateMigrationTool /quiet" + #install ADK WinPE + Start-Process -Wait -FilePath "$downloadfolder\adkwinpesetup.exe" -ArgumentList "/features OptionID.WindowsPreinstallationEnvironment /Quiet" + #install MDT locally + Start-Process -Wait -Filepath msiexec.exe -Argumentlist "/i $downloadfolder\MicrosoftDeploymentToolkit_x64.msi /q" + + #prepare MDT Server + #format and prepare "D" drive on MDT Server + Get-Disk -CimSession $MDTServer | Where-Object PartitionStyle -eq RAW | Initialize-Disk -PartitionStyle GPT -PassThru | New-Partition -UseMaximumSize -AssignDriveLetter | Format-Volume -Filesystem NTFS -AllocationUnitSize 8kb -NewFileSystemLabel "Storage" + #enable SMB FIrewall rule + Enable-NetFirewallRUle -CimSession $MDTServer -Name FPS-SMB-In-TCP + #copy binaries + New-Item -Path "\\$MDTServer\d$\" -Name INstall -ItemType Directory + Copy-Item -Path "$env:UserProfile\Downloads\*" -Destination "\\$MDTServer\d$\Install" -Recurse + + #Install MDT and Prereqs to MDT Server + Invoke-Command -ComputerName $MDTServer -ScriptBlock { + $downloadfolder="d:\Install" + #install ADK + Start-Process -Wait -FilePath "$downloadfolder\adksetup.exe" -ArgumentList "/features OptionId.DeploymentTools OptionId.UserStateMigrationTool /quiet" + #install ADK WinPE + Start-Process -Wait -FilePath "$downloadfolder\adkwinpesetup.exe" -ArgumentList "/features OptionID.WindowsPreinstallationEnvironment /Quiet" + #install MDT + Start-Process -Wait -Filepath msiexec.exe -Argumentlist "/i $downloadfolder\MicrosoftDeploymentToolkit_x64.msi /q" + } - #install SQL Express to MDT Machine (using credssp) - # Temporarily enable CredSSP delegation to avoid double-hop issue - $WindowsInstallationType=Get-ItemPropertyValue -Path 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\' -Name InstallationType - If ($WindowsInstallationType -eq "Client"){ - winrm quickconfig -force #on client is winrm not configured - } - Enable-WSManCredSSP -Role "Client" -DelegateComputer $MDTServer -Force - Invoke-Command -ComputerName $MDTServer -ScriptBlock { Enable-WSManCredSSP Server -Force } - - $SecureStringPassword = ConvertTo-SecureString $CredSSPPassword -AsPlainText -Force - $Credentials = New-Object System.Management.Automation.PSCredential ($CredSSPUserName, $SecureStringPassword) - - Invoke-Command -ComputerName $MDTServer -Credential $Credentials -Authentication Credssp -ScriptBlock { - $downloadfolder="D:\Install" - #install - $exec="$downloadfolder\SQL2019-SSEI-Expr.exe" - $params="/Action=Install /MediaPath=$downloadfolder\SQLMedia /IAcceptSqlServerLicenseTerms /quiet" - Start-Process -FilePath $exec -ArgumentList $params -NoNewWindow -Wait - } + #install SQL Express to MDT Machine (using credssp) + # Temporarily enable CredSSP delegation to avoid double-hop issue + $WindowsInstallationType=Get-ItemPropertyValue -Path 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\' -Name InstallationType + If ($WindowsInstallationType -eq "Client"){ + winrm quickconfig -force #on client is winrm not configured + } + Enable-WSManCredSSP -Role "Client" -DelegateComputer $MDTServer -Force + Invoke-Command -ComputerName $MDTServer -ScriptBlock { Enable-WSManCredSSP Server -Force } + + $SecureStringPassword = ConvertTo-SecureString $CredSSPPassword -AsPlainText -Force + $Credentials = New-Object System.Management.Automation.PSCredential ($CredSSPUserName, $SecureStringPassword) + + Invoke-Command -ComputerName $MDTServer -Credential $Credentials -Authentication Credssp -ScriptBlock { + $downloadfolder="D:\Install" + #install + $exec="$downloadfolder\SQL2019-SSEI-Expr.exe" + $params="/Action=Install /MediaPath=$downloadfolder\SQLMedia /IAcceptSqlServerLicenseTerms /quiet" + Start-Process -FilePath $exec -ArgumentList $params -NoNewWindow -Wait + } - # Disable CredSSP - Disable-WSManCredSSP -Role Client - Invoke-Command -ComputerName $MDTServer -ScriptBlock {Disable-WSManCredSSP Server} + # Disable CredSSP + Disable-WSManCredSSP -Role Client + Invoke-Command -ComputerName $MDTServer -ScriptBlock {Disable-WSManCredSSP Server} #endregion #region configure MDT - #import MDT Module - Import-Module "C:\Program Files\Microsoft Deployment Toolkit\bin\MicrosoftDeploymentToolkit.psd1" - #list commands - Get-Command -Module MicrosoftDeploymentToolkit - - #Create new Deployment Share - #Create Deployment Share - Invoke-Command -ComputerName $MDTServer -ScriptBlock { - New-Item -Path $using:DeploymentShareLocation -ItemType Directory -ErrorAction Ignore - New-SmbShare -Name "DeploymentShare$" -Path "$using:DeploymentShareLocation" -FullAccess Administrators +#import MDT Module +Import-Module "C:\Program Files\Microsoft Deployment Toolkit\bin\MicrosoftDeploymentToolkit.psd1" +#list commands +Get-Command -Module MicrosoftDeploymentToolkit + +#Create new Deployment Share + #Create Deployment Share +Invoke-Command -ComputerName $MDTServer -ScriptBlock { + New-Item -Path $using:DeploymentShareLocation -ItemType Directory -ErrorAction Ignore + New-SmbShare -Name "DeploymentShare$" -Path "$using:DeploymentShareLocation" -FullAccess Administrators +} +#map MDT deployment share as PSDrive +#sometimes happens that script to complains: The process cannot access the file '\\MDT\DeploymentShare$\Control\Settings.xml' because it is being used by another process. +do{ + New-PSDrive -Name "DS001" -PSProvider "MDTProvider" -Root "\\$MDTServer\DeploymentShare$" -Description "MDT Deployment Share" -NetworkPath "\\$MDTServer\DeploymentShare$" -Verbose | add-MDTPersistentDrive -Verbose + if (-not (get-psdrive -Name DS001)){ + Write-Output "Failed adding PSDrive - trying again" } - #map MDT deployment share as PSDrive - #sometimes happens that script to complains: The process cannot access the file '\\MDT\DeploymentShare$\Control\Settings.xml' because it is being used by another process. - do{ - New-PSDrive -Name "DS001" -PSProvider "MDTProvider" -Root "\\$MDTServer\DeploymentShare$" -Description "MDT Deployment Share" -NetworkPath "\\$MDTServer\DeploymentShare$" -Verbose | add-MDTPersistentDrive -Verbose - if (-not (get-psdrive -Name DS001)){ - Write-Output "Failed adding PSDrive - trying again" - } - }until (get-psdrive -Name DS001) - #Configure SQL Services - - Invoke-Command -ComputerName $MDTServer -scriptblock { - if ($using:Connection -eq "NamedPipes"){ - #Named Pipes - Set-ItemProperty -Path "hklm:\\SOFTWARE\Microsoft\Microsoft SQL Server\MSSQL15.SQLEXPRESS\MSSQLServer\SuperSocketNetLib\Np\" -Name Enabled -Value 1 - } +}until (get-psdrive -Name DS001) +#Configure SQL Services - if ($using:Connection -eq "TCPIP"){ - #TCP - Set-ItemProperty -Path "hklm:\\SOFTWARE\Microsoft\Microsoft SQL Server\MSSQL15.SQLEXPRESS\MSSQLServer\SuperSocketNetLib\Tcp\" -Name Enabled -Value 1 - Set-ItemProperty -Path "hklm:\\SOFTWARE\Microsoft\Microsoft SQL Server\MSSQL15.SQLEXPRESS\MSSQLServer\SuperSocketNetLib\Tcp\IPAll" -Name TcpPort -Value 1433 - } +Invoke-Command -ComputerName $MDTServer -scriptblock { + if ($using:Connection -eq "NamedPipes"){ + #Named Pipes + Set-ItemProperty -Path "hklm:\\SOFTWARE\Microsoft\Microsoft SQL Server\MSSQL15.SQLEXPRESS\MSSQLServer\SuperSocketNetLib\Np\" -Name Enabled -Value 1 + } - Restart-Service 'MSSQL$SQLEXPRESS' - Set-Service -Name SQLBrowser -StartupType Automatic - Start-Service -Name SQLBrowser + if ($using:Connection -eq "TCPIP"){ + #TCP + Set-ItemProperty -Path "hklm:\\SOFTWARE\Microsoft\Microsoft SQL Server\MSSQL15.SQLEXPRESS\MSSQLServer\SuperSocketNetLib\Tcp\" -Name Enabled -Value 1 + Set-ItemProperty -Path "hklm:\\SOFTWARE\Microsoft\Microsoft SQL Server\MSSQL15.SQLEXPRESS\MSSQLServer\SuperSocketNetLib\Tcp\IPAll" -Name TcpPort -Value 1433 } - #create Firewall rule for SQL Server - - if ($Connection -eq "TCPIP"){ - New-NetFirewallRule ` - -CimSession $MDTServer ` - -Action Allow ` - -Name "SQLExpress-In-TCP" ` - -DisplayName "SQLExpress (SQL-In)" ` - -Description "Inbound rule for SQL. [TCP-1433]" ` - -Enabled True ` - -Direction Inbound ` - -Program "%ProgramFiles%\Microsoft SQL Server\MSSQL15.SQLEXPRESS\MSSQL\Binn\sqlservr.exe" ` - -Protocol TCP ` - -LocalPort 1433 ` - -Profile Any ` - -Group "SQL Express" ` - -RemoteAddress Any - } + Restart-Service 'MSSQL$SQLEXPRESS' + Set-Service -Name SQLBrowser -StartupType Automatic + Start-Service -Name SQLBrowser +} + +#create Firewall rule for SQL Server + if ($Connection -eq "TCPIP"){ New-NetFirewallRule ` -CimSession $MDTServer ` -Action Allow ` - -Name "SQLBrowser-In-UDP" ` - -DisplayName "SQLBrowser (SQL-In-UDP)" ` - -Description "Inbound rule for SQLBrowser. [UDP-1434]" ` + -Name "SQLExpress-In-TCP" ` + -DisplayName "SQLExpress (SQL-In)" ` + -Description "Inbound rule for SQL. [TCP-1433]" ` -Enabled True ` -Direction Inbound ` - -Program "%ProgramFiles% (x86)\Microsoft SQL Server\90\Shared\sqlbrowser.exe" ` - -Protocol UDP ` - -LocalPort 1434 ` + -Program "%ProgramFiles%\Microsoft SQL Server\MSSQL15.SQLEXPRESS\MSSQL\Binn\sqlservr.exe" ` + -Protocol TCP ` + -LocalPort 1433 ` -Profile Any ` -Group "SQL Express" ` -RemoteAddress Any - - #create DB in MDT - if ($Connection -eq "NamedPipes"){ - New-MDTDatabase -path "DS001:" -SQLServer $MDTServer -Instance "SQLExpress" -Netlib "DBNMPNTW" -Database "MDTDB" -SQLShare "DeploymentShare$" -Verbose - }elseif ($Connection -eq "TCPIP"){ - New-MDTDatabase -path "DS001:" -SQLServer $MDTServer -Port "1433" -Netlib "DBMSSOCN" -Database "MDTDB" -Verbose } - #Import Operating System - $ISO = Mount-DiskImage -ImagePath "$downloadfolder\AzureStackHCI_20349.1129_en-us.iso" -PassThru - $ISOMediaPath = (Get-Volume -DiskImage $ISO).DriveLetter+':\' - Import-mdtoperatingsystem -path "DS001:\Operating Systems" -SourcePath $ISOMediaPath -DestinationFolder "Azure Stack HCI SERVERAZURESTACKHCICORE x64" -Verbose - - $ISO | Dismount-DiskImage - - #configure Deployment Share properties - Set-ItemProperty DS001:\ -name SupportX86 -value False - Set-ItemProperty DS001:\ -name Boot.x86.GenerateLiteTouchISO -value False - Set-ItemProperty DS001:\ -name Boot.x64.SelectionProfile -value "Nothing" - Set-ItemProperty DS001:\ -name Boot.x64.IncludeNetworkDrivers -value True - Set-ItemProperty DS001:\ -name Boot.x64.IncludeMassStorageDrivers -value True - Set-ItemProperty DS001:\ -name Boot.x64.IncludeAllDrivers -value False - Set-ItemProperty DS001:\ -name Boot.x64.GenerateGenericWIM -value True - #add PowerShell - $Properties=@() - $Properties+=(Get-ItemPropertyValue DS001:\ -Name Boot.x64.FeaturePacks) -split (",") - $FeaturesToAdd="winpe-netfx","winpe-powershell" - foreach ($FeatureToAdd in $FeaturesToAdd){ - if ($properties -notcontains $FeatureToAdd){ - $Properties+=$FeatureToAdd - } + New-NetFirewallRule ` + -CimSession $MDTServer ` + -Action Allow ` + -Name "SQLBrowser-In-UDP" ` + -DisplayName "SQLBrowser (SQL-In-UDP)" ` + -Description "Inbound rule for SQLBrowser. [UDP-1434]" ` + -Enabled True ` + -Direction Inbound ` + -Program "%ProgramFiles% (x86)\Microsoft SQL Server\90\Shared\sqlbrowser.exe" ` + -Protocol UDP ` + -LocalPort 1434 ` + -Profile Any ` + -Group "SQL Express" ` + -RemoteAddress Any + +#create DB in MDT +if ($Connection -eq "NamedPipes"){ + New-MDTDatabase -path "DS001:" -SQLServer $MDTServer -Instance "SQLExpress" -Netlib "DBNMPNTW" -Database "MDTDB" -SQLShare "DeploymentShare$" -Verbose +}elseif ($Connection -eq "TCPIP"){ + New-MDTDatabase -path "DS001:" -SQLServer $MDTServer -Port "1433" -Netlib "DBMSSOCN" -Database "MDTDB" -Verbose +} + +#Import Operating System +$ISO = Mount-DiskImage -ImagePath "$downloadfolder\AzureStackHCI_20349.1607_en-us.iso" -PassThru +$ISOMediaPath = (Get-Volume -DiskImage $ISO).DriveLetter+':\' +Import-mdtoperatingsystem -path "DS001:\Operating Systems" -SourcePath $ISOMediaPath -DestinationFolder "Azure Stack HCI SERVERAZURESTACKHCICORE x64" -Verbose + +$ISO | Dismount-DiskImage + +#configure Deployment Share properties +Set-ItemProperty DS001:\ -name SupportX86 -value False +Set-ItemProperty DS001:\ -name Boot.x86.GenerateLiteTouchISO -value False +Set-ItemProperty DS001:\ -name Boot.x64.SelectionProfile -value "Nothing" +Set-ItemProperty DS001:\ -name Boot.x64.IncludeNetworkDrivers -value True +Set-ItemProperty DS001:\ -name Boot.x64.IncludeMassStorageDrivers -value True +Set-ItemProperty DS001:\ -name Boot.x64.IncludeAllDrivers -value False +Set-ItemProperty DS001:\ -name Boot.x64.GenerateGenericWIM -value True +#add PowerShell +$Properties=@() +$Properties+=(Get-ItemPropertyValue DS001:\ -Name Boot.x64.FeaturePacks) -split (",") +$FeaturesToAdd="winpe-netfx","winpe-powershell" +foreach ($FeatureToAdd in $FeaturesToAdd){ + if ($properties -notcontains $FeatureToAdd){ + $Properties+=$FeatureToAdd } - Set-ItemProperty DS001:\ -name Boot.x64.FeaturePacks -value ($Properties -Join (",")) +} +Set-ItemProperty DS001:\ -name Boot.x64.FeaturePacks -value ($Properties -Join (",")) - #add Task Sequence - import-mdttasksequence -path "DS001:\Task Sequences" -Name "Azure Stack HCI Deploy" -Template "Server.xml" -Comments "" -ID "AzSHCI" -Version "1.0" -OperatingSystemPath "DS001:\Operating Systems\Azure Stack HCI SERVERAZURESTACKHCICORE in Azure Stack HCI SERVERAZURESTACKHCICORE x64 install.wim" -FullName "PFE" -OrgName "Contoso" -HomePage "about:blank" -AdminPassword "LS1setup!" -Verbose +#add Task Sequence +import-mdttasksequence -path "DS001:\Task Sequences" -Name "Azure Stack HCI Deploy" -Template "Server.xml" -Comments "" -ID "AzSHCI" -Version "1.0" -OperatingSystemPath "DS001:\Operating Systems\Azure Stack HCI SERVERAZURESTACKHCICORE in Azure Stack HCI SERVERAZURESTACKHCICORE x64 install.wim" -FullName "PFE" -OrgName "Contoso" -HomePage "about:blank" -AdminPassword "LS1setup!" -Verbose #endregion #region configure MDT run-as account - #create identity for MDT - $DefaultOUPath=(Get-ADDomain).UsersContainer - New-ADUser -Name MDTUser -AccountPassword (ConvertTo-SecureString "LS1setup!" -AsPlainText -Force) -Enabled $True -Path $DefaultOUPath -PasswordNeverExpires $True +#create identity for MDT +$DefaultOUPath=(Get-ADDomain).UsersContainer +New-ADUser -Name MDTUser -AccountPassword (ConvertTo-SecureString "LS1setup!" -AsPlainText -Force) -Enabled $True -Path $DefaultOUPath -PasswordNeverExpires $True - #add FileShare permissions for MDT Account - Invoke-Command -ComputerName $MDTServer -ScriptBlock { - Grant-SmbShareAccess -Name DeploymentShare$ -AccessRight Read -AccountName MDTUser -Confirm:$false - } - #delegate djoin permissions https://www.sevecek.com/EnglishPages/Lists/Posts/Post.aspx?ID=48 - $user = "$env:userdomain\MDTUser" - $ou = (Get-ADDomain).ComputersContainer - - DSACLS $ou /R $user - - DSACLS $ou /I:S /G "$($user):GR;;computer" - DSACLS $ou /I:S /G "$($user):CA;Reset Password;computer" - DSACLS $ou /I:S /G "$($user):WP;pwdLastSet;computer" - DSACLS $ou /I:S /G "$($user):WP;Logon Information;computer" - DSACLS $ou /I:S /G "$($user):WP;description;computer" - DSACLS $ou /I:S /G "$($user):WP;displayName;computer" - DSACLS $ou /I:S /G "$($user):WP;sAMAccountName;computer" - DSACLS $ou /I:S /G "$($user):WP;DNS Host Name Attributes;computer" - DSACLS $ou /I:S /G "$($user):WP;Account Restrictions;computer" - DSACLS $ou /I:S /G "$($user):WP;servicePrincipalName;computer" - DSACLS $ou /I:S /G "$($user):CC;computer;organizationalUnit" +#add FileShare permissions for MDT Account +Invoke-Command -ComputerName $MDTServer -ScriptBlock { + Grant-SmbShareAccess -Name DeploymentShare$ -AccessRight Read -AccountName MDTUser -Confirm:$false +} +#delegate djoin permissions https://www.sevecek.com/EnglishPages/Lists/Posts/Post.aspx?ID=48 +$user = "$env:userdomain\MDTUser" +$ou = (Get-ADDomain).ComputersContainer + +DSACLS $ou /R $user + +DSACLS $ou /I:S /G "$($user):GR;;computer" +DSACLS $ou /I:S /G "$($user):CA;Reset Password;computer" +DSACLS $ou /I:S /G "$($user):WP;pwdLastSet;computer" +DSACLS $ou /I:S /G "$($user):WP;Logon Information;computer" +DSACLS $ou /I:S /G "$($user):WP;description;computer" +DSACLS $ou /I:S /G "$($user):WP;displayName;computer" +DSACLS $ou /I:S /G "$($user):WP;sAMAccountName;computer" +DSACLS $ou /I:S /G "$($user):WP;DNS Host Name Attributes;computer" +DSACLS $ou /I:S /G "$($user):WP;Account Restrictions;computer" +DSACLS $ou /I:S /G "$($user):WP;servicePrincipalName;computer" +DSACLS $ou /I:S /G "$($user):CC;computer;organizationalUnit" #endregion #region configure Bootstrap ini and generate WinPE - #populate bootstrap.ini - $content=@" +#populate bootstrap.ini +$content=@" [Settings] Priority=Default @@ -249,109 +249,109 @@ UserID=MDTUser UserPassword=LS1setup! SkipBDDWelcome=YES "@ - #remove bootstrap.ini first (sometimes there is an error if just populating content) - Invoke-Command -ComputerName $MDTServer -ScriptBlock {Remove-Item -Path "$using:DeploymentShareLocation\Control\Bootstrap.ini" -Force} - #populate content - Set-Content -Path "\\$MDTServer\DeploymentShare$\Control\Bootstrap.ini" -Value $content +#remove bootstrap.ini first (sometimes there is an error if just populating content) +Invoke-Command -ComputerName $MDTServer -ScriptBlock {Remove-Item -Path "$using:DeploymentShareLocation\Control\Bootstrap.ini" -Force} +#populate content +Set-Content -Path "\\$MDTServer\DeploymentShare$\Control\Bootstrap.ini" -Value $content - #update deployment share to generate new WIM for WDS - if (-not(get-module MicrosoftDeploymentToolkit)){ - Import-Module "C:\Program Files\Microsoft Deployment Toolkit\bin\MicrosoftDeploymentToolkit.psd1" - } - if (-not(Get-PSDrive -Name ds001 -ErrorAction Ignore)){ - New-PSDrive -Name "DS001" -PSProvider "MDTProvider" -Root "\\$MDTServer\DeploymentShare$" -Description "MDT Deployment Share" -NetworkPath "\\$MDTServer\DeploymentShare$" -Verbose | add-MDTPersistentDrive -Verbose - } - update-mdtdeploymentshare -path "DS001:" -verbose -force +#update deployment share to generate new WIM for WDS +if (-not(get-module MicrosoftDeploymentToolkit)){ + Import-Module "C:\Program Files\Microsoft Deployment Toolkit\bin\MicrosoftDeploymentToolkit.psd1" +} +if (-not(Get-PSDrive -Name ds001 -ErrorAction Ignore)){ + New-PSDrive -Name "DS001" -PSProvider "MDTProvider" -Root "\\$MDTServer\DeploymentShare$" -Description "MDT Deployment Share" -NetworkPath "\\$MDTServer\DeploymentShare$" -Verbose | add-MDTPersistentDrive -Verbose +} +update-mdtdeploymentshare -path "DS001:" -verbose -force #endregion #region Install and configure WDS - #install WDS - Install-WindowsFeature -Name WDS -ComputerName $MDTServer -IncludeManagementTools -IncludeAllSubFeature - - # Temporarily enable CredSSP delegation to avoid double-hop issue - winrm quickconfig -force #on client is winrm not configured - Enable-WSManCredSSP -Role "Client" -DelegateComputer $MDTServer -Force - Invoke-Command -ComputerName $MDTServer -ScriptBlock { Enable-WSManCredSSP Server -Force } - - $SecureStringPassword = ConvertTo-SecureString $CredSSPPassword -AsPlainText -Force - $Credentials = New-Object System.Management.Automation.PSCredential ($CredSSPUserName, $SecureStringPassword) - - #Configure WDS - Invoke-Command -ComputerName $MDTServer -Credential $Credentials -Authentication Credssp -ScriptBlock { - $MDTServer=$using:MDTServer - wdsutil /initialize-server /reminst:$using:WDSRoot - wdsutil /start-server - wdsutil.exe /Set-Server /AnswerClients:Known - #WDSUTIL /Set-Server /AnswerClients:Known /ResponseDelay:4 - WDSUTIL /Set-Server /PxePromptPolicy /known:Noprompt /new:abort - #WDSUTIL /Set-Server /PxePromptPolicy /known:Noprompt /new:Noprompt - } +#install WDS +Install-WindowsFeature -Name WDS -ComputerName $MDTServer -IncludeManagementTools -IncludeAllSubFeature + +# Temporarily enable CredSSP delegation to avoid double-hop issue +winrm quickconfig -force #on client is winrm not configured +Enable-WSManCredSSP -Role "Client" -DelegateComputer $MDTServer -Force +Invoke-Command -ComputerName $MDTServer -ScriptBlock { Enable-WSManCredSSP Server -Force } + +$SecureStringPassword = ConvertTo-SecureString $CredSSPPassword -AsPlainText -Force +$Credentials = New-Object System.Management.Automation.PSCredential ($CredSSPUserName, $SecureStringPassword) + +#Configure WDS +Invoke-Command -ComputerName $MDTServer -Credential $Credentials -Authentication Credssp -ScriptBlock { + $MDTServer=$using:MDTServer + wdsutil /initialize-server /reminst:$using:WDSRoot + wdsutil /start-server + wdsutil.exe /Set-Server /AnswerClients:Known + #WDSUTIL /Set-Server /AnswerClients:Known /ResponseDelay:4 + WDSUTIL /Set-Server /PxePromptPolicy /known:Noprompt /new:abort + #WDSUTIL /Set-Server /PxePromptPolicy /known:Noprompt /new:Noprompt +} - #import the boot media to WDS - Invoke-Command -ComputerName $MDTServer -Credential $Credentials -Authentication Credssp -ScriptBlock { - Get-WdsBootImage | Remove-WdsBootImage - Import-wdsbootimage -path "$($using:DeploymentShareLocation)\Boot\LiteTouchPE_x64.wim" -Verbose - } +#import the boot media to WDS +Invoke-Command -ComputerName $MDTServer -Credential $Credentials -Authentication Credssp -ScriptBlock { + Get-WdsBootImage | Remove-WdsBootImage + Import-wdsbootimage -path "$($using:DeploymentShareLocation)\Boot\LiteTouchPE_x64.wim" -Verbose +} - #Disable CredSSP - Disable-WSManCredSSP -Role Client - Invoke-Command -ComputerName $MDTServer -ScriptBlock {Disable-WSManCredSSP Server} +#Disable CredSSP +Disable-WSManCredSSP -Role Client +Invoke-Command -ComputerName $MDTServer -ScriptBlock {Disable-WSManCredSSP Server} - - #Mitigate issue with Variable Window Extension - Invoke-Command -ComputerName $MDTServer -ScriptBlock { - Wdsutil /Set-TransportServer /EnableTftpVariableWindowExtension:No - } - #In case you have +#Mitigate issue with Variable Window Extension +Invoke-Command -ComputerName $MDTServer -ScriptBlock { + Wdsutil /Set-TransportServer /EnableTftpVariableWindowExtension:No +} + +#In case you have #endregion #region configure MDT Monitoring - Invoke-Command -ComputerName $MDTServer -ScriptBlock { - if (-not(get-module MicrosoftDeploymentToolkit)){ - Import-Module "C:\Program Files\Microsoft Deployment Toolkit\bin\MicrosoftDeploymentToolkit.psd1" - } - if (-not(Get-PSDrive -Name ds001 -ErrorAction Ignore)){ - New-PSDrive -Name "DS001" -PSProvider "MDTProvider" -Root "$using:DeploymentShareLocation" -Description "MDT Deployment Share" -NetworkPath "\\$using:MDTServer\DeploymentShare$" -Verbose | add-MDTPersistentDrive -Verbose - } - #configure ports in MDT - Set-ItemProperty DS001:\ -name MonitorHost -value $using:MDTServer - #enable service - Enable-MDTMonitorService -EventPort 9800 -DataPort 9801 +Invoke-Command -ComputerName $MDTServer -ScriptBlock { + if (-not(get-module MicrosoftDeploymentToolkit)){ + Import-Module "C:\Program Files\Microsoft Deployment Toolkit\bin\MicrosoftDeploymentToolkit.psd1" } + if (-not(Get-PSDrive -Name ds001 -ErrorAction Ignore)){ + New-PSDrive -Name "DS001" -PSProvider "MDTProvider" -Root "$using:DeploymentShareLocation" -Description "MDT Deployment Share" -NetworkPath "\\$using:MDTServer\DeploymentShare$" -Verbose | add-MDTPersistentDrive -Verbose + } + #configure ports in MDT + Set-ItemProperty DS001:\ -name MonitorHost -value $using:MDTServer + #enable service + Enable-MDTMonitorService -EventPort 9800 -DataPort 9801 +} - #add firewall rule - New-NetFirewallRule ` - -CimSession $MDTServer ` - -Action Allow ` - -Name "MDT-Monitoring-In-TCP" ` - -DisplayName "MDT (Monitoring-In-TCP)" ` - -Description "Inbound rule for MDT Monitoring. [TCP-9800,9801]" ` - -Enabled True ` - -Direction Inbound ` - -Program "System" ` - -Protocol UDP ` - -LocalPort 9800,9801 ` - -Profile Any ` - -Group "MDT" ` - -RemoteAddress Any +#add firewall rule +New-NetFirewallRule ` + -CimSession $MDTServer ` + -Action Allow ` + -Name "MDT-Monitoring-In-TCP" ` + -DisplayName "MDT (Monitoring-In-TCP)" ` + -Description "Inbound rule for MDT Monitoring. [TCP-9800,9801]" ` + -Enabled True ` + -Direction Inbound ` + -Program "System" ` + -Protocol UDP ` + -LocalPort 9800,9801 ` + -Profile Any ` + -Group "MDT" ` + -RemoteAddress Any #endregion #region replace customsettings.ini with all DB data to query (wizard output) - if ($Connection -eq "NamedPipes"){ - $Netlib="DBNMPNTW" - }elseif($Connection -eq "TCPIP"){ - $Netlib="DBMSSOCN" - $Creds=@" +if ($Connection -eq "NamedPipes"){ + $Netlib="DBNMPNTW" +}elseif($Connection -eq "TCPIP"){ + $Netlib="DBMSSOCN" + $Creds=@" DBID=MDTSQLUser DBPwd=LS1setup! "@ } - $content=@" +$content=@" [Settings] Priority=CSettings, CPackages, CApps, CAdmins, CRoles, Locations, LSettings, LPackages, LApps, LAdmins, LRoles, MMSettings, MMPackages, MMApps, MMAdmins, MMRoles, RSettings, RPackages, RApps, RAdmins, Default Properties=MyCustomProperty @@ -587,15 +587,16 @@ $text = [IO.File]::ReadAllText($CustomSettingsFile) -replace "`n", "`r`n" #endregion #region configure SQL to be able to access it remotely using MDTUser account(NamedPipes) or create dedicated SQL user (TCPIP) - #Add permissions for MDT account to sql database - Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force - Install-Module -Name sqlserver -AllowClobber -Force - if ((Get-ExecutionPolicy) -eq "Restricted"){ - Set-ExecutionPolicy -Scope Process -ExecutionPolicy RemoteSigned - } - if ($Connection -eq "NamedPipes"){ - #Named Pipes - $sqlscript=@" +#Add permissions for MDT account to sql database +#note: added -TrustServerCertificate as I saw an error since 05/2023 when invoke-sqlcmd ran. "The certificate chain was issued by an authority that is not trusted..." +Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force +Install-Module -Name sqlserver -AllowClobber -Force +if ((Get-ExecutionPolicy) -eq "Restricted"){ + Set-ExecutionPolicy -Scope Process -ExecutionPolicy RemoteSigned +} +if ($Connection -eq "NamedPipes"){ + #Named Pipes +$sqlscript=@" USE [master] GO CREATE LOGIN [$env:userdomain\MDTUser] FROM WINDOWS WITH DEFAULT_DATABASE=[MDTDB] @@ -610,11 +611,11 @@ ALTER ROLE [db_datareader] ADD MEMBER [$env:userdomain\mdtuser] GO "@ - Invoke-Sqlcmd -ServerInstance $MDTServer\sqlexpress -Database MDTDB -Query $sqlscript +Invoke-Sqlcmd -ServerInstance $MDTServer\sqlexpress -Database MDTDB -Query $sqlscript -TrustServerCertificate }elseif($Connection -eq "TCPIP"){ - #TCP (add user and change authentication mode to be able to use both SQL and Windows Auth - $sqlscript=@" +#TCP (add user and change authentication mode to be able to use both SQL and Windows Auth +$sqlscript=@" USE [master] GO CREATE LOGIN [MDTSQLUser] WITH PASSWORD='LS1setup!', DEFAULT_DATABASE=[MDTDB] @@ -631,12 +632,12 @@ EXEC xp_instance_regwrite N'HKEY_LOCAL_MACHINE', N'Software\Microsoft\MSSQLServe GO "@ - #TCP - Invoke-Sqlcmd -ServerInstance "tcp:$MDTServer" -Database MDTDB -Query $sqlscript - #restart service to apply mixed auth mode - Invoke-Command -ComputerName $MDTServer -scriptblock { - Restart-Service 'MSSQL$SQLEXPRESS' - } +#TCP +Invoke-Sqlcmd -ServerInstance "tcp:$MDTServer" -Database MDTDB -Query $sqlscript -TrustServerCertificate +#restart service to apply mixed auth mode +Invoke-Command -ComputerName $MDTServer -scriptblock { + Restart-Service 'MSSQL$SQLEXPRESS' +} } #endregion @@ -647,38 +648,38 @@ GO ##################################### #region Run from Hyper-V Host to create new, empty VMs - #some variables - $LabPrefix="MSLab20348.169-" - $vSwitchName="$($LabPrefix)LabSwitch" - $VMsPath="D:\MSLab20348.169\LAB\VMs" - $VMNames="AzHCI1","AzHCI2","AzHCI3","AzHCI4" - $NumberOfHDDs=4 - $SizeOfHDD=4TB - $MemoryStartupBytes=4GB - #create some blank VMs - foreach ($VMName in $VMNames){ - $VMName="$LabPrefix$VMName" - New-VM -Name $VMName -NewVHDPath "$VMsPath\$VMName\Virtual Hard Disks\$VMName.vhdx" -NewVHDSizeBytes 128GB -SwitchName $vSwitchName -Generation 2 -Path "$VMsPath" -MemoryStartupBytes $MemoryStartupBytes - 1..$NumberOfHDDs | ForEach-Object { - $VHD=New-VHD -Path "$VMsPath\$VMName\Virtual Hard Disks\HDD$_.vhdx" -SizeBytes $SizeOfHDD - Add-VMHardDiskDrive -VMName $VMName -Path "$VMsPath\$VMName\Virtual Hard Disks\HDD$_.vhdx" - } - #Add Adapter - Add-VMNetworkAdapter -VMName $VMName -SwitchName $vSwitchName - #configure Nested Virt and 2 cores - Set-VMProcessor -ExposeVirtualizationExtensions $true -VMName $VMName -Count 2 - #configure Memory - Set-VMMemory -VMName $VMName -DynamicMemoryEnabled $false - #configure network adapters - Set-VMNetworkAdapter -VMName $VMName -AllowTeaming On -MacAddressSpoofing On - Set-VMNetworkAdapterVlan -VMName $VMName -Trunk -NativeVlanId 0 -AllowedVlanIdList "1-10" - #disable automatic checkpoints - if ((get-vm -VMName $VMName).AutomaticCheckpointsEnabled -eq $True){ - Set-VM -Name $VMName -AutomaticCheckpointsEnabled $False - } - #Start VM - Start-VM -Name $VMName - } +#some variables +$LabPrefix="MSLab20348.169-" +$vSwitchName="$($LabPrefix)LabSwitch" +$VMsPath="D:\MSLab20348.169\LAB\VMs" +$VMNames="AzHCI1","AzHCI2","AzHCI3","AzHCI4" +$NumberOfHDDs=4 +$SizeOfHDD=4TB +$MemoryStartupBytes=4GB +#create some blank VMs +foreach ($VMName in $VMNames){ + $VMName="$LabPrefix$VMName" + New-VM -Name $VMName -NewVHDPath "$VMsPath\$VMName\Virtual Hard Disks\$VMName.vhdx" -NewVHDSizeBytes 128GB -SwitchName $vSwitchName -Generation 2 -Path "$VMsPath" -MemoryStartupBytes $MemoryStartupBytes + 1..$NumberOfHDDs | ForEach-Object { + $VHD=New-VHD -Path "$VMsPath\$VMName\Virtual Hard Disks\HDD$_.vhdx" -SizeBytes $SizeOfHDD + Add-VMHardDiskDrive -VMName $VMName -Path "$VMsPath\$VMName\Virtual Hard Disks\HDD$_.vhdx" + } + #Add Adapter + Add-VMNetworkAdapter -VMName $VMName -SwitchName $vSwitchName + #configure Nested Virt and 2 cores + Set-VMProcessor -ExposeVirtualizationExtensions $true -VMName $VMName -Count 2 + #configure Memory + Set-VMMemory -VMName $VMName -DynamicMemoryEnabled $false + #configure network adapters + Set-VMNetworkAdapter -VMName $VMName -AllowTeaming On -MacAddressSpoofing On + Set-VMNetworkAdapterVlan -VMName $VMName -Trunk -NativeVlanId 0 -AllowedVlanIdList "1-10" + #disable automatic checkpoints + if ((get-vm -VMName $VMName).AutomaticCheckpointsEnabled -eq $True){ + Set-VM -Name $VMName -AutomaticCheckpointsEnabled $False + } + #Start VM + Start-VM -Name $VMName +} #endregion @@ -687,123 +688,123 @@ GO ######################################## #region Create hash table out of machines that attempted boot last 5 minutes - #in real world scenairos you can have hash table like this: - <# +#in real world scenairos you can have hash table like this: +<# +$HVHosts = @() +$HVHosts+=@{ComputerName="AzSHCI1" ;IPAddress="10.0.0.101" ; MACAddress="00:15:5D:01:20:3B" ; GUID="FFB429BB-1521-47C6-88BF-F5F2D1BA17F5"} +$HVHosts+=@{ComputerName="AzSHCI2" ;IPAddress="10.0.0.102" ; MACAddress="00:15:5D:01:20:3E" ; GUID="056423D7-6BAC-460F-AD0D-78AF6D26E151"} +$HVHosts+=@{ComputerName="AzSHCI3" ;IPAddress="10.0.0.103" ; MACAddress="00:15:5D:01:20:40" ; GUID="D1DF2157-8EE0-4C7A-A0FD-6E7779C62FCE"} +$HVHosts+=@{ComputerName="AzSHCI4" ;IPAddress="10.0.0.104" ; MACAddress="00:15:5D:01:20:42" ; GUID="7392BBB5-99D7-4EEE-9C22-4CA6EB6058FB"} +#> + +#grab machines that attempted to boot in last 5 minutes and create hash table. +$HVHosts=Invoke-Command -ComputerName $MDTServer -ScriptBlock { + $IpaddressScope="10.0.0." + $IPAddressStart=101 #starting this number IPs will be asigned + $ServersNamePrefix="AzSHCI" + $events=Get-WinEvent -FilterHashtable @{LogName="Microsoft-Windows-Deployment-Services-Diagnostics/Operational";Id=4132;StartTime=(get-date).AddMinutes(-5)} | Where-Object Message -like "*it is not recognized*" | Sort-Object TimeCreated $HVHosts = @() - $HVHosts+=@{ComputerName="AzSHCI1" ;IPAddress="10.0.0.101" ; MACAddress="00:15:5D:01:20:3B" ; GUID="FFB429BB-1521-47C6-88BF-F5F2D1BA17F5"} - $HVHosts+=@{ComputerName="AzSHCI2" ;IPAddress="10.0.0.102" ; MACAddress="00:15:5D:01:20:3E" ; GUID="056423D7-6BAC-460F-AD0D-78AF6D26E151"} - $HVHosts+=@{ComputerName="AzSHCI3" ;IPAddress="10.0.0.103" ; MACAddress="00:15:5D:01:20:40" ; GUID="D1DF2157-8EE0-4C7A-A0FD-6E7779C62FCE"} - $HVHosts+=@{ComputerName="AzSHCI4" ;IPAddress="10.0.0.104" ; MACAddress="00:15:5D:01:20:42" ; GUID="7392BBB5-99D7-4EEE-9C22-4CA6EB6058FB"} - #> - - #grab machines that attempted to boot in last 5 minutes and create hash table. - $HVHosts=Invoke-Command -ComputerName $MDTServer -ScriptBlock { - $IpaddressScope="10.0.0." - $IPAddressStart=101 #starting this number IPs will be asigned - $ServersNamePrefix="AzSHCI" - $events=Get-WinEvent -FilterHashtable @{LogName="Microsoft-Windows-Deployment-Services-Diagnostics/Operational";Id=4132;StartTime=(get-date).AddMinutes(-5)} | Where-Object Message -like "*it is not recognized*" | Sort-Object TimeCreated - $HVHosts = @() - $GUIDS=@() - $i=1 - foreach ($event in $events){ - [System.Diagnostics.Eventing.Reader.EventLogRecord]$event=$event - if (!($guids).Contains($event.properties.value[2])){ - $HVHosts+= @{ ComputerName="$ServersNamePrefix$i";GUID = $event.properties.value[2] -replace '[{}]' ; MACAddress = $event.properties.value[0] -replace "-",":" ; IPAddress="$IpaddressScope$($IPAddressStart.tostring())"} - $i++ - $IPAddressStart++ - $GUIDS+=$event.properties.value[2] - } + $GUIDS=@() + $i=1 + foreach ($event in $events){ + [System.Diagnostics.Eventing.Reader.EventLogRecord]$event=$event + if (!($guids).Contains($event.properties.value[2])){ + $HVHosts+= @{ ComputerName="$ServersNamePrefix$i";GUID = $event.properties.value[2] -replace '[{}]' ; MACAddress = $event.properties.value[0] -replace "-",":" ; IPAddress="$IpaddressScope$($IPAddressStart.tostring())"} + $i++ + $IPAddressStart++ + $GUIDS+=$event.properties.value[2] } - Return $HVHosts } + Return $HVHosts +} $HVHosts #endregion #region create DHCP reservation for machines - #Create DHCP reservations for Hyper-V hosts - #Add DHCP Reservations - foreach ($HVHost in $HVHosts){ - if (!(Get-DhcpServerv4Reservation -ErrorAction SilentlyContinue -ComputerName $DHCPServer -ScopeId $ScopeID -ClientId ($HVHost.MACAddress).Replace(":","") | Where-Object IPAddress -eq $HVHost.IPAddress)){ - Add-DhcpServerv4Reservation -ComputerName $DHCPServer -ScopeId $ScopeID -IPAddress $HVHost.IPAddress -ClientId ($HVHost.MACAddress).Replace(":","") - } +#Create DHCP reservations for Hyper-V hosts + #Add DHCP Reservations + foreach ($HVHost in $HVHosts){ + if (!(Get-DhcpServerv4Reservation -ErrorAction SilentlyContinue -ComputerName $DHCPServer -ScopeId $ScopeID -ClientId ($HVHost.MACAddress).Replace(":","") | Where-Object IPAddress -eq $HVHost.IPAddress)){ + Add-DhcpServerv4Reservation -ComputerName $DHCPServer -ScopeId $ScopeID -IPAddress $HVHost.IPAddress -ClientId ($HVHost.MACAddress).Replace(":","") } + } - #configure NTP server in DHCP (might be useful if Servers have issues with time) - if (!(get-DhcpServerv4OptionValue -ComputerName $DHCPServer -ScopeId $ScopeID -OptionId 042 -ErrorAction SilentlyContinue)){ - Set-DhcpServerv4OptionValue -ComputerName $DHCPServer -ScopeId $ScopeID -OptionId 042 -Value "10.0.0.1" - } +#configure NTP server in DHCP (might be useful if Servers have issues with time) + if (!(get-DhcpServerv4OptionValue -ComputerName $DHCPServer -ScopeId $ScopeID -OptionId 042 -ErrorAction SilentlyContinue)){ + Set-DhcpServerv4OptionValue -ComputerName $DHCPServer -ScopeId $ScopeID -OptionId 042 -Value "10.0.0.1" + } #endregion #region add deploy info to AD Object and MDT Database - #download and unzip mdtdb (blog available in web.archive only https://web.archive.org/web/20190421025144/https://blogs.technet.microsoft.com/mniehaus/2009/05/14/manipulating-the-microsoft-deployment-toolkit-database-using-powershell/) - #Start-BitsTransfer -Source https://msdnshared.blob.core.windows.net/media/TNBlogsFS/prod.evol.blogs.technet.com/telligent.evolution.components.attachments/01/5209/00/00/03/24/15/04/MDTDB.zip -Destination $env:USERPROFILE\Downloads\MDTDB.zip - Start-BitsTransfer -Source https://github.com/microsoft/MSLab/raw/master/Scenarios/AzSHCI%20and%20MDT/MDTDB.zip -Destination $env:USERPROFILE\Downloads\MDTDB.zip +#download and unzip mdtdb (blog available in web.archive only https://web.archive.org/web/20190421025144/https://blogs.technet.microsoft.com/mniehaus/2009/05/14/manipulating-the-microsoft-deployment-toolkit-database-using-powershell/) +#Start-BitsTransfer -Source https://msdnshared.blob.core.windows.net/media/TNBlogsFS/prod.evol.blogs.technet.com/telligent.evolution.components.attachments/01/5209/00/00/03/24/15/04/MDTDB.zip -Destination $env:USERPROFILE\Downloads\MDTDB.zip +Start-BitsTransfer -Source https://github.com/microsoft/MSLab/raw/master/Scenarios/AzSHCI%20and%20MDT/MDTDB.zip -Destination $env:USERPROFILE\Downloads\MDTDB.zip - Expand-Archive -Path $env:USERPROFILE\Downloads\MDTDB.zip -DestinationPath $env:USERPROFILE\Downloads\MDTDB\ - if ((Get-ExecutionPolicy) -eq "Restricted"){ - Set-ExecutionPolicy -Scope Process -ExecutionPolicy RemoteSigned +Expand-Archive -Path $env:USERPROFILE\Downloads\MDTDB.zip -DestinationPath $env:USERPROFILE\Downloads\MDTDB\ +if ((Get-ExecutionPolicy) -eq "Restricted"){ + Set-ExecutionPolicy -Scope Process -ExecutionPolicy RemoteSigned +} +Import-Module $env:USERPROFILE\Downloads\MDTDB\MDTDB.psm1 +#make sure DS is connected + if (-not(get-module MicrosoftDeploymentToolkit)){ + Import-Module "C:\Program Files\Microsoft Deployment Toolkit\bin\MicrosoftDeploymentToolkit.psd1" } - Import-Module $env:USERPROFILE\Downloads\MDTDB\MDTDB.psm1 - #make sure DS is connected - if (-not(get-module MicrosoftDeploymentToolkit)){ - Import-Module "C:\Program Files\Microsoft Deployment Toolkit\bin\MicrosoftDeploymentToolkit.psd1" - } - if (-not(Get-PSDrive -Name ds001 -ErrorAction Ignore)){ - New-PSDrive -Name "DS001" -PSProvider "MDTProvider" -Root "\\$MDTServer\DeploymentShare$" -Description "MDT Deployment Share" -NetworkPath "\\$MDTServer\DeploymentShare$" -Verbose | add-MDTPersistentDrive -Verbose - } - #Connect to DB - #Connect-MDTDatabase -database mdtdb -sqlServer $MDTServer -instance SQLExpress - Connect-MDTDatabase -drivePath "DS001:\" + if (-not(Get-PSDrive -Name ds001 -ErrorAction Ignore)){ + New-PSDrive -Name "DS001" -PSProvider "MDTProvider" -Root "\\$MDTServer\DeploymentShare$" -Description "MDT Deployment Share" -NetworkPath "\\$MDTServer\DeploymentShare$" -Verbose | add-MDTPersistentDrive -Verbose + } +#Connect to DB + #Connect-MDTDatabase -database mdtdb -sqlServer $MDTServer -instance SQLExpress + Connect-MDTDatabase -drivePath "DS001:\" - #add hosts to MDT DB - foreach ($HVHost in $HVHosts){ - if (-not(Get-AdComputer -Filter "Name -eq `"$($HVHost.ComputerName)`"")){ - New-ADComputer -Name $hvhost.ComputerName - } - #add to MDT DB - if (-not (Get-MDTComputer -macAddress $HVHost.MACAddress)){ - New-MDTComputer -macAddress $HVHost.MACAddress -description $HVHost.ComputerName -uuid $HVHost.GUID -settings @{ - ComputerName = $HVHost.ComputerName - OSDComputerName = $HVHost.ComputerName - #SkipBDDWelcome = 'Yes' - } +#add hosts to MDT DB +foreach ($HVHost in $HVHosts){ + if (-not(Get-AdComputer -Filter "Name -eq `"$($HVHost.ComputerName)`"")){ + New-ADComputer -Name $hvhost.ComputerName + } + #add to MDT DB + if (-not (Get-MDTComputer -macAddress $HVHost.MACAddress)){ + New-MDTComputer -macAddress $HVHost.MACAddress -description $HVHost.ComputerName -uuid $HVHost.GUID -settings @{ + ComputerName = $HVHost.ComputerName + OSDComputerName = $HVHost.ComputerName + #SkipBDDWelcome = 'Yes' } - Get-MDTComputer -macAddress $HVHost.MACAddress | Set-MDTComputerRole -roles JoinDomain,AZSHCI } + Get-MDTComputer -macAddress $HVHost.MACAddress | Set-MDTComputerRole -roles JoinDomain,AZSHCI +} - #Configure MDT DB Roles - if (-not (Get-MDTRole -name azshci)){ - New-MDTRole -name AZSHCI -settings @{ - SkipTaskSequence = 'YES' - SkipWizard = 'YES' - SkipSummary = 'YES' - SkipApplications = 'YES' - TaskSequenceID = 'AZSHCI' - SkipFinalSummary = 'YES' - FinishAction = 'LOGOFF' - } +#Configure MDT DB Roles + if (-not (Get-MDTRole -name azshci)){ + New-MDTRole -name AZSHCI -settings @{ + SkipTaskSequence = 'YES' + SkipWizard = 'YES' + SkipSummary = 'YES' + SkipApplications = 'YES' + TaskSequenceID = 'AZSHCI' + SkipFinalSummary = 'YES' + FinishAction = 'LOGOFF' } + } - if (-not (Get-MDTRole -name JoinDomain)){ - New-MDTRole -name JoinDomain -settings @{ - SkipComputerName ='YES' - SkipDomainMembership='YES' - JoinDomain = $env:USERDNSDomain - DomainAdmin ='MDTUser' - DomainAdminDomain = $env:userdomain - DomainAdminPassword ='LS1setup!' - } + if (-not (Get-MDTRole -name JoinDomain)){ + New-MDTRole -name JoinDomain -settings @{ + SkipComputerName ='YES' + SkipDomainMembership='YES' + JoinDomain = $env:USERDNSDomain + DomainAdmin ='MDTUser' + DomainAdminDomain = $env:userdomain + DomainAdminPassword ='LS1setup!' } - - #allow machines to boot from PXE from DC by adding info into AD Object - foreach ($HVHost in $HVHosts){ - [guid]$guid=$HVHost.GUID - Set-ADComputer -identity $hvhost.ComputerName -replace @{netbootGUID = $guid} - #Set-ADComputer -identity $hvhost.ComputerName -replace @{netbootMachineFilePath = "DC"} } +#allow machines to boot from PXE from DC by adding info into AD Object +foreach ($HVHost in $HVHosts){ + [guid]$guid=$HVHost.GUID + Set-ADComputer -identity $hvhost.ComputerName -replace @{netbootGUID = $guid} + #Set-ADComputer -identity $hvhost.ComputerName -replace @{netbootMachineFilePath = "DC"} +} + #endregion ################################################ @@ -811,11 +812,11 @@ $HVHosts ################################################ #region remove pxe boot after install is done - foreach ($HVHost in $HVHosts){ - [guid]$guid=$HVHost.GUID - Set-ADComputer -identity $hvhost.ComputerName -remove @{netbootGUID = $guid} - Set-ADComputer -identity $hvhost.ComputerName -remove @{netbootMachineFilePath = "DC"} - } +foreach ($HVHost in $HVHosts){ + [guid]$guid=$HVHost.GUID + Set-ADComputer -identity $hvhost.ComputerName -remove @{netbootGUID = $guid} + Set-ADComputer -identity $hvhost.ComputerName -remove @{netbootMachineFilePath = "DC"} +} #endregion ############################################################################################################################################ @@ -835,59 +836,59 @@ $Headers=@{"Accept"="application/json"} $ContentType='application/json' function Ignore-SSLCertificates { - $Provider = New-Object Microsoft.CSharp.CSharpCodeProvider - $Compiler = $Provider.CreateCompiler() - $Params = New-Object System.CodeDom.Compiler.CompilerParameters - $Params.GenerateExecutable = $false - $Params.GenerateInMemory = $true - $Params.IncludeDebugInformation = $false - $Params.ReferencedAssemblies.Add("System.DLL") > $null - $TASource=@' - namespace Local.ToolkitExtensions.Net.CertificatePolicy +$Provider = New-Object Microsoft.CSharp.CSharpCodeProvider +$Compiler = $Provider.CreateCompiler() +$Params = New-Object System.CodeDom.Compiler.CompilerParameters +$Params.GenerateExecutable = $false +$Params.GenerateInMemory = $true +$Params.IncludeDebugInformation = $false +$Params.ReferencedAssemblies.Add("System.DLL") > $null +$TASource=@' + namespace Local.ToolkitExtensions.Net.CertificatePolicy + { + public class TrustAll : System.Net.ICertificatePolicy { - public class TrustAll : System.Net.ICertificatePolicy + public bool CheckValidationResult(System.Net.ServicePoint sp,System.Security.Cryptography.X509Certificates.X509Certificate cert, System.Net.WebRequest req, int problem) { - public bool CheckValidationResult(System.Net.ServicePoint sp,System.Security.Cryptography.X509Certificates.X509Certificate cert, System.Net.WebRequest req, int problem) - { - return true; - } + return true; } } + } '@ - $TAResults=$Provider.CompileAssemblyFromSource($Params,$TASource) - $TAAssembly=$TAResults.CompiledAssembly - $TrustAll = $TAAssembly.CreateInstance("Local.ToolkitExtensions.Net.CertificatePolicy.TrustAll") - [System.Net.ServicePointManager]::CertificatePolicy = $TrustAll +$TAResults=$Provider.CompileAssemblyFromSource($Params,$TASource) +$TAAssembly=$TAResults.CompiledAssembly +$TrustAll = $TAAssembly.CreateInstance("Local.ToolkitExtensions.Net.CertificatePolicy.TrustAll") +[System.Net.ServicePointManager]::CertificatePolicy = $TrustAll } #ignoring cert is needed for posh5. In 6 and newer you can just add -SkipCertificateCheck Ignore-SSLCertificates #reboot machines foreach ($idrac_ip in $idrac_ips){ - #Configure PXE for next reboot - $JsonBody = @{ Boot = @{ - "BootSourceOverrideTarget"="Pxe" - }} | ConvertTo-Json -Compress - $uri = "https://$idrac_ip/redfish/v1/Systems/System.Embedded.1" - Invoke-RestMethod -Body $JsonBody -Method Patch -ContentType $ContentType -Headers $Headers -Uri $uri -Credential $Credentials - - #Validate - $uri="https://$idrac_ip/redfish/v1/Systems/System.Embedded.1/" - $Result=Invoke-RestMethod -Method Get -ContentType $ContentType -Headers $Headers -Uri $uri -Credential $Credentials - $Result.Boot.BootSourceOverrideTarget - - #check reboot options - #$uri="https://$idrac_ip/redfish/v1/Systems/System.Embedded.1/" - #$Result=Invoke-RestMethod -Method Get -ContentType $ContentType -Headers $Headers -Uri $uri -Credential $Credentials - #$Result.Actions.'#ComputerSystem.Reset'.'ResetType@Redfish.AllowableValues' - - #reboot - #possible values: On,ForceOff,ForceRestart,GracefulShutdown,PushPowerButton,Nmi,PowerCycle - $JsonBody = @{ "ResetType" = "ForceRestart"} | ConvertTo-Json -Compress - $uri = "https://$idrac_ip/redfish/v1/Systems/System.Embedded.1/Actions/ComputerSystem.Reset" - Invoke-RestMethod -Body $JsonBody -Method Post -ContentType $ContentType -Headers $Headers -Uri $uri -Credential $Credentials - - Start-Sleep 10 +#Configure PXE for next reboot +$JsonBody = @{ Boot = @{ + "BootSourceOverrideTarget"="Pxe" + }} | ConvertTo-Json -Compress +$uri = "https://$idrac_ip/redfish/v1/Systems/System.Embedded.1" +Invoke-RestMethod -Body $JsonBody -Method Patch -ContentType $ContentType -Headers $Headers -Uri $uri -Credential $Credentials + +#Validate +$uri="https://$idrac_ip/redfish/v1/Systems/System.Embedded.1/" +$Result=Invoke-RestMethod -Method Get -ContentType $ContentType -Headers $Headers -Uri $uri -Credential $Credentials +$Result.Boot.BootSourceOverrideTarget + +#check reboot options +#$uri="https://$idrac_ip/redfish/v1/Systems/System.Embedded.1/" +#$Result=Invoke-RestMethod -Method Get -ContentType $ContentType -Headers $Headers -Uri $uri -Credential $Credentials +#$Result.Actions.'#ComputerSystem.Reset'.'ResetType@Redfish.AllowableValues' + +#reboot +#possible values: On,ForceOff,ForceRestart,GracefulShutdown,PushPowerButton,Nmi,PowerCycle +$JsonBody = @{ "ResetType" = "ForceRestart"} | ConvertTo-Json -Compress +$uri = "https://$idrac_ip/redfish/v1/Systems/System.Embedded.1/Actions/ComputerSystem.Reset" +Invoke-RestMethod -Body $JsonBody -Method Post -ContentType $ContentType -Headers $Headers -Uri $uri -Credential $Credentials + +Start-Sleep 10 } #endregion @@ -898,123 +899,118 @@ foreach ($idrac_ip in $idrac_ips){ ####################################################### #region Create hash table out of machines that attempted boot last 5 minutes - #in real world scenairos you can have hash table like this: - <# +#in real world scenairos you can have hash table like this: +<# +$HVHosts = @() +$HVHosts+=@{ComputerName="AxNode1" ;IPAddress="10.0.0.120" ; MACAddress="0C:42:A1:DD:57:DC" ; GUID="4C4C4544-004D-5410-8031-B4C04F373733"} +$HVHosts+=@{ComputerName="AxNode2" ;IPAddress="10.0.0.121" ; MACAddress="0C:42:A1:DD:57:C8" ; GUID="4C4C4544-004D-5410-8033-B4C04F373733"} +$HVHosts+=@{ComputerName="AxNode3" ;IPAddress="10.0.0.122" ; MACAddress="10:70:FD:08:E6:B4" ; GUID="4C4C4544-004D-5810-8046-B2C04F574D33"} +$HVHosts+=@{ComputerName="AxNode4" ;IPAddress="10.0.0.123" ; MACAddress="10:70:FD:08:E6:BC" ; GUID="4C4C4544-004D-5810-8046-B3C04F574D33"} +#> + +#grab machines that attempted to boot in last 5 minutes and create hash table. +$HVHosts=Invoke-Command -ComputerName $MDTServer -ScriptBlock { + $IpaddressScope="10.0.0." + $IPAddressStart=120 #starting this number IPs will be asigned + $ServersNamePrefix="AxNode" + $events=Get-WinEvent -FilterHashtable @{LogName="Microsoft-Windows-Deployment-Services-Diagnostics/Operational";Id=4132;StartTime=(get-date).AddMinutes(-5)} | Where-Object Message -like "*it is not recognized*" | Sort-Object TimeCreated $HVHosts = @() - $HVHosts+=@{ComputerName="AxNode1" ;IPAddress="10.0.0.120" ; MACAddress="0C:42:A1:DD:57:DC" ; GUID="4C4C4544-004D-5410-8031-B4C04F373733"} - $HVHosts+=@{ComputerName="AxNode2" ;IPAddress="10.0.0.121" ; MACAddress="0C:42:A1:DD:57:C8" ; GUID="4C4C4544-004D-5410-8033-B4C04F373733"} - $HVHosts+=@{ComputerName="AxNode3" ;IPAddress="10.0.0.122" ; MACAddress="10:70:FD:08:E6:B4" ; GUID="4C4C4544-004D-5810-8046-B2C04F574D33"} - $HVHosts+=@{ComputerName="AxNode4" ;IPAddress="10.0.0.123" ; MACAddress="10:70:FD:08:E6:BC" ; GUID="4C4C4544-004D-5810-8046-B3C04F574D33"} - #> - - #grab machines that attempted to boot in last 5 minutes and create hash table. - $HVHosts=Invoke-Command -ComputerName $MDTServer -ScriptBlock { - $IpaddressScope="10.0.0." - $IPAddressStart=120 #starting this number IPs will be asigned - $ServersNamePrefix="AxNode" - $events=Get-WinEvent -FilterHashtable @{LogName="Microsoft-Windows-Deployment-Services-Diagnostics/Operational";Id=4132;StartTime=(get-date).AddMinutes(-5)} | Where-Object Message -like "*it is not recognized*" | Sort-Object TimeCreated - $HVHosts = @() - $GUIDS=@() - $i=1 - foreach ($event in $events){ - [System.Diagnostics.Eventing.Reader.EventLogRecord]$event=$event - if (!($guids).Contains($event.properties.value[2])){ - $HVHosts+= @{ ComputerName="$ServersNamePrefix$i";GUID = $event.properties.value[2] -replace '[{}]' ; MACAddress = $event.properties.value[0] -replace "-",":" ; IPAddress="$IpaddressScope$($IPAddressStart.tostring())"} - $i++ - $IPAddressStart++ - $GUIDS+=$event.properties.value[2] - } + $GUIDS=@() + $i=1 + foreach ($event in $events){ + [System.Diagnostics.Eventing.Reader.EventLogRecord]$event=$event + if (!($guids).Contains($event.properties.value[2])){ + $HVHosts+= @{ ComputerName="$ServersNamePrefix$i";GUID = $event.properties.value[2] -replace '[{}]' ; MACAddress = $event.properties.value[0] -replace "-",":" ; IPAddress="$IpaddressScope$($IPAddressStart.tostring())"} + $i++ + $IPAddressStart++ + $GUIDS+=$event.properties.value[2] } - Return $HVHosts } - - + Return $HVHosts +} #endregion #region create DHCP reservation for machines - #Create DHCP reservations for Hyper-V hosts - #Add DHCP Reservations - foreach ($HVHost in $HVHosts){ - if (!(Get-DhcpServerv4Reservation -ErrorAction SilentlyContinue -ComputerName $DHCPServer -ScopeId $ScopeID -ClientId ($HVHost.MACAddress).Replace(":","") | Where-Object IPAddress -eq $HVHost.IPAddress)){ - Add-DhcpServerv4Reservation -ComputerName $DHCPServer -ScopeId $ScopeID -IPAddress $HVHost.IPAddress -ClientId ($HVHost.MACAddress).Replace(":","") - } - } +#Create DHCP reservations for Hyper-V hosts + #Add DHCP Reservations + foreach ($HVHost in $HVHosts){ + Add-DhcpServerv4Reservation -ComputerName $DHCPServer -ScopeId $ScopeID -IPAddress $HVHost.IPAddress -ClientId ($HVHost.MACAddress).Replace(":","") -ErrorAction Ignore + } + +#configure NTP server in DHCP (might be useful if Servers have issues with time) + Set-DhcpServerv4OptionValue -ComputerName $DHCPServer -ScopeId $ScopeID -OptionId 042 -Value "10.0.0.1" -ErrorAction Ignore - #configure NTP server in DHCP (might be useful if Servers have issues with time) - if (!(get-DhcpServerv4OptionValue -ComputerName $DHCPServer -ScopeId $ScopeID -OptionId 042 -ErrorAction SilentlyContinue)){ - Set-DhcpServerv4OptionValue -ComputerName $DHCPServer -ScopeId $ScopeID -OptionId 042 -Value "10.0.0.1" - } #endregion #region add deploy info to AD Object and MDT Database - #download and unzip mdtdb (blog available in web.archive only https://web.archive.org/web/20190421025144/https://blogs.technet.microsoft.com/mniehaus/2009/05/14/manipulating-the-microsoft-deployment-toolkit-database-using-powershell/) - #Start-BitsTransfer -Source https://msdnshared.blob.core.windows.net/media/TNBlogsFS/prod.evol.blogs.technet.com/telligent.evolution.components.attachments/01/5209/00/00/03/24/15/04/MDTDB.zip -Destination $env:USERPROFILE\Downloads\MDTDB.zip - Start-BitsTransfer -Source https://github.com/microsoft/MSLab/raw/master/Scenarios/AzSHCI%20and%20MDT/MDTDB.zip -Destination $env:USERPROFILE\Downloads\MDTDB.zip +#download and unzip mdtdb (blog available in web.archive only https://web.archive.org/web/20190421025144/https://blogs.technet.microsoft.com/mniehaus/2009/05/14/manipulating-the-microsoft-deployment-toolkit-database-using-powershell/) +#Start-BitsTransfer -Source https://msdnshared.blob.core.windows.net/media/TNBlogsFS/prod.evol.blogs.technet.com/telligent.evolution.components.attachments/01/5209/00/00/03/24/15/04/MDTDB.zip -Destination $env:USERPROFILE\Downloads\MDTDB.zip +Start-BitsTransfer -Source https://github.com/microsoft/MSLab/raw/master/Scenarios/AzSHCI%20and%20MDT/MDTDB.zip -Destination $env:USERPROFILE\Downloads\MDTDB.zip - Expand-Archive -Path $env:USERPROFILE\Downloads\MDTDB.zip -DestinationPath $env:USERPROFILE\Downloads\MDTDB\ - if ((Get-ExecutionPolicy) -eq "Restricted"){ - Set-ExecutionPolicy -Scope Process -ExecutionPolicy RemoteSigned +Expand-Archive -Path $env:USERPROFILE\Downloads\MDTDB.zip -DestinationPath $env:USERPROFILE\Downloads\MDTDB\ +if ((Get-ExecutionPolicy) -eq "Restricted"){ + Set-ExecutionPolicy -Scope Process -ExecutionPolicy RemoteSigned +} +Import-Module $env:USERPROFILE\Downloads\MDTDB\MDTDB.psm1 +#make sure DS is connected + if (-not(get-module MicrosoftDeploymentToolkit)){ + Import-Module "C:\Program Files\Microsoft Deployment Toolkit\bin\MicrosoftDeploymentToolkit.psd1" } - Import-Module $env:USERPROFILE\Downloads\MDTDB\MDTDB.psm1 - #make sure DS is connected - if (-not(get-module MicrosoftDeploymentToolkit)){ - Import-Module "C:\Program Files\Microsoft Deployment Toolkit\bin\MicrosoftDeploymentToolkit.psd1" - } - if (-not(Get-PSDrive -Name ds001 -ErrorAction Ignore)){ - New-PSDrive -Name "DS001" -PSProvider "MDTProvider" -Root "\\$MDTServer\DeploymentShare$" -Description "MDT Deployment Share" -NetworkPath "\\$MDTServer\DeploymentShare$" -Verbose | add-MDTPersistentDrive -Verbose - } - #Connect to DB - #Connect-MDTDatabase -database mdtdb -sqlServer $MDTServer -instance SQLExpress - Connect-MDTDatabase -drivePath "DS001:\" + if (-not(Get-PSDrive -Name ds001 -ErrorAction Ignore)){ + New-PSDrive -Name "DS001" -PSProvider "MDTProvider" -Root "\\$MDTServer\DeploymentShare$" -Description "MDT Deployment Share" -NetworkPath "\\$MDTServer\DeploymentShare$" -Verbose | add-MDTPersistentDrive -Verbose + } +#Connect to DB + #Connect-MDTDatabase -database mdtdb -sqlServer $MDTServer -instance SQLExpress + Connect-MDTDatabase -drivePath "DS001:\" - #add hosts to MDT DB - foreach ($HVHost in $HVHosts){ - if (-not(Get-AdComputer -Filter "Name -eq `"$($HVHost.ComputerName)`"")){ - New-ADComputer -Name $hvhost.ComputerName - } - #add to MDT DB - if (-not (Get-MDTComputer -macAddress $HVHost.MACAddress)){ - New-MDTComputer -macAddress $HVHost.MACAddress -description $HVHost.ComputerName -uuid $HVHost.GUID -settings @{ - ComputerName = $HVHost.ComputerName - OSDComputerName = $HVHost.ComputerName - #SkipBDDWelcome = 'Yes' - } +#add hosts to MDT DB +foreach ($HVHost in $HVHosts){ + if (-not(Get-AdComputer -Filter "Name -eq `"$($HVHost.ComputerName)`"")){ + New-ADComputer -Name $hvhost.ComputerName + } + #add to MDT DB + if (-not (Get-MDTComputer -macAddress $HVHost.MACAddress)){ + New-MDTComputer -macAddress $HVHost.MACAddress -description $HVHost.ComputerName -uuid $HVHost.GUID -settings @{ + ComputerName = $HVHost.ComputerName + OSDComputerName = $HVHost.ComputerName + #SkipBDDWelcome = 'Yes' } - Get-MDTComputer -macAddress $HVHost.MACAddress | Set-MDTComputerRole -roles JoinDomain,AZSHCI } + Get-MDTComputer -macAddress $HVHost.MACAddress | Set-MDTComputerRole -roles JoinDomain,AZSHCI +} - #Configure MDT DB Roles - if (-not (Get-MDTRole -name azshci)){ - New-MDTRole -name AZSHCI -settings @{ - SkipTaskSequence = 'YES' - SkipWizard = 'YES' - SkipSummary = 'YES' - SkipApplications = 'YES' - TaskSequenceID = 'AZSHCI' - SkipFinalSummary = 'YES' - FinishAction = 'LOGOFF' - } +#Configure MDT DB Roles + if (-not (Get-MDTRole -name azshci)){ + New-MDTRole -name AZSHCI -settings @{ + SkipTaskSequence = 'YES' + SkipWizard = 'YES' + SkipSummary = 'YES' + SkipApplications = 'YES' + TaskSequenceID = 'AZSHCI' + SkipFinalSummary = 'YES' + FinishAction = 'LOGOFF' } + } - if (-not (Get-MDTRole -name JoinDomain)){ - New-MDTRole -name JoinDomain -settings @{ - SkipComputerName ='YES' - SkipDomainMembership='YES' - JoinDomain = $env:USERDNSDomain - DomainAdmin ='MDTUser' - DomainAdminDomain = $env:userdomain - DomainAdminPassword ='LS1setup!' - } + if (-not (Get-MDTRole -name JoinDomain)){ + New-MDTRole -name JoinDomain -settings @{ + SkipComputerName ='YES' + SkipDomainMembership='YES' + JoinDomain = $env:USERDNSDomain + DomainAdmin ='MDTUser' + DomainAdminDomain = $env:userdomain + DomainAdminPassword ='LS1setup!' } - - #allow machines to boot from PXE from DC by adding info into AD Object - foreach ($HVHost in $HVHosts){ - [guid]$guid=$HVHost.GUID - Set-ADComputer -identity $hvhost.ComputerName -replace @{netbootGUID = $guid} - #Set-ADComputer -identity $hvhost.ComputerName -replace @{netbootMachineFilePath = "DC"} } +#allow machines to boot from PXE from DC by adding info into AD Object +foreach ($HVHost in $HVHosts){ + [guid]$guid=$HVHost.GUID + Set-ADComputer -identity $hvhost.ComputerName -replace @{netbootGUID = $guid} + #Set-ADComputer -identity $hvhost.ComputerName -replace @{netbootMachineFilePath = "DC"} +} + #endregion #region update task sequence with powershell script to install OS to smallest disk right before "New Computer only" group @@ -1023,11 +1019,11 @@ $PSScriptName="OSDDiskIndex.ps1" $PSScriptContent=@' $Disks=Get-CimInstance win32_DiskDrive if ($Disks.model -contains "DELLBOSS VD"){ - #exact model for Dell AX node (DELLBOSS VD) - $TSenv:OSDDiskIndex=($Disks | Where-Object Model -eq "DELLBOSS VD").Index +#exact model for Dell AX node (DELLBOSS VD) +$TSenv:OSDDiskIndex=($Disks | Where-Object Model -eq "DELLBOSS VD").Index }else{ - #or just smallest disk - $TSenv:OSDDiskIndex=($Disks | Where-Object MediaType -eq "Fixed hard disk media" | Sort-Object Size | Select-Object -First 1).Index +#or just smallest disk +$TSenv:OSDDiskIndex=($Disks | Where-Object MediaType -eq "Fixed hard disk media" | Sort-Object Size | Select-Object -First 1).Index } <# In case you need PowerShell and pause Task Sequence you can use this code: #source: http://wiki.wladik.net/windows/mdt/powershell-scripting @@ -1039,24 +1035,24 @@ Start PowerShell #> '@ - #update Tasksequence - $TS=Invoke-Command -ComputerName $MDTServer -ScriptBlock {Get-Content -Path $using:DeploymentShareLocation\Control\$using:TaskSequenceID\ts.xml} - $TextToSearch=' ' - $PoshScript=@" - - - $PSScriptName - - - - cscript.exe "%SCRIPTROOT%\ZTIPowerShell.wsf - +#update Tasksequence +$TS=Invoke-Command -ComputerName $MDTServer -ScriptBlock {Get-Content -Path $using:DeploymentShareLocation\Control\$using:TaskSequenceID\ts.xml} +$TextToSearch=' ' +$PoshScript=@" + + + $PSScriptName + + + + cscript.exe "%SCRIPTROOT%\ZTIPowerShell.wsf + $TextToSearch "@ - $NewTS=$TS.replace($TextToSearch,$PoshScript) - Invoke-Command -ComputerName $MDTServer -ScriptBlock {Set-Content -Path $using:DeploymentShareLocation\Control\$using:TaskSequenceID\ts.xml -Value $using:NewTS} - #insert script - Invoke-Command -ComputerName $MDTServer -ScriptBlock {Set-Content -Path $using:DeploymentShareLocation\Scripts\$using:PSScriptName -Value $using:PSScriptContent} +$NewTS=$TS.replace($TextToSearch,$PoshScript) +Invoke-Command -ComputerName $MDTServer -ScriptBlock {Set-Content -Path $using:DeploymentShareLocation\Control\$using:TaskSequenceID\ts.xml -Value $using:NewTS} +#insert script +Invoke-Command -ComputerName $MDTServer -ScriptBlock {Set-Content -Path $using:DeploymentShareLocation\Scripts\$using:PSScriptName -Value $using:PSScriptContent} #endregion @@ -1065,119 +1061,119 @@ $TextToSearch #Download DSU #https://github.com/DellProSupportGse/Tools/blob/main/DART.ps1 - #grab DSU links from Dell website - $URL="https://dl.dell.com/omimswac/dsu/" - $Results=Invoke-WebRequest $URL -UseDefaultCredentials - $Links=$results.Links.href | Select-Object -Skip 1 - #create PSObject from results - $DSUs=@() - foreach ($Link in $Links){ - $DSUs+=[PSCustomObject]@{ - Link = "https://dl.dell.com$Link" - Version = $link -split "_" | Select-Object -Last 2 | Select-Object -First 1 - } +#grab DSU links from Dell website +$URL="https://dl.dell.com/omimswac/dsu/" +$Results=Invoke-WebRequest $URL -UseDefaultCredentials +$Links=$results.Links.href | Select-Object -Skip 1 +#create PSObject from results +$DSUs=@() +foreach ($Link in $Links){ + $DSUs+=[PSCustomObject]@{ + Link = "https://dl.dell.com$Link" + Version = $link -split "_" | Select-Object -Last 2 | Select-Object -First 1 } - #download latest to separate folder - $LatestDSU=$DSUs | Sort-Object Version | Select-Object -Last 1 - $Folder="$env:USERPROFILE\Downloads\DSU" - if (-not (Test-Path $Folder)){New-Item -Path $Folder -ItemType Directory} - Start-BitsTransfer -Source $LatestDSU.Link -Destination $Folder\DSU.exe - - #add DSU as application to MDT - Import-Module "C:\Program Files\Microsoft Deployment Toolkit\bin\MicrosoftDeploymentToolkit.psd1" - if (-not(Get-PSDrive -Name ds001 -ErrorAction Ignore)){ - New-PSDrive -Name "DS001" -PSProvider "MDTProvider" -Root "\\$MDTServer\DeploymentShare$" -Description "MDT Deployment Share" -NetworkPath "\\$MDTServer\DeploymentShare$" -Verbose | add-MDTPersistentDrive -Verbose - } - $AppName="Dell DSU $($LatestDSU.Version)" - Import-MDTApplication -path "DS001:\Applications" -enable "True" -Name $AppName -ShortName "DSU" -Version $LatestDSU.Version -Publisher "Dell" -Language "" -CommandLine "DSU.exe /silent" -WorkingDirectory ".\Applications\$AppName" -ApplicationSourcePath $Folder -DestinationFolder $AppName -Verbose - #grap package ID for role config - $DSUID=(Get-ChildItem -Path DS001:\Applications | Where-Object Name -eq $AppName).GUID +} +#download latest to separate folder +$LatestDSU=$DSUs | Sort-Object Version | Select-Object -Last 1 +$Folder="$env:USERPROFILE\Downloads\DSU" +if (-not (Test-Path $Folder)){New-Item -Path $Folder -ItemType Directory} +Start-BitsTransfer -Source $LatestDSU.Link -Destination $Folder\DSU.exe + +#add DSU as application to MDT +Import-Module "C:\Program Files\Microsoft Deployment Toolkit\bin\MicrosoftDeploymentToolkit.psd1" +if (-not(Get-PSDrive -Name ds001 -ErrorAction Ignore)){ + New-PSDrive -Name "DS001" -PSProvider "MDTProvider" -Root "\\$MDTServer\DeploymentShare$" -Description "MDT Deployment Share" -NetworkPath "\\$MDTServer\DeploymentShare$" -Verbose | add-MDTPersistentDrive -Verbose +} +$AppName="Dell DSU $($LatestDSU.Version)" +Import-MDTApplication -path "DS001:\Applications" -enable "True" -Name $AppName -ShortName "DSU" -Version $LatestDSU.Version -Publisher "Dell" -Language "" -CommandLine "DSU.exe /silent" -WorkingDirectory ".\Applications\$AppName" -ApplicationSourcePath $Folder -DestinationFolder $AppName -Verbose +#grap package ID for role config +$DSUID=(Get-ChildItem -Path DS001:\Applications | Where-Object Name -eq $AppName).GUID #download catalog and create answer file to run DSU - #Dell Azure Stack HCI driver catalog https://downloads.dell.com/catalog/ASHCI-Catalog.xml.gz - #Download catalog - Start-BitsTransfer -Source "https://downloads.dell.com/catalog/ASHCI-Catalog.xml.gz" -Destination "$env:UserProfile\Downloads\ASHCI-Catalog.xml.gz" - #unzip gzip to a folder https://scatteredcode.net/download-and-extract-gzip-tar-with-powershell/ - $Folder="$env:USERPROFILE\Downloads\DSUPackage" - if (-not (Test-Path $Folder)){New-Item -Path $Folder -ItemType Directory} - Function Expand-GZipArchive{ - Param( - $infile, - $outfile = ($infile -replace '\.gz$','') - ) - $input = New-Object System.IO.FileStream $inFile, ([IO.FileMode]::Open), ([IO.FileAccess]::Read), ([IO.FileShare]::Read) - $output = New-Object System.IO.FileStream $outFile, ([IO.FileMode]::Create), ([IO.FileAccess]::Write), ([IO.FileShare]::None) - $gzipStream = New-Object System.IO.Compression.GzipStream $input, ([IO.Compression.CompressionMode]::Decompress) - $buffer = New-Object byte[](1024) - while($true){ - $read = $gzipstream.Read($buffer, 0, 1024) - if ($read -le 0){break} - $output.Write($buffer, 0, $read) - } - $gzipStream.Close() - $output.Close() - $input.Close() - } - Expand-GZipArchive "$env:UserProfile\Downloads\ASHCI-Catalog.xml.gz" "$folder\ASHCI-Catalog.xml" - #create answerfile for DU - $content='@ - a - c - @' - Set-Content -Path "$folder\answer.txt" -Value $content -NoNewline - $content='"C:\Program Files\Dell\DELL EMC System Update\DSU.exe" --catalog-location=ASHCI-Catalog.xml --apply-upgrades <# - #Create output folder - $FolderName=$xml.manifest.version - New-Item -ItemType Directory -Name $FolderName -Path "$env:UserProfile\Downloads" -ErrorAction Ignore - New-Item -ItemType Directory -Name "RebootRequired" -Path "$env:UserProfile\Downloads\$FolderName" -ErrorAction Ignore - New-Item -ItemType Directory -Name "RebootNotRequired" -Path "$env:UserProfile\Downloads\$FolderName" -ErrorAction Ignore - #download files - foreach ($item in $xml.manifest.softwarecomponent){ - $filename=$($item.path.split("/")|Select-Object -Last 1) - if ($item.RebootRequired -eq "True"){ - Start-BitsTransfer -Source "https://downloads.dell.com/$($item.path)" -Destination "$env:UserProfile\Downloads\$FolderName\RebootRequired\$filename" -DisplayName "Downloading $filename releasedate $($item.releaseDate)" - }else{ - Start-BitsTransfer -Source "https://downloads.dell.com/$($item.path)" -Destination "$env:UserProfile\Downloads\$FolderName\RebootNotRequired\$filename" -DisplayName "Downloading $filename releasedate $($item.releaseDate)" - } +#Create output folder +$FolderName=$xml.manifest.version +New-Item -ItemType Directory -Name $FolderName -Path "$env:UserProfile\Downloads" -ErrorAction Ignore +New-Item -ItemType Directory -Name "RebootRequired" -Path "$env:UserProfile\Downloads\$FolderName" -ErrorAction Ignore +New-Item -ItemType Directory -Name "RebootNotRequired" -Path "$env:UserProfile\Downloads\$FolderName" -ErrorAction Ignore +#download files +foreach ($item in $xml.manifest.softwarecomponent){ + $filename=$($item.path.split("/")|Select-Object -Last 1) + if ($item.RebootRequired -eq "True"){ + Start-BitsTransfer -Source "https://downloads.dell.com/$($item.path)" -Destination "$env:UserProfile\Downloads\$FolderName\RebootRequired\$filename" -DisplayName "Downloading $filename releasedate $($item.releaseDate)" + }else{ + Start-BitsTransfer -Source "https://downloads.dell.com/$($item.path)" -Destination "$env:UserProfile\Downloads\$FolderName\RebootNotRequired\$filename" -DisplayName "Downloading $filename releasedate $($item.releaseDate)" } +} #> #endregion @@ -1196,57 +1192,57 @@ $Headers=@{"Accept"="application/json"} $ContentType='application/json' function Ignore-SSLCertificates { - $Provider = New-Object Microsoft.CSharp.CSharpCodeProvider - $Compiler = $Provider.CreateCompiler() - $Params = New-Object System.CodeDom.Compiler.CompilerParameters - $Params.GenerateExecutable = $false - $Params.GenerateInMemory = $true - $Params.IncludeDebugInformation = $false - $Params.ReferencedAssemblies.Add("System.DLL") > $null - $TASource=@' - namespace Local.ToolkitExtensions.Net.CertificatePolicy +$Provider = New-Object Microsoft.CSharp.CSharpCodeProvider +$Compiler = $Provider.CreateCompiler() +$Params = New-Object System.CodeDom.Compiler.CompilerParameters +$Params.GenerateExecutable = $false +$Params.GenerateInMemory = $true +$Params.IncludeDebugInformation = $false +$Params.ReferencedAssemblies.Add("System.DLL") > $null +$TASource=@' + namespace Local.ToolkitExtensions.Net.CertificatePolicy + { + public class TrustAll : System.Net.ICertificatePolicy { - public class TrustAll : System.Net.ICertificatePolicy + public bool CheckValidationResult(System.Net.ServicePoint sp,System.Security.Cryptography.X509Certificates.X509Certificate cert, System.Net.WebRequest req, int problem) { - public bool CheckValidationResult(System.Net.ServicePoint sp,System.Security.Cryptography.X509Certificates.X509Certificate cert, System.Net.WebRequest req, int problem) - { - return true; - } + return true; } } + } '@ - $TAResults=$Provider.CompileAssemblyFromSource($Params,$TASource) - $TAAssembly=$TAResults.CompiledAssembly - $TrustAll = $TAAssembly.CreateInstance("Local.ToolkitExtensions.Net.CertificatePolicy.TrustAll") - [System.Net.ServicePointManager]::CertificatePolicy = $TrustAll +$TAResults=$Provider.CompileAssemblyFromSource($Params,$TASource) +$TAAssembly=$TAResults.CompiledAssembly +$TrustAll = $TAAssembly.CreateInstance("Local.ToolkitExtensions.Net.CertificatePolicy.TrustAll") +[System.Net.ServicePointManager]::CertificatePolicy = $TrustAll } #ignoring cert is needed for posh5. In 6 and newer you can just add -SkipCertificateCheck Ignore-SSLCertificates #reboot machines foreach ($idrac_ip in $idrac_ips){ - #Configure PXE for next reboot - $JsonBody = @{ Boot = @{ - "BootSourceOverrideTarget"="Pxe" - }} | ConvertTo-Json -Compress - $uri = "https://$idrac_ip/redfish/v1/Systems/System.Embedded.1" - Invoke-RestMethod -Body $JsonBody -Method Patch -ContentType $ContentType -Headers $Headers -Uri $uri -Credential $Credentials - - #Validate - $uri="https://$idrac_ip/redfish/v1/Systems/System.Embedded.1/" - $Result=Invoke-RestMethod -Method Get -ContentType $ContentType -Headers $Headers -Uri $uri -Credential $Credentials - $Result.Boot.BootSourceOverrideTarget - - #check reboot options - #$uri="https://$idrac_ip/redfish/v1/Systems/System.Embedded.1/" - #$Result=Invoke-RestMethod -Method Get -ContentType $ContentType -Headers $Headers -Uri $uri -Credential $Credentials - #$Result.Actions.'#ComputerSystem.Reset'.'ResetType@Redfish.AllowableValues' - - #reboot - #possible values: On,ForceOff,ForceRestart,GracefulShutdown,PushPowerButton,Nmi,PowerCycle - $JsonBody = @{ "ResetType" = "ForceRestart"} | ConvertTo-Json -Compress - $uri = "https://$idrac_ip/redfish/v1/Systems/System.Embedded.1/Actions/ComputerSystem.Reset" - Invoke-RestMethod -Body $JsonBody -Method Post -ContentType $ContentType -Headers $Headers -Uri $uri -Credential $Credentials +#Configure PXE for next reboot +$JsonBody = @{ Boot = @{ + "BootSourceOverrideTarget"="Pxe" + }} | ConvertTo-Json -Compress +$uri = "https://$idrac_ip/redfish/v1/Systems/System.Embedded.1" +Invoke-RestMethod -Body $JsonBody -Method Patch -ContentType $ContentType -Headers $Headers -Uri $uri -Credential $Credentials + +#Validate +$uri="https://$idrac_ip/redfish/v1/Systems/System.Embedded.1/" +$Result=Invoke-RestMethod -Method Get -ContentType $ContentType -Headers $Headers -Uri $uri -Credential $Credentials +$Result.Boot.BootSourceOverrideTarget + +#check reboot options +#$uri="https://$idrac_ip/redfish/v1/Systems/System.Embedded.1/" +#$Result=Invoke-RestMethod -Method Get -ContentType $ContentType -Headers $Headers -Uri $uri -Credential $Credentials +#$Result.Actions.'#ComputerSystem.Reset'.'ResetType@Redfish.AllowableValues' + +#reboot +#possible values: On,ForceOff,ForceRestart,GracefulShutdown,PushPowerButton,Nmi,PowerCycle +$JsonBody = @{ "ResetType" = "ForceRestart"} | ConvertTo-Json -Compress +$uri = "https://$idrac_ip/redfish/v1/Systems/System.Embedded.1/Actions/ComputerSystem.Reset" +Invoke-RestMethod -Body $JsonBody -Method Post -ContentType $ContentType -Headers $Headers -Uri $uri -Credential $Credentials } #endregion @@ -1258,9 +1254,9 @@ foreach ($idrac_ip in $idrac_ips){ #region remove pxe boot after install is done foreach ($HVHost in $HVHosts){ - [guid]$guid=$HVHost.GUID - Set-ADComputer -identity $hvhost.ComputerName -remove @{netbootGUID = $guid} - Set-ADComputer -identity $hvhost.ComputerName -remove @{netbootMachineFilePath = "DC"} +[guid]$guid=$HVHost.GUID +Set-ADComputer -identity $hvhost.ComputerName -remove @{netbootGUID = $guid} +Set-ADComputer -identity $hvhost.ComputerName -remove @{netbootMachineFilePath = "DC"} } #endregion @@ -1271,38 +1267,38 @@ foreach ($HVHost in $HVHosts){ ############################################################ #region Add Windows Server Task Sequence - $MDTServer="MDT" - if (-not(get-module MicrosoftDeploymentToolkit)){ - Import-Module "C:\Program Files\Microsoft Deployment Toolkit\bin\MicrosoftDeploymentToolkit.psd1" - } +$MDTServer="MDT" +if (-not(get-module MicrosoftDeploymentToolkit)){ Import-Module "C:\Program Files\Microsoft Deployment Toolkit\bin\MicrosoftDeploymentToolkit.psd1" - if (-not(Get-PSDrive -Name ds001 -ErrorAction Ignore)){ - New-PSDrive -Name "DS001" -PSProvider "MDTProvider" -Root "\\$MDTServer\DeploymentShare$" -Description "MDT Deployment Share" -NetworkPath "\\$MDTServer\DeploymentShare$" -Verbose | add-MDTPersistentDrive -Verbose - } +} +Import-Module "C:\Program Files\Microsoft Deployment Toolkit\bin\MicrosoftDeploymentToolkit.psd1" +if (-not(Get-PSDrive -Name ds001 -ErrorAction Ignore)){ + New-PSDrive -Name "DS001" -PSProvider "MDTProvider" -Root "\\$MDTServer\DeploymentShare$" -Description "MDT Deployment Share" -NetworkPath "\\$MDTServer\DeploymentShare$" -Verbose | add-MDTPersistentDrive -Verbose +} - #Grab Server ISO - Write-Output "Please select ISO image with Windows Server 2022" - [reflection.assembly]::loadwithpartialname("System.Windows.Forms") - $openFile = New-Object System.Windows.Forms.OpenFileDialog -Property @{ - Title="Please select ISO image with Windows Server 2022" - } - $openFile.Filter = "iso files (*.iso)|*.iso|All files (*.*)|*.*" - If($openFile.ShowDialog() -eq "OK"){ - Write-Output "File $($openfile.FileName) selected" - } - if (!$openFile.FileName){ - Write-Error "Iso was not selected..." - } - $ISOServerPath=$openFile.FileName +#Grab Server ISO + Write-Output "Please select ISO image with Windows Server 2022" + [reflection.assembly]::loadwithpartialname("System.Windows.Forms") + $openFile = New-Object System.Windows.Forms.OpenFileDialog -Property @{ + Title="Please select ISO image with Windows Server 2022" + } + $openFile.Filter = "iso files (*.iso)|*.iso|All files (*.*)|*.*" + If($openFile.ShowDialog() -eq "OK"){ + Write-Output "File $($openfile.FileName) selected" + } + if (!$openFile.FileName){ + Write-Error "Iso was not selected..." + } + $ISOServerPath=$openFile.FileName - #Import Operating System - $ISO = Mount-DiskImage -ImagePath $ISOServerPath -PassThru - $ISOMediaPath = (Get-Volume -DiskImage $ISO).DriveLetter+':\' - Import-mdtoperatingsystem -path "DS001:\Operating Systems" -SourcePath $ISOMediaPath -DestinationFolder "Windows Server 2022 x64" -Verbose - $ISO | Dismount-DiskImage +#Import Operating System +$ISO = Mount-DiskImage -ImagePath $ISOServerPath -PassThru +$ISOMediaPath = (Get-Volume -DiskImage $ISO).DriveLetter+':\' +Import-mdtoperatingsystem -path "DS001:\Operating Systems" -SourcePath $ISOMediaPath -DestinationFolder "Windows Server 2022 x64" -Verbose +$ISO | Dismount-DiskImage - #add Task Sequence - import-mdttasksequence -path "DS001:\Task Sequences" -Name "Windows Server Deploy" -Template "Server.xml" -Comments "" -ID "WinSRV" -Version "1.0" -OperatingSystemPath "DS001:\Operating Systems\Windows Server 2022 SERVERDATACENTERCORE in Windows Server 2022 x64 install.wim" -FullName "PFE" -OrgName "Contoso" -HomePage "about:blank" -AdminPassword "LS1setup!" -Verbose +#add Task Sequence +import-mdttasksequence -path "DS001:\Task Sequences" -Name "Windows Server Deploy" -Template "Server.xml" -Comments "" -ID "WinSRV" -Version "1.0" -OperatingSystemPath "DS001:\Operating Systems\Windows Server 2022 SERVERDATACENTERCORE in Windows Server 2022 x64 install.wim" -FullName "PFE" -OrgName "Contoso" -HomePage "about:blank" -AdminPassword "LS1setup!" -Verbose #endregion @@ -1321,59 +1317,59 @@ $Headers=@{"Accept"="application/json"} $ContentType='application/json' function Ignore-SSLCertificates { - $Provider = New-Object Microsoft.CSharp.CSharpCodeProvider - $Compiler = $Provider.CreateCompiler() - $Params = New-Object System.CodeDom.Compiler.CompilerParameters - $Params.GenerateExecutable = $false - $Params.GenerateInMemory = $true - $Params.IncludeDebugInformation = $false - $Params.ReferencedAssemblies.Add("System.DLL") > $null - $TASource=@' - namespace Local.ToolkitExtensions.Net.CertificatePolicy +$Provider = New-Object Microsoft.CSharp.CSharpCodeProvider +$Compiler = $Provider.CreateCompiler() +$Params = New-Object System.CodeDom.Compiler.CompilerParameters +$Params.GenerateExecutable = $false +$Params.GenerateInMemory = $true +$Params.IncludeDebugInformation = $false +$Params.ReferencedAssemblies.Add("System.DLL") > $null +$TASource=@' + namespace Local.ToolkitExtensions.Net.CertificatePolicy + { + public class TrustAll : System.Net.ICertificatePolicy { - public class TrustAll : System.Net.ICertificatePolicy + public bool CheckValidationResult(System.Net.ServicePoint sp,System.Security.Cryptography.X509Certificates.X509Certificate cert, System.Net.WebRequest req, int problem) { - public bool CheckValidationResult(System.Net.ServicePoint sp,System.Security.Cryptography.X509Certificates.X509Certificate cert, System.Net.WebRequest req, int problem) - { - return true; - } + return true; } } + } '@ - $TAResults=$Provider.CompileAssemblyFromSource($Params,$TASource) - $TAAssembly=$TAResults.CompiledAssembly - $TrustAll = $TAAssembly.CreateInstance("Local.ToolkitExtensions.Net.CertificatePolicy.TrustAll") - [System.Net.ServicePointManager]::CertificatePolicy = $TrustAll +$TAResults=$Provider.CompileAssemblyFromSource($Params,$TASource) +$TAAssembly=$TAResults.CompiledAssembly +$TrustAll = $TAAssembly.CreateInstance("Local.ToolkitExtensions.Net.CertificatePolicy.TrustAll") +[System.Net.ServicePointManager]::CertificatePolicy = $TrustAll } #ignoring cert is needed for posh5. In 6 and newer you can just add -SkipCertificateCheck Ignore-SSLCertificates #reboot machines foreach ($idrac_ip in $idrac_ips){ - #Configure PXE for next reboot - $JsonBody = @{ Boot = @{ - "BootSourceOverrideTarget"="Pxe" - }} | ConvertTo-Json -Compress - $uri = "https://$idrac_ip/redfish/v1/Systems/System.Embedded.1" - Invoke-RestMethod -Body $JsonBody -Method Patch -ContentType $ContentType -Headers $Headers -Uri $uri -Credential $Credentials - - #Validate - $uri="https://$idrac_ip/redfish/v1/Systems/System.Embedded.1/" - $Result=Invoke-RestMethod -Method Get -ContentType $ContentType -Headers $Headers -Uri $uri -Credential $Credentials - $Result.Boot.BootSourceOverrideTarget - - #check reboot options - #$uri="https://$idrac_ip/redfish/v1/Systems/System.Embedded.1/" - #$Result=Invoke-RestMethod -Method Get -ContentType $ContentType -Headers $Headers -Uri $uri -Credential $Credentials - #$Result.Actions.'#ComputerSystem.Reset'.'ResetType@Redfish.AllowableValues' - - #reboot - #possible values: On,ForceOff,ForceRestart,GracefulShutdown,PushPowerButton,Nmi,PowerCycle - $JsonBody = @{ "ResetType" = "ForceRestart"} | ConvertTo-Json -Compress - $uri = "https://$idrac_ip/redfish/v1/Systems/System.Embedded.1/Actions/ComputerSystem.Reset" - Invoke-RestMethod -Body $JsonBody -Method Post -ContentType $ContentType -Headers $Headers -Uri $uri -Credential $Credentials - - Start-Sleep 10 +#Configure PXE for next reboot +$JsonBody = @{ Boot = @{ + "BootSourceOverrideTarget"="Pxe" + }} | ConvertTo-Json -Compress +$uri = "https://$idrac_ip/redfish/v1/Systems/System.Embedded.1" +Invoke-RestMethod -Body $JsonBody -Method Patch -ContentType $ContentType -Headers $Headers -Uri $uri -Credential $Credentials + +#Validate +$uri="https://$idrac_ip/redfish/v1/Systems/System.Embedded.1/" +$Result=Invoke-RestMethod -Method Get -ContentType $ContentType -Headers $Headers -Uri $uri -Credential $Credentials +$Result.Boot.BootSourceOverrideTarget + +#check reboot options +#$uri="https://$idrac_ip/redfish/v1/Systems/System.Embedded.1/" +#$Result=Invoke-RestMethod -Method Get -ContentType $ContentType -Headers $Headers -Uri $uri -Credential $Credentials +#$Result.Actions.'#ComputerSystem.Reset'.'ResetType@Redfish.AllowableValues' + +#reboot +#possible values: On,ForceOff,ForceRestart,GracefulShutdown,PushPowerButton,Nmi,PowerCycle +$JsonBody = @{ "ResetType" = "ForceRestart"} | ConvertTo-Json -Compress +$uri = "https://$idrac_ip/redfish/v1/Systems/System.Embedded.1/Actions/ComputerSystem.Reset" +Invoke-RestMethod -Body $JsonBody -Method Post -ContentType $ContentType -Headers $Headers -Uri $uri -Credential $Credentials + +Start-Sleep 10 } #endregion @@ -1384,121 +1380,121 @@ foreach ($idrac_ip in $idrac_ips){ ####################################################### #region Create hash table out of machines that attempted boot last 5 minutes - #in real world scenairos you can have hash table like this: - <# +#in real world scenairos you can have hash table like this: +<# +$HVHosts = @() +$HVHosts+=@{ComputerName="R440Node1" ;IPAddress="10.0.0.131" ; MACAddress="34:80:0D:91:0B:66" ; GUID="4C4C4544-0051-5610-8056-B8C04F323333"} +$HVHosts+=@{ComputerName="R440Node2" ;IPAddress="10.0.0.132" ; MACAddress="34:80:0D:91:0B:54" ; GUID="4C4C4544-0051-5610-8054-B8C04F323333"} +#> + +#grab machines that attempted to boot in last 5 minutes and create hash table. +$HVHosts=Invoke-Command -ComputerName $MDTServer -ScriptBlock { + $IpaddressScope="10.0.0." + $IPAddressStart=122 #starting this number IPs will be asigned + $ServersNamePrefix="R440Node" + $events=Get-WinEvent -FilterHashtable @{LogName="Microsoft-Windows-Deployment-Services-Diagnostics/Operational";Id=4132;StartTime=(get-date).AddMinutes(-5)} | Where-Object Message -like "*it is not recognized*" | Sort-Object TimeCreated $HVHosts = @() - $HVHosts+=@{ComputerName="R440Node1" ;IPAddress="10.0.0.131" ; MACAddress="34:80:0D:91:0B:66" ; GUID="4C4C4544-0051-5610-8056-B8C04F323333"} - $HVHosts+=@{ComputerName="R440Node2" ;IPAddress="10.0.0.132" ; MACAddress="34:80:0D:91:0B:54" ; GUID="4C4C4544-0051-5610-8054-B8C04F323333"} - #> - - #grab machines that attempted to boot in last 5 minutes and create hash table. - $HVHosts=Invoke-Command -ComputerName $MDTServer -ScriptBlock { - $IpaddressScope="10.0.0." - $IPAddressStart=122 #starting this number IPs will be asigned - $ServersNamePrefix="R440Node" - $events=Get-WinEvent -FilterHashtable @{LogName="Microsoft-Windows-Deployment-Services-Diagnostics/Operational";Id=4132;StartTime=(get-date).AddMinutes(-5)} | Where-Object Message -like "*it is not recognized*" | Sort-Object TimeCreated - $HVHosts = @() - $GUIDS=@() - $i=1 - foreach ($event in $events){ - [System.Diagnostics.Eventing.Reader.EventLogRecord]$event=$event - if (!($guids).Contains($event.properties.value[2])){ - $HVHosts+= @{ ComputerName="$ServersNamePrefix$i";GUID = $event.properties.value[2] -replace '[{}]' ; MACAddress = $event.properties.value[0] -replace "-",":" ; IPAddress="$IpaddressScope$($IPAddressStart.tostring())"} - $i++ - $IPAddressStart++ - $GUIDS+=$event.properties.value[2] - } + $GUIDS=@() + $i=1 + foreach ($event in $events){ + [System.Diagnostics.Eventing.Reader.EventLogRecord]$event=$event + if (!($guids).Contains($event.properties.value[2])){ + $HVHosts+= @{ ComputerName="$ServersNamePrefix$i";GUID = $event.properties.value[2] -replace '[{}]' ; MACAddress = $event.properties.value[0] -replace "-",":" ; IPAddress="$IpaddressScope$($IPAddressStart.tostring())"} + $i++ + $IPAddressStart++ + $GUIDS+=$event.properties.value[2] } - Return $HVHosts } + Return $HVHosts +} #endregion #region create DHCP reservation for machines - #Create DHCP reservations for Hyper-V hosts - #Add DHCP Reservations - foreach ($HVHost in $HVHosts){ - if (!(Get-DhcpServerv4Reservation -ErrorAction SilentlyContinue -ComputerName $DHCPServer -ScopeId $ScopeID -ClientId ($HVHost.MACAddress).Replace(":","") | Where-Object IPAddress -eq $HVHost.IPAddress)){ - Add-DhcpServerv4Reservation -ComputerName $DHCPServer -ScopeId $ScopeID -IPAddress $HVHost.IPAddress -ClientId ($HVHost.MACAddress).Replace(":","") - } +#Create DHCP reservations for Hyper-V hosts + #Add DHCP Reservations + foreach ($HVHost in $HVHosts){ + if (!(Get-DhcpServerv4Reservation -ErrorAction SilentlyContinue -ComputerName $DHCPServer -ScopeId $ScopeID -ClientId ($HVHost.MACAddress).Replace(":","") | Where-Object IPAddress -eq $HVHost.IPAddress)){ + Add-DhcpServerv4Reservation -ComputerName $DHCPServer -ScopeId $ScopeID -IPAddress $HVHost.IPAddress -ClientId ($HVHost.MACAddress).Replace(":","") } + } - #configure NTP server in DHCP (might be useful if Servers have issues with time) - if (!(get-DhcpServerv4OptionValue -ComputerName $DHCPServer -ScopeId $ScopeID -OptionId 042 -ErrorAction SilentlyContinue)){ - Set-DhcpServerv4OptionValue -ComputerName $DHCPServer -ScopeId $ScopeID -OptionId 042 -Value "10.0.0.1" - } +#configure NTP server in DHCP (might be useful if Servers have issues with time) + if (!(get-DhcpServerv4OptionValue -ComputerName $DHCPServer -ScopeId $ScopeID -OptionId 042 -ErrorAction SilentlyContinue)){ + Set-DhcpServerv4OptionValue -ComputerName $DHCPServer -ScopeId $ScopeID -OptionId 042 -Value "10.0.0.1" + } #endregion #region add deploy info to AD Object and MDT Database - #download and unzip mdtdb (blog available in web.archive only https://web.archive.org/web/20190421025144/https://blogs.technet.microsoft.com/mniehaus/2009/05/14/manipulating-the-microsoft-deployment-toolkit-database-using-powershell/) - #Start-BitsTransfer -Source https://msdnshared.blob.core.windows.net/media/TNBlogsFS/prod.evol.blogs.technet.com/telligent.evolution.components.attachments/01/5209/00/00/03/24/15/04/MDTDB.zip -Destination $env:USERPROFILE\Downloads\MDTDB.zip - Start-BitsTransfer -Source https://github.com/microsoft/MSLab/raw/master/Scenarios/AzSHCI%20and%20MDT/MDTDB.zip -Destination $env:USERPROFILE\Downloads\MDTDB.zip +#download and unzip mdtdb (blog available in web.archive only https://web.archive.org/web/20190421025144/https://blogs.technet.microsoft.com/mniehaus/2009/05/14/manipulating-the-microsoft-deployment-toolkit-database-using-powershell/) +#Start-BitsTransfer -Source https://msdnshared.blob.core.windows.net/media/TNBlogsFS/prod.evol.blogs.technet.com/telligent.evolution.components.attachments/01/5209/00/00/03/24/15/04/MDTDB.zip -Destination $env:USERPROFILE\Downloads\MDTDB.zip +Start-BitsTransfer -Source https://github.com/microsoft/MSLab/raw/master/Scenarios/AzSHCI%20and%20MDT/MDTDB.zip -Destination $env:USERPROFILE\Downloads\MDTDB.zip - Expand-Archive -Path $env:USERPROFILE\Downloads\MDTDB.zip -DestinationPath $env:USERPROFILE\Downloads\MDTDB\ -Force - if ((Get-ExecutionPolicy) -eq "Restricted"){ - Set-ExecutionPolicy -Scope Process -ExecutionPolicy RemoteSigned +Expand-Archive -Path $env:USERPROFILE\Downloads\MDTDB.zip -DestinationPath $env:USERPROFILE\Downloads\MDTDB\ -Force +if ((Get-ExecutionPolicy) -eq "Restricted"){ + Set-ExecutionPolicy -Scope Process -ExecutionPolicy RemoteSigned +} +Import-Module $env:USERPROFILE\Downloads\MDTDB\MDTDB.psm1 +#make sure DS is connected + if (-not(get-module MicrosoftDeploymentToolkit)){ + Import-Module "C:\Program Files\Microsoft Deployment Toolkit\bin\MicrosoftDeploymentToolkit.psd1" } - Import-Module $env:USERPROFILE\Downloads\MDTDB\MDTDB.psm1 - #make sure DS is connected - if (-not(get-module MicrosoftDeploymentToolkit)){ - Import-Module "C:\Program Files\Microsoft Deployment Toolkit\bin\MicrosoftDeploymentToolkit.psd1" - } - if (-not(Get-PSDrive -Name ds001 -ErrorAction Ignore)){ - New-PSDrive -Name "DS001" -PSProvider "MDTProvider" -Root "\\$MDTServer\DeploymentShare$" -Description "MDT Deployment Share" -NetworkPath "\\$MDTServer\DeploymentShare$" -Verbose | add-MDTPersistentDrive -Verbose - } - #Connect to DB - #Connect-MDTDatabase -database mdtdb -sqlServer $MDTServer -instance SQLExpress - Connect-MDTDatabase -drivePath "DS001:\" + if (-not(Get-PSDrive -Name ds001 -ErrorAction Ignore)){ + New-PSDrive -Name "DS001" -PSProvider "MDTProvider" -Root "\\$MDTServer\DeploymentShare$" -Description "MDT Deployment Share" -NetworkPath "\\$MDTServer\DeploymentShare$" -Verbose | add-MDTPersistentDrive -Verbose + } +#Connect to DB + #Connect-MDTDatabase -database mdtdb -sqlServer $MDTServer -instance SQLExpress + Connect-MDTDatabase -drivePath "DS001:\" - #add hosts to MDT DB - foreach ($HVHost in $HVHosts){ - if (-not(Get-AdComputer -Filter "Name -eq `"$($HVHost.ComputerName)`"")){ - New-ADComputer -Name $hvhost.ComputerName - } - #add to MDT DB - if (-not (Get-MDTComputer -macAddress $HVHost.MACAddress)){ - New-MDTComputer -macAddress $HVHost.MACAddress -description $HVHost.ComputerName -uuid $HVHost.GUID -settings @{ - ComputerName = $HVHost.ComputerName - OSDComputerName = $HVHost.ComputerName - #SkipBDDWelcome = 'Yes' - } +#add hosts to MDT DB +foreach ($HVHost in $HVHosts){ + if (-not(Get-AdComputer -Filter "Name -eq `"$($HVHost.ComputerName)`"")){ + New-ADComputer -Name $hvhost.ComputerName + } + #add to MDT DB + if (-not (Get-MDTComputer -macAddress $HVHost.MACAddress)){ + New-MDTComputer -macAddress $HVHost.MACAddress -description $HVHost.ComputerName -uuid $HVHost.GUID -settings @{ + ComputerName = $HVHost.ComputerName + OSDComputerName = $HVHost.ComputerName + #SkipBDDWelcome = 'Yes' } - Get-MDTComputer -macAddress $HVHost.MACAddress | Set-MDTComputerRole -roles JoinDomain,WinSRV } + Get-MDTComputer -macAddress $HVHost.MACAddress | Set-MDTComputerRole -roles JoinDomain,WinSRV +} - #Configure MDT DB Roles - if (-not (Get-MDTRole -name WinSRV)){ - New-MDTRole -name WinSRV -settings @{ - SkipTaskSequence = 'YES' - SkipWizard = 'YES' - SkipSummary = 'YES' - SkipApplications = 'YES' - TaskSequenceID = 'WinSRV' - SkipFinalSummary = 'YES' - FinishAction = 'LOGOFF' - } +#Configure MDT DB Roles + if (-not (Get-MDTRole -name WinSRV)){ + New-MDTRole -name WinSRV -settings @{ + SkipTaskSequence = 'YES' + SkipWizard = 'YES' + SkipSummary = 'YES' + SkipApplications = 'YES' + TaskSequenceID = 'WinSRV' + SkipFinalSummary = 'YES' + FinishAction = 'LOGOFF' } + } - if (-not (Get-MDTRole -name JoinDomain)){ - New-MDTRole -name JoinDomain -settings @{ - SkipComputerName ='YES' - SkipDomainMembership='YES' - JoinDomain = $env:USERDNSDomain - DomainAdmin ='MDTUser' - DomainAdminDomain = $env:userdomain - DomainAdminPassword ='LS1setup!' - } + if (-not (Get-MDTRole -name JoinDomain)){ + New-MDTRole -name JoinDomain -settings @{ + SkipComputerName ='YES' + SkipDomainMembership='YES' + JoinDomain = $env:USERDNSDomain + DomainAdmin ='MDTUser' + DomainAdminDomain = $env:userdomain + DomainAdminPassword ='LS1setup!' } - - #allow machines to boot from PXE from DC by adding info into AD Object - foreach ($HVHost in $HVHosts){ - [guid]$guid=$HVHost.GUID - Set-ADComputer -identity $hvhost.ComputerName -replace @{netbootGUID = $guid} - #Set-ADComputer -identity $hvhost.ComputerName -replace @{netbootMachineFilePath = "DC"} } +#allow machines to boot from PXE from DC by adding info into AD Object +foreach ($HVHost in $HVHosts){ + [guid]$guid=$HVHost.GUID + Set-ADComputer -identity $hvhost.ComputerName -replace @{netbootGUID = $guid} + #Set-ADComputer -identity $hvhost.ComputerName -replace @{netbootMachineFilePath = "DC"} +} + #endregion #region update task sequence with powershell script to install OS to smallest disk right before "New Computer only" group @@ -1507,11 +1503,11 @@ $PSScriptName="OSDDiskIndex.ps1" $PSScriptContent=@' $Disks=Get-CimInstance win32_DiskDrive if ($Disks.model -contains "DELLBOSS VD"){ - #exact model for Dell AX node (DELLBOSS VD) - $TSenv:OSDDiskIndex=($Disks | Where-Object Model -eq "DELLBOSS VD").Index +#exact model for Dell AX node (DELLBOSS VD) +$TSenv:OSDDiskIndex=($Disks | Where-Object Model -eq "DELLBOSS VD").Index }else{ - #or just smallest disk - $TSenv:OSDDiskIndex=($Disks | Where-Object MediaType -eq "Fixed hard disk media" | Sort-Object Size | Select-Object -First 1).Index +#or just smallest disk +$TSenv:OSDDiskIndex=($Disks | Where-Object MediaType -eq "Fixed hard disk media" | Sort-Object Size | Select-Object -First 1).Index } <# In case you need PowerShell and pause Task Sequence you can use this code: #source: http://wiki.wladik.net/windows/mdt/powershell-scripting @@ -1523,128 +1519,128 @@ Start PowerShell #> '@ - #update Tasksequence - $TS=Invoke-Command -ComputerName $MDTServer -ScriptBlock {Get-Content -Path $using:DeploymentShareLocation\Control\$using:TaskSequenceID\ts.xml} - $TextToSearch=' ' - $PoshScript=@" - - - $PSScriptName - - - - cscript.exe "%SCRIPTROOT%\ZTIPowerShell.wsf - +#update Tasksequence +$TS=Invoke-Command -ComputerName $MDTServer -ScriptBlock {Get-Content -Path $using:DeploymentShareLocation\Control\$using:TaskSequenceID\ts.xml} +$TextToSearch=' ' +$PoshScript=@" + + + $PSScriptName + + + + cscript.exe "%SCRIPTROOT%\ZTIPowerShell.wsf + $TextToSearch "@ - $NewTS=$TS.replace($TextToSearch,$PoshScript) - Invoke-Command -ComputerName $MDTServer -ScriptBlock {Set-Content -Path $using:DeploymentShareLocation\Control\$using:TaskSequenceID\ts.xml -Value $using:NewTS} - #insert script - Invoke-Command -ComputerName $MDTServer -ScriptBlock {Set-Content -Path $using:DeploymentShareLocation\Scripts\$using:PSScriptName -Value $using:PSScriptContent} +$NewTS=$TS.replace($TextToSearch,$PoshScript) +Invoke-Command -ComputerName $MDTServer -ScriptBlock {Set-Content -Path $using:DeploymentShareLocation\Control\$using:TaskSequenceID\ts.xml -Value $using:NewTS} +#insert script +Invoke-Command -ComputerName $MDTServer -ScriptBlock {Set-Content -Path $using:DeploymentShareLocation\Scripts\$using:PSScriptName -Value $using:PSScriptContent} #endregion #region update task sequence with drivers $RoleName="AXNodeDrivers" if (-not (Get-MDTRole -name $RoleName)){ - #Download DSU - #https://github.com/DellProSupportGse/Tools/blob/main/DART.ps1 - - #grab DSU links from Dell website - $URL="https://dl.dell.com/omimswac/dsu/" - $Results=Invoke-WebRequest $URL -UseDefaultCredentials - $Links=$results.Links.href | Select-Object -Skip 1 - #create PSObject from results - $DSUs=@() - foreach ($Link in $Links){ - $DSUs+=[PSCustomObject]@{ - Link = "https://dl.dell.com$Link" - Version = $link -split "_" | Select-Object -Last 2 | Select-Object -First 1 - } - } - #download latest to separate folder - $LatestDSU=$DSUs | Sort-Object Version | Select-Object -Last 1 - $Folder="$env:USERPROFILE\Downloads\DSU" - if (-not (Test-Path $Folder)){New-Item -Path $Folder -ItemType Directory} - Start-BitsTransfer -Source $LatestDSU.Link -Destination $Folder\DSU.exe +#Download DSU +#https://github.com/DellProSupportGse/Tools/blob/main/DART.ps1 - #add DSU as application to MDT - Import-Module "C:\Program Files\Microsoft Deployment Toolkit\bin\MicrosoftDeploymentToolkit.psd1" - if (-not(Get-PSDrive -Name ds001 -ErrorAction Ignore)){ - New-PSDrive -Name "DS001" -PSProvider "MDTProvider" -Root "\\$MDTServer\DeploymentShare$" -Description "MDT Deployment Share" -NetworkPath "\\$MDTServer\DeploymentShare$" -Verbose | add-MDTPersistentDrive -Verbose - } - $AppName="Dell DSU $($LatestDSU.Version)" - Import-MDTApplication -path "DS001:\Applications" -enable "True" -Name $AppName -ShortName "DSU" -Version $LatestDSU.Version -Publisher "Dell" -Language "" -CommandLine "DSU.exe /silent" -WorkingDirectory ".\Applications\$AppName" -ApplicationSourcePath $Folder -DestinationFolder $AppName -Verbose - #grap package ID for role config - $DSUID=(Get-ChildItem -Path DS001:\Applications | Where-Object Name -eq $AppName).GUID - - #download catalog and create answer file to run DSU - #Dell Azure Stack HCI driver catalog https://downloads.dell.com/catalog/ASHCI-Catalog.xml.gz - #Download catalog - Start-BitsTransfer -Source "https://downloads.dell.com/catalog/ASHCI-Catalog.xml.gz" -Destination "$env:UserProfile\Downloads\ASHCI-Catalog.xml.gz" - #unzip gzip to a folder https://scatteredcode.net/download-and-extract-gzip-tar-with-powershell/ - $Folder="$env:USERPROFILE\Downloads\DSUPackage" - if (-not (Test-Path $Folder)){New-Item -Path $Folder -ItemType Directory} - Function Expand-GZipArchive{ - Param( - $infile, - $outfile = ($infile -replace '\.gz$','') - ) - $input = New-Object System.IO.FileStream $inFile, ([IO.FileMode]::Open), ([IO.FileAccess]::Read), ([IO.FileShare]::Read) - $output = New-Object System.IO.FileStream $outFile, ([IO.FileMode]::Create), ([IO.FileAccess]::Write), ([IO.FileShare]::None) - $gzipStream = New-Object System.IO.Compression.GzipStream $input, ([IO.Compression.CompressionMode]::Decompress) - $buffer = New-Object byte[](1024) - while($true){ - $read = $gzipstream.Read($buffer, 0, 1024) - if ($read -le 0){break} - $output.Write($buffer, 0, $read) - } - $gzipStream.Close() - $output.Close() - $input.Close() - } - Expand-GZipArchive "$env:UserProfile\Downloads\ASHCI-Catalog.xml.gz" "$folder\ASHCI-Catalog.xml" - #create answerfile for DU - $content='@ - a - c - @' - Set-Content -Path "$folder\answer.txt" -Value $content -NoNewline - $content='"C:\Program Files\Dell\DELL EMC System Update\DSU.exe" --catalog-location=ASHCI-Catalog.xml --apply-upgrades $null - $TASource=@' - namespace Local.ToolkitExtensions.Net.CertificatePolicy +$Provider = New-Object Microsoft.CSharp.CSharpCodeProvider +$Compiler = $Provider.CreateCompiler() +$Params = New-Object System.CodeDom.Compiler.CompilerParameters +$Params.GenerateExecutable = $false +$Params.GenerateInMemory = $true +$Params.IncludeDebugInformation = $false +$Params.ReferencedAssemblies.Add("System.DLL") > $null +$TASource=@' + namespace Local.ToolkitExtensions.Net.CertificatePolicy + { + public class TrustAll : System.Net.ICertificatePolicy { - public class TrustAll : System.Net.ICertificatePolicy + public bool CheckValidationResult(System.Net.ServicePoint sp,System.Security.Cryptography.X509Certificates.X509Certificate cert, System.Net.WebRequest req, int problem) { - public bool CheckValidationResult(System.Net.ServicePoint sp,System.Security.Cryptography.X509Certificates.X509Certificate cert, System.Net.WebRequest req, int problem) - { - return true; - } + return true; } } + } '@ - $TAResults=$Provider.CompileAssemblyFromSource($Params,$TASource) - $TAAssembly=$TAResults.CompiledAssembly - $TrustAll = $TAAssembly.CreateInstance("Local.ToolkitExtensions.Net.CertificatePolicy.TrustAll") - [System.Net.ServicePointManager]::CertificatePolicy = $TrustAll +$TAResults=$Provider.CompileAssemblyFromSource($Params,$TASource) +$TAAssembly=$TAResults.CompiledAssembly +$TrustAll = $TAAssembly.CreateInstance("Local.ToolkitExtensions.Net.CertificatePolicy.TrustAll") +[System.Net.ServicePointManager]::CertificatePolicy = $TrustAll } #ignoring cert is needed for posh5. In 6 and newer you can just add -SkipCertificateCheck Ignore-SSLCertificates #reboot machines foreach ($idrac_ip in $idrac_ips){ - #Configure PXE for next reboot - $JsonBody = @{ Boot = @{ - "BootSourceOverrideTarget"="Pxe" - }} | ConvertTo-Json -Compress - $uri = "https://$idrac_ip/redfish/v1/Systems/System.Embedded.1" - Invoke-RestMethod -Body $JsonBody -Method Patch -ContentType $ContentType -Headers $Headers -Uri $uri -Credential $Credentials - - #Validate - $uri="https://$idrac_ip/redfish/v1/Systems/System.Embedded.1/" - $Result=Invoke-RestMethod -Method Get -ContentType $ContentType -Headers $Headers -Uri $uri -Credential $Credentials - $Result.Boot.BootSourceOverrideTarget - - #check reboot options - #$uri="https://$idrac_ip/redfish/v1/Systems/System.Embedded.1/" - #$Result=Invoke-RestMethod -Method Get -ContentType $ContentType -Headers $Headers -Uri $uri -Credential $Credentials - #$Result.Actions.'#ComputerSystem.Reset'.'ResetType@Redfish.AllowableValues' - - #reboot - #possible values: On,ForceOff,ForceRestart,GracefulShutdown,PushPowerButton,Nmi,PowerCycle - $JsonBody = @{ "ResetType" = "ForceRestart"} | ConvertTo-Json -Compress - $uri = "https://$idrac_ip/redfish/v1/Systems/System.Embedded.1/Actions/ComputerSystem.Reset" - Invoke-RestMethod -Body $JsonBody -Method Post -ContentType $ContentType -Headers $Headers -Uri $uri -Credential $Credentials +#Configure PXE for next reboot +$JsonBody = @{ Boot = @{ + "BootSourceOverrideTarget"="Pxe" + }} | ConvertTo-Json -Compress +$uri = "https://$idrac_ip/redfish/v1/Systems/System.Embedded.1" +Invoke-RestMethod -Body $JsonBody -Method Patch -ContentType $ContentType -Headers $Headers -Uri $uri -Credential $Credentials + +#Validate +$uri="https://$idrac_ip/redfish/v1/Systems/System.Embedded.1/" +$Result=Invoke-RestMethod -Method Get -ContentType $ContentType -Headers $Headers -Uri $uri -Credential $Credentials +$Result.Boot.BootSourceOverrideTarget + +#check reboot options +#$uri="https://$idrac_ip/redfish/v1/Systems/System.Embedded.1/" +#$Result=Invoke-RestMethod -Method Get -ContentType $ContentType -Headers $Headers -Uri $uri -Credential $Credentials +#$Result.Actions.'#ComputerSystem.Reset'.'ResetType@Redfish.AllowableValues' + +#reboot +#possible values: On,ForceOff,ForceRestart,GracefulShutdown,PushPowerButton,Nmi,PowerCycle +$JsonBody = @{ "ResetType" = "ForceRestart"} | ConvertTo-Json -Compress +$uri = "https://$idrac_ip/redfish/v1/Systems/System.Embedded.1/Actions/ComputerSystem.Reset" +Invoke-RestMethod -Body $JsonBody -Method Post -ContentType $ContentType -Headers $Headers -Uri $uri -Credential $Credentials } -#endregion - +#endregion \ No newline at end of file diff --git a/Scenarios/AzSHCI and Migration from Windows Server/Scenario.ps1 b/Scenarios/AzSHCI and Migration from Windows Server/Scenario.ps1 index 11247e64..af12bf13 100644 --- a/Scenarios/AzSHCI and Migration from Windows Server/Scenario.ps1 +++ b/Scenarios/AzSHCI and Migration from Windows Server/Scenario.ps1 @@ -163,6 +163,7 @@ $DestinationClusterName="AzSHCI-Cluster" $SourceStoragePath="C:\ClusterStorage\CSV1" $DestinationStoragePath="C:\ClusterStorage\CSV1" + $DestinationSwitchName=(Get-VMSwitch -CimSession ((Get-ClusterNode -Cluster $ClusterName).Name | Select-Object -First 1)).Name $VMNames=(Get-VM -cimsession (get-clusternode -cluster $SourceClusterName).Name | Where-Object Path -Like "$SourceStoragePath*").Name # Temporarily enable CredSSP delegation to avoid double-hop issue @@ -178,27 +179,42 @@ #do the move foreach ($VMName in $VMNames){ #remove VM from HA Resources + Write-Output "$(get-date -Format 'yyyy/MM/dd hh:mm:ss tt') : Removing VM $($VM.VMName) From Cluster resources" Get-ClusterResource -Cluster $SourceClusterName -name "Virtual Machine $VMName" -ErrorAction Ignore | Remove-ClusterResource -force Get-ClusterGroup -Cluster $SourceClusterName -Name $VMName -ErrorAction Ignore | Remove-ClusterGroup -force - #Grab random node in cluster $DestinationClusterName + #Grab random node in cluster $DestinationClusterName (does not have to be random of course) $VM=Get-VM -Cimsession (get-clusternode -cluster $SourceClusterName).Name -Name $VMName - $VM | Stop-VM -Save + Write-Output "$(get-date -Format 'yyyy/MM/dd hh:mm:ss tt') : Stopping VM $($VM.VMName)" + $VM | Stop-VM + #or just save it, but we need to update version anyway + #$VM | Stop-VM -Save + #If there is different switch name in destination node, you should consider disconnecting vNICs before removing VM and saving config + $VM | Get-VMNetworkAdapter | Disconnect-VMNetworkAdapter #Backup config + Write-Output "$(get-date -Format 'yyyy/MM/dd hh:mm:ss tt') : Backing up VM config" Invoke-Command -ComputerName $SourceClusterName -ScriptBlock {Copy-Item -Path "$($using:VM.Path)\Virtual Machines" -Destination "$($using:VM.Path)\Virtual Machines Bak" -Recurse} #If there is different switch name in destination node, you should consider disconnecting vNICs first #$VM | Get-VMNetworkAdapter | Disconnect-VMNetworkAdapter - #Remove VM - $VM | Remove-VM -Force + #Remove VM (Just making sure hyper-v command is used, because SCVMM Remove-VM also removes VHDs) + Write-Output "$(get-date -Format 'yyyy/MM/dd hh:mm:ss tt') : Removing VM" + $VM | Hyper-V\Remove-VM -Force #Restore Config #Invoke-Command -ComputerName $SourceClusterName -ScriptBlock {Copy-Item -Path "$($using:VM.Path)\Virtual Machines Bak\*" -Destination "$($using:VM.Path)\Virtual Machines" -Recurse} Invoke-Command -ComputerName $SourceClusterName -ScriptBlock {Move-Item -Path "$($using:VM.Path)\Virtual Machines Bak\*" -Destination "$($using:VM.Path)\Virtual Machines"} Invoke-Command -ComputerName $SourceClusterName -ScriptBlock {Remove-Item -Path "$($using:VM.Path)\Virtual Machines Bak\"} + #zip config to have a backup (in case something goes wrong with updating VM version) + Invoke-Command -ComputerName $SourceClusterName -ScriptBlock {Compress-Archive -Path "$($using:VM.Path)\Virtual Machines\" -DestinationPath "$($using:VM.Path)\Virtual Machines.zip"} #Copy machine to destination node using CredSSP $VolumeName=$DestinationStoragePath | Split-Path -Leaf + Write-Output "$(get-date -Format 'yyyy/MM/dd hh:mm:ss tt') : Copying VM $($VM.VMName) to \\$DestinationClusterName\ClusterStorage$\$VolumeName\" Invoke-Command -ComputerName ($Servers | Get-Random) -Credential $Credentials -Authentication Credssp -ScriptBlock {Copy-Item -Path "$($using:VM.Path)" -Destination "\\$using:DestinationClusterName\ClusterStorage$\$using:VolumeName\" -Recurse} + Write-Output "$(get-date -Format 'yyyy/MM/dd hh:mm:ss tt') : Copying VM $($VM.VMName) Finished" #Import VM and Start + Write-Output "$(get-date -Format 'yyyy/MM/dd hh:mm:ss tt') : Importing VM $($VM.VMName) And Starting" $DestinationHost=(get-clusternode -cluster $DestinationClusterName | get-random).Name $NewVM=Import-VM -Path "$DestinationStoragePath\$($VM.Name)\Virtual Machines\$($VM.ID.GUID).vmcx" -CimSession $DestinationHost + $NewVM | Update-VMVersion -Force + $NewVM | Get-VMNetworkAdapter | Connect-VMNetworkAdapter -SwitchName $DestinationSwitchName $NewVM | Start-VM } @@ -213,6 +229,7 @@ $DestinationClusterName="AzSHCI-Cluster" $SourceClusterVolumes=(Get-ClusterSharedVolume -Cluster $SourceClusterName).sharedvolumeinfo.Friendlyvolumename $DestinationClusterVolumes=(Get-ClusterSharedVolume -Cluster $DestinationClusterName).sharedvolumeinfo.Friendlyvolumename + $DestinationSwitchName=(Get-VMSwitch -CimSession ((Get-ClusterNode -Cluster $ClusterName).Name | Select-Object -First 1)).Name # Temporarily enable CredSSP delegation to avoid double-hop issue $Servers=(get-clusternode -cluster $SourceClusterName).Name @@ -234,27 +251,45 @@ } $VMNames=(Get-VM -cimsession (get-clusternode -cluster $SourceClusterName).Name | Where-Object Path -Like "$SourceClusterVolume*").Name foreach ($VMName in $VMNames){ + + #remove VM from HA Resources + Write-Output "$(get-date -Format 'yyyy/MM/dd hh:mm:ss tt') : Removing VM $($VM.VMName) From Cluster resources" Get-ClusterResource -Cluster $SourceClusterName -name "Virtual Machine $VMName" -ErrorAction Ignore | Remove-ClusterResource -force Get-ClusterGroup -Cluster $SourceClusterName -Name $VMName -ErrorAction Ignore | Remove-ClusterGroup -force + #Grab random node in cluster $DestinationClusterName (does not have to be random of course) $VM=Get-VM -Cimsession (get-clusternode -cluster $SourceClusterName).Name -Name $VMName - $VM | Stop-VM -Save - #If there is different switch name in destination node, you should consider disconnecting vNICs first - #$VM | Get-VMNetworkAdapter | Disconnect-VMNetworkAdapter + Write-Output "$(get-date -Format 'yyyy/MM/dd hh:mm:ss tt') : Stopping VM $($VM.VMName)" + $VM | Stop-VM + #or just save it, but we need to update version anyway + #$VM | Stop-VM -Save + #If there is different switch name in destination node, you should consider disconnecting vNICs before removing VM and saving config + $VM | Get-VMNetworkAdapter | Disconnect-VMNetworkAdapter #Backup config + Write-Output "$(get-date -Format 'yyyy/MM/dd hh:mm:ss tt') : Backing up VM config" Invoke-Command -ComputerName $SourceClusterName -ScriptBlock {Copy-Item -Path "$($using:VM.Path)\Virtual Machines" -Destination "$($using:VM.Path)\Virtual Machines Bak" -Recurse} - #Remove VM - $VM | Remove-VM -Force + #If there is different switch name in destination node, you should consider disconnecting vNICs first + #$VM | Get-VMNetworkAdapter | Disconnect-VMNetworkAdapter + #Remove VM (Just making sure hyper-v command is used, because SCVMM Remove-VM also removes VHDs) + Write-Output "$(get-date -Format 'yyyy/MM/dd hh:mm:ss tt') : Removing VM" + $VM | Hyper-V\Remove-VM -Force #Restore Config #Invoke-Command -ComputerName $SourceClusterName -ScriptBlock {Copy-Item -Path "$($using:VM.Path)\Virtual Machines Bak\*" -Destination "$($using:VM.Path)\Virtual Machines" -Recurse} Invoke-Command -ComputerName $SourceClusterName -ScriptBlock {Move-Item -Path "$($using:VM.Path)\Virtual Machines Bak\*" -Destination "$($using:VM.Path)\Virtual Machines"} Invoke-Command -ComputerName $SourceClusterName -ScriptBlock {Remove-Item -Path "$($using:VM.Path)\Virtual Machines Bak\"} + #zip config to have a backup (in case something goes wrong with updating VM version) + Invoke-Command -ComputerName $SourceClusterName -ScriptBlock {Compress-Archive -Path "$($using:VM.Path)\Virtual Machines\" -DestinationPath "$($using:VM.Path)\Virtual Machines.zip"} #Copy machine to destination node using CredSSP - $VolumeName=$DestinationClusterVolumes[$index] | Split-Path -Leaf + $VolumeName=$DestinationStoragePath | Split-Path -Leaf + Write-Output "$(get-date -Format 'yyyy/MM/dd hh:mm:ss tt') : Copying VM $($VM.VMName) to \\$DestinationClusterName\ClusterStorage$\$VolumeName\" Invoke-Command -ComputerName ($Servers | Get-Random) -Credential $Credentials -Authentication Credssp -ScriptBlock {Copy-Item -Path "$($using:VM.Path)" -Destination "\\$using:DestinationClusterName\ClusterStorage$\$using:VolumeName\" -Recurse} + Write-Output "$(get-date -Format 'yyyy/MM/dd hh:mm:ss tt') : Copying VM $($VM.VMName) Finished" #Import VM and Start + Write-Output "$(get-date -Format 'yyyy/MM/dd hh:mm:ss tt') : Importing VM $($VM.VMName) And Starting" $DestinationHost=(get-clusternode -cluster $DestinationClusterName | get-random).Name - $NewVM=Import-VM -Path "$($DestinationClusterVolumes[$index])\$($VM.Name)\Virtual Machines\$($VM.ID.GUID).vmcx" -CimSession $DestinationHost + $NewVM=Import-VM -Path "$DestinationStoragePath\$($VM.Name)\Virtual Machines\$($VM.ID.GUID).vmcx" -CimSession $DestinationHost + $NewVM | Update-VMVersion -Force + $NewVM | Get-VMNetworkAdapter | Connect-VMNetworkAdapter -SwitchName $DestinationSwitchName $NewVM | Start-VM } }