AutomatedLabExchange2016.psm1
function Copy-LabExchange2016InstallationFiles { Write-LogFunctionEntry Write-ScreenInfo -Message 'Download Exchange 2016 requirements' -TaskStart $downloadTargetFolder = Join-Path -Path $labSources -ChildPath SoftwarePackages Write-ScreenInfo 'Downloading the files to the local or Azure LabSources folder...' -TaskStart #Write-ScreenInfo -Message "Downloading Exchange 2016 from '$exchangeDownloadLink'" #Get-LabInternetFile -Uri $exchangeDownloadLink -Path $downloadTargetFolder -ErrorAction Stop Write-ScreenInfo -Message "Downloading UCMA from '$ucmaDownloadLink'" Get-LabInternetFile -Uri $ucmaDownloadLink -Path $downloadTargetFolder -ErrorAction Stop Write-ScreenInfo -Message "Downloading .net Framework 4.5.2 from '$dotnet452DownloadLink'" Get-LabInternetFile -Uri $dotnet452DownloadLink -Path $downloadTargetFolder -ErrorAction Stop Write-ScreenInfo -Message "Downloading .net Framework 4.6.2 from '$dotnet462DownloadLink'" Get-LabInternetFile -Uri $dotnet462DownloadLink -Path $downloadTargetFolder -ErrorAction Stop Write-ScreenInfo 'finished' -TaskEnd #distribute the sources to all exchange servers and the RootDC Write-ScreenInfo 'Copying sources to Exchange Servers' -TaskStart foreach ($exchangeServer in $exchangeServers | Where-Object HostType -eq HyperV) { Write-ScreenInfo "Copying to server '$exchangeServer'..." -NoNewLine #Copy-LabFileItem -Path (Join-Path -Path $downloadTargetFolder -ChildPath $exchangeInstallFileName) -DestinationFolderPath C:\Install -ComputerName $exchangeServer Copy-LabFileItem -Path (Join-Path -Path $downloadTargetFolder -ChildPath $ucmaInstallFileName) -DestinationFolderPath C:\Install -ComputerName $exchangeServer Copy-LabFileItem -Path (Join-Path -Path $downloadTargetFolder -ChildPath $dotnet452InstallFileName) -DestinationFolderPath C:\Install -ComputerName $exchangeServer Copy-LabFileItem -Path (Join-Path -Path $downloadTargetFolder -ChildPath $dotnet462InstallFileName) -DestinationFolderPath C:\Install -ComputerName $exchangeServer Write-ScreenInfo 'finished' } Write-ScreenInfo 'finished copying file to Exchange Servers' -TaskEnd #now distribute the sources to all Hyper-V Root DCs that Write-ScreenInfo 'Copying sources to Root DCs' -TaskStart foreach ($rootDc in $exchangeRootDCs) { Write-ScreenInfo "Copying to server '$rootDc'..." -NoNewLine #Copy-LabFileItem -Path (Join-Path -Path $downloadTargetFolder -ChildPath $exchangeInstallFileName) -DestinationFolderPath C:\Install -ComputerName $rootDc Copy-LabFileItem -Path (Join-Path -Path $downloadTargetFolder -ChildPath $dotnet452InstallFileName) -DestinationFolderPath C:\Install -ComputerName $rootDc Copy-LabFileItem -Path (Join-Path -Path $downloadTargetFolder -ChildPath $dotnet462InstallFileName) -DestinationFolderPath C:\Install -ComputerName $rootDc Write-ScreenInfo 'finished' } Write-ScreenInfo 'Finished copying file to RootDCs' -TaskEnd #Write-ScreenInfo 'Finished downloading Exchange 2016 requirements' -TaskEnd #Write-ScreenInfo 'Exctracting Exchange Installation files on all machines' -TaskStart #$machines = (@($exchangeServers) + $exchangeRootDCs) #$jobs = Install-LabSoftwarePackage -LocalPath "C:\Install\$ExchangeInstallFileName" -CommandLine '/X:C:\Install\ExchangeInstall /Q' -ComputerName $machines -AsJob -PassThru -NoDisplay #Wait-LWLabJob -Job $jobs -ProgressIndicator $ProgressIndicatorForJob -NoDisplay Write-ScreenInfo 'finished' -TaskEnd } function Start-ExchangeInstallSequence { param( [Parameter(Mandatory)] [string]$Activity, [Parameter(Mandatory)] [string]$ComputerName, [Parameter(Mandatory)] [string]$CommandLine ) Write-LogFunctionEntry try { $disk = Mount-LabIsoImage -ComputerName $machine -IsoPath $isoImage.Path -PassThru -SupressOutput $job = Install-LabSoftwarePackage -ComputerName $ComputerName -LocalPath "$($disk.DriveLetter)\setup.exe" -CommandLine $CommandLine -AsJob -NoDisplay -PassThru -ErrorAction Stop -ErrorVariable exchangeError $result = Wait-LWLabJob -Job $job -NoDisplay -NoNewLine -ProgressIndicator 15 -PassThru -ErrorAction Stop Dismount-LabIsoImage -ComputerName $machine -SupressOutput } catch { if ($_ -match '(.+reboot.+pending.+)|(.+pending.+reboot.+)') { Write-ScreenInfo "Activity '$Activity' did not succeed, Exchange Server '$ComputerName' needs to be restarted first." -Type Warning Restart-LabVM -ComputerName $ComputerName -Wait Start-Sleep -Seconds 30 #as the feature installation can trigger a 2nd reboot, wait for the machine after 30 seconds again Wait-LabVM -ComputerName $ComputerName try { Write-ScreenInfo "Calling activity '$Activity' again." $job = Install-LabSoftwarePackage -ComputerName $ComputerName -LocalPath "$($disk.DriveLetter)\setup.exe" -CommandLine $CommandLine -AsJob -NoDisplay -PassThru -ErrorAction Stop -ErrorVariable exchangeError $result = Wait-LWLabJob -Job $job -NoDisplay -NoNewLine -ProgressIndicator 15 -PassThru -ErrorAction Stop } catch { Write-ScreenInfo "Activity '$Activity' did not succeed, but did not ask for a reboot, retrying the last time" -Type Warning if ($_ -notmatch '(.+reboot.+pending.+)|(.+pending.+reboot.+)') { $job = Install-LabSoftwarePackage -ComputerName $ComputerName -LocalPath "$($disk.DriveLetter)\setup.exe" -CommandLine $CommandLine -AsJob -NoDisplay -PassThru -ErrorAction Stop -ErrorVariable exchangeError $result = Wait-LWLabJob -Job $job -NoDisplay -NoNewLine -ProgressIndicator 15 -PassThru -ErrorAction Stop } } } else { $resultVariable = New-Variable -Name ("AL_$([guid]::NewGuid().Guid)") -Scope Global -PassThru $resultVariable.Value = $_.Exception Write-Error "Exchange task '$Activity' failed on '$ComputerName'. See content of $($resultVariable.Name) for details." } } Write-ScreenInfo -Message "Finished activity '$Activity'" -TaskEnd $result Write-LogFunctionExit } #region Install-LabExchange2016 function Install-LabExchange2016 { [cmdletBinding()] param ( [switch]$All, [switch]$CopyExchange2016InstallationFiles, [switch]$AddAdRightsInRootDomain, [switch]$InstallWindowsFeatures, [switch]$InstallRequirements, [switch]$PrepareSchema, [switch]$PrepareAD, [switch]$PrepareAllDomains, [switch]$InstallExchange, [switch]$CreateCheckPoints ) Write-LogFunctionEntry #$exchangeDownloadLink = New-Object System.Uri((Get-Module AutomatedLab)[0].PrivateData.Exchange2016DownloadLink) $ucmaDownloadLink = New-Object System.Uri((Get-Module AutomatedLab)[0].PrivateData.ExchangeUcmaDownloadLink) $dotnet452DownloadLink = New-Object System.Uri((Get-Module AutomatedLab)[0].PrivateData.dotnet452DownloadLink) $dotnet462DownloadLink = New-Object System.Uri((Get-Module AutomatedLab)[0].PrivateData.dotnet462DownloadLink) #$exchangeInstallFileName = $exchangeDownloadLink.Segments[$exchangeDownloadLink.Segments.Count-1] $ucmaInstallFileName = $ucmaDownloadLink.Segments[$ucmaDownloadLink.Segments.Count-1] $dotnet452InstallFileName = $dotnet452DownloadLink.Segments[$dotnet452DownloadLink.Segments.Count-1] $dotnet462InstallFileName = $dotnet462DownloadLink.Segments[$dotnet462DownloadLink.Segments.Count-1] $roleName = [AutomatedLab.Roles]::Exchange2016 $start = Get-Date $lab = Get-Lab $jobs = @() $progressIndicatorForJob = 15 $exchangeServers = Get-LabVM -Role Exchange2016 if (-not $exchangeServers) { Write-Error 'No Exchange 2016 servers defined in the lab. Skipping installation' return } $isoImage = $lab.Sources.ISOs | Where-Object Name -eq $roleName if (-not $isoImage) { Write-LogFunctionExitWithError -Message "There is no ISO image available to install the role '$roleName'. Please add the required ISO to the lab and name it '$roleName'" return } Write-ScreenInfo -Message 'Waiting for machines to start up' -NoNewLine Start-LabVM -ComputerName $exchangeServers -ProgressIndicator 15 $exchangeRootDomains = (Get-LabVM -Role Exchange2016).DomainName | Sort-Object -Unique | ForEach-Object { $lab.GetParentDomain($_).Name } $exchangeRootDCs = Get-LabVM -Role RootDC | Where-Object DomainName -in $exchangeRootDomains Wait-LabVM -ComputerName $exchangeRootDCs Wait-LabVM -ComputerName $exchangeServers if ($CopyExchange2016InstallationFiles -or $All) { Copy-LabExchange2016InstallationFiles } if ($AddAdRightsInRootDomain -or $All) { #region Add AD permissions in the root domain if Exchange is installed in a child domain foreach ($machine in $exchangeServers) { $rootDomain = $lab.GetParentDomain($machine.DomainName) $rootDc = Get-LabVM -Role RootDC | Where-Object DomainName -eq $rootDomain #if the exchange server is in a child domain the administrator of the child domain will be added to the group 'Organization Management' of the root domain if ($machine.DomainName -ne $rootDc.DomainName) { $dc = Get-LabVM -Role FirstChildDC | Where-Object DomainName -eq $machine.DomainName $userName = ($lab.Domains | Where-Object Name -eq $machine.DomainName).Administrator.UserName Invoke-LabCommand -ComputerName $rootDc -ActivityName "Add '$userName' to Forest Management" -NoDisplay -ScriptBlock { param($userName, $Server) $user = Get-ADUser -Identity $userName -Server $Server Add-ADGroupMember -Identity 'Schema Admins' -Members $user Add-ADGroupMember -Identity 'Enterprise Admins' -Members $user } -ArgumentList $userName, $dc.FQDN } } } Write-ScreenInfo -Message "Preparing machines: '$($exchangeServers -join ', ')'" -TaskStart if ($InstallWindowsFeatures -or $All) { Write-Verbose 'Installing Windows Features Server-Media-Foundation, RSAT' $jobs += Install-LabWindowsFeature -ComputerName $exchangeServers -FeatureName Server-Media-Foundation, RSAT -UseLocalCredential -AsJob -PassThru -NoDisplay Wait-LWLabJob -Job $jobs -ProgressIndicator $progressIndicatorForJob -NoDisplay Restart-LabVM -ComputerName $exchangeServers -Wait -ProgressIndicator 45 } if ($InstallRequirements -or $All) { $jobs += Install-LabSoftwarePackage -ComputerName $exchangeServers -LocalPath "C:\Install\$ucmaInstallFileName" -CommandLine '/Quiet /Log c:\ucma.txt' -AsJob -PassThru -NoDisplay Wait-LWLabJob -Job $jobs -NoDisplay -ProgressIndicator 10 #.net 4.5.2 is not needed for Exchange 2016 #$jobs += Install-LabSoftwarePackage -ComputerName $exchangeServers -LocalPath "C:\Install\$dotnet452InstallFileName" -CommandLine '/q /norestart /log c:\dotnet452.txt' -AsJob -NoDisplay -AsScheduledJob -UseShellExecute -PassThru #$jobs += Install-LabSoftwarePackage -ComputerName $exchangeRootDCs -LocalPath "C:\Install\$dotnet452InstallFileName" -CommandLine '/q /norestart /log c:\dotnet452.txt' -AsJob -NoDisplay -AsScheduledJob -UseShellExecute -PassThru $jobs += Install-LabSoftwarePackage -ComputerName $exchangeServers -LocalPath "C:\Install\$dotnet462InstallFileName" -CommandLine '/q /norestart /log c:\dotnet462.txt' -AsJob -NoDisplay -AsScheduledJob -UseShellExecute -PassThru $jobs += Install-LabSoftwarePackage -ComputerName $exchangeRootDCs -LocalPath "C:\Install\$dotnet462InstallFileName" -CommandLine '/q /norestart /log c:\dotnet462.txt' -AsJob -NoDisplay -AsScheduledJob -UseShellExecute -PassThru Wait-LWLabJob -Job $jobs -NoDisplay -ProgressIndicator 10 Write-ScreenInfo -Message 'Restarting machines' -NoNewLine Restart-LabVM -ComputerName $exchangeRootDCs -Wait -ProgressIndicator 10 Sync-LabActiveDirectory -ComputerName $exchangeRootDCs } Write-ScreenInfo -Message "Finished preparing machines: '$($exchangeServers -join ', ')'" -TaskEnd $exchangeServersByDomain = Get-LabVM -Role Exchange2016 | Group-Object -Property DomainName foreach ($machine in ($exchangeServersByDomain.Group | Select-Object -First 1)) { $rootDomain = $lab.GetParentDomain($machine.DomainName) $rootDc = $lab.Machines | Where-Object { $_.Roles.Name -contains 'RootDC' -and $_.DomainName -eq $rootDomain } | Select-Object -First 1 $exchangeOrganization = ($machine.Roles | Where-Object Name -eq Exchange2016).Properties.OrganizationName if (-not $exchangeOrganization) { Write-Error "The Exchange Organization is not set for machine '$machine'. Skipping installation" continue } if ($machine.DomainName -ne $rootDc.DomainName) { $prepMachine = $rootDc } else { $prepMachine = $machine } Write-ScreenInfo -Message "Performing AD prerequisites on machine '$prepMachine'" -TaskStart # PREPARE SCHEMA if ($PrepareSchema -or $All) { $global:AL_Result_PrepareSchema = Start-ExchangeInstallSequence -Activity 'Exchange PrepareSchema' -ComputerName $prepMachine -CommandLine '/PrepareSchema /IAcceptExchangeServerLicenseTerms' -ErrorAction Stop } # PREPARE AD if ($PrepareAD -or $All) { $commandLine = '/PrepareAD /OrganizationName:"{0}" /IAcceptExchangeServerLicenseTerms' -f $exchangeOrganization $global:AL_Result_PrepareAD = Start-ExchangeInstallSequence -Activity 'Exchange PrepareAD' -ComputerName $prepMachine -CommandLine $commandLine -ErrorAction Stop } #PREPARE ALL DOMAINS if ($PrepareAllDomains -or $All) { $global:AL_Result_PrepareAllDomains = Start-ExchangeInstallSequence -Activity 'Exchange PrepareAllDomains' -ComputerName $prepMachine -CommandLine '/PrepareAllDomains /IAcceptExchangeServerLicenseTerms' -ErrorAction Stop } } if ($PrepareSchema -or $PrepareAD -or $PrepareAllDomains -or $All) { Write-ScreenInfo -Message 'Triggering AD replication' Get-LabVM -Role RootDC | ForEach-Object { Sync-LabActiveDirectory -ComputerName $_ } Write-ScreenInfo -Message 'Restarting machines' -NoNewLine Restart-LabVM -ComputerName $exchangeRootDCs -Wait -ProgressIndicator 10 Restart-LabVM -ComputerName $exchangeServers -Wait -ProgressIndicator 10 } if ($InstallExchange -or $All) { foreach ($machine in $exchangeServers) { Write-ScreenInfo -Message "Installing Exchange Server 2016 on machine '$machine'" -TaskStart $exchangeOrganization = ($machine.Roles | Where-Object Name -eq Exchange2016).Properties.OrganizationName #FINALLY INSTALL EXCHANGE Write-ScreenInfo -Message 'Install Exchange Server 2016' $commandLine = '/Mode:Install /Roles:mb,mt /InstallWindowsComponents /OrganizationName:{0} /IAcceptExchangeServerLicenseTerms' -f $exchangeOrganization $result = Start-ExchangeInstallSequence -Activity 'Exchange Components' -ComputerName $machine -CommandLine $commandLine -ErrorAction Stop Set-Variable -Name "AL_Result_ExchangeInstall_$machine" -Value $result -Scope Global Write-ScreenInfo -Message "Finished installing Exchange Server 2016 on machine '$machine'" -TaskEnd } } if ($InstallExchange -or $All) { Write-ScreenInfo -Message 'Restarting machines' -NoNewLine Restart-LabVM -ComputerName $exchangeServers -Wait -ProgressIndicator 5 } Write-LogFunctionExit } #endregion Install-LabExchange2016 |