AutomatedLabExchange2013.psm1
#region script blocks $ucmaCmd = { param( [Parameter(Mandatory = $true)] [string]$UcmaInstallLink ) $ucmaInstallUri = New-Object System.Uri($UcmaInstallLink) $ucmaInstallFileName = $ucmaInstallUri.Segments[$ucmaInstallUri.Segments.Count-1] $retries = 5 New-Item C:\Install -Type Directory -ErrorAction SilentlyContinue | Out-Null $start = Get-Date Write-Verbose 'Downloading the Unified Communications Managed API 4.0 Runtime installation files...' while (-not (Test-Path -Path "C:\Install\$ucmaInstallFileName") -and $retries -gt 0) { reg.exe add 'HKLM\SOFTWARE\Microsoft\Active Setup\Installed Components\{A509B1A7-37EF-4b3f-8CFC-4F3A74704073}' /v IsInstalled /t REG_DWORD /d 0 /f #disable admin IE Enhanced Security Configuration reg.exe add 'HKLM\SOFTWARE\Microsoft\Active Setup\Installed Components\{A509B1A8-37EF-4b3f-8CFC-4F3A74704073}' /v IsInstalled /t REG_DWORD /d 0 /f #disable user IE Enhanced Security Configuration $client = New-Object System.Net.WebClient $client.DownloadFile($UcmaInstallLink, "C:\Install\$ucmaInstallFileName") $retries-- if (-not (Test-Path -Path "C:\Install\$ucmaInstallFileName")) { Start-Sleep -Seconds 30 } } $end = Get-Date Write-Verbose "...downloading the Unified Communications Managed API 4.0 Runtime installation files took $($end - $start)" Write-Verbose 'Installing Unified Communications Managed API 4.0 Runtime...' $start = Get-Date & "C:\Install\$ucmaInstallFileName" /Quiet | Out-Null $end = Get-Date Write-Verbose "...installing Unified Communications Managed API 4.0 Runtime took $($end - $start)" } $exchangeDownloadCmd = { param( [Parameter(Mandatory = $true)] [string]$ExchangeInstallLink ) $exchangeInstallUri = New-Object System.Uri($ExchangeInstallLink) $exchangeInstallFileName = $ExchangeInstallUri.Segments[$ExchangeInstallUri.Segments.Count-1] $retries = 5 New-Item C:\Install -Type Directory -ErrorAction SilentlyContinue | Out-Null $start = Get-Date Write-Verbose 'Downloading the Exchange Installation files...' while (-not (Test-Path -Path "C:\Install\$exchangeInstallFileName") -and $retries -gt 0) { reg.exe add 'HKLM\SOFTWARE\Microsoft\Active Setup\Installed Components\{A509B1A7-37EF-4b3f-8CFC-4F3A74704073}' /v IsInstalled /t REG_DWORD /d 0 /f #disable admin IE Enhanced Security Configuration reg.exe add 'HKLM\SOFTWARE\Microsoft\Active Setup\Installed Components\{A509B1A8-37EF-4b3f-8CFC-4F3A74704073}' /v IsInstalled /t REG_DWORD /d 0 /f #disable user IE Enhanced Security Configuration $client = New-Object System.Net.WebClient $client.DownloadFile($exchangeInstallLink, "C:\Install\$exchangeInstallFileName") $retries-- if (-not (Test-Path -Path "C:\Install\$exchangeInstallFileName")) { Start-Sleep -Seconds 30 } } $end = Get-Date Write-Verbose "...downloading the Exchange Installation files took $($end - $start)" Write-Verbose 'Extracting the Exchange Installation files...' $start = Get-Date & "C:\Install\$ExchangeInstallFileName" /X:C:\Install\ExchangeInstall /Q | Out-Null $end = Get-Date Write-Verbose "...extracting the Exchange Installation files took $($end - $start)" } $exchangeExtractCmd = { param( [Parameter(Mandatory = $true)] [string]$ExchangeInstallLink ) $exchangeInstallUri = New-Object System.Uri($ExchangeInstallLink) $exchangeInstallFileName = $ExchangeInstallUri.Segments[$ExchangeInstallUri.Segments.Count-1] Write-Verbose 'Extracting the Exchange Installation files...' $start = Get-Date & "C:\Install\$ExchangeInstallFileName" /X:C:\Install\ExchangeInstall /Q | Out-Null $end = Get-Date Write-Verbose "...extracting the Exchange Installation files took $($end - $start)" } $exchangeSchemaUpdateCmd = { $VerbosePreference = 'Continue' Write-Verbose 'Starting the Exchange Schema Update...' $start = Get-Date $Error.Clear() $result = New-Object PSObject -Property @{ InstallStatus = $null; InstallMessage = $null; InstallErrors = $null } $result.InstallMessage = & 'c:\Install\ExchangeInstall\setup.exe' /PrepareSchema /IAcceptExchangeServerLicenseTerms if ($Error.Exception.Message -match '(.+reboot.+pending.+)|(.+pending.+reboot.+)') { $result.InstallStatus = 'RebootRequired' } elseif ($Error.Count -eq 0) { $result.InstallStatus = 'Success' } else { $result.InstallStatus = 'Failed' } $end = Get-Date Write-Verbose "...Exchange Schema Update took $($end - $start)" $result.InstallErrors = $Error return $result } $exchangePrepAllDomainsCmd = { $VerbosePreference = 'Continue' Write-Verbose 'Starting the Exchange Domain Prep for all domains...' $start = Get-Date $Error.Clear() $result = New-Object PSObject -Property @{ InstallStatus = $null; InstallMessage = $null; InstallErrors = $null } $result.InstallMessage = & 'c:\Install\ExchangeInstall\setup.exe' /PrepareAllDomains /IAcceptExchangeServerLicenseTerms if ($Error.Exception.Message -match '(.+reboot.+pending.+)|(.+pending.+reboot.+)') { $result.InstallStatus = 'RebootRequired' } elseif ($Error.Count -eq 0) { $result.InstallStatus = 'Success' } else { $result.InstallStatus = 'Failed' } $end = Get-Date Write-Verbose "...Exchange Domain Prep for all domains took $($end - $start)" $result.InstallErrors = $Error return $result } $exchangePrepareADCmd = { param( [Parameter(Mandatory = $true)] [string]$OrganizationName ) $VerbosePreference = 'Continue' Write-Verbose 'Starting the Exchange AD Prep...' $start = Get-Date $Error.Clear() $result = New-Object PSObject -Property @{ InstallStatus = $null; InstallMessage = $null; InstallErrors = $null } $result.InstallMessage = & 'c:\Install\ExchangeInstall\setup.exe' /PrepareAD /OrganizationName:"$OrganizationName" /IAcceptExchangeServerLicenseTerms if ($Error.Exception.Message -match '(.+reboot.+pending.+)|(.+pending.+reboot.+)') { $result.InstallStatus = 'RebootRequired' } elseif ($Error.Count -eq 0) { $result.InstallStatus = 'Success' } else { $result.InstallStatus = 'Failed' } $end = Get-Date Write-Verbose "...Exchange AD Prep took $($end - $start)" $result.InstallErrors = $Error return $result } $exchangeSetupCmd = { param( [Parameter(Mandatory = $true)] [string]$OrganizationName ) $VerbosePreference = 'Continue' Write-Verbose 'Starting the Exchange Installation...' $start = Get-Date $Error.Clear() $result = New-Object PSObject -Property @{ InstallStatus = $null; InstallMessage = $null; InstallErrors = $null } $result.InstallMessage = & 'c:\Install\ExchangeInstall\setup.exe' /Mode:Install /Roles:ca,mb /InstallWindowsComponents /OrganizationName:"$OrganizationName" /IAcceptExchangeServerLicenseTerms if ($Error.Exception.Message -match '(.+reboot.+pending.+)|(.+pending.+reboot.+)') { $result.InstallStatus = 'RebootRequired' } elseif ($Error.Count -eq 0) { $result.InstallStatus = 'Success' } else { $result.InstallStatus = 'Failed' } $end = Get-Date Write-Verbose "...Exchange Installation took $($end - $start)" $result.InstallErrors = $Error return $result } function Copy-LabExchangeInstallationFiles { # .ExternalHelp AutomatedLab.Help.xml param( [Parameter(Mandatory = $true)] [AutomatedLab.Machine]$Machine ) $exchangeInstallLink = $MyInvocation.MyCommand.Module.PrivateData.Exchange2013DownloadLink $ucmaInstallLink = $MyInvocation.MyCommand.Module.PrivateData.ExchangeUcmaDownloadLink #copy the files to the destination machine if ($Machine.HostType -eq 'HyperV') { $exchangeInstallUri = New-Object System.Uri($exchangeInstallLink) $exchangeInstallFileName = $ExchangeInstallUri.Segments[$ExchangeInstallUri.Segments.Count-1] $ucmaInstallUri = New-Object System.Uri($ucmaInstallLink) $ucmaInstallFileName = $ucmaInstallUri.Segments[$ucmaInstallUri.Segments.Count-1] $ucmaFile = Get-ChildItem -Path $labSources -Filter $ucmaInstallFileName -Recurse if (-not $ucmaFile) { try { Write-Host "Downloading '$ucmaInstallFileName' from '$ucmaInstallLink'..." -NoNewline Get-LabInternetFile -Uri $ucmaInstallLink -Path $labSources\SoftwarePackages\$ucmaInstallFileName -ErrorAction Stop Write-Host 'finished' $ucmaFile = Get-ChildItem -Path $labSources -Filter $ucmaInstallFileName -Recurse } catch { throw "The Unified Communications Managed API 4.0 Runtime installation file ($ucmaInstallFileName) does not exist and could not be downloaded. Please put the file in the LabSources\SoftwarePackages folder and start the Exchange installation again (Install-Lab -Exchange2013)" } } Copy-LabFileItem -Path $ucmaFile.FullName -DestinationFolder C:\Install -ComputerName $Machine $exchangeFile = Get-ChildItem -Path $labSources -Filter $exchangeInstallFileName -Recurse if (-not $exchangeFile) { try { Write-Host "Downloading '$exchangeInstallFileName' from '$exchangeInstallLink'..." -NoNewline Get-LabInternetFile -Uri $exchangeInstallLink -Path $labSources\SoftwarePackages\$exchangeInstallFileName -ErrorAction Stop Write-Host 'finished' $exchangeFile = Get-ChildItem -Path $labSources -Filter $exchangeInstallFileName -Recurse } catch { throw "The Exchange 2013 installation file ($exchangeInstallFileName) does not exist and could not be downloaded. Please put the file in the LabSources\SoftwarePackages folder and start the Exchange installation again (Install-Lab -Exchange2013)" } } Copy-LabFileItem -Path $exchangeFile.FullName -DestinationFolder C:\Install -ComputerName $Machine } } #endregion script blocks #region Install-LabExchange2013 function Install-LabExchange2013 { # .ExternalHelp AutomatedLab.Help.xml [cmdletBinding()] param ([switch]$CreateCheckPoints) #$start = Get-Date Write-LogFunctionEntry $lab = Get-Lab $machines = Get-LabMachine -Role Exchange2013 $exchangeInstallLink = $MyInvocation.MyCommand.Module.PrivateData.Exchange2013DownloadLink $ucmaInstallLink = $MyInvocation.MyCommand.Module.PrivateData.ExchangeUcmaDownloadLink if (-not $machines) { Write-Verbose 'No Exchange 2013 servers defined in the lab. Skipping installation' return } Write-ScreenInfo -Message 'Waiting for machines to start up' -NoNewLine Start-LabVM -ComputerName $machines -Wait -ProgressIndicator 15 $jobs = @() Write-ScreenInfo -Message "Preparing machines: '$($machines -join ', ')'" -TaskStart $ProgressIndicatorForJob = 15 foreach ($machine in $machines) { $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 if ($rootDc.HostType -eq 'HyperV') { Write-ScreenInfo -Message 'Extracting Files' -NoNewLine Copy-LabExchangeInstallationFiles -Machine $rootDc $jobs += Invoke-LabCommand -ActivityName 'Extracting Files' -ComputerName $rootDc -ScriptBlock $exchangeExtractCmd -ArgumentList $exchangeInstallLink -AsJob -PassThru -NoDisplay } elseif ($rootDc.HostType -eq 'Azure') { Write-ScreenInfo -Message 'Downloading Files' -NoNewLine $jobs += Invoke-LabCommand -ActivityName 'Downloading Files' -ComputerName $rootDc -ScriptBlock $exchangeDownloadCmd -ArgumentList $exchangeInstallLink -AsJob -PassThru -NoDisplay $ProgressIndicatorForJob = 30 } } if ($machine.HostType -eq 'HyperV') { Write-ScreenInfo -Message 'Extracting Files' -NoNewLine Copy-LabExchangeInstallationFiles -Machine $machine $jobs += Invoke-LabCommand -ActivityName 'Extracting Files' -ComputerName $machine -ScriptBlock $exchangeExtractCmd ` -ArgumentList $exchangeInstallLink -AsJob -PassThru -NoDisplay } elseif ($rootDc.HostType -eq 'Azure') { Write-ScreenInfo -Message 'Downloading Files' -NoNewLine $jobs += Invoke-LabCommand -ActivityName 'Downloading Files' -ComputerName $machine -ScriptBlock $exchangeDownloadCmd ` -ArgumentList $exchangeInstallLink -AsJob -PassThru -NoDisplay $ProgressIndicatorForJob = 30 } } Wait-LWLabJob -Job (Install-LabWindowsFeature -ComputerName $machines -FeatureName Server-Media-Foundation, RSAT -UseLocalCredential -AsJob -PassThru -NoDisplay) ` -NoDisplay -NoNewLine -ProgressIndicator 15 Wait-LWLabJob -Job $jobs -ProgressIndicator $ProgressIndicatorForJob -NoDisplay Write-ScreenInfo -Message "Finished preparing machines: '$($machines -join ', ')'" -TaskEnd Write-ScreenInfo -Message 'Restarting machines' -NoNewLine Restart-LabVM -ComputerName $machines -Wait -ProgressIndicator 45 Sync-LabActiveDirectory -ComputerName $rootDc $jobs.Clear() foreach ($machine in $machines) { 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 Exchange2013).Properties.OrganizationName if ($machine.DomainName -ne $rootDc.DomainName) { $prepMachine = $rootDc } else { $prepMachine = $machine } # PREPARE SCHEMA Write-ScreenInfo -Message 'Performing Exchange Schema Update' -NoNewLine $exchangeSchemaUpdateJob = Invoke-LabCommand -ActivityName 'Performing Exchange Schema Update' -ComputerName $prepMachine -ScriptBlock $exchangeSchemaUpdateCmd -UseCredSsp -PassThru -NoDisplay -AsJob $result = Wait-LwLabJob -Job $exchangeSchemaUpdateJob -ProgressIndicator 30 -NoDisplay -ReturnResults -ErrorAction SilentlyContinue if ($result.InstallStatus -eq 'RebootRequired') { Write-ScreenInfo -Message "Restarting '$prepMachine' before updating the schema" Restart-LabVM -ComputerName $prepMachine -Wait -ProgressIndicator 45 Write-ScreenInfo -Message 'Restarting Exchange Schema Update' $exchangeSchemaUpdateJob = Invoke-LabCommand -ActivityName '(Re)Performing Exchange Schema Update' -ComputerName $prepMachine -ScriptBlock $exchangeSchemaUpdateCmd -UseCredSsp -PassThru -NoDisplay -AsJob $result = Wait-LwLabJob -Job $exchangeSchemaUpdateJob -ProgressIndicator 30 -NoDisplay -ReturnResults -ErrorAction SilentlyContinue } if ($result.InstallStatus -ne 'Success') { $resultVariable = New-Variable -Name ("AL_$([guid]::NewGuid().Guid)") -Scope Global -PassThru $resultVariable.Value = $result Write-Error "Exchange Schema Update failed on server '$prepMachine'. See content of $($resultVariable.Name) for details." return } # PREPARE AD Write-ScreenInfo -Message 'Performing Exchange AD Prep' -NoNewLine $exchangeADPrepJob = Invoke-LabCommand -ActivityName 'Performing Exchange AD Prep' -ComputerName $prepMachine -ScriptBlock $exchangePrepareADCmd ` -ArgumentList $exchangeOrganization -UseCredSsp -PassThru -NoDisplay -AsJob -ErrorAction SilentlyContinue $result = Wait-LwLabJob -Job $exchangeADPrepJob -ReturnResults -ProgressIndicator 30 -NoDisplay -ErrorAction SilentlyContinue if ($result.InstallStatus -eq 'RebootRequired') { Write-ScreenInfo -Message "Restarting machine '$prepMachine' before updating the schema" Restart-LabVM -ComputerName $prepMachine -Wait -ProgressIndicator 45 Write-ScreenInfo -Message 'Restarting Exchange AD Prep' $exchangeADPrepJob = Invoke-LabCommand -ActivityName 'Restarting Exchange AD Prep' -ComputerName $prepMachine -ScriptBlock $exchangePrepareADCmd ` -ArgumentList $exchangeOrganization -UseCredSsp -PassThru -NoDisplay -AsJob -ErrorAction SilentlyContinue $result = Wait-LwLabJob -Job $exchangeADPrepJob -ProgressIndicator 30 -NoDisplay -ReturnResults -ErrorAction SilentlyContinue } if ($result.InstallStatus -ne 'Success') { $resultVariable = New-Variable -Name ("AL_$([guid]::NewGuid().Guid)") -Scope Global -PassThru $resultVariable.Value = $result Write-Error "Exchange AD Prep failed on server '$prepMachine'. See content of $($resultVariable.Name) for details." return } #PREPARE ALL DOMAINS Write-ScreenInfo -Message 'Preparing All Domains' -NoNewLine $ExchangePrepareAllDomains = Invoke-LabCommand -ActivityName 'Preparing All Domains' -ComputerName $prepMachine -ScriptBlock $exchangePrepAllDomainsCmd ` -UseCredSsp -PassThru -NoDisplay -AsJob $result = Wait-LwLabJob -Job $ExchangePrepareAllDomains -ReturnResults -ErrorAction SilentlyContinue -ProgressIndicator 10 -NoDisplay if ($result.InstallStatus -eq 'RebootRequired') { Write-ScreenInfo -Message "Restarting machine '$prepMachine' before preparing all domains" Restart-LabVM -ComputerName $prepMachine -Wait -ProgressIndicator 45 $ExchangePrepareAllDomains = Invoke-LabCommand -ActivityName 'Restarting preparing All Domains' -ComputerName $prepMachine ` -ScriptBlock $exchangePrepAllDomainsCmd -UseCredSsp -PassThru -NoDisplay -AsJob $result = Wait-LwLabJob -Job $ExchangePrepareAllDomains -ReturnResults -ErrorAction SilentlyContinue -ProgressIndicator 10 -NoDisplay } if ($result.InstallStatus -ne 'Success') { $resultVariable = New-Variable -Name ("AL_$([guid]::NewGuid().Guid)") -Scope Global -PassThru $resultVariable.Value = $result Write-Error "Exchange Prep all domains failed on server '$prepMachine'. See content of $($resultVariable.Name) for details." return } Write-ScreenInfo -Message "Finished performing pre-requisites for machine '$machine'" -TaskEnd } Write-ScreenInfo -Message 'Triggering replication and installing Ucma' -NoNewLine Get-LabMachine -Role RootDC | ForEach-Object { Sync-LabActiveDirectory -ComputerName $_ } $jobs += Invoke-LabCommand -ActivityName 'Install Ucma' -ComputerName $machines -ScriptBlock $ucmaCmd ` -ArgumentList $ucmaInstallLink -AsJob -PassThru -NoDisplay Wait-LWLabJob -Job $jobs -NoDisplay -ProgressIndicator 10 Write-ScreenInfo -Message 'Restarting machines' -NoNewLine Restart-LabVM -ComputerName $machines -Wait -ProgressIndicator 45 foreach ($machine in $machines) { 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' -NoNewLine $exchangeInstallJob = Invoke-LabCommand -ActivityName 'Install Exchange' -ComputerName $machine -ScriptBlock $exchangeSetupCmd ` -ArgumentList $exchangeOrganization -UseCredSsp -PassThru -NoDisplay -Asjob -ErrorAction SilentlyContinue $result = Wait-LwLabJob -Job $exchangeInstallJob -ReturnResults -ErrorAction SilentlyContinue -ProgressIndicator 120 -NoDisplay if ($result.InstallStatus -eq 'RebootRequired') { Write-ScreenInfo -Message "Restarting machine '$machine' as part of the installation" -NoNewLine Restart-LabVM -ComputerName $machine -Wait -ProgressIndicator 45 Write-ScreenInfo -Message 'Continuing installation' -NoNewLine $exchangeInstallJob = Invoke-LabCommand -ActivityName 'Install Exchange' -ComputerName $machine -ScriptBlock $exchangeSetupCmd ` -ArgumentList $exchangeOrganization -UseCredSsp -PassThru -NoDisplay -Asjob -ErrorAction SilentlyContinue $result = Wait-LwLabJob -Job $exchangeInstallJob -ReturnResults -ErrorAction SilentlyContinue -ProgressIndicator 120 -NoDisplay -Timeout 120 } if (-not $result) { Write-ScreenInfo -Message "No result, restarting '$machine' to retry install" -NoNewLine Restart-LabVM -ComputerName $machine -Wait -ProgressIndicator 45 Write-ScreenInfo -Message 'Install Exchange Server 2013' -NoNewLine $exchangeInstallJob = Invoke-LabCommand -ActivityName 'Install Exchange (retry)' -ComputerName $machine -ScriptBlock $exchangeSetupCmd -ArgumentList $exchangeOrganization -UseCredSsp -PassThru -NoDisplay -Asjob -ErrorAction SilentlyContinue Wait-LwLabJob -Job $exchangeInstallJob -ProgressIndicator 120 -NoDisplay -Timeout 120 $result = $exchangeInstallJob | Receive-Job -ErrorAction SilentlyContinue } if ($result.InstallStatus -ne 'Success') { $resultVariable = New-Variable -Name ("AL_$([guid]::NewGuid().Guid)") -Scope Global -PassThru $resultVariable.Value = $result Write-Error "Exchange installation failed on server '$machine'. See content of $($resultVariable.Name) for details." continue } Write-ScreenInfo -Message "Finished installing Exchange Server 2013 on machine '$machine'" -TaskEnd } Write-LogFunctionExit } #endregion Install-LabExchange2013 |