AutomatedLabExchange2013.psm1
function Copy-LabExchange2013InstallationFiles { Write-LogFunctionEntry Write-ScreenInfo -Message 'Download Exchange 2013 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 2013 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.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 $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 $dotnet462InstallFileName) -DestinationFolderPath C:\Install -ComputerName $rootDc Write-ScreenInfo 'finished' } Write-ScreenInfo 'Finished copying file to RootDCs' -TaskEnd Write-ScreenInfo 'Finished downloading Exchange 2013 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 { $job = Install-LabSoftwarePackage -ComputerName $ComputerName -LocalPath C:\Install\ExchangeInstall\setup.exe -CommandLine $CommandLine -AsJob -NoDisplay -PassThru -ErrorAction Stop -ErrorVariable exchangeError $result = Wait-LWLabJob -Job $job -NoDisplay -NoNewLine -ProgressIndicator 15 -PassThru -ErrorAction Stop } 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' agian." $job = Install-LabSoftwarePackage -ComputerName $ComputerName -LocalPath C:\Install\ExchangeInstall\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 C:\Install\ExchangeInstall\setup.exe -CommandLine $CommandLine -AsJob -NoDisplay -PassThru -ErrorAction Stop -ErrorVariable exchangeError $result = Wait-LWLabJob -Job $job -NoDisplay -NoNewLine -ProgressIndicator 15 -PassThru } } } else { $resultVariable = New-Variable -Name ("AL_$([guid]::NewGuid().Guid)") -Scope Global -PassThru $resultVariable.Value = $exchangeError 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-LabExchange2013 function Install-LabExchange2013 { [cmdletBinding()] param ( [switch]$All, [switch]$CopyExchange2013InstallationFiles, [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.Exchange2013DownloadLink) $ucmaDownloadLink = New-Object System.Uri((Get-Module AutomatedLab)[0].PrivateData.ExchangeUcmaDownloadLink) $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] $dotnet462InstallFileName = $dotnet462DownloadLink.Segments[$dotnet462DownloadLink.Segments.Count-1] $start = Get-Date $lab = Get-Lab $jobs = @() $progressIndicatorForJob = 15 $exchangeServers = Get-LabVM -Role Exchange2013 if (-not $exchangeServers) { Write-Error 'No Exchange 2013 servers defined in the lab. Skipping installation' return } Write-ScreenInfo -Message 'Waiting for machines to start up' -NoNewLine Start-LabVM -ComputerName $exchangeServers -ProgressIndicator 15 $exchangeRootDomains = (Get-LabVM -Role Exchange2013).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 ($CopyExchange2013InstallationFiles -or $All) { Copy-LabExchange2013InstallationFiles } 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 $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 Exchange2013 | 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 Exchange2013).Properties.OrganizationName 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 2013 on machine '$machine'" -TaskStart $exchangeOrganization = ($machine.Roles | Where-Object Name -eq Exchange2013).Properties.OrganizationName #FINALLY INSTALL EXCHANGE Write-ScreenInfo -Message 'Install Exchange Server 2013' $commandLine = '/Mode:Install /Roles:ca,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 2013 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-LabExchange2013 |