Install-Dynamics365Server.psm1
function Install-Dynamics365Server { param ( [Parameter(Mandatory=$true)] [string] $MediaDir, [Parameter(Mandatory=$true)] [string] $LicenseKey, [Parameter(Mandatory=$true)] [switch] $CreateDatabase, [Parameter(Mandatory=$true)] [string] $SqlServer, [Parameter(Mandatory=$true)] [string] $PrivUserGroup, [Parameter(Mandatory=$true)] [string] $SQLAccessGroup, [Parameter(Mandatory=$true)] [string] $UserGroup, [Parameter(Mandatory=$true)] [string] $ReportingGroup, [Parameter(Mandatory=$true)] [string] $PrivReportingGroup, [Parameter(Mandatory=$true)] [pscredential] $CrmServiceAccount, [Parameter(Mandatory=$true)] [pscredential] $DeploymentServiceAccount, [Parameter(Mandatory=$true)] [pscredential] $SandboxServiceAccount, [Parameter(Mandatory=$true)] [pscredential] $VSSWriterServiceAccount, [Parameter(Mandatory=$true)] [pscredential] $AsyncServiceAccount, [Parameter(Mandatory=$true)] [pscredential] $MonitoringServiceAccount, [Parameter(Mandatory=$true)] [switch] $CreateWebSite, [Parameter(Mandatory=$true)] [int] $WebSitePort, [Parameter(Mandatory=$true)] [string] $WebSiteUrl, [Parameter(Mandatory=$true)] [string] $Organization, [Parameter(Mandatory=$true)] [string] $OrganizationUniqueName, [Parameter(Mandatory=$true)] [string] $ReportingUrl, [string] $InstallDir, [string] $IncomingExchangeServer, [string] $BaseISOCurrencyCode, [string] $BaseCurrencyName, [string] $BaseCurrencySymbol, [int] $BaseCurrencyPrecision, [string] $OrganizationCollation, [switch] $SQM = $false, [switch] $MUOptin = $false, [switch] $Reboot = $false, [pscredential] $InstallAccount, [string] $LogFilePath = $null, [ValidateRange(1,3600)] [int] $LogFilePullIntervalInSeconds = 30, [switch] $LogFilePullToOutput = $false ) $setupFilePath = "$mediaDir\SetupServer.exe"; $fileVersion = ( Get-Command $setupFilePath ).FileVersionInfo.FileVersionRaw.ToString(); Write-Output "Version of software to be installed: $fileVersion"; $testScriptBlock = { try { Add-PSSnapin Microsoft.Crm.PowerShell -ErrorAction Ignore if ( Get-PSSnapin Microsoft.Crm.PowerShell -ErrorAction Ignore ) { $CrmOrganization = Get-CrmOrganization; $CrmOrganization.Version; } else { "Could not load Microsoft.Crm.PowerShell PSSnapin"; } } catch { Write-Output "$(Get-Date) Caught an exception: $($_.Exception.Message)"; $_.Exception.Message; } } if ( $installAccount ) { $testResponse = Invoke-Command -ScriptBlock $testScriptBlock $env:COMPUTERNAME -Credential $installAccount -Authentication CredSSP; } else { $testResponse = Invoke-Command -ScriptBlock $testScriptBlock; } $productDetected = $null; if ( $testResponse.StartsWith( "9." ) -or $testResponse.StartsWith( "8." ) ) { $productDetected = $testResponse; } # Starting installation only when no Dynamics products is installed. if ( !$productDetected ) { $xml = [xml]""; $crmSetupElement = $xml.CreateElement( "CRMSetup" ); $serverElement = $xml.CreateElement( "Server" ); #Patch is a required element for setup $patchElement = $xml.CreateElement( "Patch" ); $patchElement.SetAttribute( "Update", $false ) | Out-Null; $serverElement.AppendChild( $patchElement ) | Out-Null; $licenseKeyElement = $xml.CreateElement( "LicenseKey" ); $licenseKeyElement.InnerText = $licenseKey; $serverElement.AppendChild( $licenseKeyElement ) | Out-Null; if ( $installDir ) { $installDirElement = $xml.CreateElement( "InstallDir" ); $installDirElement.InnerText = $installDir; $serverElement.AppendChild( $installDirElement ) | Out-Null; } $databaseElement = $xml.CreateElement( "Database" ); $databaseElement.SetAttribute( "create", $createDatabase ) | Out-Null; $serverElement.AppendChild( $databaseElement ) | Out-Null; $SQLServerElement = $xml.CreateElement( "SqlServer" ); $SQLServerElement.InnerText = $SQLServer; $serverElement.AppendChild( $SQLServerElement ) | Out-Null; $groupsElement = $xml.CreateElement( "Groups" ); $groupsElement.SetAttribute( "autogroupmanagementoff", $false ) | Out-Null; $privUserGroupElement = $xml.CreateElement( "PrivUserGroup" ); $privUserGroupElement.InnerText = $privUserGroup; $groupsElement.AppendChild( $privUserGroupElement ) | Out-Null; $SQLAccessGroupElement = $xml.CreateElement( "SQLAccessGroup" ); $SQLAccessGroupElement.InnerText = $SQLAccessGroup; $groupsElement.AppendChild( $SQLAccessGroupElement ) | Out-Null; $userGroupElement = $xml.CreateElement( "UserGroup" ); $userGroupElement.InnerText = $userGroup; $groupsElement.AppendChild( $userGroupElement ) | Out-Null; $reportingGroupElement = $xml.CreateElement( "ReportingGroup" ); $reportingGroupElement.InnerText = $reportingGroup; $groupsElement.AppendChild( $reportingGroupElement ) | Out-Null; $privReportingGroupElement = $xml.CreateElement( "PrivReportingGroup" ); $privReportingGroupElement.InnerText = $privReportingGroup; $groupsElement.AppendChild( $privReportingGroupElement ) | Out-Null; $serverElement.AppendChild( $groupsElement ) | Out-Null; $CRMServiceAccountElement = $xml.CreateElement( "CrmServiceAccount" ); $CRMServiceAccountElement.SetAttribute( "type", "DomainUser" ) | Out-Null; $serviceAccountLoginElement = $xml.CreateElement( "ServiceAccountLogin" ); $serviceAccountLoginElement.InnerText = $CRMServiceAccount.UserName; $CRMServiceAccountElement.AppendChild( $serviceAccountLoginElement ) | Out-Null; $serviceAccountPasswordElement = $xml.CreateElement( "ServiceAccountPassword" ); $serviceAccountPasswordElement.InnerText = $CRMServiceAccount.GetNetworkCredential().Password; $CRMServiceAccountElement.AppendChild( $serviceAccountPasswordElement ) | Out-Null; $serverElement.AppendChild( $CRMServiceAccountElement ) | Out-Null; $deploymentServiceAccountElement = $xml.CreateElement( "DeploymentServiceAccount" ); $deploymentServiceAccountElement.SetAttribute( "type", "DomainUser" ) | Out-Null; $serviceAccountLoginElement = $xml.CreateElement( "ServiceAccountLogin" ); $serviceAccountLoginElement.InnerText = $deploymentServiceAccount.UserName; $deploymentServiceAccountElement.AppendChild( $serviceAccountLoginElement ) | Out-Null; $serviceAccountPasswordElement = $xml.CreateElement( "ServiceAccountPassword" ); $serviceAccountPasswordElement.InnerText = $deploymentServiceAccount.GetNetworkCredential().Password; $deploymentServiceAccountElement.AppendChild( $serviceAccountPasswordElement ) | Out-Null; $serverElement.AppendChild( $deploymentServiceAccountElement ) | Out-Null; $sandboxServiceAccountElement = $xml.CreateElement( "SandboxServiceAccount" ); $sandboxServiceAccountElement.SetAttribute( "type", "DomainUser" ) | Out-Null; $serviceAccountLoginElement = $xml.CreateElement( "ServiceAccountLogin" ); $serviceAccountLoginElement.InnerText = $sandboxServiceAccount.UserName; $sandboxServiceAccountElement.AppendChild( $serviceAccountLoginElement ) | Out-Null; $serviceAccountPasswordElement = $xml.CreateElement( "ServiceAccountPassword" ); $serviceAccountPasswordElement.InnerText = $sandboxServiceAccount.GetNetworkCredential().Password; $sandboxServiceAccountElement.AppendChild( $serviceAccountPasswordElement ) | Out-Null; $serverElement.AppendChild( $sandboxServiceAccountElement ) | Out-Null; $VSSWriterServiceAccountElement = $xml.CreateElement( "VSSWriterServiceAccount" ); $VSSWriterServiceAccountElement.SetAttribute( "type", "DomainUser" ) | Out-Null; $serviceAccountLoginElement = $xml.CreateElement( "ServiceAccountLogin" ); $serviceAccountLoginElement.InnerText = $VSSWriterServiceAccount.UserName; $VSSWriterServiceAccountElement.AppendChild( $serviceAccountLoginElement ) | Out-Null; $serviceAccountPasswordElement = $xml.CreateElement( "ServiceAccountPassword" ); $serviceAccountPasswordElement.InnerText = $VSSWriterServiceAccount.GetNetworkCredential().Password; $VSSWriterServiceAccountElement.AppendChild( $serviceAccountPasswordElement ) | Out-Null; $serverElement.AppendChild( $VSSWriterServiceAccountElement ) | Out-Null; $asyncServiceAccountElement = $xml.CreateElement( "AsyncServiceAccount" ); $asyncServiceAccountElement.SetAttribute( "type", "DomainUser" ) | Out-Null; $serviceAccountLoginElement = $xml.CreateElement( "ServiceAccountLogin" ); $serviceAccountLoginElement.InnerText = $asyncServiceAccount.UserName; $asyncServiceAccountElement.AppendChild( $serviceAccountLoginElement ) | Out-Null; $serviceAccountPasswordElement = $xml.CreateElement( "ServiceAccountPassword" ); $serviceAccountPasswordElement.InnerText = $asyncServiceAccount.GetNetworkCredential().Password; $asyncServiceAccountElement.AppendChild( $serviceAccountPasswordElement ) | Out-Null; $serverElement.AppendChild( $asyncServiceAccountElement ) | Out-Null; $monitoringServiceAccountElement = $xml.CreateElement( "MonitoringServiceAccount" ); $monitoringServiceAccountElement.SetAttribute( "type", "DomainUser" ) | Out-Null; $serviceAccountLoginElement = $xml.CreateElement( "ServiceAccountLogin" ); $serviceAccountLoginElement.InnerText = $monitoringServiceAccount.UserName; $monitoringServiceAccountElement.AppendChild( $serviceAccountLoginElement ) | Out-Null; $serviceAccountPasswordElement = $xml.CreateElement( "ServiceAccountPassword" ); $serviceAccountPasswordElement.InnerText = $monitoringServiceAccount.GetNetworkCredential().Password; $monitoringServiceAccountElement.AppendChild( $serviceAccountPasswordElement ) | Out-Null; $serverElement.AppendChild( $monitoringServiceAccountElement ) | Out-Null; #WebsiteUrl is required $webSiteUrlElement = $xml.CreateElement( "WebsiteUrl" ); $webSiteUrlElement.SetAttribute( "create", $createWebSite ) | Out-Null; $webSiteUrlElement.SetAttribute( "port", $webSitePort ) | Out-Null; $webSiteUrlElement.InnerText = $webSiteUrl; $serverElement.AppendChild( $webSiteUrlElement ) | Out-Null; if ( $incomingExchangeServer ) { $emailElement = $xml.CreateElement( "Email" ); $incomingExchangeServerElement = $xml.CreateElement( "IncomingExchangeServer" ); $incomingExchangeServerElement.SetAttribute( "name", $incomingExchangeServer ) | Out-Null; $emailElement.AppendChild( $incomingExchangeServerElement ) | Out-Null; $serverElement.AppendChild( $emailElement ) | Out-Null; } $organizationUniqueNameElement = $xml.CreateElement( "OrganizationUniqueName" ); $organizationUniqueNameElement.InnerText = $organizationUniqueName; $serverElement.AppendChild( $organizationUniqueNameElement ) | Out-Null; $organizationElement = $xml.CreateElement( "Organization" ); $organizationElement.InnerText = $organization; $serverElement.AppendChild( $organizationElement ) | Out-Null; if ( $baseISOCurrencyCode -or $baseCurrencyName -or $baseCurrencySymbol -or $baseCurrencyPrecision ) { $baseCurrencyElement = $xml.CreateElement( "basecurrency" ); if ( $baseISOCurrencyCode ) { $baseCurrencyElement.SetAttribute( "isocurrencycode", $baseISOCurrencyCode ) | Out-Null; } if ( $baseCurrencyName ) { $baseCurrencyElement.SetAttribute( "currencyname", $baseCurrencyName ) | Out-Null; } if ( $baseCurrencySymbol ) { $baseCurrencyElement.SetAttribute( "currencysymbol", $baseCurrencySymbol ) | Out-Null; } if ( $baseCurrencyPrecision ) { $baseCurrencyElement.SetAttribute( "currencyprecision", $baseCurrencyPrecision ) | Out-Null; } $serverElement.AppendChild( $baseCurrencyElement ) | Out-Null; } if ( $organizationCollation ) { $organizationCollationElement = $xml.CreateElement( "OrganizationCollation" ); $organizationCollationElement.InnerText = $organizationCollation; $serverElement.AppendChild( $organizationCollationElement ) | Out-Null; } $reportingElement = $xml.CreateElement( "Reporting" ); $reportingElement.SetAttribute( "URL", $ReportingUrl ) | Out-Null; $serverElement.AppendChild( $reportingElement ) | Out-Null; $SQMElement = $xml.CreateElement( "SQM" ); $SQMElement.SetAttribute( "optin", $SQM ) | Out-Null; $serverElement.AppendChild( $SQMElement ) | Out-Null; $MUOptinElement = $xml.CreateElement( "muoptin" ); $MUOptinElement.SetAttribute( "optin", $MUOptin ) | Out-Null; $serverElement.AppendChild( $MUOptinElement ) | Out-Null; $remoteInstallElement = $xml.CreateElement( "remoteinstall" ); $remoteInstallElement.InnerText = $true; $serverElement.AppendChild( $remoteInstallElement ) | Out-Null; $rebootElement = $xml.CreateElement( "Reboot" ); $rebootElement.InnerText = $reboot; $serverElement.AppendChild( $rebootElement ) | Out-Null; $crmSetupElement.AppendChild( $serverElement ) | Out-Null; $xml.AppendChild( $crmSetupElement ) | Out-Null; $stringWriter = New-Object System.IO.StringWriter; $xmlWriter = New-Object System.Xml.XmlTextWriter $stringWriter; $xmlWriter.Formatting = "indented"; $xml.WriteTo( $xmlWriter ); $xmlWriter.Flush(); $stringWriter.Flush(); $localInstallationScriptBlock = { param( $setupFilePath, $xmlConfig, $logFilePath, $logFilePullIntervalInSeconds, $logFilePullToOutput) $tempFileName = [guid]::NewGuid().Guid; $tempFilePath = "$env:Temp\$tempFileName.xml"; Write-Output "$(Get-Date) Saving configuration temporary to $tempFilePath"; Set-Content -Path $tempFilePath -Value $xmlConfig; Write-Output "$(Get-Date) Starting $setupFilePath"; $installCrmScript = { param( $setupFilePath, $tempFilePath, $logFilePath ); Write-Output "Start-Process '$setupFilePath' -ArgumentList '/Q /config $tempFilePath /L $logFilePath' -Wait;"; Start-Process "$setupFilePath" -ArgumentList "/Q /config $tempFilePath /L $logFilePath" -Wait; } $job = Start-Job -ScriptBlock $installCrmScript -ArgumentList $setupFilePath, $tempFilePath, $logFilePath; Write-Output "$(Get-Date) Started installation job, log will be saved in $logFilePath"; $lastLinesCount = 0; While ( $job.State -ne "Completed" ) { Write-Output "$(Get-Date) Waiting until CRM installation job is done, sleeping $logFilePullIntervalInSeconds sec"; Start-Sleep $logFilePullIntervalInSeconds; if(($logFilePullToOutput -eq $True) -and ((Test-Path $logFilePath) -eq $True)) { $linesCount = (Get-Content $logFilePath | Measure-Object -Line).Lines; $newLinesCount = $linesCount - $lastLinesCount; if($newLinesCount -gt 0) { Write-Output "$(Get-Date) - new logs: $newLinesCount lines"; $lines = Get-Content $logFilePath | Select-Object -First $newLinesCount -Skip $lastLinesCount; foreach($line in $lines) { Write-Output $line; } } else { Write-Output "$(Get-Date) - no new logs"; } $lastLinesCount = $linesCount; } } Write-Output "$(Get-Date) Job is complete, output:"; Write-Output ( Receive-Job $job ); Remove-Job $job; Start-Sleep 10; Write-Output "$(Get-Date) Removing xml configuration file"; Remove-Item $tempFilePath; } if([String]::IsNullOrEmpty($logFilePath) -eq $True) { $timeStamp = ( Get-Date -Format u ).Replace(" ","-").Replace(":","-"); $logFilePath = "$env:Temp\CRMInstallationLog_$timeStamp.txt"; } if ( $installAccount ) { Invoke-Command -ScriptBlock $localInstallationScriptBlock ` -ComputerName $env:COMPUTERNAME ` -Credential $installAccount ` -Authentication CredSSP ` -ArgumentList $setupFilePath, $stringWriter.ToString(), $logFilePath, $logFilePullIntervalInSeconds, $logFilePullToOutput; } else { Invoke-Command -ScriptBlock $localInstallationScriptBlock ` -ArgumentList $setupFilePath, $stringWriter.ToString(), $logFilePath, $logFilePullIntervalInSeconds, $logFilePullToOutput; } Write-Output "Sleeping after installation"; Start-Sleep 10; $testScriptBlock = { try { Add-PSSnapin Microsoft.Crm.PowerShell -ErrorAction Ignore if ( Get-PSSnapin Microsoft.Crm.PowerShell -ErrorAction Ignore ) { $CrmOrganization = Get-CrmOrganization; $CrmOrganization.Version; } else { "Could not load Microsoft.Crm.PowerShell PSSnapin"; } } catch { $_.Exception.Message; } } if ( $installAccount ) { $testResponse = Invoke-Command -ScriptBlock $testScriptBlock ` -ComputerName $env:COMPUTERNAME ` -Credential $installAccount ` -Authentication CredSSP } else { $testResponse = Invoke-Command -ScriptBlock $testScriptBlock } Write-Output "Sleeping after version obtaining"; Start-Sleep 10; if ( $testResponse -eq $fileVersion ) { Write-Output "Installation is finished and verified successfully"; } else { if( (Test-Path $logFilePath) -eq $True) { $errorLines = Get-Content $logFilePath | Select-String -Pattern "Error" -SimpleMatch; if($null -ne $errorLines) { "Errors from install log: $logFilePath"; foreach($errorLine in $errorLines) { $errorLine; } } } $errorMessage = "Installation job finished but the product is still not installed. Current product version installed is '$testResponse'"; Write-Output $errorMessage; Throw $errorMessage; } } else { Write-Output "Product version '$testResponse' is already installed, skipping"; } } |