Month: July 2015

CRM Server – Scripted installation

Yet another nice script from Mads Hjort Larsen.
I simply hadn’t found any automated installation of CRM server, including prerequisites SQL Reporting Services native mode and Watson components, so we decided to work this out. The script requires both SQL installation media as well as CRM Server media.

Script runtime is long, approximately 1 hour. That is just how long it takes to install all components. For reference, here is a video of a script run.
Video

NB: For some reason the parser is unable to parse to full script below.
Copy into Powershell ISE or similar will yield a fully color coded script.
NBB: This script has severe issues against named instances of SQL. We will issue an update once we have tested this.

Clear-Host
Set-Location $PSScriptRoot

#region Variables
$CRMgroupOU = '<CRM OU>' # Specifies the Active Directory organizational unit (OU) where the security groups will be created, for example, <OU> OU=CRM, DC=MySubDomain, DC=MyDomain, DC=com</OU>.
$SQLserver = '<SQL SERVER>'

#Media
$SQLmedia = 'D:' # Location of SQL server install media
$CRMmedia = 'E:' # Location of CRM server install media

$CRMupdateDir = "$PSScriptRoot\updates" # Directory that SERVER, ROUTER, SRS and MUI update files will be downloaded to.

#Accounts
$CRMSetupUser = '<CRM Setup User>' # Local Admin on CRM Server, Local Admin on SQL Server, Delegate Full Control on CRM Group OU
$CRMSetupUserPassword = '<CRM Setup User Password>'

$svcCRMApp = '<CRM AppPool account>' # Specifies the service account used to run the Microsoft Dynamics CRM Unzip Service and ASP.NET AppPool application pool identity.
$svcCRMAppPassword = '<CRM AppPool account password>'

$svcCRMDeploy = '<CRM Deployment account>' # Specifies the service account to use for the Deployment Web service.
$svcCRMDeployPassword = '<CRM Deployment account password>'

$svcCRMSandbox = '<CRM Sandbox account>' # Specifies the service account to use for the Sandbox Processing service.
$svcCRMSandboxPassword = '<CRM Sandbox account password>'

$svcCRMVSS = '<CRM VSS account>' # Specifies the service account to use for the Microsoft Dynamics CRM VSS Writer service.
$svcCRMVSSPassword = '<CRM VSS account password>'

$svcCRMAsync = '<CRM aSynch account>' # Specifies the service account to use for the asynchronous processing services.
$svcCRMAsyncPassword = '<CRM aSynch account password>'

$svcCRMMonitor = '<CRM Monitor account>' # Specifies the service account to use for the Microsoft Dynamics CRM Monitoring service.
$svcCRMMonitorPassword = '<CRM Monitor account password>'

$svcCRMEmailRouter = '<CRM Email router account>' # Specifies the service account to use for the Microsoft Dynamics CRM Email Router service.
$svcCRMEmailRouterPassword = 'CRM Email router account password>'

#ReportingServices
$RSappPoolAccount = '<SQL Reporting Services account>'
$RSappPoolAccountPassword = '<SQL Reporting Services account password>'

$RSdbname = $env:COMPUTERNAME+'_SQL_Reporting' # SQL Reporting Services database name.
$ReportingServicesURL = $env:COMPUTERNAME+":80" # SQL Reporting services URL (name and port)

$RSemailServer = "smtp.mycompany.com"
$RSemailAddress = "reporting.services@mycompany.co.uk"

$DBinstance = '<SQL Client Alias>' # Should be a SQL Client connection alias (Not SQL server alias).
$RSinstance = '' # If this value is left blank "MSSQLSERVER" will be used

#CRM XML
# https://technet.microsoft.com/en-us/library/hh699830.aspx
$patchUpdate = 'True' # Determines the behavior of the update Microsoft Dynamics CRM Server Setup technology. This feature lets Setup perform a one-time search for, and if applicable, download and apply the latest installation files for Microsoft Dynamics CRM.
$patchPath = '' # \\ServerName\ShareName\patchfile.msp

$licenseKey = '<License Key>' # Specifies the product key for this deployment. The configuration file can contain only one Microsoft Dynamics CRM product key.
$databaseCreate = 'true' # Values for this parameter are either true or false. True causes Setup to create a new Microsoft Dynamics CRM configuration database. False causes Setup to connect to an existing Microsoft Dynamics CRM configuration database.
$reportingUrl = "http://$env:COMPUTERNAME/ReportServer" # Specifies the URL of the Report Server.
$Collation = 'Latin1_General_CI_AI' # Specifies the SQL Server database collation name to use for the organization database. The default collation depends on the language of Microsoft Dynamics CRM Server that you’re installing, for example, Latin1_General_Cl_Al, which is the default collation for English (US) language deployments.

# After Setup is complete, you cannot change the base ISO currency code. However, you can change the base-currency name, base-currency symbol, and base-currency precision.
$currencycode="<ISO Currency code>" # Specifies the ISO three-letter currency-code, display name, and symbol to use for the base currency. For example, if you want to use U.S. dollars as the base currency, use isocurrencycode="USD". You must use a valid ISO currency description for the isocurrencycode attribute.
$currencyname="<Currency Name>" # You must also specify the currency-name and currency-symbol display names for the ISO base currency. For example, if the ISO currency code is USD, the currency name should be "US Dollar" and the currency symbol should be "$". However, you can use any string that you want for these attributes.
$currencysymbol="<Currency Symbol>" 
$currencyprecision="<Currency Precision>" # You must specify the precision for the base currency that you specified in the currencycode attribute. Valid values depend on the type of currency that you specify. For example, USD valid values are 1 – 9 and the default value is 2.

$displayName = '<CRM Display Name>' # Specifies the long name of your organization. The name can be up to 250 characters long and extended characters are supported.
$uniqueName = '<Organizational Name>' # Specifies the name of your organization in the URL that users will use to access the deployment. There is a 30 character limit. Extended characters or spaces are not allowed. If you don’t specify this element or leave the value blank, Setup will generate a short name based on the <Organization> element value.

$createNewWebsite = 'True' # Specifies the website to be used for Microsoft Dynamics CRM Server. Use Create="true" to create a new Microsoft Dynamics CRM website and leave the value $webSitePath blank.
$websitePort = '<CRM Port>' # Use port="TCPportnumber", where TCPportnumber is a valid and available TCP port number, to specify the port for connecting to the Microsoft Dynamics CRM Server application. If left blank, the port number that will be used is 5555. If $createNewWebsite="false", the port attribute is ignored.
$webSitePath = '' # Only needed if $createNewWebsite = 'false'

$SQMoptin = 'False' # Specifies whether you will participate in the Customer Experience Improvement Program.
$MUoptin = 'False' # Specifies whether to use Microsoft Update to download and install updates to Microsoft Dynamics CRM Server and other installed applications. After the installation is completed, this feature helps keep your computer up-to-date on an ongoing basis.

