<# .Synopsis Installs and configures an initial Symbio Web installation. .DESCRIPTION This script installs and configures an initial installation of Symbio Web. All parameters and settings used during installation are provided by an external file called 'parameters.json'. The following parameters are currently supported: SourcePath: The path to the installation files. Can be an URL to the installation package, a path to the local installation package or a path to a local folder containing the already extracted installation package. TargetPath: The installation directory BaseTargetPath: The path pointing to the installation directory without the instance name. BaseBackupPath: The path pointing to the backup directory without the instance name. InstanceName: The name of the instance. CertificatesPath: The path to the SAML certificates. LinceseKey: The license key that will be used. AppSettings: All settings in the AppSettings section will be updated or added to the web.config file in the installation directory. SYMBIO_STORAGE_LOCATION: SYMBIO_STORAGE_SETTINGS: Contains the connection string IISSettings: This section contains settings to used to configure the IIS website to be used. Enabled: If set to 'true' a new IIS website will be created SiteName: The name of the website ApplicationName: The name of the application added to the website. This setting is optional. ApplicationPoolName: The name of the application pool. If the application pool already exists it will be recreated. ApplicationPoolUser: The user which is running the application pool. ApplicationPoolPassword: The password for the user that is running the application pool. Bindings: Contains the bindings to be used for the website. If 'https' is used as the protocol, please provide the hash value of the certificate. .PARAMETER SourcePath [Mandatory] The path pointing to the installation source files. Can be one of the following: * The URL to the installation package, e.g. http://www.symbioworld.com/download/symbio/Symbio1801.zip * The local path of the installation package * The local path of the folder containing the extracted installation package .PARAMETER BaseTargetPath [Mandatory] The path pointing to the installation directory without the instance name. .PARAMETER BaseBackupPath [Opt] The path pointing to the backup directory without the instance name. .PARAMETER InstanceName [Mandatory] The name of the instance. .PARAMETER CertificatesPath [Opt] The path to the SAML certificates. .PARAMETER ParametersFile The path pointing to a file containt the parameters to be used during installation. If not specified, a local file called 'parameters.json' will be used. .PARAMETER HostUser [Opt] With this specification, you overwrite the HostName for the binding specified in the parameters.json. .PARAMETER WithoutIIS [Opt] By specifying these parameters you switch off the check/installation of the IIS. .EXAMPLE ./Install-SymbioWeb.ps1 http://www.symbioworld.com/download/symbio/Symbio1801.zip C:\SymbioWeb .EXAMPLE ./Install-SymbioWeb.ps1 C:\Downloads\SymbioWeb.zip C:\SymbioWeb -ParametersFile symbio-prod.json #> [CmdletBinding()] Param( [Parameter(Position = 0)] [string]$SourcePath, [Parameter(Position = 1)] [string]$BaseTargetPath, [Parameter(Position = 2)] [string]$BaseBackupPath, [Parameter(Position = 3)] [string]$InstanceName, [Parameter(Position = 4)] [string]$CertificatesPath, [Parameter(Position = 5)] [string]$ParametersFile = $PSScriptRoot+"\parameters.json", [string]$HostUser, [switch]$WithoutIIS ) $Global:AppPools = New-Object System.Collections.ArrayList If (-NOT ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")) { Write-Host "You must be Administrator to Run this Script!" -ForegroundColor Red Break } If(!(Get-Module -Name Webadministration -ListAvailable)){ Write-Host Installing Module, please wait -ForegroundColor Yellow add-windowsfeature Web-Server,Web-Scripting-Tools | Out-Null } Import-Module -Name Webadministration Start-Sleep 2 ############################################################################### ### FUNCTIONS ############################################################################### function Write-LogHeader { [CmdletBinding()] Param( [Parameter(Mandatory = $true)] [string]$text ) Write-Host Write-Host ($text.ToUpper().PadRight(60)) -ForegroundColor white -BackgroundColor Blue Write-Host } function Write-Log { [CmdletBinding()] Param( [Parameter(Mandatory = $true)] [string]$text ) Write-Host (" {0,-50}" -f ($text + " ... ")) -NoNewline } function Write-LogDone { Write-Host "Done" -ForegroundColor Green } function Write-LogPassed { [CmdletBinding()] Param( [Parameter(Mandatory = $true)] [string]$text ) Write-Host $text -ForegroundColor Green } function Write-LogWarning { [CmdletBinding()] Param( [Parameter(Mandatory = $true)] [string]$text ) Write-Host $text -ForegroundColor Yellow } function Write-LogFailed { [CmdletBinding()] Param( [Parameter(Mandatory = $true)] [string]$text ) Write-Host $text -ForegroundColor Red } function Show-Banner { Write-Host Write-Host " ____ _ _ " Write-Host " / ___| _ _ _ __ ___ | |__ (_) ___ " Write-Host " \___ \ | | | || '_ ` _ \ | '_ \ | | / _ \" Write-Host " ___) || |_| || | | | | || |_) || || (_) |" Write-Host " |____/ \__, ||_| |_| |_||_.__/ |_| \___/ " Write-Host " |___/ " Write-Host " Ploetz + Zeller GmbH " Write-Host " http://www.symbioworld.com " Write-Host Write-Host } function New-TemporaryFileWithExtension { [CmdletBinding()] Param( [Parameter(Mandatory = $true)] [string]$extension ) $directory = [System.IO.Path]::GetTempPath() $name = [System.IO.Path]::ChangeExtension([System.Guid]::NewGuid(), $extension) New-Item -ItemType File -Path (Join-Path $directory $name) } function Get-SSLBindingPath { [CmdletBinding()] Param( [Parameter(Mandatory = $true)] [string]$IPAddress, [Parameter(Mandatory = $true)] [int]$Port, [Parameter()] [string]$HostName ) if ($IPAddress -eq "*") { $result = "0.0.0.0!{0}" -f $Port } else { $result = "{0}!{1}" -f $IPAddress, $Port } if ($HostName) { $result = "{0}!{1}" -f $result, $HostName } $result } function Set-LicenseKey { [CmdletBinding()] Param( [Parameter(Mandatory = $true)] [string]$key ) $path = "HKLM:\Software\Ploetz + Zeller\SymbioSuite\LicenseContainer" $md5 = New-Object -TypeName System.Security.Cryptography.MD5CryptoServiceProvider $utf8 = New-Object -TypeName System.Text.UTF8Encoding $name = [System.BitConverter]::ToString($md5.ComputeHash($utf8.GetBytes($key))).Replace("-", ""); if (!(Test-Path $path)) { New-Item -Path $path -Force | Out-Null New-ItemProperty -Path $path -Name $name -Value $key -PropertyType String -Force | Out-Null } else { New-ItemProperty -Path $path -Name $name -Value $key -PropertyType String -Force | Out-Null } } function Update-WebConfig { [CmdletBinding()] Param( [Parameter(Mandatory = $true)] [ValidateScript({Test-Path $_ -PathType Leaf})] [string]$WebConfigFile, [Parameter(Mandatory = $true)] [ValidateScript({ConvertFrom-Json $_})] [string]$Json ) $xml = [xml](Get-Content $WebConfigFile) foreach ($setting in ((ConvertFrom-Json $json).PSObject.Members | Where-Object { $_.MemberType -eq 'NoteProperty'})) { $xmlNode = $xml.SelectSingleNode("//appSettings/add[@key='" + $setting.Name + "']") if ($xmlNode) { # Update an existing xml node $xmlNode.Value = $setting.Value } else { # Create a new xml xml node $xmlNode = $xml.CreateElement("add") $xmlAttribute = $xml.CreateAttribute("key") $xmlAttribute.Value = $setting.Name $xmlNode.Attributes.Append($xmlAttribute) | Out-Null $xmlAttribute = $xml.CreateAttribute("value") $xmlAttribute.Value = $setting.Value $xmlNode.Attributes.Append($xmlAttribute) | Out-Null $xml.SelectSingleNode("//appSettings").AppendChild($xmlNode) | Out-Null } } $xml.Save($WebConfigFile) } function Update-AppSettings { [CmdletBinding()] Param( [Parameter(Mandatory = $true)] [ValidateScript({Test-Path $_ -PathType Leaf})] [string]$AppSettingsFile, [Parameter(Mandatory = $true)] [System.Object]$Json ) ## Read AppSettingsFile to Json. $rootJson = ConvertFrom-Json(Get-Content $AppSettingsFile -Raw) if($rootJson) { if($rootJson.ConnectionStrings) { $rootJson.ConnectionStrings = $Json.ConnectionStrings } if($rootJson.ConfigStoreTableName) { $rootJson.ConfigStoreTableName = $Json.ConfigStoreTableName } $jsonString = ConvertTo-Json $rootJson if($jsonString.Contains("PoolTokenDirectory") -and $jsonString.Contains("AccessToken") -and $jsonString.Contains("SsrsDomain")) { $rootJson.PoolTokenDirectory = $Json.PoolTokenDirectory $rootJson.AccessToken = $Json.AccessToken $rootJson.SsrsDomain = $Json.SsrsDomain } (ConvertTo-Json $rootJson -Depth 100) | Out-File $AppSettingsFile } } function Update-IIS { [CmdletBinding()] Param( [Parameter(Mandatory = $true)] [ValidateScript({Test-Path $_ -PathType Container})] [string]$Path, [Parameter(Mandatory = $true)] [ValidateScript({ConvertFrom-Json $_})] [string]$Json, [Parameter(Mandatory = $true)] [string]$InstanceName ) $settings = (ConvertFrom-Json $Json) if ($settings.Enabled -eq $false) { Return } if($settings.SiteName -eq "") { $settings.SiteName = $InstanceName } # (Re-)Create the application pool if (Test-Path (Join-Path "IIS:\AppPools" $settings.ApplicationPoolName)) { Remove-WebAppPool -Name $settings.ApplicationPoolName } $appPool = New-WebAppPool $settings.ApplicationPoolName $appPool.ManagedRuntimeVersion = $settings.ManagedRuntimeVersion $appPool.Processmodel.loadUserProfile = $settings.LoadUserProfile $appPool.Processmodel.IdentityType = $settings.IdentityType $appPool.Processmodel.UserName = $settings.ApplicationPoolUser $appPool.Processmodel.Password = $settings.ApplicationPoolPassword $appPool | set-item Set-ItemProperty ("IIS:\AppPools\$($settings.ApplicationPoolName)") -Name processModel.idleTimeout -Value ([timespan]::FromMinutes(200)) # Create the website $website = New-Website -Name $settings.SiteName -PhysicalPath $Path -ApplicationPool $settings.ApplicationPoolName -Force # Create the optional application if ($settings.ApplicationName) { Set-ItemProperty (Join-Path "IIS:\Sites" $settings.SiteName) -Name "PhysicalPath" -Value "" New-WebApplication -Site $settings.SiteName -Name $settings.ApplicationName -PhysicalPath $Path -ApplicationPool $settings.ApplicationPoolName | Out-Null } # Create the bindings Get-WebBinding -Name $settings.SiteName | Remove-WebBinding $settings.Bindings | ForEach-Object { $SslFlag = 0 if($_.SslFlag -and $_.SslFlag -ne "" -and $_.SslFlag -ne " ") { $SslFlag = $_.SslFlag } New-WebBinding -Name $settings.SiteName -Protocol $_.Protocol -IPAddress $_.IPAddress -Port $_.Port -HostHeader $_.HostName -SslFlags $SslFlag $CertificateHash = $_.CertificateHash if($_.CertificateCN) { $CN = $_.CertificateCN $CertificateCN = (Get-ChildItem -Path cert:LocalMachine\Root, cert:LocalMachine\My -Recurse | where Subject -like "*$CN*" | select -ExpandProperty Thumbprint) if($CertificateCN.Count -gt 1) { $CertificateCN = $CertificateCN[0] } $CertificateHash = $CertificateCN } if((Test-Path (Join-Path cert:\LocalMachine\Root $CertificateHash)) -or ((Test-Path (Join-Path cert:\LocalMachine\My $CertificateHash)))) { $ErrorActionPreference = 'silentlycontinue' if ($CertificateHash -and (($certificate = Get-Item (Join-Path cert:\LocalMachine\Root $CertificateHash)) -or ($certificate = Get-Item (Join-Path cert:\LocalMachine\My $CertificateHash)))) { $ErrorActionPreference = 'continue' Push-Location IIS:\SslBindings if (Test-Path (Get-SSLBindingPath $_.IPAddress $_.Port)) { Remove-Item (Get-SSLBindingPath $_.IPAddress $_.Port) } $certificate | New-Item (Get-SSLBindingPath $_.IPAddress $_.Port $_.HostName) | Out-Null Pop-Location } } else { Write-Host ("`r`n CertificateHash {0} not found. Port binding is not carried out!" -f $CertificateHash) -ForegroundColor Red } } Start-Website -Name $settings.SiteName } function Get-Settings { [CmdletBinding()] Param( [Parameter(Mandatory, ValueFromPipeline)] [ValidateScript({ConvertFrom-Json $_})] [string]$Json ) $parameters = ConvertFrom-Json $Json $parameters } function Get-PrerequisitesMetaData { Param( [Parameter()] [string]$filename ) if (Test-Path $parameters.SourcePath -PathType Container) { $metaData = Get-Content (Join-Path $parameters.SourcePath $filename) | ConvertFrom-Json } $metaData } function Test-Prerequisite { Param( [Parameter(ValueFromPipeline, Mandatory)] [object]$installedVersion, [Parameter(Mandatory)] [object[]]$requiredVersion, [Parameter(Mandatory)] [string]$headerText, [Parameter()] [bool]$failIfNotSatisfied = $true, [Parameter()] [string]$NotFoundMessage ) Write-LogHeader $headerText Write-Host ' Required:' $requiredVersion | ForEach-Object { Write-Host " $_" } Write-Host Write-Host ' Installed:' if ($installedVersion.Major -eq 0 -and $installedVersion.Minor -eq 0) { $notFound = $true } elseif ($requiredVersion.Count -gt 1) { $result = ($found | Where-Object { $found -like '*$_*' }) -ne $null } elseif ($requiredVersion[0] -as [Version]) { $result = (New-Object Version($installedVersion)) -ge (New-Object Version($requiredVersion[0])) } else { $result = (New-Object Version($installedVersion, 0)) -ge (New-Object Version($requiredVersion[0], 0)) } if ($notFound) { Write-Host ' not found'$NotFoundMessage -ForegroundColor Red } elseif ($result) { Write-Host " $installedVersion" -ForegroundColor Green } elseif ($failIfNotSatisfied) { Write-Host " $installedVersion" -ForegroundColor Red } else { Write-Host " $installedVersion" -ForegroundColor Yellow } } function Get-NetFrameworkVersion { Param( [Parameter()] [bool]$runDownlaod = $true ) $netRegKey = Get-Childitem 'HKLM:\SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full' if($netRegKey.GetType().IsArray){ $netRegKeyLast =$netRegKey.GetValue($netRegKey.Length-1) $release = $netRegKeyLast.GetValue("Release") } else { $release = $netRegKey.GetValue("Release") } Switch ($release) { 378389 { $netFrameworkVersion = New-Object -TypeName System.Version -ArgumentList (4, 5, 0, 0) } 378675 { $netFrameworkVersion = New-Object -TypeName System.Version -ArgumentList (4, 5, 1, 0) } 378758 { $netFrameworkVersion = New-Object -TypeName System.Version -ArgumentList (4, 5, 1, 0) } 379893 { $netFrameworkVersion = New-Object -TypeName System.Version -ArgumentList (4, 5, 2, 0) } 393295 { $netFrameworkVersion = New-Object -TypeName System.Version -ArgumentList (4, 6, 0, 0) } 393297 { $netFrameworkVersion = New-Object -TypeName System.Version -ArgumentList (4, 6, 0, 0) } 394254 { $netFrameworkVersion = New-Object -TypeName System.Version -ArgumentList (4, 6, 1, 0) } 394271 { $netFrameworkVersion = New-Object -TypeName System.Version -ArgumentList (4, 6, 1, 0) } 394802 { $netFrameworkVersion = New-Object -TypeName System.Version -ArgumentList (4, 6, 2, 0) } 394806 { $netFrameworkVersion = New-Object -TypeName System.Version -ArgumentList (4, 6, 2, 0) } 460798 { $netFrameworkVersion = New-Object -TypeName System.Version -ArgumentList (4, 7, 0, 0) } 460805 { $netFrameworkVersion = New-Object -TypeName System.Version -ArgumentList (4, 7, 0, 0) } 461308 { $netFrameworkVersion = New-Object -TypeName System.Version -ArgumentList (4, 7, 1, 0) } 461310 { $netFrameworkVersion = New-Object -TypeName System.Version -ArgumentList (4, 7, 1, 0) } 461808 { $netFrameworkVersion = New-Object -TypeName System.Version -ArgumentList (4, 7, 2, 0) } 461814 { $netFrameworkVersion = New-Object -TypeName System.Version -ArgumentList (4, 7, 2, 0) } Default { $netFrameworkVersion = New-Object -TypeName System.Version -ArgumentList (0, 0, 0, 0) } } [object[]]$baseVersion = $prerequisites.NetFrameworkVersion if((New-Object Version($netFrameworkVersion)) -lt (New-Object Version($baseVersion[0])) -and $runDownlaod) { $netFrameworkVersion = GetAndInstallNetFramework } $netFrameworkVersion } function GetAndInstallNetFramework () { if ([System.Uri]::IsWellFormedUriString($parameters.NetFrameworkPath, [System.UriKind]::Absolute)) { Write-LogHeader "Downloading NetFramework package" $netExe = New-TemporaryFileWithExtension ".exe" Write-Log "Downloading NetFramework" (New-Object System.Net.WebClient).DownloadFile($parameters.NetFrameworkPath,$netExe) $parameters.NetFrameworkPath= $netExe Write-LogDone } Write-Log "Install NetFramework" Start-Process -FilePath $parameters.NetFrameworkPath -ArgumentList @("/q /norestart") -Wait Write-LogDone Get-NetFrameworkVersion $false } function Get-NetCoreHostVersion{ $coreVersion = New-Object Version(0, 0, 0) if (Test-Path HKLM:\SOFTWARE\dotnet\Setup\InstalledVersions\x64\sharedhost) { $coreRegKey = Get-ItemProperty HKLM:\SOFTWARE\dotnet\Setup\InstalledVersions\x64\sharedhost $coreVersion = $coreRegKey.Version } $coreVersion } function Get-OSVersion { Get-WmiObject -Class Win32_OperatingSystem | ForEach-Object -MemberName Caption } function Get-IISVersion { if(!$WithoutIIS) { Enable-IIS | Out-Null } # | Select-Object VersionString $iisVersion = Get-ItemProperty HKLM:\SOFTWARE\Microsoft\InetStp if($iisVersion) { [Version]"$($iisVersion.MajorVersion).$($iisVersion.MinorVersion)" } else { [Version]"0.0" } } function Get-SQLServerVersion { if (!(Test-Path "HKLM:\SOFTWARE\Microsoft\Microsoft SQL Server") -or !(Test-Path "HKLM:\SOFTWARE\Microsoft\Microsoft SQL Server\Instance Names\SQL")) { New-Object Version(0, 0) } else { $instances = (Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\Microsoft SQL Server").InstalledInstances $instances | ForEach-Object { $instanceName = (Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\Microsoft SQL Server\Instance Names\SQL").$_ [Version](Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\Microsoft SQL Server\$instanceName\Setup").Version } | Sort-Object -Property Major, Minor -Descending | Select-Object -First 1 } } function Get-ProcessorCount { $processorCount = Get-WmiObject -Class Win32_Processor | Select-Object NumberOfLogicalProcessors -First 1 $processorCount.NumberOfLogicalProcessors } function Get-MemoryCount { $memoryCount = Get-WmiObject -Class Win32_OperatingSystem | Select-Object TotalVisibleMemorySize -First 1 [math]::Ceiling($memoryCount.TotalVisibleMemorySize / 1024 / 1024) } function ConvertFrom-SecureToPlain { param([Parameter(Mandatory=$true)] [System.Security.SecureString] $password) # Create a "password pointer" $passwordPointer = [Runtime.InteropServices.Marshal]::SecureStringToBSTR($password) # Get the plain text version of the password $plainTextPassword = [Runtime.InteropServices.Marshal]::PtrToStringAuto($passwordPointer) # Free the pointer [Runtime.InteropServices.Marshal]::ZeroFreeBSTR($passwordPointer) # Return the plain text password $plainTextPassword } function Enable-IIS { Enable-WindowsOptionalFeature -Online -FeatureName IIS-WebServerRole -All Enable-WindowsOptionalFeature -Online -FeatureName IIS-WebServer -All Enable-WindowsOptionalFeature -Online -FeatureName IIS-CommonHttpFeatures -All Enable-WindowsOptionalFeature -Online -FeatureName IIS-HttpErrors -All Enable-WindowsOptionalFeature -Online -FeatureName IIS-ApplicationDevelopment -All Enable-WindowsOptionalFeature -Online -FeatureName IIS-NetFxExtensibility45 -All Enable-WindowsOptionalFeature -Online -FeatureName IIS-HealthAndDiagnostics -All Enable-WindowsOptionalFeature -Online -FeatureName IIS-HttpLogging -All Enable-WindowsOptionalFeature -Online -FeatureName IIS-RequestMonitor -All Enable-WindowsOptionalFeature -Online -FeatureName IIS-Security -All Enable-WindowsOptionalFeature -Online -FeatureName IIS-RequestFiltering -All Enable-WindowsOptionalFeature -Online -FeatureName IIS-Performance -All Enable-WindowsOptionalFeature -Online -FeatureName IIS-WebServerManagementTools -All Enable-WindowsOptionalFeature -Online -FeatureName IIS-ManagementConsole -All Enable-WindowsOptionalFeature -Online -FeatureName IIS-BasicAuthentication -All Enable-WindowsOptionalFeature -Online -FeatureName IIS-WindowsAuthentication -All Enable-WindowsOptionalFeature -Online -FeatureName IIS-StaticContent -All Enable-WindowsOptionalFeature -Online -FeatureName IIS-DefaultDocument -All Enable-WindowsOptionalFeature -Online -FeatureName IIS-DirectoryBrowsing -All Enable-WindowsOptionalFeature -Online -FeatureName IIS-ASPNET45 -All Enable-WindowsOptionalFeature -Online -FeatureName IIS-ISAPIExtensions -All Enable-WindowsOptionalFeature -Online -FeatureName IIS-ISAPIFilter -All Enable-WindowsOptionalFeature -Online -FeatureName IIS-HttpCompressionStatic -All } function Test-PendingReboot { if ((Get-ChildItem "HKLM:\Software\Microsoft\Windows\CurrentVersion\Component Based Servicing\RebootPending" -ea Ignore) -or (Get-Item "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update\RebootRequired" -ea Ignore) -or (Get-ItemProperty "HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager" -Name PendingFileRenameOperations -ea Ignore)) { return $true } try { $status = ([wmiclass]"\\.\root\ccm\clientsdk:CCM_ClientUtilities").DetermineIfRebootPending() if(($status -ne $null) -and $status.RebootPending){ return $true } }catch{} return $false } function RegisterService { Param( [Parameter()] [string]$serviceName, [Parameter()] [string]$targetPath, [Parameter()] [string]$applicationName ) if(Get-Service -Name $serviceName -ErrorAction SilentlyContinue) { StopService $binding (Get-WmiObject Win32_Service -filter "name='$serviceName'").Delete() | Out-Null Write-Log "Removing an existing service" Start-Sleep 60 Write-LogDone } Write-Log "Service will be registered" New-Service -Name $serviceName -BinaryPathName "$targetPath\$applicationName" -StartupType Automatic | Out-Null Start-Service -Name $serviceName | Out-Null Write-LogDone Get-Service -Name $serviceName } function StopService { Param( [Parameter()] [string]$serviceName ) $service = Get-Service -Name $serviceName -ErrorAction SilentlyContinue if(($service) -and ($service.Status -eq "Running" )) { Stop-Service -Name $serviceName | Out-Null Start-Sleep 10 } } function PortBinding { Param( [Parameter()] [System.Object]$binding ) Write-Host "Add URL reservation entry" $reservation = Invoke-Expression -Command "netsh http show urlacl" $binding | ForEach-Object { $url = $_.Protocol+"://"+$_.HostName+":"+$_.Port+"/" $user = $_.HostUser if($HostUser) { $user = $HostUser } $reservation | ForEach-Object{ if($_.Contains("$url")) { Invoke-Expression -Command "netsh http delete urlacl url=$url" } } Invoke-Expression -Command "netsh http add urlacl url=$url user=$user" $sslBindings = Invoke-Expression -Command "netsh http show sslcert" $cert = ($_.CertificateHash).Trim().ToUpper() $url = $_.HostName+":"+$_.Port $guid = [guid]::NewGuid().Guid $sslBindings | ForEach-Object { if($_.Contains($url)) { Invoke-Expression -Command "netsh http delete sslcert hostnameport=$url" | Out-Null } } Invoke-Expression -Command "netsh http add sslcert hostnameport=$url certhash=$cert appid='{$guid}' certstorename=MY" } Write-LogDone } function CheckAppConfig{ Param( [Parameter()] [string]$appConfig, [Parameter()] [string]$targetPath, [Parameter()] [string]$applicationName ) $xml = [xml](Get-Content "$targetPath\$applicationName.config") $xmlNode = $xml.SelectSingleNode("//applicationSettings") if ($xmlNode) { # Update an existing xml node $xmlNode.InnerXml = $appConfig } elseif($xml.SelectSingleNode("//configuration")) { # Create a new xml xml node $newNode = $xml.CreateElement("applicationSettings") $newNode.InnerXml = $appConfig $xml.configuration.AppendChild($newNode) } else { $Exception = New-Object System.Exception -ErrorAction Stop -ArgumentList("Your configuration file is corrupted. The node configuration was not found.") throw $Exception } $xml.Save("$targetPath\$applicationName.config") } function Stop-WebAppPoolsBySites { Param( [Parameter()] [string]$targetPath ) (Get-ChildItem -Path IIS:\Sites).Name | ForEach-Object{ $properties = Get-ItemProperty IIS:\Sites\$_ if($properties.PhysicalPath -eq $targetPath) { $Global:AppPools.Add($properties.applicationPool) | Out-Null } } $Global:AppPools | ForEach-Object{ if ((Get-WebAppPoolState $_).Value -ne 'Stopped') { Stop-WebAppPool -Name $_ } } Start-Sleep 10 } function Start-WebAppPools{ $Global:AppPools | ForEach-Object{ if ((Get-WebAppPoolState $_).Value -ne 'Started') { Start-WebAppPool -Name $_ } } } function Create-Backup { Param ( [Parameter(Mandatory)] [string]$TargetPath, [Parameter(Mandatory)] [string]$BackupPath ) # Backup the installation folder if(Test-Path($TargetPath)) { Write-LogHeader "Execute Backup" Write-Log "Perform backup for $TargetPath" if($backupsToKeep -eq $null) { $backupsToKeep = 1 } $date = (Get-Date).ToString("yyMMdd-hhmmss") $targetDirectoryName = [System.IO.Path]::GetFileNameWithoutExtension($TargetPath) $backupFolderPath = "$BackupPath\$date-$targetDirectoryName" robocopy "$TargetPath" "$backupFolderPath" /E /MT /R:1 /W:1 | Out-Null Write-LogDone Write-Log "Compress and Clean up BackUp" Compress-Archive -Path "$backupFolderPath\*" -DestinationPath "$backupFolderPath" Get-ChildItem $BackupPath -Exclude *.zip | Remove-Item -Force -Recurse Write-LogDone } } function Copy-Certificates { param ( [Parameter(Mandatory)] [string]$TargetPath, [Parameter(Mandatory)] [string]$Certificates ) Write-LogHeader "Preparation for SAML" Write-Log "Copy Certificates" robocopy "$Certificates" "$TargetPath\App_Data" /E /MT /R:1 /W:1 |Out-Null Write-LogDone } function Check-Parameters { param ( [Parameter(Mandatory)] [object]$params ) if($params.SourcePath -eq $null -or $params.SourcePath -eq "" -or $params.BaseTargetPath -eq $null -or $params.BaseTargetPath -eq "" -or $params.InstanceName -eq $null -or $params.InstanceName -eq "") { $Exception = New-Object System.Exception -ErrorAction Stop -ArgumentList("Error in the parameters, please check the mandatory fields.") throw $Exception } } ############################################################################### ### MAIN ############################################################################### if((Test-PendingReboot)) { Write-Warning("A reboot is pending, this can lead to unexpected errors.") } Show-Banner # READ AND VALIDATE PARAMETERS Write-LogHeader "Settings" Write-Log ("Loading settings from "+ $ParametersFile) $parameters = Get-Content $ParametersFile -Raw -Encoding UTF8 | Get-Settings if ($SourcePath) { $parameters.SourcePath = $SourcePath } if ($BaseTargetPath) { $parameters.BaseTargetPath = $BaseTargetPath } if ($BaseBackupPath) { $parameters.BaseBackupPath = $BaseBackupPath } if ($InstanceName) { $parameters.InstanceName = $InstanceName } if ($CertificatesPath) { $parameters.CertificatesPath = $CertificatesPath } Check-Parameters $parameters $TargetPath = $parameters.BaseTargetPath+"\"+$parameters.InstanceName $BackupPath = $parameters.BaseBackupPath+"\"+$parameters.InstanceName Write-LogDone if (($parameters.IISSettings.ApplicationPoolPassword -eq '' -or $parameters.IISSettings.ApplicationPoolPassword -eq $null) -and $parameters.IISSettings.IdentityType -eq 3) { Write-Host "`r`n" $parameters.IISSettings.ApplicationPoolPassword = ConvertFrom-SecureToPlain(Read-Host -AsSecureString -Prompt ' Type password for application pool account') } Write-LogHeader "Prepare installation" if(!(Test-Path "C:\PZ-Tmp")) { md "C:\PZ-Tmp" | Out-Null } # DOWNLOAD INSTALLATION PACKAGE if ([System.Uri]::IsWellFormedUriString($parameters.SourcePath, [System.UriKind]::Absolute)) { $packageFile = New-TemporaryFileWithExtension ".zip" # Download package Write-Log "Downloading package" Invoke-WebRequest $parameters.SourcePath -OutFile $packageFile $parameters.SourcePath = Resolve-Path $packageFile Write-LogDone } # PREPARE FROM PACKAGE if (Test-Path $parameters.SourcePath -PathType Leaf) { # Extract package Write-Log "Extracting package" if($parameters.SourcePath.ToString().Contains("\AppData\Local\Temp")) { Expand-Archive $parameters.SourcePath "C:\PZ-Tmp" -Force } else { if(!(Test-Path "C:\PZ-Tmp\Atefct")) { md "C:\PZ-Tmp\Atefct" | Out-Null } $name = [System.IO.Path]::GetFileName($parameters.SourcePath) Copy-Item $parameters.SourcePath "C:\PZ-Tmp\Atefct" -Force Expand-Archive "C:\PZ-Tmp\Atefct\$name" "C:\PZ-Tmp" -Force Remove-Item "C:\PZ-Tmp\Atefct\" -Recurse -Force } Write-LogDone } # PREPARE FROM DIRECTORY elseif (Test-Path $parameters.SourcePath -PathType Container) { # Copy files Write-Log "Copying files" Copy-Item (Join-Path $parameters.SourcePath "*") "C:\PZ-Tmp" -Force -Recurse Write-LogDone } else { Write-Host (Test-Path $parameters.SourcePath -PathType Container) Write-Host $parameters.SourcePath $Exception = New-Object System.Exception -ErrorAction Stop -ArgumentList("No source data found! Please check your parameter file or change the location of execution..") throw $Exception } $parameters.SourcePath = "C:\PZ-Tmp" # CHECK PREREQUISITES $prerequisites = Get-PrerequisitesMetaData "prerequisites.json" if ($prerequisites) { Test-Prerequisite (Get-OSVersion) $prerequisites.OSVersion 'Checking OS Version' $false Test-Prerequisite (Get-NetFrameworkVersion) $prerequisites.NetFrameworkVersion 'Checking .NET Framework Version' -NotFoundMessage "`r`n .NetFramework could not be found or Installed. Please install it if necessary.`r`n Download: https://www.microsoft.com/net/download/thank-you/net462 " if($prerequisites.NetCoreVersion) { Test-Prerequisite (Get-NetCoreHostVersion) $prerequisites.NetCoreVersion 'Checking .NetCore Server Hosting Version' -NotFoundMessage "`r`n .NetCore Server Hosting could not be found. Please install it if necessary.`r`n Download https://aka.ms/dotnetcore.2.0.0-windowshosting " } if($prerequisites.IISVersion) { Test-Prerequisite (Get-IISVersion) $prerequisites.IISVersion 'Checking IIS Version' } if($prerequisites.SQLServerVersion) { Test-Prerequisite (Get-SQLServerVersion) $prerequisites.SQLServerVersion 'Checking SQL Server Version' $false } Test-Prerequisite (Get-ProcessorCount) $prerequisites.Processors 'Checking Processor (Cores)' $false Test-Prerequisite (Get-MemoryCount) $prerequisites.Memory 'Checking Memory (GB)' $false } else { Write-Host "No prerequisites.json found." -ForegroundColor Yellow -BackgroundColor Black } if($parameters.ServiceSettings) { StopService $parameters.ServiceSettings.Bindings[0] } Stop-WebAppPoolsBySites $TargetPath if($parameters.BaseBackupPath -ne $null -and $parameters.BaseBackupPath -ne "" -and $parameters.InstanceName -ne $null -and $parameters.InstanceName -ne "") { Create-Backup $TargetPath $BackupPath } # INSTALL FROM DIRECTORY if (Test-Path $parameters.SourcePath -PathType Container) { Write-LogHeader "Installing from directory" # Copy files Write-Log "Copying files" if(-Not (Test-Path($TargetPath))) { md -Path $TargetPath | Out-Null } Copy-Item (Join-Path $parameters.SourcePath "*") $TargetPath -Force -Recurse Write-LogDone } if($parameters.CertificatesPath) { Copy-Certificates $TargetPath $parameters.CertificatesPath } # CONFIGURATION Write-LogHeader "Configuration" if ($parameters.WebConfig -ne $null -and (Test-Path (Join-Path $TargetPath "web.config") -PathType Leaf)) { Write-Log "Applying settings to web.config" Update-WebConfig (Join-Path $TargetPath "web.config") (ConvertTo-Json $parameters.WebConfig) Write-LogDone } if ($parameters.AppSettings -ne $null -and (Test-Path (Join-Path $TargetPath "appsettings.json") -PathType Leaf)) { Write-Log "Applying settings to appsettings.json" Update-AppSettings (Join-Path $TargetPath "appsettings.json") $parameters.AppSettings Write-LogDone } if ($parameters.LicenseKey -ne $null -and $parameters.LicenseKey -ne '') { Write-Log "Installing license key" Set-LicenseKey $parameters.LicenseKey Write-LogDone } if ($parameters.IISSettings.Enabled) { Write-Log "Updating IIS configuration" Update-IIS $TargetPath (ConvertTo-Json $parameters.IISSettings) $parameters.InstanceName Write-LogDone } if($parameters.ServiceSettings) { CheckAppConfig $parameters.ServiceSettings.AppConfig $TargetPath $parameters.ServiceSettings.ApplicationName PortBinding $parameters.ServiceSettings.Bindings if($parameters.ServiceSettings.ApplicationName) { RegisterService $parameters.ServiceSettings.ServiceName $TargetPath $parameters.ServiceSettings.ApplicationName } } if($parameters.PostInstall) { Write-LogHeader "Running PostInstall" .("$TargetPath\PostInstall.ps1") -Json (ConvertTo-Json($parameters.PostInstall) -Depth 100) } Start-WebAppPools Write-LogHeader "Tidying up" Remove-Item "C:\PZ-Tmp" -Recurse -Force Write-Host Write-Host " Completed" -ForegroundColor Green Write-Host