Saritasa.AppDeploy.psm1
Add-Type -TypeDefinition @"
public enum AppDeployOverwriteMode { Backup = 0, Overwrite = 1 } "@ function Invoke-DesktopProjectDeployment { [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [System.Management.Automation.Runspaces.PSSession] $Session, [Parameter(Mandatory = $true)] [string] $BinPath, [Parameter(Mandatory = $true)] [string] $DestinationPath, [ScriptBlock] $BeforeDeploy, [ScriptBlock] $AfterDeploy, [AppDeployOverwriteMode] $OverwriteMode = [AppDeployOverwriteMode]::Backup ) Get-CallerPreference -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState Write-Information 'Creating ZIP archive...' $archiveName = "$([guid]::NewGuid()).zip" Compress-Archive "$BinPath\*" $archiveName -Force Write-Information 'Done.' $remoteTempDir = Get-RemoteTempPath $Session $remoteArchive = "$remoteTempDir\$archiveName" Write-Information "Copying $archiveName to remote server..." Copy-Item ".\$archiveName" $remoteTempDir -ToSession $Session Write-Information 'Done.' Invoke-Command -Session $Session -ScriptBlock $BeforeDeploy if ($OverwriteMode -eq [AppDeployOverwriteMode]::Backup) { Invoke-Command -Session $Session -ScriptBlock ` { $backupPath = "$($using:DestinationPath)Old" if (Test-Path $backupPath) { Remove-Item $backupPath -Recurse } if (Test-Path $using:DestinationPath) { $retries = 0 while ($true) { try { Rename-Item $using:DestinationPath $backupPath -EA Stop break } catch { $retries++ if ($retries -eq 10) { throw } else { Write-Warning 'Warning: Rename operation failed. Retrying...' Start-Sleep $retries } } } } # Directory should exist, if PSCX is used. New-Item -ItemType directory $using:DestinationPath Expand-Archive $using:remoteArchive $using:DestinationPath } } # OverwriteMode - Backup elseif ($OverwriteMode -eq [AppDeployOverwriteMode]::Overwrite) { Invoke-Command -Session $session -ScriptBlock ` { Expand-Archive $using:remoteArchive $using:DestinationPath -Force } } else { throw 'Unknown OverwriteMode.' } Invoke-Command -Session $Session -ScriptBlock ` { $appName = (Get-Item $using:DestinationPath).BaseName Write-Information "$appName app is updated." Remove-Item $using:remoteTempDir -Recurse -ErrorAction Stop } Invoke-Command -Session $Session -ScriptBlock $AfterDeploy } <# .NOTES User should have 'Log on as a service right (https://technet.microsoft.com/en-us/library/cc739424(v=ws.10).aspx). Local user name example: .\administrator Service user accounts: LocalService, NetworkService, LocalSystem https://msdn.microsoft.com/en-us/library/windows/desktop/ms686005(v=vs.85).aspx Credentials for built-in service user accounts: New-Object System.Management.Automation.PSCredential('NT AUTHORITY\LocalService', (New-Object System.Security.SecureString)) New-Object System.Management.Automation.PSCredential('NT AUTHORITY\NetworkService', (New-Object System.Security.SecureString)) New-Object System.Management.Automation.PSCredential('.\LocalSystem', (New-Object System.Security.SecureString)) #> function Invoke-ServiceProjectDeployment { [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [System.Management.Automation.Runspaces.PSSession] $Session, [Parameter(Mandatory = $true)] [string] $ServiceName, [Parameter(Mandatory = $true)] [string] $ProjectName, [Parameter(Mandatory = $true)] [string] $BinPath, [Parameter(Mandatory = $true)] [string] $DestinationPath, [System.Management.Automation.PSCredential] [System.Management.Automation.Credential()] $ServiceCredential ) Get-CallerPreference -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState Invoke-DesktopProjectDeployment -Session $Session -DestinationPath $DestinationPath -BinPath $BinPath ` -BeforeDeploy ` { $service = Get-Service | ? { $_.Name -eq $using:ServiceName } if ($service) { Stop-Service $service Write-Information "Service $using:ServiceName is stopped." } else { Write-Information "Service $using:ServiceName is not installed." } } ` -AfterDeploy ` { $service = Get-Service | ? { $_.Name -eq $using:ServiceName } if ($service) { Start-Service $service -ErrorAction Stop Write-Information "Service $using:ServiceName is started." } else { Write-Information "Creating $using:ServiceName service..." $credential = $using:ServiceCredential if ($credential.UserName -notlike '*\*') # Not a domain user. { $credential = New-Object System.Management.Automation.PSCredential(".\$($credential.UserName)", $credential.Password) } $service = New-Service -Name $using:ServiceName -ErrorAction Stop -Credential $credential ` -BinaryPathName "$using:DestinationPath\$using:ProjectName.exe" Write-Information "Done." Start-Service $using:ServiceName -ErrorAction Stop Write-Information "Service $using:ServiceName is started." } } } |