$ifdsettings = 'False' # This option should only be used for Internet-facing deployment. Set enabled = "true" to notify Microsoft Dynamics CRM server Setup to configure the deployment for Internet access. If the <ifdsettings> element is not specified, the enabled attribute value is set to false.
$internalnetworkaddress = '10.0.0.1-255.0.0.0' # IP address and subnet mask, such as 157.56.137.105-255.255.255.0. This is the internal IP address and the associated subnet mask of the subnet where your internal users reside. The subnets you enter here will be for the computers that you want to be considered as internal and you do not want the users to login through the IFD environment when they are on these subnets. To enter multiple subnets use a comma to separate the values in the configuration file Note: If you leave this element blank, all communication to the Microsoft Dynamics CRM server will be considered as internal and users will default to windows authentication when hitting the Microsoft Dynamics CRM website.
$rootdomainscheme = 'http' # Must be https, which will use secure sockets layer (SSL), or http, which will use the nonsecure HTTP protocol. Note: Setup does not require SSL on the Web site where Microsoft Dynamics CRM is installed. We strongly recommend that you specify the https value in the rootdomainscheme element. In addition, after Setup is complete, to help protect information that is transmitted between users and Microsoft Dynamics CRM Server, we recommend that you configure the Web site to require SSL. 
$sdkrootdomain = 'api.contoso.com' # Specifies the domain name that will be used for applications that use the methods described in the Microsoft Dynamics CRM 4.0 Software Development Kit (SDK). The value that is set here will be prefixed by your unique organization name to form the URL so you only need to put in the domain.com
$webapplicationrootdomain = 'app.contoso.com' # Specifies the domain name that will be used for the Microsoft Dynamics CRM Web application and Microsoft Dynamics CRM for Outlook. The value that is set here will be prefixed by your unique organization name to form the URL so you only need to put in the domain.com 
$discoveryrootdomain = 'disc.contoso.com' # The root domain for the discovery Web service.

$ExchangeServerName = '' # Specifies the Microsoft Exchange Server computer or POP3 that will be used by the Email Router to route incoming email messages. If not specified and later the Email Router is used in the deployment, the computer must be added to the PrivUserGroup security group.
$nonHTTPS = 'True' # If you do not want to use HTTPS, set this to true - see https://msdn.microsoft.com/en-us/library/hh550122(v=crm.6).aspx for more info.

#Updates
   $Server = 'http://download.microsoft.com/download/5/F/4/5F43956F-E1CF-4F15-96BE-967AF29E2005/CRM2015-Server-KB3010990-ENU-amd64.exe'
   $Router = 'http://download.microsoft.com/download/5/F/4/5F43956F-E1CF-4F15-96BE-967AF29E2005/CRM2015-Router-KB3010990-ENU-amd64.exe'
      $SRS = 'http://download.microsoft.com/download/5/F/4/5F43956F-E1CF-4F15-96BE-967AF29E2005/CRM2015-Srs-KB3010990-ENU-amd64.exe'
   $MUIENU = 'http://download.microsoft.com/download/5/F/4/5F43956F-E1CF-4F15-96BE-967AF29E2005/CRM2015-Mui-KB3010990-ENU-amd64.exe'
   $MUIDAN = 'http://download.microsoft.com/download/A/C/5/AC560071-42FF-44C0-AEE7-848FF99F74D9/CRM2015-Mui-DAN-amd64.exe'
$MUIDANUPD = 'http://download.microsoft.com/download/A/A/0/AA01E7CA-9120-48E4-8637-FFD8AD5E9C55/CRM2015-Mui-KB3056327-DAN-amd64.exe'
#endregion

##############################################################################
# It should not be neccessary to edit below this line ------------------- πŸ™‚ #
##############################################################################

