Skip to content

Instantly share code, notes, and snippets.

@TimCurwick
Created February 19, 2020 23:27
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save TimCurwick/8e7ad3f98731def6beb313a6073f83b1 to your computer and use it in GitHub Desktop.
Save TimCurwick/8e7ad3f98731def6beb313a6073f83b1 to your computer and use it in GitHub Desktop.
Publish-MadUDDashboard - Enhanced and tweaked version of Publish-UDDashboard
function Publish-SSAUDDashboard
{
<#
.SYNOPSIS
Publish Universal Dashboard dashboard as a service.
Enhanced version of UD module command Publish-UDDashboard
.PARAMETER DisplayName
Display name of the service.
Required
.PARAMETER ServiceName
Name of the service.
Non-standard characters will be removed.
Optional
Defaults to the DisplayName (minus any non-standard characters)
.PARAMETER Description
Description of the service.
Optional
Defaults to "PowerShell Universal Dashboard $DisplayName"
.PARAMETER StartupType
Startup type of the service
Options
Auto
Demand (i.e. - Manual)
Disabled
Delayed-Auto
Optional
Defaults to Auto
.PARAMETER DashboardFile
Full or relative path of the dashboard script file to use.
If Dashboard file is not named "Dashboard.ps1" it will be copied to
"Dashboard.ps1"
Required
.PARAMETER TargetPath
Full or relative path and name of the folder to use for the service.
UniversalDashboard module files will be copied into TargetPath.
Optional
Defaults to present location of DashboardFile
.PARAMETER UDVersion
Version of UniversalDashboard module to use.
Optional
Defaults to latest installed version
.PARAMETER Credential
PSCredential to use as service account for the service.
Optional
Defaults to SYSTEM
.PARAMETER Start
Switch parameter
If -Start is present...
The new service is started
If -Start is not present (default)...
The new service is not started
.PARAMETER Force
Switch parameter
If -Force is present...
Any existing service with the same ServiceName will be overwritten.
If -Force is not present (default)...
An existing service with the same ServiceName will cause an error.
.NOTES
Dependencies
Module
UniversalDashboard
Requires permissions to create, modify, (and somtimes delete,
start, and stop) Windows services (usually done by running as a
local administrator)
Folder
Write permissions on TargetFolder
Files
Read permissions on DashboardFile
v 1.0 2020/ 2/19 Tim Curwick Created (Modified and enhanced version of UniversalDashboard command)
#>
[CmdletBinding()]
param(
[parameter( Mandatory )]
[string]$DisplayName,
[string]$ServiceName,
[string]$Description,
[ValidateSet( 'Auto', 'Demand', 'Disabled', 'Delayed-Auto' )]
[string]$StartupType = 'Auto',
[parameter( Mandatory )]
[string]$DashboardFile,
[string]$TargetPath,
[version]$UDVersion,
[pscredential]$Credential,
[switch]$Start,
[switch]$Force )
try
{
# Override ErrorActionPreference for error handling
$EAP = $ErrorActionPreference
$ErrorActionPreference = 'Stop'
# Get Dashboard file object
# (Will throw error if not found)
$Dashboard = Get-Item -Path $DashboardFile
# If DashboardFile was "found" but is a directory
# Throw error
If ( $Dashboard.PSIsContainer )
{
throw [System.IO.FileNotFoundException]'Unable to find the specified DashboardFile.'
}
# Set default value of ServiceName, as needed
If ( -not $ServiceName )
{
$ServiceName = $DisplayName
}
# Sanitize invalid characters from service name
# (Well, technically, most characters are allowed, but are not customarily used.
# I'm restricting you to a short list of commonly used characters.)
$ServiceName = $ServiceName -replace '[^()\-_\.0-9A-Za-z]+', ''
# Set default value of Description, as needed
If ( -not $Description )
{
$Description = "PowerShell Universal Dashboard $DisplayName"
}
# Set default value of TargetPath to dashboard location, as needed
If ( -not $TargetPath )
{
$TargetPath = $Dashboard.DirectoryName
}
# If UDVersion specified
# Get specified version of UniversalDashboard module
If ( $UDVersion )
{
# Add zeros as needed UDVersion
# (Because 2.7 does not equal 2.7.0)
While ( $UDVersion.Build -lt 0 )
{
$UDVersion = [version]( $UDVersion.ToString() + '.0' )
}
# Get specified version of UniversalDashboard
$Module = $Null
$Module = Get-Module -Name UniversalDashboard -ListAvailable |
Where-Object Version -eq $UDVersion |
Select-Object -First 1
# If specified module not found
# Throw error
If ( -not $Module )
{
throw [System.IO.FileNotFoundException]"UniversalDashboard module version `"$UDVersion`" not found."
}
}
# Else (UDVersion not specified)
# Get UniversalDashboard module
Else
{
# Get imported UniversalDashboard module
$Module = Get-Module -Name UniversalDashboard
# If not imported
# Get most recent installed version of UniversalDashboard module
If ( -not $Module )
{
$Module = Get-Module -Name UniversalDashboard -ListAvailable |
Sort-Object -Property Version -Descending |
Select-Object -First 1
}
# If Universal Dashboard module not found
# Throw error
If ( -not $Module )
{
throw [System.IO.FileNotFoundException]'UniversalDashboard module not found.'
}
}
# Get existing service, if any
$ExistingService = Get-Service -Name $ServiceName -ErrorAction SilentlyContinue
# If ServiceName already exists and Force was not specified
# Throw error
If ( $ExistingService -and -not $Force )
{
throw "A service named `"$ServiceName`" already exists. Use -Force to overwrite existing service."
}
# Stop existing service, if any, as needed
If ( $ExistingService )
{
# Capture "before" statuts
$ExistingServiceStatus = [string]$ExistingService.Status
# Stop existing service, as needed
If ( $ExistingService.CanStop )
{
try
{
# Stop existing service
# Wait up to 30 seconds
$ExistingService.Stop()
$ExistingService.WaitForStatus( 'Stopped', [timespan]'00:00:30' )
# Give it another couple seconds or we get file lock issues
Start-Sleep -Seconds 2
}
catch {}
# Set flag (for possible cleanup later)
$ExistingServiceStopped = $True
}
Else
{
# Set flag
$ExistingServiceStopped = $False
}
}
try
{
# Create directory TargetPath, as needed
If ( -not $( try { Test-Path -Path $TargetPath } catch {} ) )
{
$Null = New-Item $TargetPath -Type Directory
}
# Derive UniversalDashboard module path
$ModulePath = Split-Path -Path $Module.Path
# Copy UniversalDashboard to TargetPath
# (Skip file Dashboard.ps1, if any, to avoid overwriting DashboardFile)
Get-ChildItem -Path $ModulePath -Exclude Dashboard.ps1 |
Copy-Item -Destination $TargetPath -Recurse -Force
# Copy DashboardFile to TargetPath and name, as needed
If ( $Dashboard.Name -ne 'Dashboard.ps1' -or $Dashboard.DirectoryName -ne $TargetPath )
{
$TargetDashboardFile = Join-Path -Path $TargetPath -ChildPath 'Dashboard.ps1'
Copy-Item -Path $Dashboard.FullName -Destination $TargetDashboardFile -Force
}
}
# Error copying files to TargetPath
# Turn existing service, if any, back on, if needed
# Rethrow error
catch
{
try
{
# If we previously stopped the existing service
# Start it
If ( $ExistingService -and $ExistingServiceStatus -eq 'Running' -and $ExistingServiceStopped )
{
# Start existing service
# Wait up to 10 seconds
$ExistingService.Start()
$ExistingService.WaitForStatus( 'Running', [timespan]'00:00:10' )
}
}
catch {}
throw $_
}
try
{
# If ExistingService
# Delete it
If ( $ExistingService )
{
$Step = "Deleting existing service `"$ServiceName`"."
# Delete existing service
$Result = sc.exe delete $ServiceName
# If result is not Success
# Throw error
If ( -not ( $Result -like '*Success*' ) )
{
throw ( $Result -join ' ' )
}
}
$Step = "Creating service ServiceName `"$ServiceName`" DisplayName `"$BinPath`" BinPath `"$BinPath`" StartType `"$StartupType`"."
# Build path to service executable
$BinPath = [System.IO.Path]::Combine( $TargetPath, 'net472', 'UniversalDashboard.Server.exe' )
# Create service
$Result = sc.exe create $ServiceName DisplayName= "$DisplayName" binPath= "$BinPath --run-as-service" start= "$StartupType"
# If result is not Success
# Throw error
If ( -not ( $Result -like '*Success*' ) )
{
throw ( $Result -join ' ' )
}
$Step = "Setting service ServiceName `"$ServiceName`" Description `"$Description`"."
# Set description
$Result = sc.exe description $ServiceName "$Description"
# If result is not Success
# Throw error
If ( -not ( $Result -like '*Success*' ) )
{
throw ( $Result -join ' ' )
}
# If Credential specified
# Set service credential
If ( $Credential )
{
$Step = "Setting service Credential, user name `"$($Credential.UserName)`"."
# Set service credential
$Result = ( Get-WmiObject -ClassName win32_service -Filter "name='$ServiceName'" ).
Change( $Null, $Null, $Null, $Null, $Null, $Null, $Credential.UserName, $Credential.GetNetworkCredential().Password, $Null, $Null, $Null )
# If result has a non-zero return code
# Throw error
If ( $Result -match 'ReturnValue.+[1-9]' )
{
throw
}
}
# If Start specified
# Start service
If ( $Start )
{
$Step = "Starting service ServiceName `"$ServiceName`"."
# Set service credential
$Result = sc.exe start $ServiceName
# If result is not Success
# Throw error
If ( $Result -like '*Fail*' )
{
throw ( $Result -join ' ' )
}
}
}
catch
{
throw ( "Error: $Step $($_.Exception.Message)" )
}
}
# Clean up
finally
{
$ErrorActionPreference = $EAP
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment