JobScheduler.ps1
Set-StrictMode -Version 2 function Install-TelligentJobScheduler { <# .SYNOPSIS Installs the Telligent Job Scheduler .PARAMETER JobSchedulerPath The path to install the Job Scheduler at .PARAMETER Package The installation package containing the Job Scheduler installation files. .PARAMETER WebsitePath The path containing the Telligent Community website. Any configuration, addons and hotfixed are copied from here into the Job Scheduler. .PARAMETER InstallService Specify this flag to install the Job Scheduler as a service. You will have to manually run the Job Scheduler as required. .PARAMETER ServiceName The name to use when installing the Service .PARAMETER ServiceCredential The credentials for the service to run under #> [CmdletBinding(DefaultParameterSetName='NoService')] param( [Parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] [ValidateScript({ Test-TelligentPath $_ -IsValid})] [string]$JobSchedulerPath, [Parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] [ValidateScript({Test-Zip $_ })] [string]$Package, [Parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] [ValidateScript({ Test-TelligentPath $_ -Web })] [string]$WebsitePath, [Parameter(ParameterSetName='InstallService')] [switch]$InstallService, [Parameter(ParameterSetName='InstallService', Mandatory=$true)] [ValidateNotNullOrEmpty()] [string]$ServiceName, [Parameter(ParameterSetName='InstallService',Mandatory=$true)] [ValidateNotNullOrEmpty()] [PSCredential]$ServiceCredential ) Write-Progress 'Installing Job Scheduler' 'Creating Directory' if(!(Test-Path $JobSchedulerPath)) { New-Item $JobSchedulerPath -ItemType Directory | out-null } $info = Get-TelligentCommunity $WebsitePath if ($info.PlatformVersion.Major -ge 8 ) { $zipDirName = if ($info.Platformversion -ge 9.1) { 'JobServer' } else { 'Jobservice' } Expand-Zip $Package $JobSchedulerPath -ZipDirectory $zipDirName $tempDir = Join-Path ([System.IO.Path]::GetFullPath($env:TEMP)) ([guid]::NewGuid()) Expand-Zip -Path $package -Destination $tempDir -ZipDirectory SqlScripts -ZipFile Jobs_InstallUpdate.sql $sqlScript = Join-Path $tempDir Jobs_InstallUpdate.sql if (Test-Path $sqlScript) { Write-Progress 'Job Scheduler' 'Executing Job Scheduler SQL' Invoke-TelligentSqlCmd -WebsitePath $WebsitePath -File $sqlScript } } else { Write-Progress 'Job Scheduler' 'Extracting Base Job Scheduler' Expand-Zip $Package $JobSchedulerPath -ZipDirectory tasks } Write-Progress 'Job Scheduler' 'Updating Job Scheduler from Website' Update-TelligentJobSchedulerFromWeb $WebsitePath $JobSchedulerPath if($InstallService){ Install-TelligentJobSchedulerService $ServiceName $JobSchedulerPath $Credential } } function Install-TelligentJobSchedulerService { <# .SYNOPSIS Installs the Telligent Job Scheduler as a windows service allation files. .PARAMETER Name The name to use when installing the Service .PARAMETER JobSchedulerPath The path the Job Scheduler has been installed to. .PARAMETER Credential The credentials for the service to run under .PARAMETER StartupType The startup type to use for the service. #> [CmdletBinding()] param( [Parameter(Mandatory=$true,Position=1)] [ValidateNotNullOrEmpty()] [string]$Name, [Parameter(Mandatory=$true,Position=2)] [ValidateNotNullOrEmpty()] [ValidateScript({ Test-TelligentPath $_ -JobScheduler -AllowEmpty})] [string]$JobSchedulerPath, [Parameter(Mandatory=$true,Position=3)] [ValidateNotNullOrEmpty()] [PSCredential]$Credential, [ValidateSet('Automatic', 'Manual', 'Disabled')] [string]$StartupType = 'Automatic' ) $localJsPath = $JobSchedulerPath | Convert-Path $splat = @{} if ($localJsPath.StartsWith('\\')) { $path = Expand-UNCPath $localJsPath if (!$path.LocalPath ) { Write-Error "Unable to determine local path for '$localJsPath'" return; } $splat.ComputerName = $path.ComputerName $localJsPath = $path.LocalPath } if (Join-Path $JobSchedulerPath Telligent.Jobs.Server.exe | Test-Path) { $servicePath = "$($localJsPath.TrimEnd('\'))\Telligent.Jobs.Server.exe" $serviceName = "Telligent.Jobs.Server-$Name" } else { $servicePath = "$($localJsPath.TrimEnd('\'))\Telligent.JobScheduler.Service.exe" $serviceName = "Telligent.JobScheduler-$Name" } if ($splat.ContainsKey('ComputerName')) { Write-Verbose "Setting up service on '$($splat.ComputerName)'" } else { Write-Verbose 'Setting up service' } $displayName = "Telligent Job Scheduler - $Name" $localSqlServer = ('.','(local)','localhost') -contains (Get-TelligentCommunity $JobSchedulerPath).DatabaseServer Invoke-Command @splat -ArgumentList @($serviceName, $servicePath, $displayName, $StartupType, $Credential, $localSqlServer) { param ( [Parameter(Mandatory=$true,Position=1)] [ValidateNotNullOrEmpty()] [string]$serviceName, [Parameter(Mandatory=$true,Position=2)] [ValidateNotNullOrEmpty()] [ValidateScript({ Test-Path $_ -PathType Leaf})] [string]$servicePath, [Parameter(Mandatory=$true,Position=3)] [string]$displayName, [ValidateNotNullOrEmpty()] [Parameter(Mandatory=$true,Position=4)] [ValidateNotNullOrEmpty()] [ValidateSet('Automatic', 'Manual', 'Disabled')] [string]$startupType, [Parameter(Mandatory=$true,Position=5)] [ValidateNotNullOrEmpty()] [PSCredential]$credential, [Parameter(Mandatory=$true,Position=6)] [ValidateNotNullOrEmpty()] [bool]$localSqlServer ) #New-Service doesn't support Managed Service accounts on it's Credential object $service = if ($credential.Password.Length -eq 0) { &sc.exe create "$serviceName" binPath= "$servicePath" DisplayName= "$displayName" start= auto obj= "$($credential.UserName)" } else { New-Service $serviceName ` -BinaryPathName $servicePath ` -DisplayName $displayName ` -StartupType $startupType ` -Credential $credential } if($service -and $startupType -eq 'Automatic') { Write-Verbose 'Setting automatic service recovery' # - first restart after 30 secs, subsequent every 2 mins. # - reset failure count after 20 mins &sc.exe failure "$serviceName" actions= restart/30000/restart/120000 reset= 1200 | Out-Null #If SQL is on the current server, set startup to Automatic (Delayed Startup) if ($localSqlServer) { Write-Verbose 'Changing startup mode to Automatic (Delayed Start) to prevent race conditions with SQL Server' &sc.exe config "$serviceName" start= delayed-auto | Out-Null } Start-Service $serviceName } } } function Update-TelligentJobSchedulerFromWeb { <# .SYNOPSIS Syncs configuration from a Telligent Website to the Job Server .PARAMETER WebsitePath The path to thte Telligent Community website .PARAMETER JobSchedulerPath The path to the Telligent Community Job Server #> [CmdletBinding(SupportsShouldProcess=$true)] param( [Parameter(Mandatory=$true,Position=0)] [ValidateNotNullOrEmpty()] [ValidateScript({ Test-TelligentPath $_ -Web})] [string]$WebsitePath, [Parameter(Mandatory=$true,Position=1)] [ValidateNotNullOrEmpty()] [ValidateScript({ Test-TelligentPath $_ -JobScheduler})] [string]$JobSchedulerPath ) $roboCopyParams = @( # 1 second Wait between retries, max 5 retries '/W:1', '/R:5', #No Progress, no file list, no header, no directory list, no summary, no extra file listing '/NP', '/NFL', '/NJH', '/NDL', '/NJS', '/XX' ) if ($pscmdlet.ShouldProcess($JobSchedulerPath)) { #$sharedParams += '/L' $WebsitePath = $WebsitePath.TrimEnd('\') $JobSchedulerPath = $JobSchedulerPath.TrimEnd('\') #Copy web /bin/ to JS root Write-Progress 'Job Scheduler' 'Updating binaries from web' &robocopy "$WebsitePath\bin" "$JobSchedulerPath" /e @roboCopyParams | Write-Host Write-Progress 'Job Scheduler' 'Updating config files from web' &robocopy "$WebsitePath" "$JobSchedulerPath" *.config /s /XF web.config tasks.config jobs.config /XD ControlPanel @roboCopyParams | Write-Host #TODO: is themes explicitly required if we copy *.config? Write-Progress 'Job Scheduler' 'Updating modules and languages from web' @('modules', 'languages') |% { $dir = Join-Path $WebsitePath $_ if(Test-Path $dir) { &robocopy "$dir" "$JobSchedulerPath\$_" /e /Mir @roboCopyParams | Write-Host } } #TODO: Sync sections of web.config into tellgient.js.service.exe.config # OR use web.config then merge back in JS specifics? } popd } function Remove-Service { [CmdletBinding(SupportsShouldProcess=$true, ConfirmImpact='High')] param( [Parameter(Mandatory=$true,Position=0)] [ValidateNotNullOrEmpty()] [ValidateScript({ Test-Path $_ -PathType Leaf})] [string]$Executable, [switch]$Force ) $localPath = $Executable | Convert-Path $splat = @{} if ($localPath.StartsWith('\\')) { $path = Expand-UNCPath $localPath if (!$path.LocalPath ) { Write-Error "Unable to determine local path for '$localJsPath'" return; } $splat.ComputerName = $path.ComputerName $localPath = $path.LocalPath } gwmi win32_service @splat | ? PathName -like "${localPath}*" | ? { $Force -or $PSCmdlet.ShouldProcess($_.Name) } | % { Write-Verbose "Removing Service '$($_.Name)'" $_.StopService() |Out-Null $_.Delete() | Out-Null } } |