AutomatedLabExchange2016.psm1
function Copy-LabExchange2016InstallationFiles { Write-LogFunctionEntry Write-ScreenInfo -Message 'Download Exchange 2016 requirements' -TaskStart #if there is one Exchange server on Hyper-V, make sure the required sources are in the LabSources folder $downloadTargetFolder = Join-Path -Path $labSources -ChildPath SoftwarePackages if ($exchangeServers | Where-Object HostType -eq HyperV) { Write-ScreenInfo 'Exchange Servers in the lab running on Hyper-V, downloading the files to the local 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 'finished' -TaskEnd } #if there is one Exchange server on Azure, make sure the required sources are in LabSources on Azure $1stAzureExchangeServer = $exchangeServers | Where-Object HostType -eq Azure if ($exchangeServers | Where-Object HostType -eq 'Azure') { throw (New-Object System.NotImplementedException) #TODO: The destination path on Azure needs to be defined. Invoke-LabCommand -ActivityName "Downloading Exchange Server 2016" -ComputerName $1stAzureExchangeServer -ScriptBlock { Get-LabInternetFile -Uri $exchangeDownloadLink -Path $downloadTargetFolder -ErrorAction Stop Get-LabInternetFile -Uri $ucmaDownloadLink -Path $downloadTargetFolder -ErrorAction Stop Get-LabInternetFile -Uri $dotnet452DownloadLink -Path $downloadTargetFolder -ErrorAction Stop } -Function (Get-Command -Name Get-LabInternetFile) -Variable (Get-Variable -Name Uri) } #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) -DestinationFolder C:\Install -ComputerName $exchangeServer Copy-LabFileItem -Path (Join-Path -Path $downloadTargetFolder -ChildPath $ucmaInstallFileName) -DestinationFolder C:\Install -ComputerName $exchangeServer Copy-LabFileItem -Path (Join-Path -Path $downloadTargetFolder -ChildPath $dotnet452InstallFileName) -DestinationFolder 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) -DestinationFolder C:\Install -ComputerName $rootDc #Copy-LabFileItem -Path (Join-Path -Path $downloadTargetFolder -ChildPath $ucmaDownloadLink) -DestinationFolder C:\Install -ComputerName $rootDc Copy-LabFileItem -Path (Join-Path -Path $downloadTargetFolder -ChildPath $dotnet452InstallFileName) -DestinationFolder C:\Install -ComputerName $rootDc Write-ScreenInfo 'finished' } Write-ScreenInfo 'finished copying file to RootDCs' -TaskEnd foreach ($exchangeServer in $exchangeServers | Where-Object HostType -eq Azure) { #TODO: The path of the Azure LabSources folder needs to be determined throw (New-Object System.NotImplementedException) #Copy-LabFileItem -Path (Join-Path -Path $downloadTargetFolder -ChildPath $exchangeInstallFileName) -DestinationFolder C:\Install -ComputerName $exchangeServer #Copy-LabFileItem -Path (Join-Path -Path $downloadTargetFolder -ChildPath $ucmaDownloadLink) -DestinationFolder C:\Install -ComputerName $exchangeServer #Copy-LabFileItem -Path (Join-Path -Path $downloadTargetFolder -ChildPath $dotnet452DownloadLink) -DestinationFolder C:\Install -ComputerName $exchangeServer } Write-ScreenInfo 'Finished downloading Exchange 2016 requirements' -TaskEnd $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 } function Start-ExchangeInstallSequence { param( [Parameter(Mandatory)] [string]$Activity, [Parameter(Mandatory)] [string]$ComputerName, [Parameter(Mandatory)] [string]$CommandLine ) Write-ScreenInfo -Message $Activity -TaskStart -NoNewLine try { $job = Install-LabSoftwarePackage -ComputerName $ComputerName -LocalPath C:\Install\ExchangeInstall\setup.exe -CommandLine $CommandLine -UseCredSsp -AsJob -PassThru -ErrorAction Stop -ErrorVariable exchangeError $result = Wait-LWLabJob -Job $job -NoDisplay -NoNewLine -ProgressIndicator 15 -ReturnResults -ErrorAction Stop } catch { if ($_ -match '(.+reboot.+pending.+)|(.+pending.+reboot.+)') { Restart-LabVM -ComputerName $ComputerName try { $job = Install-LabSoftwarePackage -ComputerName $ComputerName -LocalPath C:\Install\ExchangeInstall\setup.exe -CommandLine $CommandLine -UseCredSsp -AsJob -PassThru -ErrorAction Stop -ErrorVariable exchangeError $result = Wait-LWLabJob -Job $job -NoDisplay -NoNewLine -ProgressIndicator 15 -ReturnResults -ErrorAction Stop } catch { if ($_ -notmatch '(.+reboot.+pending.+)|(.+pending.+reboot.+)') { $job = Install-LabSoftwarePackage -ComputerName $ComputerName -LocalPath C:\Install\ExchangeInstall\setup.exe -CommandLine $CommandLine -UseCredSsp -AsJob -PassThru -ErrorAction Stop -ErrorVariable exchangeError $result = Wait-LWLabJob -Job $job -NoDisplay -NoNewLine -ProgressIndicator 15 -ReturnResults -ErrorAction Stop } } } else { $resultVariable = New-Variable -Name ("AL_$([guid]::NewGuid().Guid)") -Scope Global -PassThru $resultVariable.Value = $exchangeError Write-Error "Exchange Schema Update failed on server '$ComputerName'. See content of $($resultVariable.Name) for details." } } Write-ScreenInfo -Message "Finished activity '$Activity'" -TaskEnd $result } #region Install-LabExchange2016 function Install-LabExchange2016 { [cmdletBinding()] param ([switch]$CreateCheckPoints) Write-LogFunctionEntry $start = Get-Date $labSources = Get-LabSourcesLocation $lab = Get-Lab $exchangeServers = Get-LabMachine -Role Exchange2016 if (-not $exchangeServers) { Write-Verbose 'No Exchange 2016 servers defined in the lab. Skipping installation' return } $exchangeRootDomains = $lab.GetParentDomain((Get-LabMachine -Role Exchange2016).DomainName).Name $exchangeRootDCs = Get-LabMachine -Role RootDC | Where-Object DomainName -in $exchangeRootDomains $exchangeDownloadLink = New-Object System.Uri($MyInvocation.MyCommand.Module.PrivateData.Exchange2016DownloadLink) $ucmaDownloadLink = New-Object System.Uri($MyInvocation.MyCommand.Module.PrivateData.ExchangeUcmaDownloadLink) $dotnet452DownloadLink = New-Object System.Uri($MyInvocation.MyCommand.Module.PrivateData.dotnet452DownloadLink) $exchangeInstallFileName = $exchangeDownloadLink.Segments[$ucmaInstallUri.Segments.Count-1] $ucmaInstallFileName = $ucmaDownloadLink.Segments[$ucmaInstallUri.Segments.Count-1] $dotnet452InstallFileName = $dotnet452DownloadLink.Segments[$ucmaInstallUri.Segments.Count-1] Copy-LabExchange2016InstallationFiles Write-ScreenInfo -Message 'Waiting for machines to start up' -NoNewLine Start-LabVM -ComputerName $exchangeServers -Wait -ProgressIndicator 15 $jobs = @() Write-ScreenInfo -Message "Preparing machines: '$($exchangeServers -join ', ')'" -TaskStart #region Add AD permissions in the root domain if Exchange is installed in a child domain $progressIndicatorForJob = 15 foreach ($machine in $exchangeServers) { $rootDomain = $lab.GetParentDomain($machine.DomainName) $rootDc = Get-LabMachine -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-LabMachine -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 -UseCredSsp } } #endregion 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 $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 $jobs += Install-LabSoftwarePackage -ComputerName $exchangeServers -LocalPath "C:\Install\$dotnet452InstallFileName" -CommandLine '/q /norestart /log c:\dotnet452.txt' -AsJob -AsScheduledJob -UseShellExecute -PassThru -NoDisplay $jobs += Install-LabSoftwarePackage -ComputerName $exchangeRootDCs -LocalPath "C:\Install\$dotnet452InstallFileName" -CommandLine '/q /norestart /log c:\dotnet452.txt' -AsJob -AsScheduledJob -UseShellExecute -PassThru -NoDisplay Wait-LWLabJob -Job $jobs -NoDisplay -ProgressIndicator 10 Write-ScreenInfo -Message "Finished preparing machines: '$($exchangeServers -join ', ')'" -TaskEnd Write-ScreenInfo -Message 'Restarting machines' -NoNewLine Restart-LabVM -ComputerName $exchangeServers -Wait -ProgressIndicator 45 Sync-LabActiveDirectory -ComputerName $rootDc foreach ($machine in $exchangeServers) { Write-ScreenInfo -Message "Performing pre-requisites for machine '$machine'" -TaskStart $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 ($machine.DomainName -ne $rootDc.DomainName) { $prepMachine = $rootDc } else { $prepMachine = $machine } # PREPARE SCHEMA $result = Start-ExchangeInstallSequence -Activity 'Exchange PrepareSchema' -ComputerName $prepMachine -CommandLine '/PrepareSchema /IAcceptExchangeServerLicenseTerms' -ErrorAction Stop # PREPARE AD $commandLine = '/PrepareAD /OrganizationName:"{0}" /IAcceptExchangeServerLicenseTerms' -f $exchangeOrganization $result = Start-ExchangeInstallSequence -Activity 'Exchange PrepareAD' -ComputerName $prepMachine -CommandLine $commandLine -ErrorAction Stop #PREPARE ALL DOMAINS $result = Start-ExchangeInstallSequence -Activity 'Exchange PrepareAllDomains' -ComputerName $prepMachine -CommandLine '/PrepareAllDomains /IAcceptExchangeServerLicenseTerms' -ErrorAction Stop } Write-ScreenInfo -Message 'Triggering replication' -NoNewLine Get-LabMachine -Role RootDC | ForEach-Object { Sync-LabActiveDirectory -ComputerName $_ } Write-ScreenInfo -Message 'Restarting machines' -NoNewLine Restart-LabVM -ComputerName $exchangeServers -Wait -ProgressIndicator 5 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' -NoNewLine $commandLine = '/Mode:Install /Roles:mb /InstallWindowsComponents /OrganizationName:{0} /IAcceptExchangeServerLicenseTerms' -f $exchangeOrganization $result = Start-ExchangeInstallSequence -Activity 'Exchange PrepareAllDomains' -ComputerName $prepMachine -CommandLine $commandLine -ErrorAction Stop Write-ScreenInfo -Message "Finished installing Exchange Server 2016 on machine '$machine'" -TaskEnd } Write-LogFunctionExit } #endregion Install-LabExchange2016 |