$SQLinstallFile = $SQLmedia+'\Setup.exe'
$CRMinstallFile = $CRMmedia+'\Server\amd64\SetupServer.exe' # Location of SetupServer.exe
$SRSInstallFile = $CRMmedia+'\Server\amd64\SrsDataConnector\SetupSrsDataConnector.exe'
$EmailRouterInstallFile = $CRMmedia+'\EmailRouter\amd64\SetupEmailRouter.exe' # Location of Email Router setup file.
$ADserver = ([ADSI]”LDAP://RootDSE”).dnsHostName

function Add-Clock {
    $code = { 
    $start = Get-Date
    do {
        $now = Get-Date
        $diff = $now-$start
        $title = ("$diff").Split('.')[0].ToString()
        [System.Console]::Title = "Approx. time elapsed: "+$title
        Start-Sleep -Seconds 1
        } while ($true)
    }

$ps = [PowerShell]::Create()
$null = $ps.AddScript($code)
$ps.BeginInvoke()
}

if (!(Test-Path placeholder.tmp)) {
Add-Clock | Out-Null
Clear-Host
Write-Host ''
Write-Host ' β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•— β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•— β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•— β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•— β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•— β–ˆβ–ˆβ•—   β–ˆβ–ˆβ•—β–ˆβ–ˆβ•—β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—β–ˆβ–ˆβ•—β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—' -ForegroundColor Green
Write-Host ' β–ˆβ–ˆβ•”β•β•β–ˆβ–ˆβ•—β–ˆβ–ˆβ•”β•β•β–ˆβ–ˆβ•—β–ˆβ–ˆβ•”β•β•β•β•β•β–ˆβ–ˆβ•”β•β•β–ˆβ–ˆβ•—β–ˆβ–ˆβ•”β•β•β•β•β•β–ˆβ–ˆβ•”β•β•β•β–ˆβ–ˆβ•—β–ˆβ–ˆβ•‘   β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•”β•β•β•β•β•β–ˆβ–ˆβ•‘β•šβ•β•β–ˆβ–ˆβ•”β•β•β•β–ˆβ–ˆβ•”β•β•β•β•β•β–ˆβ–ˆβ•”β•β•β•β•β•' -ForegroundColor Green
Write-Host ' β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•”β•β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•”β•β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—  β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•”β•β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—  β–ˆβ–ˆβ•‘   β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•‘   β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•‘β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—β–ˆβ–ˆβ•‘   β–ˆβ–ˆβ•‘   β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—  β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—' -ForegroundColor Green
Write-Host ' β–ˆβ–ˆβ•”β•β•β•β• β–ˆβ–ˆβ•”β•β•β–ˆβ–ˆβ•—β–ˆβ–ˆβ•”β•β•β•  β–ˆβ–ˆβ•”β•β•β–ˆβ–ˆβ•—β–ˆβ–ˆβ•”β•β•β•  β–ˆβ–ˆβ•‘β–„β–„ β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•‘   β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•‘β•šβ•β•β•β•β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•‘   β–ˆβ–ˆβ•‘   β–ˆβ–ˆβ•”β•β•β•  β•šβ•β•β•β•β–ˆβ–ˆβ•‘' -ForegroundColor Green
Write-Host ' β–ˆβ–ˆβ•‘     β–ˆβ–ˆβ•‘  β–ˆβ–ˆβ•‘β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—β–ˆβ–ˆβ•‘  β–ˆβ–ˆβ•‘β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—β•šβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•”β•β•šβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•”β•β–ˆβ–ˆβ•‘β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•‘β–ˆβ–ˆβ•‘   β–ˆβ–ˆβ•‘   β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•‘' -ForegroundColor Green
Write-Host ' β•šβ•β•     β•šβ•β•  β•šβ•β•β•šβ•β•β•β•β•β•β•β•šβ•β•  β•šβ•β•β•šβ•β•β•β•β•β•β• β•šβ•β•β–€β–€β•β•  β•šβ•β•β•β•β•β• β•šβ•β•β•šβ•β•β•β•β•β•β•β•šβ•β•   β•šβ•β•   β•šβ•β•β•β•β•β•β•β•šβ•β•β•β•β•β•β•' -ForegroundColor Green
Write-Host ''

Write-Host 'Verifying SQL server setup...'
$Fulltext = Get-Service -ComputerName $sqlServer -DisplayName "SQL Full-Text*"
if ($Fulltext -eq $null){Write-Host "Full Text Search is not installed on $sqlServer" -ForegroundColor Red}
else{Write-Host $Fulltext.DisplayName "found on $SQLserver"}

$SQLAgent = Get-Service -ComputerName $sqlServer -DisplayName "SQL Server Agent*"
if ($SQLAgent -eq $null){Write-Host "SQL Agent is not installed on $sqlServer" -ForegroundColor Red}
else{Write-Host $SQLAgent.DisplayName "found on $SQLserver";Invoke-Command -ComputerName $sqlServer -ScriptBlock {Start-Service -Name SQLSERVERAGENT}}

if ($Fulltext -eq $null -OR $SQLAgent -eq $null){Exit}

New-Item placeholder.tmp -Type File -Force | Out-Null

#region HostsFileAndSQLAlias
Write-Host "`nAdding to Hosts file: " -NoNewline
$SQLServerIP = (Test-Connection -ComputerName $SQLserver -Count 1).IPV4Address.IPAddressToString

$hostsPath = "$env:windir\System32\drivers\etc\hosts"
$hosts = get-content $hostsPath
$hostLine = $SQLServerIP +' '+ $DBinstance
Write-Host $hostLine

if ($hosts -notcontains $hostLine){
    $newHosts = $hosts+$hostLine
    $newHosts | Out-File $hostsPath -Force
    }

# Check SQL alias
Write-Host "`nCreating TCP/IP Aliases`n"
Start-Process $env:windir\System32\cliconfg.exe
Start-Process $env:windir\sysWOW64\cliconfg.exe
Sleep 1
Stop-Process -Name cliconfg

# These are the two Registry locations for the SQL Alias locations
$x86 = "HKLM:\Software\Microsoft\MSSQLServer\Client\ConnectTo"
$x64 = "HKLM:\Software\Wow6432Node\Microsoft\MSSQLServer\Client\ConnectTo"
 
# Test to see if the ConnectTo key already exists, and create it if it doesn't.
if ((test-path -path $x86) -ne $True){New-Item $x86 | Out-Null}
if ((test-path -path $x64) -ne $True){New-Item $x64 | Out-Null}
 
$TCPAlias = "DBMSSOCN," + $SQLserver
 
New-ItemProperty -Path $x86 -Name $DBinstance -PropertyType String -Value $TCPAlias -Force | Out-Null
New-ItemProperty -Path $x64 -Name $DBinstance -PropertyType String -Value $TCPAlias -Force | Out-Null
#endregion

#region CreateUsers
[ScriptBlock]$ScriptBlock = { 
  param(
    [string]$CRMgroupOU,
    [string]$CRMSetupUser,
    [string]$CRMSetupUserPassword, 
    [string]$svcCRMApp,
    [string]$svcCRMAppPassword, 
    [string]$svcCRMDeploy,
    [string]$svcCRMDeployPassword, 
    [string]$svcCRMSandbox,
    [string]$svcCRMSandboxPassword, 
    [string]$svcCRMVSS,
    [string]$svcCRMVSSPassword, 
    [string]$svcCRMAsync,
    [string]$svcCRMAsyncPassword, 
    [string]$svcCRMMonitor,
    [string]$svcCRMMonitorPassword, 
    [string]$svcCRMEmailRouter,
    [string]$svcCRMEmailRouterPassword, 
    [string]$RSappPoolAccount,
    [string]$RSappPoolAccountPassword
    )

    function AddUser ($user,$pass){
        if(!(Get-ADUser -Filter {sAMAccountname -eq $user})) {
            "Creating $user"
            New-ADUser -Name $user -AccountPassword (ConvertTo-SecureString -AsPlainText $pass -Force) -Enabled $true -Path $CRMgroupOU
            }
        else{"User $user already exists."}
    }

    if(!(Get-ADOrganizationalUnit -Filter {DistinguishedName -like $CRMgroupOU})){
        "Creating OU ($CRMgroupOU)"
        New-ADOrganizationalUnit -Name $CRMgroupOU.Split(',')[0].Split('=')[1] -Path ($CRMgroupOU.Split(',')[1..($CRMgroupOU.SPlit(',').Count)] -join ',')
        }
    else{"OU ($CRMgroupOU) already exists."}
    
    Write-Host "`nCreating accounts:"
    AddUser $CRMSetupUser $CRMSetupUserPassword
    AddUser $svcCRMApp $svcCRMAppPassword
    AddUser $svcCRMDeploy $svcCRMDeployPassword
    AddUser $svcCRMSandbox $svcCRMSandboxPassword
    AddUser $svcCRMVSS $svcCRMVSSPassword
    AddUser $svcCRMAsync $svcCRMAsyncPassword
    AddUser $svcCRMMonitor $svcCRMMonitorPassword
    AddUser $svcCRMEmailRouter $svcCRMEmailRouterPassword
    AddUser $RSappPoolAccount $RSappPoolAccountPassword
}

Invoke-Command -ComputerName $ADserver -ScriptBlock $ScriptBlock -ArgumentList $CRMgroupOU, $CRMSetupUser, $CRMSetupUserPassword, $svcCRMApp, $svcCRMAppPassword, $svcCRMDeploy, $svcCRMDeployPassword, $svcCRMSandbox, $svcCRMSandboxPassword, $svcCRMVSS, $svcCRMVSSPassword, $svcCRMAsync, $svcCRMAsyncPassword, $svcCRMMonitor, $svcCRMMonitorPassword, $svcCRMEmailRouter, $svcCRMEmailRouterPassword, $RSappPoolAccount, $RSappPoolAccountPassword
#endregion

#region LogonAsService
# http://anexinetisg.blogspot.dk/2014/02/grant-users-log-on-as-service-right-via.html
function Grant-LogOnAsService{
param(
    [string[]] $users
    ) 
 
    #Get list of currently used SIDs 
    secedit /export /cfg tempexport.inf | Out-Null
    $curSIDs = Select-String .\tempexport.inf -Pattern "SeServiceLogonRight" 
    $Sids = $curSIDs.line 
    $sidstring = ""
    foreach($user in $users){
        $objUser = New-Object System.Security.Principal.NTAccount($user)
        $strSID = $objUser.Translate([System.Security.Principal.SecurityIdentifier])
        if(!$Sids.Contains($strSID) -and !$sids.Contains($user)){
            $sidstring += ",*$strSID"
        }
    }
    if($sidstring){
        $newSids = $sids + $sidstring
        Write-Host "`nNew Sids: " -NoNewline
        $newsids.Split(',')
        $tempinf = Get-Content tempexport.inf
        $tempinf = $tempinf.Replace($Sids,$newSids)
        Add-Content -Path tempimport.inf -Value $tempinf
        secedit /import /db secedit.sdb /cfg ".\tempimport.inf" | Out-Null
        secedit /configure /db secedit.sdb | Out-Null
 
        gpupdate /force | Out-Null
    }
    else{
        Write-Host "`nNo new sids"
    }
 
    Remove-Item ".\tempimport.inf" -Force -ErrorAction SilentlyContinue
    Remove-Item ".\secedit.sdb" -Force -ErrorAction SilentlyContinue
    Remove-Item ".\tempexport.inf" -Force -ErrorAction SilentlyContinue
}

Grant-LogOnAsService -users $svcCRMApp, $svcCRMDeploy, $svcCRMSandbox, $svcCRMVSS, $svcCRMAsync, $svcCRMMonitor
#endregion

#region Add-DomainUserToLocalGroup
# http://blogs.technet.com/b/heyscriptingguy/archive/2010/08/19/use-powershell-to-add-domain-users-to-a-local-group.aspx
function Add-DomainUserToLocalGroup { 
    [cmdletBinding()] 
    Param( 
        [Parameter(Mandatory=$True)] 
        [string]$computer, 
        [Parameter(Mandatory=$True)] 
        [string]$group, 
        [Parameter(Mandatory=$True)] 
        [string]$domain, 
        [Parameter(Mandatory=$True)] 
        [string]$user 
    ) 
    $de = [ADSI]"WinNT://$computer/$Group,group" 
    $de.psbase.Invoke("Add",([ADSI]"WinNT://$domain/$user").path) 
}

Add-DomainUserToLocalGroup -user $CRMSetupUser -group 'Administrators' -domain $env:USERDOMAIN -computer $env:COMPUTERNAME
Add-DomainUserToLocalGroup -user $CRMSetupUser -group 'Administrators' -domain $env:USERDOMAIN -computer $SQLserver

Add-DomainUserToLocalGroup -user $svcCRMApp -group 'Administrators' -domain $env:USERDOMAIN -computer $env:COMPUTERNAME
Add-DomainUserToLocalGroup -user $svcCRMApp -group 'Performance Log Users' -domain $env:USERDOMAIN -computer $env:COMPUTERNAME

Add-DomainUserToLocalGroup -user $svcCRMDeploy -group 'Administrators' -domain $env:USERDOMAIN -computer $env:COMPUTERNAME
Add-DomainUserToLocalGroup -user $svcCRMDeploy -group 'Administrators' -domain $env:USERDOMAIN -computer $SQLserver

Add-DomainUserToLocalGroup -user $svcCRMAsync -group 'Performance Log Users' -domain $env:USERDOMAIN -computer $env:COMPUTERNAME
#endregion

# Delegate Full Control on CRM Group OU
$ScriptBlock = [Scriptblock]::Create('dsacls "'+$CRMgroupOU+'"'+" /G $env:USERDOMAIN\$CRMSetupUser"+':GA /I:T')
Invoke-Command -ComputerName $ADserver -ScriptBlock $ScriptBlock | Out-Null

# Grant $CRMsetupUser SysAdm rights on the SQL server
$ScriptBlock = {
    param([string]$CRMSetupUser)
    Import-Module -Name SQLPS -DisableNameChecking
    Add-Type -AssemblyName "Microsoft.SqlServer.Smo, Version=10.0.0.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91"
    $smo = New-Object Microsoft.SqlServer.Management.Smo.Server $env:COMPUTERNAME
    $sqlUser = New-Object -TypeName Microsoft.SqlServer.Management.Smo.Login -ArgumentList $smo,"$env:USERDOMAIN\$CRMSetupUser"
    $sqlUser.LoginType = 'WindowsUser'
    $sqlUser.PasswordPolicyEnforced = $false
    $sqlUser.Create()
    $smo.Logins["$env:USERDOMAIN\$CRMSetupUser"].AddToRole('sysadmin')
    }
Invoke-Command -ComputerName $SQLserver -ScriptBlock $ScriptBlock -ArgumentList $CRMSetupUser

#region ReportingServices
if ($RSinstance -eq ''){$RSinstance = 'MSSQLSERVER'}

$temp = $WarningPreference
$WarningPreference = 'SilentlyContinue'
Install-WindowsFeature –Name NET-Framework-Core
$WarningPreference = $temp

$ArgumentList = '/ACTION="Install" /FEATURES=RS /RSINSTALLMODE="FilesOnlyMode" /INSTANCENAME="'+$RSinstance+'" /INSTANCEID="'+$RSinstance+'" /ENU="True" /QUIETSIMPLE="True" /IACCEPTSQLSERVERLICENSETERMS /UpdateEnabled="True" /UpdateSource="MU" /ERRORREPORTING="False" /SQMREPORTING="'+$SQMoptin+'" /RSSVCACCOUNT='+$env:USERDOMAIN+'\'+$RSappPoolAccount+' /RSSVCPASSWORD='+$RSappPoolAccountPassword+' /RSSVCSTARTUPTYPE="Automatic"'
Start-Process $SQLinstallFile -ArgumentList $ArgumentList -Wait

Write-Host "`nGenerating Reporting Services configuration databases..."

$s = New-Object Management.ManagementScope("\\localhost\root\Microsoft\SqlServer\ReportServer\RS_$RSinstance\v12\admin")
$s.Connect()

$sc = New-Object Management.ManagementClass("\\localhost\root\Microsoft\SqlServer\ReportServer\RS_$RSinstance\v12\admin:MSReportServer_ConfigurationSetting")
$sc.Get()

$insts = $sc.GetInstances()
foreach ($o in $insts) { $inst = $o; }

# Set Virtual Directories
$inst.RemoveURL("ReportServerWebService", "http://$ReportingServicesURL", 1033) | Out-Null
$inst.RemoveURL("ReportManager", "http://$ReportingServicesURL", 1033) | Out-Null

$inst.SetVirtualDirectory("ReportServerWebService", "ReportServer", 1033) | Out-Null
$inst.SetVirtualDirectory("ReportManager", "ReportManager", 1033) | Out-Null

$inst.ReserveURL("ReportServerWebService", "http://$ReportingServicesURL", 1033) | Out-Null
$inst.ReserveURL("ReportManager", "http://$ReportingServicesURL", 1033) | Out-Null

# Create Reporting Services Database
$script = $inst.GenerateDatabaseCreationScript($RSdbname, 1033, $false)

[scriptblock]$scriptBlock = {
  param($script,$DBinstance)
    $script.Script | Out-File rs.sql
    sqlcmd -i rs.sql
    Remove-Item rs.sql
}
Invoke-Command -ComputerName $sqlServer -ScriptBlock $scriptBlock -ArgumentList $script,$DBinstance | Out-Null

$script = $inst.GenerateDatabaseRightsScript("$env:USERDOMAIN\$RSappPoolAccount", $RSdbname, $false, $true)

[scriptblock]$scriptBlock = {
  param($script,$DBinstance)
    $script.Script | Out-File rs.sql
    sqlcmd -i rs.sql
    Remove-Item rs.sql
}
Invoke-Command -ComputerName $sqlServer -ScriptBlock $scriptBlock -ArgumentList $script,$DBinstance | Out-Null

$inst.SetDatabaseConnection($DBinstance, $RSdbname, 2, "", "") | Out-Null
$inst.SetWindowsServiceIdentity($false, "$env:USERDOMAIN\$RSappPoolAccount", $RSappPoolAccountPassword) | Out-Null
#$inst.SetUnattendedExecutionAccount("<domain\user>", "<password>") | Out-Null
$inst.SetServiceState($true, $true, $true) | Out-Null
$inst.SetEmailConfiguration($true, $RSemailServer, $RSemailAddress) | Out-Null
#endregion

#region CRMserverPreReqs
Write-Host "`nInstalling CRM server prereqs:"
Write-Host "1of6 - Microsoft .NET Framework 4.5.2"
$file = gci ($CRMmedia+'\Redist\dotNETFX')
Start-Process $file.FullName -ArgumentList '/PASSIVE /NORESTART' -Wait

Write-Host "2of6 - Microsoft Application Error Reporting" 
$file = gci ($CRMmedia+'\Server\amd64\DW')
Start-Process $file.FullName -ArgumentList 'APPGUID=91710409-8000-11D3-8CFE-0150048383C9 /PASSIVE /NORESTART' -Wait

Write-Host "3of6 - SQL Native Client" 
$file = gci ($CRMmedia+'\Redist\SQLNativeClient')
Start-Process $file.FullName -ArgumentList '/PASSIVE /NORESTART' -Wait

Write-Host "4of6 - SQL Clr types" 
$file = gci ($CRMmedia+'\Redist\SQLSystemCLRTypes\*64*')
Start-Process $file.FullName -ArgumentList '/PASSIVE /NORESTART' -Wait

Write-Host "5of6 - SQL Server Management Objects" 
$file = gci ($CRMmedia+'\Redist\SQLSharedManagementObjects')
Start-Process $file.FullName -ArgumentList '/PASSIVE /NORESTART' -Wait

Write-Host "6of6 - Microsoft SQL Reporting Service Report Viewer Control" 
$file = gci ($CRMmedia+'\Redist\ReportViewer')
Start-Process $file.FullName -ArgumentList '/PASSIVE /NORESTART' -Wait
#endregion

#region AutoRun&AutoLogon
New-ItemProperty HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnce -Name "Test" -Value ("$pshome\powershell.exe -Command `"& `'$PSCommandPath`'`"") | Out-Null

Set-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon" -Name DefaultUserName -Value "$env:USERDOMAIN\$CRMSetupUser"
Set-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon" -Name DefaultPassword -Value "$CRMSetupUserPassword"
Set-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon" -Name AutoAdminLogon -Value "1"
Set-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon" -Name ForceAutoLogon -Value "1"
#endregion

Write-Host "`nRebooting..."
Restart-Computer
}
else{
Add-Clock | Out-Null
#region CRMinstall
Clear-Host
Write-Host ''
Write-Host '  β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•— β–ˆβ–ˆβ–ˆβ•—   β–ˆβ–ˆβ–ˆβ•—    β–ˆβ–ˆβ•—β–ˆβ–ˆβ–ˆβ•—   β–ˆβ–ˆβ•—β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•— β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•— β–ˆβ–ˆβ•—     β–ˆβ–ˆβ•—     ' -ForegroundColor Green
Write-Host ' β–ˆβ–ˆβ•”β•β•β•β•β•β–ˆβ–ˆβ•”β•β•β–ˆβ–ˆβ•—β–ˆβ–ˆβ–ˆβ–ˆβ•— β–ˆβ–ˆβ–ˆβ–ˆβ•‘    β–ˆβ–ˆβ•‘β–ˆβ–ˆβ–ˆβ–ˆβ•—  β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•”β•β•β•β•β•β•šβ•β•β–ˆβ–ˆβ•”β•β•β•β–ˆβ–ˆβ•”β•β•β–ˆβ–ˆβ•—β–ˆβ–ˆβ•‘     β–ˆβ–ˆβ•‘     ' -ForegroundColor Green
Write-Host ' β–ˆβ–ˆβ•‘     β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•”β•β–ˆβ–ˆβ•”β–ˆβ–ˆβ–ˆβ–ˆβ•”β–ˆβ–ˆβ•‘    β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•”β–ˆβ–ˆβ•— β–ˆβ–ˆβ•‘β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—   β–ˆβ–ˆβ•‘   β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•‘β–ˆβ–ˆβ•‘     β–ˆβ–ˆβ•‘     ' -ForegroundColor Green
Write-Host ' β–ˆβ–ˆβ•‘     β–ˆβ–ˆβ•”β•β•β–ˆβ–ˆβ•—β–ˆβ–ˆβ•‘β•šβ–ˆβ–ˆβ•”β•β–ˆβ–ˆβ•‘    β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•‘β•šβ–ˆβ–ˆβ•—β–ˆβ–ˆβ•‘β•šβ•β•β•β•β–ˆβ–ˆβ•‘   β–ˆβ–ˆβ•‘   β–ˆβ–ˆβ•”β•β•β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•‘     β–ˆβ–ˆβ•‘     ' -ForegroundColor Green
Write-Host ' β•šβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—β–ˆβ–ˆβ•‘  β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•‘ β•šβ•β• β–ˆβ–ˆβ•‘    β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•‘ β•šβ–ˆβ–ˆβ–ˆβ–ˆβ•‘β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•‘   β–ˆβ–ˆβ•‘   β–ˆβ–ˆβ•‘  β–ˆβ–ˆβ•‘β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—' -ForegroundColor Green
Write-Host '  β•šβ•β•β•β•β•β•β•šβ•β•  β•šβ•β•β•šβ•β•     β•šβ•β•    β•šβ•β•β•šβ•β•  β•šβ•β•β•β•β•šβ•β•β•β•β•β•β•   β•šβ•β•   β•šβ•β•  β•šβ•β•β•šβ•β•β•β•β•β•β•β•šβ•β•β•β•β•β•β•' -ForegroundColor Green
Write-Host ''

$xml = $null
$CRMxmlfile = "$PSScriptRoot\CRMSetupconfig.xml"

Write-Host 'Creating CRM Server XML answer file...'

$xml += '<CRMSetup>';$xml += "`r`n"
$xml += '<Server>';$xml += "`r`n"
$xml += '<Patch update="'+$patchUpdate+'">'+$patchPath+'</Patch>';$xml += "`r`n"
$xml += "<LicenseKey>$licenseKey</LicenseKey>";$xml += "`r`n"
$xml += "<SqlServer>$DBinstance</SqlServer>";$xml += "`r`n"
$xml += '<Database create="'+$databaseCreate+'"/>';$xml += "`r`n"
$xml += '<Reporting URL="'+$reportingUrl+'" />';$xml += "`r`n"
$xml += "<OrganizationCollation>$collation</OrganizationCollation>";$xml += "`r`n"
$xml += '<basecurrency isocurrencycode="'+$currencycode+'" currencyname="'+$currencyname+'" currencysymbol="'+$currencysymbol+'" currencyprecision="'+$currencyprecision+'"/>';$xml += "`r`n"
$xml += "<Organization>$displayName</Organization>";$xml += "`r`n"
$xml += "<OrganizationUniqueName>$uniqueName</OrganizationUniqueName>";$xml += "`r`n"
$xml += "<OU>$CRMgroupOU</OU>";$xml += "`r`n"
$xml += '<WebsiteUrl create="'+$createNewWebsite+'" port="'+$websitePort+'">'+$webSitePath+'</WebsiteUrl>';$xml += "`r`n"
$xml += "<InstallDir>C:\Program Files\Microsoft Dynamics CRM</InstallDir>";$xml += "`r`n"
$xml += '';$xml += "`r`n"
$xml += '<CrmServiceAccount type="DomainUser">';$xml += "`r`n"
$xml += "   <ServiceAccountLogin>$env:USERDOMAIN\$svcCRMApp</ServiceAccountLogin>" ;$xml += "`r`n"
$xml += "   <ServiceAccountPassword>$svcCRMAppPassword</ServiceAccountPassword>";$xml += "`r`n"
$xml += '</CrmServiceAccount>';$xml += "`r`n"
$xml += '';$xml += "`r`n"
$xml += '<SandboxServiceAccount type="DomainUser">';$xml += "`r`n"
$xml += "  <ServiceAccountLogin>$env:USERDOMAIN\$svcCRMSandbox</ServiceAccountLogin>";$xml += "`r`n"
$xml += "  <ServiceAccountPassword>$svcCRMSandboxPassword</ServiceAccountPassword>";$xml += "`r`n"
$xml += '</SandboxServiceAccount>';$xml += "`r`n"
$xml += '';$xml += "`r`n"
$xml += '<DeploymentServiceAccount type="DomainUser">';$xml += "`r`n"
$xml += "  <ServiceAccountLogin>$env:USERDOMAIN\$svcCRMDeploy</ServiceAccountLogin>";$xml += "`r`n"
$xml += "  <ServiceAccountPassword>$svcCRMDeployPassword</ServiceAccountPassword>";$xml += "`r`n"
$xml += '</DeploymentServiceAccount>';$xml += "`r`n"
$xml += '';$xml += "`r`n"
$xml += '<AsyncServiceAccount type="DomainUser">';$xml += "`r`n"
$xml += "  <ServiceAccountLogin>$env:USERDOMAIN\$svcCRMAsync</ServiceAccountLogin>";$xml += "`r`n"
$xml += "  <ServiceAccountPassword>$svcCRMAsyncPassword</ServiceAccountPassword>";$xml += "`r`n"
$xml += '</AsyncServiceAccount>';$xml += "`r`n"
$xml += '';$xml += "`r`n"
$xml += '<VSSWriterServiceAccount type="DomainUser">';$xml += "`r`n"
$xml += "  <ServiceAccountLogin>$env:USERDOMAIN\$svcCRMVSS</ServiceAccountLogin>";$xml += "`r`n"
$xml += "  <ServiceAccountPassword>$svcCRMVSSPassword</ServiceAccountPassword>";$xml += "`r`n"
$xml += '</VSSWriterServiceAccount>';$xml += "`r`n"
$xml += '';$xml += "`r`n"
$xml += '<MonitoringServiceAccount type="DomainUser">';$xml += "`r`n"
$xml += "  <ServiceAccountLogin>$env:USERDOMAIN\$svcCRMMonitor</ServiceAccountLogin>";$xml += "`r`n"
$xml += "  <ServiceAccountPassword>$svcCRMMonitorPassword</ServiceAccountPassword>";$xml += "`r`n"
$xml += '</MonitoringServiceAccount>';$xml += "`r`n"
$xml += '';$xml += "`r`n"
$xml += '<SQM optin="'+$SQMoptin+'"/>';$xml += "`r`n"
$xml += '<muoptin optin="'+$MUoptin+'"/>';$xml += "`r`n"
$xml += '';$xml += "`r`n"
$xml += '<!-- Settings for IFD installation. May be skipped for intranet-only deployment or to configure IFD later. -->';$xml += "`r`n"
$xml += ' <ifdsettings enabled="'+$ifdsettings+'">';$xml += "`r`n"
$xml += '      <!-- Define what address considered internal, required only if enabled=true -->';$xml += "`r`n"
$xml += '      <internalnetworkaddress>'+$internalnetworkaddress+'</internalnetworkaddress>';$xml += "`r`n"
$xml += '      <!-- Define URLs with IFD authentication, required only if enabled=true -->';$xml += "`r`n"
$xml += '      <rootdomainscheme>'+$rootdomainscheme+'</rootdomainscheme>';$xml += "`r`n"
$xml += '      <sdkrootdomain>'+$sdkrootdomain+'</sdkrootdomain>';$xml += "`r`n"
$xml += '      <webapplicationrootdomain>'+$webapplicationrootdomain+'</webapplicationrootdomain>';$xml += "`r`n"
$xml += '      <discoveryrootdomain>'+$discoveryrootdomain+'</discoveryrootdomain>';$xml += "`r`n"
$xml += ' </ifdsettings>';$xml += "`r`n"
$xml += '';$xml += "`r`n"
$xml += ' <Email>';$xml += "`r`n"
$xml += '  <IncomingExchangeServer name="'+$ExchangeServerName+'"/>';$xml += "`r`n"
$xml += ' </Email>';$xml += "`r`n"
$xml += '<LaunchReportingExtensionsSetup>False</LaunchReportingExtensionsSetup>';$xml += "`r`n"
$xml += ' </Server>';$xml += "`r`n"
$xml += '</CRMSetup>';$xml += "`r`n"

$xml | Out-File $CRMxmlfile -Force

Write-Host "Installing CRM server... pull up a chair, this part could take a while, like 30-40 mins :)`n"
Start-Process $CRMinstallFile -ArgumentList "/Q /config $CRMxmlfile" -Wait
Remove-Item placeholder.tmp
#endregion

#region SrsDataConnector
$xml = $null
$SRSxmlfile = "$PSScriptRoot\SrsDataConnectorSetupConfig.xml"

Write-Host 'Creating SrsDataConnector XML answer file...'

$xml += '<crmsetup>';$xml += "`r`n"
$xml += '<srsdataconnector>';$xml += "`r`n"
$xml += '<configdbserver>'+$SQLserver+'</configdbserver>';$xml += "`r`n"
$xml += '<autoupdateconfigdb>1</autoupdateconfigdb>';$xml += "`r`n"
$xml += '<reportserverurl>'+$reportingUrl+'</reportserverurl>';$xml += "`r`n"
$xml += '<autogroupmanagementoff>0</autogroupmanagementoff>';$xml += "`r`n"
$xml += '<instancename>'+$RSinstance+'</instancename>';$xml += "`r`n"
$xml += '<configsku>OnPremise</configsku>';$xml += "`r`n"
$xml += '<webstore enabled="false" configdb="false" />';$xml += "`r`n"
$xml += '<patch update="'+$patchUpdate+'" />';$xml += "`r`n"
$xml += '<muoptin optin="'+$MUoptin+'" />';$xml += "`r`n"
$xml += '<MonitoringServiceAccount type="DomainUser">';$xml += "`r`n"
$xml += '  <ServiceAccountLogin>'+$svcCRMMonitor+'</ServiceAccountLogin>';$xml += "`r`n"
$xml += '  <ServiceAccountPassword>'+$svcCRMMonitorPassword+'</ServiceAccountPassword>';$xml += "`r`n"
$xml += '</MonitoringServiceAccount>';$xml += "`r`n"
$xml += '</srsdataconnector>';$xml += "`r`n"
$xml += '</crmsetup>';$xml += "`r`n"
$xml | Out-File $SRSxmlfile

Write-Host " Installing SrsDataConnector...`n"
Start-Process $SRSInstallFile -ArgumentList "/Q /Config $SRSxmlfile" -Wait
#endregion

#region EmailRouter
# https://support.microsoft.com/en-us/kb/951401
Write-Host "Downloading Microsoft Exchange Server MAPI Client and Collaboration Data Objects 1.2.1"
$source = 'http://bumboks.dk/ExchangeMapiCdo.MSI'
$destination = "$PSScriptRoot\ExchangeMapiCdo.msi"
Invoke-WebRequest $source -OutFile $destination
Write-Host " Installing ExchangeMapiCdo.msi`n"
Start-Process $destination -ArgumentList '/passive' -Wait

$xml = $null
$EmailRouterSetupXMLfile = "$PSScriptRoot\EmailRouterSetupConfig.xml"

Write-Host 'Creating Email Router XML answer file...'

$xml += '<CRMSetup>';$xml += "`r`n"
$xml += '<EmailRouter>';$xml += "`r`n"
$xml += '<Features>';$xml += "`r`n"
$xml += '  <SinkService />';$xml += "`r`n"
$xml += '  <RulesWizard />';$xml += "`r`n"
$xml += '</Features>';$xml += "`r`n"
$xml += '<Patch update="'+$patchUpdate+'"></Patch>';$xml += "`r`n"
$xml += '<muoptin optin="'+$MUoptin+'" />';$xml += "`r`n"
$xml += '<InstallDir>C:\Program Files\Microsoft Dynamics CRM Email Router</InstallDir>';$xml += "`r`n"
$xml += '</EmailRouter>';$xml += "`r`n"
$xml += '</CRMSetup>';$xml += "`r`n"
$xml | Out-File $EmailRouterSetupXMLfile

Write-Host " Installing Email Router...`n"
Start-Process $EmailRouterInstallFile -ArgumentList "/Q /CONFIG $EmailRouterSetupXMLfile" -Wait

if($nonHTTPS -eq $true) {New-Item -Path HKLM:\Software\Microsoft\MSCRM\DisableSecureDecryptionKey -Value "1" | Out-Null}
else {New-Item -Path HKLM:\Software\Microsoft\MSCRM\DisableSecureDecryptionKey -Value "0" | Out-Null}

# Make the Windows Service ”Microsoft CRM Email Router” run under the svcCRMEmailRouter account.
$account="$env:USERDOMAIN\$svcCRMEmailRouter"
$service="displayname='Microsoft CRM Email Router'"

$svc=gwmi win32_service -filter $service
$svc.StopService() | Out-Null
$svc.change($null,$null,$null,$null,$null,$null,$account,$svcCRMEmailRouterpassword,$null,$null,$null) | Out-Null
$svc.StartService() | Out-Null
#endregion

#region updates
function DownloadUpdate ($type,$url) {
    $file = $url.Split('/')[-1]
    $dest = "$CRMupdateDir\$file"
    Write-Host "Downloading $file"
    $uri = New-Object "System.Uri" "$url"
    $request = [System.Net.HttpWebRequest]::Create($uri)
    $request.set_Timeout(5000)
    $response = $request.GetResponse()
    $totalLength = [System.Math]::Floor($response.get_ContentLength()/1024)
    $length = $response.get_ContentLength()
    $responseStream = $response.GetResponseStream()
    $destStream = New-Object -TypeName System.IO.FileStream -ArgumentList $dest, Create
    $buffer = New-Object byte[] 10KB
    $count = $responseStream.Read($buffer,0,$buffer.length)
    $downloadedBytes = $count
    while ($count -gt 0)
        {
        [System.Console]::CursorLeft=0
        [System.Console]::Write(" Downloaded {0}K of {1}K ({2}%)", [System.Math]::Floor($downloadedBytes/1024), $totalLength, [System.Math]::Round(($downloadedBytes / $length) * 100,0))
        $destStream.Write($buffer, 0, $count)
        $count=$responseStream.Read($buffer,0,$buffer.length)
        $downloadedBytes += $count
        }
    Write-Host ''
    $destStream.Flush()
    $destStream.Close()
    $destStream.Dispose()
    $responseStream.Dispose()
    
    New-Item "$CRMupdateDir\$type" -ItemType Directory | Out-Null
    Write-Host " Extracting... " -NoNewline
    Start-Process $CRMupdateDir\$file -ArgumentList "/extract:$CRMupdateDir\$type /passive" -Wait
    Write-Host "Installing... " -NoNewline
    if (Test-Path "$CRMupdateDir\$type\CrmUpdateWrapper.exe"){Start-Process "$CRMupdateDir\$type\CrmUpdateWrapper.exe" -ArgumentList "/quiet /norestart" -Verb RunAs -Wait}
    else{Start-Process msiexec.exe -ArgumentList "/i $CRMupdateDir\$type\MUISetup_1030_amd64.msi /quiet /norestart" -Verb RunAs -Wait}
    Write-Host "Done.`n"
    }

if (!(Test-Path $CRMupdateDir)) {New-Item $CRMupdateDir -ItemType Directory | Out-Null}
DownloadUpdate Server $Server
DownloadUpdate Router $Router
DownloadUpdate SRS $SRS
DownloadUpdate MUI-ENU $MUIENU
DownloadUpdate MUI-DAN $MUIDAN
DownloadUpdate MUI-DAN-UPD $MUIDANUPD
#endregion

#region MaintenancePlan 
# https://habaneroconsulting.com/insights/create-developer-sql-maintenance-plans-using-sql-agent-jobs-and-powershell
Invoke-Command -ComputerName $SQLserver -ScriptBlock {
    $JobName = ("{0}_SQL_Maintenance" -f $env:COMPUTERNAME)
    $DropExisting = $false
    $InstanceName = "MSSQLSERVER"
 
    [System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SqlServer.Smo") | Out-Null
 
    # Get the Default Instance
    $srv = New-Object -TypeName Microsoft.SqlServer.Management.SMO.Server
 
    # Get the SQL Agent Service for the Instance
    $Service = Get-Service -DisplayName ("SQL Server Agent ({0})" -f $InstanceName)
 
    # Set the SQL Agent Service startup type to Automatic and attempt to start it
    if($Service -eq $null){
        Throw ("Sql Sserver Agent ({0}) Not Found" -f $InstanceName)
    } else {
    Write-Host "Setting SQL Agent Startup type to Automatic"
        $Service | Set-Service -StartupType Automatic -ErrorAction Stop
        if($Service.Status -ieq "Stopped"){
            Write-Host "Agent Not Running"
            $Service.Start()
            Write-Host "Waiting for Service to Start." -NoNewline
            $Service.Refresh();
            While($Service.Status -ieq "StartPending"){
                $Service.Refresh()
                Write-Host "." -NoNewline
                Start-Sleep 2
            }
            if($Service.Status -ine "Running"){
                Write-Host ""
                Throw "An error occurred while starting `"SQL SERVER AGENT ($InstanceName)`". Try starting the service Manually. Then re-run this command."
            }
            Write-Host ".Started"
            $srv.JobServer.Refresh()
            Write-Host "Sleep for 10 Seconds While Agent wakes up..."
            Start-Sleep 10
            # Depending on the speed of your server, it may take longer than 10 seconds for the agent to wake up. You will know if the rest of the script fails.
        }
    }
    # Look for Existing Jobs with the same name.
    Try{
        $Jobs = $srv.JobServer.Jobs
        foreach($Job in $Jobs){
            if($Job.Name -ieq $JobName){
                Write-Host ("This Server already has a Job Called `'{0}`'." -f $JobName)
                if($DropExisting){
                    Write-Host ("Dropping Job: `'{0}`'." -f $JobName)
                    $Job.Drop()
                    break
                } else {
                    return
                }
            }
        }
    } Catch {
        Throw $_
    }
 
    # Define a Job object variable by supplying the Agent and the name arguments in the constructor and setting properties.
    $job = New-Object -TypeName Microsoft.SqlServer.Management.SMO.Agent.Job -argumentlist $srv.JobServer, $JobName
 
    # Create the job on the instance of SQL Server Agent.
    Write-Host ("Creating job `'{0}`'." -f $JobName)
    $job.Create()
    $job.ApplyToTargetServer("(local)") #Job will run against the Agent's Local Server
 
    # Define a JobStep object variable by supplying the parent job and name arguments in the constructor.
    $jobstep = New-Object -TypeName Microsoft.SqlServer.Management.SMO.Agent.JobStep -argumentlist $job, "Clean Up"

    $jobstep.Command = "SET QUOTED_IDENTIFIER ON 

    IF EXISTS (SELECT name from sys.indexes
                      WHERE name = N'CRM_AsyncOperation_CleanupCompleted')
          DROP Index AsyncOperationBase.CRM_AsyncOperation_CleanupCompleted
    GO
    CREATE NONCLUSTERED INDEX CRM_AsyncOperation_CleanupCompleted
    ON [dbo].[AsyncOperationBase] ([StatusCode],[StateCode],[OperationType])
    GO

    declare @DeleteRowCount int
    Select @DeleteRowCount = 2000
    declare @DeletedAsyncRowsTable table (AsyncOperationId uniqueidentifier not null primary key)
    declare @continue int, @rowCount int
    select @continue = 1
    while (@continue = 1)
    begin      
    begin tran      
    insert into @DeletedAsyncRowsTable(AsyncOperationId)
          Select top (@DeleteRowCount) AsyncOperationId from AsyncOperationBase
          where OperationType in (1, 9, 12, 25, 27, 10) AND StateCode = 3 AND StatusCode in (30, 32)     
           Select @rowCount = 0
          Select @rowCount = count(*) from @DeletedAsyncRowsTable
          select @continue = case when @rowCount <= 0 then 0 else 1 end      
            if (@continue = 1)        begin
                delete WorkflowLogBase from WorkflowLogBase W, @DeletedAsyncRowsTable d
                where W.AsyncOperationId = d.AsyncOperationId             
     delete BulkDeleteFailureBase From BulkDeleteFailureBase B, @DeletedAsyncRowsTable d
                where B.AsyncOperationId = d.AsyncOperationId
     delete WorkflowWaitSubscriptionBase from WorkflowWaitSubscriptionBase WS, @DeletedAsyncRowsTable d
     where WS.AsyncOperationId = d.AsyncOperationID 
                delete AsyncOperationBase From AsyncOperationBase A, @DeletedAsyncRowsTable d
                where A.AsyncOperationId = d.AsyncOperationId             
                delete @DeletedAsyncRowsTable      
    end       
    commit
    end
    --Drop the Index on AsyncOperationBase
    DROP INDEX AsyncOperationBase.CRM_AsyncOperation_CleanupCompleted"
 
    $jobstep.OnSuccessAction = [Microsoft.SqlServer.Management.SMO.Agent.StepCompletionAction]::GoToNextStep
    $jobstep.OnFailAction = [Microsoft.SqlServer.Management.SMO.Agent.StepCompletionAction]::QuitWithFailure
 
    # Create the job step on the instance of SQL Agent.
    $jobstep.Create()

    $jobstep = New-Object -TypeName Microsoft.SqlServer.Management.SMO.Agent.JobStep -argumentlist $job, "Update Statistics"

    $jobstep.Command = "UPDATE STATISTICS [dbo].[AsyncOperationBase] WITH FULLSCAN
    UPDATE STATISTICS [dbo].[DuplicateRecordBase] WITH FULLSCAN
    UPDATE STATISTICS [dbo].[BulkDeleteOperationBase] WITH FULLSCAN
    UPDATE STATISTICS [dbo].[WorkflowLogBase] WITH FULLSCAN
    UPDATE STATISTICS [dbo].[WorkflowWaitSubscriptionBase] WITH FULLSCAN"

    $jobstep.OnSuccessAction = [Microsoft.SqlServer.Management.SMO.Agent.StepCompletionAction]::QuitWithSuccess
    $jobstep.OnFailAction = [Microsoft.SqlServer.Management.SMO.Agent.StepCompletionAction]::QuitWithFailure
 
    # Create the job step on the instance of SQL Agent.
    $jobstep.Create()
 
    # Define a JobSchedule object variable by supplying the parent job and name arguments in the constructor.
    $jobsch =  New-Object -TypeName Microsoft.SqlServer.Management.SMO.Agent.JobSchedule -argumentlist $job, "Monthly_Job_Schedule"
 
    # Set properties to define the schedule frequency, and duration.
    $jobsch.FrequencyTypes = [Microsoft.SqlServer.Management.SMO.Agent.FrequencyTypes]::MonthlyRelative
    $jobsch.FrequencyRelativeIntervals = [Microsoft.SqlServer.Management.SMO.Agent.FrequencyRelativeIntervals]::Last
    $jobsch.FrequencyInterval = [Microsoft.SqlServer.Management.SMO.Agent.MonthlyRelativeWeekDays]::EveryDay
    $jobsch.FrequencyRecurrenceFactor = 1
    $jobsch.ActiveStartDate = Get-Date
    $starttime = New-Object -TypeName TimeSpan -ArgumentList 23, 30, 0
    $jobsch.ActiveStartTimeOfDay = $starttime
 
    # Create the job schedule on the instance of SQL Agent.
    $jobsch.Create();
    Write-Host ("Job `'{0}`' successfully created..." -f $JobName)

    }
#endregion

#region removeAutoLogon
Remove-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon" -Name DefaultUserName
Remove-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon" -Name DefaultPassword
Remove-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon" -Name AutoAdminLogon
Remove-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon" -Name ForceAutoLogon
#endregion

#region TheEnd
#Clear-Host
Write-Host ''
Write-Host '      .                             ..              .x+=:.         ..                                              ..  ' -ForegroundColor Green
Write-Host '  .x88888x.                   . uW8"       .8u     z`    ^%      dF                                               888B.' -ForegroundColor Green
Write-Host ' :8**888888X.  :>        u.   `t888       m888R-      .   <k    `88bu.             u.      u.    u.              48888E' -ForegroundColor Green
Write-Host ' f    `888888x./   ...ue888b   8888   .    98P      .@8Ned8"    `*88888bu    ...ue888b   x@88k u@88c.      .u    `8888Β΄' -ForegroundColor Green
Write-Host '`       `*88888~   888R Y888r  9888.z88N   ^8     .@^%8888"       ^"*8888N   888R Y888r ^"8888""8888"   ud8888.   Y88F ' -ForegroundColor Green
Write-Host ' \.    .  `?)X.    888R I888>  9888  888E  J"    x88:  `)8b.     beWE "888L  888R I888>   8888  888R  :888`8888.  `88  ' -ForegroundColor Green
Write-Host '  `~=-^   X88> ~   888R I888>  9888  888E +"     8888N=*8888     888E  888E  888R I888>   8888  888R  d888 `88%"   8F  ' -ForegroundColor Green
Write-Host '         X8888  ~  888R I888>  9888  888E         %8"    R88     888E  888E  888R I888>   8888  888R  8888.+"      4   ' -ForegroundColor Green
Write-Host '         488888   u8888cJ888   9888  888E          @8Wou 9%      888E  888F u8888cJ888    8888  888R  8888L        .   ' -ForegroundColor Green
Write-Host ' .xx.     88888X   "*888*P"   .8888  888"        .888888P`      .888N..888   "*888*P"    "*88*" 8888" `8888c. .+  u8N. ' -ForegroundColor Green
Write-Host '`*8888.   `88888>    `Y"       `%888*%"          `   ^"F         `"888*""      `Y"         ""   `Y"    "88888%   "*88% ' -ForegroundColor Green
Write-Host '  88888    `8888>                 "`                                ""                                   "YP`      ""  ' -ForegroundColor Green
Write-Host '  `8888>    `888                                                                                                       ' -ForegroundColor Green
Write-Host '   "8888     8%                                                                                                        ' -ForegroundColor Green
Write-Host '    `"888x:-"                                                                                                          ' -ForegroundColor Green
Write-Host '                                      Now go configure the CRM Email Router πŸ™‚                                         ' -ForegroundColor Cyan
Read-Host
#endregion
}
Advertisements