1
\$\begingroup\$

Working in a very restrictive environment I have been forced to rely on tools which are already installed on my PC, in order to automate tasks, especially ones launched through SSH. My previous attempt was a Python one, using Fabric, however that was nixed due to the external requirements (Python + Fabric itself).

So the new one is much more basic, Powershell (pre-installed) and plink.exe (ssh equivalent, part of Putty, "graciously" provided by our infrastructure overlords).

I basically recreated the Python version, in a cruder form, by controlling the plink.exe process directly and redirecting input/output.

Without further ado, the culprits:

./spoc.ps1 - entry point

./powershell - scripts which do the "heavy lifting"

./configurations - scripts which contain just configuration variables

./commands - convenience commands used by the entry point

./spoc.ps1

param(
    [String]$Environment = "local",
    [String]$Application = "test-app",
    [String]$Command = "scp",
    [ValidateSet("All", "File", "Console", "None")]
    [String]$LogOutput = "Console"
)

Set-StrictMode -Version Latest
$ErrorActionPreference = "Stop"

$scriptRoot = Split-Path -Parent -Path $MyInvocation.MyCommand.Definition
. "${scriptRoot}\powershell\common.ps1"
. "${scriptRoot}\powershell\package.ps1"
. "${scriptRoot}\powershell\oneshot.ps1"

$env:sshUser = getUser
$env:logOutput = "Console"

##- DynamicParameter.
# Parameter with set validation based on an array provided at runtime.
function DynamicParameter {
    param (
        $name,
        $completionValues
    )
    $attributeCollection = New-Object Collections.ObjectModel.Collection[System.Attribute]

    $parameterAttribute = New-Object Management.Automation.ParameterAttribute
    $parameterAttribute.Mandatory = $true
    $attributeCollection.Add($parameterAttribute)

    $validateSetAttribute = New-Object Management.Automation.ValidateSetAttribute($completionValues)
    $attributeCollection.Add($validateSetAttribute)

    $runtimeParameter = New-Object `
        Management.Automation.RuntimeDefinedParameter($name, [string], $attributeCollection)
    return $runtimeParameter
}
#-##

##- Spoc.
function Spoc {
    ##- Parameters.
    [CmdletBinding()]
    param (
        [ValidateSet("All", "File", "Console", "None")]
        [String]$LogOutput = "Console"
    )
    DynamicParam {
        $runtimeParameters = New-Object Management.Automation.RuntimeDefinedParameterDictionary

        $environmentCompletions = $(Get-ChildItem -Path ".\configurations" -Directory | Select-Object -ExpandProperty Name)
        $environmentParameter = DynamicParameter "Environment" $environmentCompletions
        $runtimeParameters.Add("Environment", $environmentParameter)

        $applicationCompletions = $(Get-ChildItem -Path ".\configurations\test" | Select-Object -ExpandProperty Name | % { $_.replace(".ps1", "") })
        $applicationCompletions += "test-app"
        $applicationCompletions += "test-app-zip"
        $applicationParameter = DynamicParameter "Application" $applicationCompletions
        $runtimeParameters.Add("Application", $applicationParameter)

        $commandCompletions = $(Get-ChildItem -Path ".\commands\*.ps1" | Select-Object -ExpandProperty Name | % { $_.replace(".ps1", "") })
        $commandParameter = DynamicParameter "Command" $commandCompletions
        $runtimeParameters.Add("Command", $commandParameter)

        return $runtimeParameters
    }

    begin {
        $Environment = $PsBoundParameters["Environment"]
        $Application = $PsBoundParameters["Application"]
        $Command = $PsBoundParameters["Command"]
    }
    #-##

    process {

        ##- Parameter warnings.
        if($Environment -eq "local") {
            Warning "No environment parameter provided, using default value: local."
        }
        if($Application -eq "test-app") {
            Warning "No application parameter provided, using default value: test-app."
        }
        if($Command -eq "scp") {
            Warning "No command parameter provided, using default value: scp."
        }
        #-##

        $env:sshUser = getUser
        $env:password = getPassword
        $logTargets = @("All", "File", "Console", "None")
        $env:logOutput = $LogOutput

        . "${scriptRoot}\configurations\${Environment}\${Application}.ps1"
        Invoke-Expression "${scriptRoot}\commands\${Command}.ps1"
    }
}
#-##

if(!($MyInvocation.InvocationName -eq ".")) {
    Spoc -Environment $Environment -Application $Application -Command $Command -LogOutput $LogOutput
}

./powershell/common.ps1

Set-StrictMode -Version Latest
$ErrorActionPreference = "Stop"

##- Logging functions.
function global:Write {
    param (
        [Parameter(Mandatory)]
        [string]$logLevel,
        [Parameter(Mandatory)]
        [String]$message
    )
    $messageTime = $(Get-Date -format "HH:mm:ss dd.MM.yyyy")
    $color = "Gray"
    if($logLevel -eq "warning") {
            $color = "Yellow"
    }
    if($logLevel -eq "error") {
            $color = "Red"
    }
    if(($env:logOutput -eq "All") -or ($env:logOutput -eq "Console")) {
        Write-Host -ForegroundColor $color $message
    }
    if(($env:logOutput -eq "All") -or ($env:logOutput -eq "File")) {
        Write-Output "[${logLevel}][${messageTime}]${message}" | Out-File -Append "Z:\work\tmp\launcher.txt"
    }
}

function global:Info {
    ##- Parameters.
    param (
            [Parameter(Mandatory)]
            [AllowEmptyString()]
            [String]$message
    )
    #-##
    global:Write "info" $message
}

function global:Warning {
    ##- Parameters.
    param (
            [Parameter(Mandatory)]
            [AllowEmptyString()]
            [String]$message
    )
    #-##
    global:Write "warning" $message
}

function global:Error {
    ##- Parameters.
    param (
            [Parameter(Mandatory)]
            [AllowEmptyString()]
            [String]$message
    )
    #-##
    global:Write "error" $message
}


function global:ErrorExit {
    ##- Parameters.
    param (
            [Parameter(Mandatory)]
            [AllowEmptyString()]
            [String]$message
    )
    #-##
    global:Error $message
    throw
}
#-##

##- Environment, user, password.
function getUser() {
    return "${env:USERNAME}a"
}

function getPassword() {
    $password = Read-Host -Prompt "Password" -AsSecureString
    $password = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($password)
    $password = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($password)
    return $password
}
#-##

##- BaseRun.
function BaseRun
{
    ##- Parameters.
    param (
            [Parameter(Mandatory)]
            [String]$hostname,
            [Parameter(Mandatory)]
            [String]$command,
            [String]$arguments
    )
    #-##

    ##- Process info.
    $procInfo = New-Object System.Diagnostics.ProcessStartInfo
    $procInfo.RedirectStandardOutput = $true
    $procInfo.RedirectStandardError = $true
    $procInfo.RedirectStandardInput = $true
    $procInfo.FileName = $command
    $procInfo.Arguments = $arguments
    $procInfo.UseShellExecute = $false
    #-##

    $process = New-Object System.Diagnostics.Process
    $process.StartInfo = $procInfo

    $global:hostname = $hostname
    $global:outputBuffer = New-Object System.Text.StringBuilder

    try {
        ##- Register output and error event handlers.
        Register-ObjectEvent -InputObject $process `
            -EventName OutputDataReceived -SourceIdentifier processOutputDataReceived `
            -Action {

            $data = $EventArgs.data
            if($data -ne $null) {
                if($data -notmatch "\[sudo\] password for.*") {
                    $outputBuffer.Append("${data}`n")
                    Info "[${hostname}][output] ${data}"
                }
            }
        } | Out-Null

        Register-ObjectEvent -InputObject $process `
            -EventName ErrorDataReceived -SourceIdentifier processErrorDataReceived `
            -Action {

            $data = $EventArgs.data
            if($data -ne $null) {
                $outputBuffer.Append("${data}`n")
                Error "[${hostname}][error] ${data}"
            }
        } | Out-Null
        #-##

        [void]$process.Start()

        ##- Input password.
        # Only input the password for plink.exe (non-local execution)
        # or for pscp.exe (local execution of scp).
        if (($hostname -ne "local") -or ($command -match "pscp.exe")) {
            if(!(Test-Path Env:\sshUser)) {
                ErrorExit "No password provided, set `$env:password"
            }
            $inputStream = $process.StandardInput
            # A slightly longer timeout is needed to make sure that the app was started.
            Start-Sleep -m 2000
            $inputStream.Write("${env:password}`n")
            $inputStream.Close()
        }
        #-##

        $process.BeginOutputReadLine()
        $process.BeginErrorReadLine()

        $process.WaitForExit()
    }
    finally {
        Unregister-Event -SourceIdentifier processOutputDataReceived
        Unregister-Event -SourceIdentifier processErrorDataReceived
    }

    # Return both the exit code and the command output.
    $process.ExitCode
    $outputBuffer.ToString()
}
#-##

##- LocalRun.
function LocalRun {
    ##- Parameters.
    param (
            [Parameter(Mandatory)]
            [String]$command,
            [bool]$captureOutput = $false
    )
    #-##
    Info "[local][command] $command"

    $commandComponents = $command -split " "
    $command = $commandComponents[0]
    if($commandComponents.Count -gt 2) {
        $arguments = $commandComponents[1..$($commandComponents.Count - 1)] -join " "
    } else {
        $arguments = ""
    }

    $baseRunResult = BaseRun "local" $command $arguments

    if ($baseRunResult[0] -ne 0) {
        Start-Sleep -m 1000
        ErrorExit "Execution stopped due to an error, check the command output."
    }

    if($captureOutput -eq $true) {
        return $baseRunResult[1]
    }
}
#-##

##- Run.
function Run {
    ##- Parameters.
    param (
            [Parameter(Mandatory)]
            [String]$sshHostname,
            [Parameter(Mandatory)]
            [String]$command,
            [bool]$captureOutput = $false
    )
    #-##
    Info "[${sshHostname}][command] $command"

    $initialLogOutput = $env:logOutput
    # Silence the output so we don't echo the password.
    $env:logOutput = "None"

    # Dummy plink connection to check the connectivity, password, etc.
    $plink = "C:\Tools\PuTTY\plink.exe"
    $plinkNonInteractiveArgs = "-batch -v -pw ${env:password}"
    $plinkInteractiveArgs = "-l ${env:sshUser} -t ${sshHostname} whoami"
    $plinkArguments = "${plinkNonInteractiveArgs} ${plinkInteractiveArgs}"

    $sshCheck = BaseRun $sshHostname $plink $plinkArguments

    $env:logOutput = $initialLogOutput

    $lastLine = $($sshCheck -split "`n")[-2]
    if($lastLine -match "Host does not exist") {
        ErrorExit "Cannot connect to ${sshHostname}, host not found."
    }
    if($lastLine -match "Disconnected: User aborted at host key verification") {
        Error "Cannot authenticate to ${sshHostname}, server key not accepted."
        ErrorExit "Run ${plink} ${plinkInteractiveArgs} and accept the key."
    }
    if($lastLine -match "Disconnected: Unable to authenticate") {
        ErrorExit "Cannot authenticate to ${sshHostname}, wrong password."
    }

    $baseRunResult = BaseRun $sshHostname "C:\Tools\PuTTY\plink.exe" `
        "-batch -l ${env:sshUser} -pw ${env:password} -t ${sshHostname} ${command}"

    if ($baseRunResult[0] -ne 0) {
        Start-Sleep -m 1000
        ErrorExit "Execution stopped due to an error, check the command output."
    }

    if($captureOutput -eq $true) {
        return $baseRunResult[1]
    }
}
#-##

##- RunAsUser.
function RunAsUser {
    ##- Parameters.
    param (
            [Parameter(Mandatory)]
            [String]$sshHostname,
            [Parameter(Mandatory)]
            [String]$applicationUser,
            [Parameter(Mandatory)]
            [String]$command,
            [bool]$captureOutput = $false
    )
    #-##

    $command = "sudo su - ${applicationUser}<<END`n${command}`nEND"

    $runResult = Run $sshHostname $command $captureOutput

    if($captureOutput -eq $true) {
        return $runResult
    }
}
#-##

##- Command line functions.
function Local-Scp {
    ##- Parameters.
    param (
        [Parameter(Mandatory)]
        [String]$fileToCopy,
        [Parameter(Mandatory)]
        [String]$sourceServer,
        [Parameter(Mandatory)]
        [String]$sourceFolder,
        [Parameter(Mandatory)]
        [String]$targetFolder,
        [bool]$captureOutput = $false
    )
    #-##

    $pscp = "C:\Tools\PuTTY\pscp.exe"
    $pscpArguments = "${env:sshUser}@${sourceServer}:${sourceFolder}/${fileToCopy} ${targetFolder}/${fileToCopy}"

    $localRunResult = LocalRun "${pscp} ${pscpArguments}" $captureOutput

    if($captureOutput -eq $true) {
        return $localRunResult
    }
}

function Scp-File {
    ##- Parameters.
    param (
        [Parameter(Mandatory)]
        [String]$fileToCopy,
        [Parameter(Mandatory)]
        [String]$sourceServer,
        [Parameter(Mandatory)]
        [String]$sourceFolder,
        [Parameter(Mandatory)]
        [String]$targetServer,
        [Parameter(Mandatory)]
        [String]$targetFolder,
        [bool]$captureOutput = $false
    )
    #-##

    $runResult = Run $targetServer `
        "scp ${sourceServer}:${sourceFolder}/${fileToCopy} ${targetFolder}" $captureOutput
    if($captureOutput -eq $true) {
        return $runResult
    }
}

function Ssh-Command {
    ##- Parameters.
    param (
        [Parameter(Mandatory)]
        [String]$sshHostname,
        [Parameter(Mandatory)]
        [String]$command,
        [bool]$captureOutput = $false
    )
    #-##

    $runResult = Run $sshHostname $command $captureOutput

    if($captureOutput -eq $true) {
        return $runResult
    }
}

function Ssh-CommandAsUser {
    ##- Parameters.
    param (
        [Parameter(Mandatory)]
        [String]$sshHostname,
        [Parameter(Mandatory)]
        [String]$applicationUser,
        [Parameter(Mandatory)]
        [String]$command,
        [bool]$captureOutput = $false
    )
    #-##

    $runResult = RunAsUser $sshHostname $applicationUser $command $captureOutput

    if($captureOutput -eq $true) {
        return $runResult
    }
}

#-##

./powershell/package.ps1

Set-StrictMode -Version Latest
$ErrorActionPreference = "Stop"

##- Scp.
function Scp {
    ##- Parameters.
    param (
        [Parameter(Mandatory)]
        [String]$fileToCopy,
        [Parameter(Mandatory)]
        [String]$sourceServer,
        [Parameter(Mandatory)]
        [String]$sourceFolder,
        [Parameter(Mandatory)]
        [String]$targetServer,
        [Parameter(Mandatory)]
        [String]$targetFolder,
        [Parameter(Mandatory)]
        [String]$targetUser
    )
    #-##

    Info "Starting to scp."

    $intermediaryServer = "dummy-server"

    # Short server name, used as a subfolder in /data/packages on ldps101.
    # Example: lcob, lbat, etc.
    $serverFolder = $($targetServer -split "\.")[0] -replace "[0-9]*", ""
    $intermediaryFolder =  "/data/packages/${serverFolder}/${targetUser}"
    Ssh-Command $intermediaryServer "mkdir -pv ${intermediaryFolder}"

    Scp-File $fileToCopy $sourceServer $sourceFolder $intermediaryServer $intermediaryFolder

    Ssh-Command $intermediaryServer "chmod -v ugo+rwx ${intermediaryFolder}/${fileToCopy}"

    $scpTime = $(Get-Date -format yyyyMMdd)
    $targetTmpFolder = "/tmp/${serverFolder}/${targetUser}/${scpTime}"
    Ssh-Command $targetServer "mkdir -pv ${targetTmpFolder}"

    Scp-File $fileToCopy $intermediaryServer $intermediaryFolder $targetServer $targetTmpFolder
    Ssh-CommandAsUser $targetServer $targetUser "mkdir -pv ${targetFolder}"
    Ssh-CommandAsUser $targetServer $targetUser "cp -v ${targetTmpFolder}/${fileToCopy} ${targetFolder}"
    Ssh-Command $targetServer "rm -rvf ${targetTmpFolder}"

    Info "Finished scping."
}
#-##

##- Backup.
function Backup {
    ##- Parameters.
    param(
        [Parameter(Mandatory)]
        [String]$installationFolder,
        [Parameter(Mandatory)]
        [String]$newArchive,
        [Parameter(Mandatory)]
        [String]$exclusions,
        [Parameter(Mandatory)]
        [String]$applicationUser,
        [Parameter(Mandatory)]
        [String]$targetServer
    )
    #-##

    Info "Starting to backup."

    $tarListCommand = "tar --exclude=`"*/*`" -tvf ${installationFolder}/package-repo/${newArchive}"
    $zipListCommand = "zipinfo -1 ${installationFolder}/package-repo/${newArchive} -x `"*/**`""

    $archiveInfo = @{
        ".tar" = @("tar -cvf", $tarListCommand);
        ".tar.gz" = @("tar -czvf", $tarListCommand);
        ".tar.bz2" = @("tar -cjvf", $tarListCommand);
        ".zip" = @("zip -r", $zipListCommand)
    }
    $index = 0
    foreach ($archiveType in $archiveInfo.GetEnumerator()) {
        if ($newArchive.EndsWith($archiveType.Name)) {
            $archiveListCommand = $archiveType.Value[1]
            $newArchiveBasename = $newArchive -replace $archiveType.Name, ""
            $archiveFormat = $archiveType.Name
            $archiveCommand = $archiveType.Value[0]
        } else {
            $index += 1
        }
    }
    if($index -ge $archiveInfo.Count) {
        ErrorExit "Unsupported archive format: ${newArchive}. Supported formats: tar, tar.gz, tar.bz2, zip."
    }

    $archiveTime = $(Get-Date -format yyyyMMdd)
    $archiveName = "${newArchiveBasename}_${archiveTime}.bak${archiveFormat}"

    $archiveListOutput = Ssh-CommandAsUser $targetServer $applicationUser $archiveListCommand $true
    $installationFolderListOutput = Ssh-CommandAsUser `
        $targetServer $applicationUser "ls -p1 ${installationFolder}" $true

    # The files to archive are files that are both in the new tar and in the current installation.
    # We don't archive excluded files (temporary files, input files, output files, in general).
    $toArchiveList = ""
    foreach($archiveListFile in $archiveListOutput.Split("`r`n")) {
        $archiveListFile = $archiveListFile.Split(" ")[-1]
        if(($installationFolderListOutput.Split("`r`n") -contains $archiveListFile) -and
        ($exclusions.Split(",") -notcontains $archiveListFile)) {
            $toArchiveList += "$archiveListFile "
        }
    }

    if ($toArchiveList -ne " ") {
        $archivePath = "${installationFolder}/package-repo/${archiveName}"
        $archiveCommand = "${archiveCommand} ${archivePath} ${toArchiveList}"
        Ssh-CommandAsUser $targetServer $applicationUser "cd ${installationFolder}; $archiveCommand"
    } else {
        Warning "Not archiving anything as the list of files to archive was empty."
    }

    $oldBackupsCommand = "ls -1rt ${installationFolder}/package-repo | grep ${Application}"
    $oldBackups = Ssh-CommandAsUser $targetServer $applicationUser $oldBackupsCommand $true
    $oldBackups = $oldBackups -replace "${env:sshUser}'s password:", ""
    $initialOldBackupsList = $oldBackups.Split("`n")

    # Remove empty lines in the output.
    $oldBackupsList = @()
    foreach ($oldBackup in $initialOldBackupsList) {
        if($oldBackup -ne "") {
            $oldBackupsList += $oldBackup
        }
    }

    if($oldBackupsList.Count -gt 3) {
        # Ignore the last 2 elements: current backup and archive for the new deployment.
        $backupsToDelete = $oldBackupsList[0..($oldBackupsList.Count - 3)]

        $cdCommand = "cd ${installationFolder}/package-repo"
        #Ssh-CommandAsUser $targetServer $applicationUser "${cdCommand}; rm -v ${backupsToDelete}"
    }

    Info "Finished backing up."
}
#-##

##- Clean.
function Clean {
    ##- Parameters.
    param(
        [Parameter(Mandatory)]
        $targetFolder, 
        [Parameter(Mandatory)]
        $applicationUser, 
        [Parameter(Mandatory)]
        $targetServer
    )
    #-##

    Info "Starting to clean."

    $cleanCommand = "rm -rfv ${targetFolder}"
    Ssh-CommandAsUser $targetServer $applicationUser $cleanCommand

    Info "Finished cleaning."
}
#-##

##- Unpack.
function Unpack {
    ##- Parameters.
    param(
        [Parameter(Mandatory)]
        $archiveName, 
        [Parameter(Mandatory)]
        $installationFolder, 
        [Parameter(Mandatory)]
        $applicationUser, 
        [Parameter(Mandatory)]
        $targetServer
    )
    #-##

    Info "Starting to unpack."

    $archiveInfo = @{
        ".tar" = @("tar -xvf", "-C");
        ".tar.gz" = @("tar -xzvf", "-C");
        ".tar.bz2" = @("tar -xjvf", "-C");
        ".zip" = @("unzip -o", "-d")
    }
    $index = 0
    foreach ($archiveType in $archiveInfo.GetEnumerator()) {
        if ($archiveName.EndsWith($archiveType.Name)) {
            $unpackCommand = $archiveType.Value[0]
            $unpackFolderFlag = $archiveType.Value[1]
            $archiveUnpackCommand = "${unpackCommand} " +
                "${installationFolder}/package-repo/${archiveName} " +
                "${unpackFolderFlag} ${installationFolder}"
        } else {
            $index += 1
        }
    }
    if($index -ge $archiveInfo.Count) {
        ErrorExit "Unsupported archive format: ${archiveName}. Supported formats: tar, tar.gz, tar.bz2, zip."
    }

    Ssh-CommandAsUser $targetServer $applicationUser $archiveUnpackCommand

    Info "Finished unpacking."
}
#-##

./powershell/oneshot.ps1

Set-StrictMode -Version Latest
$ErrorActionPreference = "Stop"

##- Download-Folder.
function Download-Folder {
    ##- Parameters.
    param (
        [Parameter(Mandatory)]
        [String]$sourceServer,
        [Parameter(Mandatory)]
        [String]$sourceUser,
        [Parameter(Mandatory)]
        [String]$sourceFolder,
        [String]$exclusions = "dummy-exclusion",
        [Parameter(Mandatory)]
        [String]$targetFolder
    )
    #-##

    Info "Starting to download folder."

    $archiveTime = $(Get-Date -format yyyyMMdd)
    $temporaryFolder = "/tmp/${sourceUser}${archiveTime}"
    Ssh-CommandAsUser $sourceServer $sourceUser "mkdir -pv ${temporaryFolder}"

    $archiveCommand = "tar -czvf ${temporaryFolder}/archive.tar.gz ${sourceFolder}"
    Ssh-CommandAsUser $sourceServer $sourceUser $archiveCommand

    Local-Scp "archive.tar.gz" $sourceServer $temporaryFolder $targetFolder

    Ssh-CommandAsUser $sourceServer $sourceUser "rm -rvf ${temporaryFolder}"

    Info "Finished downloading."
}
#-##

##- Setup-Cobol
function Setup-Cobol {
    ##- Parameters.
    param (
        [Parameter(Mandatory)]
        [ValidateSet("test", "hom1", "hom2", "hom3")]
        [String]$environment,
        [Parameter(Mandatory)]
        [String]$cobolPackageInfo
    )
    #-##

    Info "Creating UC4 Cobol deployment package."

    $cobolFolder = "T:\dummy-folder"
    #$cobolFolder = "Z:\work\tmp\uc4"
    $cobolCsv="${cobolFolder}\packages_deployment_UC4T.csv"

    $environment = $environment.ToUpper()
    $cobolDate = $(Get-Date -format yyyyMMdd)
    $cobolPackageInfo = $cobolPackageInfo.Replace("date_of_delivery(YYYYMMDD)", $cobolDate)
    $cobolPackageInfo += ";$environment"
    $cobolPackageInfo = $cobolPackageInfo -replace "`r`n", ""

    Info "Will write ${cobolPackageInfo} to ${cobolCsv}."

    if(Test-Path $cobolCsv) {
        ErrorExit "Cobol file already exists, aborting."
    }

    try {
        New-Item -ItemType Directory $cobolFolder
    } catch {
    }

    $encoding = New-Object Text.AsciiEncoding
    $writer = New-Object IO.StreamWriter($cobolCsv, $false, $encoding)
    $writer.Write("${cobolPackageInfo}`n")
    $writer.Close()

    Info "Wrote ${cobolPackageInfo} to ${cobolCsv}."
}
#-##

##- Create-Branch
function Create-Branch {
    ##- Parameters.
    param (
        [Parameter(Mandatory)]
        [String]$branchName
    )
    #-##

    Info "Creating SVN branch."

    $env:sshUser = getUser
    $env:password = getPassword
    $sshHostname = "dummy-server"
    $svnScript = "/home/dummy-user/utilities/svn.sh"

    Ssh-Command -sshHostname $sshHostname -command "${svnScript} branch ${branchName} HEAD trunk"

    Info "Finished creating SVN branch."
}
#-##

##- Install-Script
function Install-Script {
    ##- Parameters.
    param (
        [ValidateSet("dev", "test", "test2", "hom1", "hom2", "hom3", "sup", "prod")]
        [String]$environment = "test",
        [Parameter(Mandatory)]
        [String]$project,
        [String]$tnsString = "dummy-tns",
        [String]$user = "designer",
        [Parameter(Mandatory)]
        [String]$archive
    )
    #-##

    Info "Installing SQL script."

    $env:password = getPassword

    # The environment should actually be uppercase, especially the directories.
    $environment = $environment.ToUpper()

    if ($environment -eq "PROD") {
        Warning "Running on production, are you sure you want to run the script?"
        $production = Read-Host -Prompt "Enter `"PROD`" if you want to continue: "
        if ($production -ne "PROD") {
            ErrorExit "Production run cancelled by user."
        }
    }

    $installDate = $(Get-Date -format yyyyMMdd)

    $targetRootDir = "T:\dummy-z"
    #$targetRootDir = "Z:\work\tmp"

    $sourceRootDir = "Z:\Downloads"
    $scriptDir = ${archive} -replace ".zip", ""
    $scriptVersion = 1
    $unversionedDir = "${targetRootDir}/${environment}/${project}/${installDate}/${scriptDir}"
    $targetDir="${unversionedDir}_${scriptVersion}"

    while (Test-Path "${unversionedDir}_${scriptVersion}") {
        $scriptVersion += 1
        $targetDir="${unversionedDir}_${scriptVersion}"
    }

    New-Item -ItemType Directory -Force -Verbose -Path $targetDir | Out-Null
    $originalLocation = Get-Location
    Set-Location -Verbose $targetDir
    Move-Item -verbose "${sourceRootDir}/${archive}" "${targetDir}"
    #Copy-Item -Verbose "${sourceRootDir}/${archive}" "${targetDir}"

    Info "Unpacking SQL script archive."
    unzip.exe -o $archive
    if($LastExitCode -ne 0) {
        ErrorExit "Unzip failed with exit code: ${LastExitCode}"
    }
    Info "Finished unpacking SQL script archive."

    $installScripts = Get-ChildItem "*instal*.sql" | Select -expand Name
    if(!$installScripts) {
        ErrorExit "No SQL install scripts found, aborting."
    }
    foreach($installScript in $installScripts) {
        Info "sqlplus ${user}/{env:password}@`"${tnsString}`" @${installScript}"
        Write-Output exit | sqlplus.exe ${user}/${env:password}@"${tnsString}" @$installScript
        if($LastExitCode -ne 0) {
            ErrorExit "SqlPlus failed with exit code: ${LastExitCode}"
        }
    }

    $installLogs = Get-ChildItem "*.log" | Select -expand Name
    $timestampedLog = ""
    $timestampedLogNames = ""
    foreach($installLog in $installLogs) {
        $timestampedLog = $installLog -replace ".log", ""
        $timestampedLog += "${environment}_${installDate}_${scriptVersion}.log"

        Move-Item -Verbose $installLog $timestampedLog
        $timestampedLogNames += $timestampedLog
    }

    # Put log file name in the Windows clipboard.
    Write-Output $timestampedLogNames | clip.exe

    # Open Windows Explorer with the log already selected.
    try {
        Get-Command explorer.exe | Out-Null
        explorer.exe "/e,/select,`"${timestampedLog}`""
        if($LastExitCode -ne 0) {
            ErrorExit "Explorer failed with exit code: ${LastExitCode}"
        }
    } catch {
        ErrorExit "Explorer not found?!?"
    }

    Set-Location -verbose $originalLocation

    Info "Finished installing SQL script."
}
#-##

##- Wait
function Wait {
    Info "Press any key to continue."
    $host.UI.RawUI.ReadKey("NoEcho, IncludeKeyDown") | Out-Null
    Info "Key pressed, continuing."
}
#-##

./configurations/local/test-app.ps1

$SOURCE_SERVER="dummy-server"
$ARCHIVE_NAME="test-app-1.0.0.tar.gz"
$APPLICATION_FOLDER="test-app"
$INSTALLATION_FOLDER="/home/dummy-user"
$INSTALLATION_SERVER="dummy-server"
$SU_USER="dummy-user"

# Configuration values for the scp command.
$SCP_FILE_TO_COPY=${ARCHIVE_NAME}
$SCP_SOURCE_FOLDER="${INSTALLATION_FOLDER}/todeploy"
$SCP_TARGET_FOLDER="${INSTALLATION_FOLDER}/package-repo"
$SCP_SOURCE_SERVER=${SOURCE_SERVER}
$SCP_TARGET_SERVER=${INSTALLATION_SERVER}
$SCP_TARGET_USER=${SU_USER}

# Configuration values for the archive command.
$BACKUP_INSTALLATION_FOLDER=${INSTALLATION_FOLDER}
$BACKUP_NEW_ARCHIVE=${ARCHIVE_NAME}
$BACKUP_EXCLUSIONS="dummy-exclusion"
$BACKUP_APPLICATION_USER=${SU_USER}
$BACKUP_TARGET_SERVER=${INSTALLATION_SERVER}

# Configuration values for the clean command.
$CLEAN_TARGET_FOLDER="${INSTALLATION_FOLDER}/${APPLICATION_FOLDER}"
$CLEAN_APPLICATION_USER=${SU_USER}
$CLEAN_TARGET_SERVER=${INSTALLATION_SERVER}

# Configuration values for the deploy command.
$UNPACK_INSTALLATION_FOLDER=${INSTALLATION_FOLDER}
$UNPACK_ARCHIVE_NAME=${ARCHIVE_NAME}
$UNPACK_APPLICATION_USER=${SU_USER}
$UNPACK_TARGET_SERVER=${INSTALLATION_SERVER}

./commands/scp.ps1

Scp $SCP_FILE_TO_COPY $SCP_SOURCE_SERVER $SCP_SOURCE_FOLDER `
    $SCP_TARGET_SERVER $SCP_TARGET_FOLDER $SCP_TARGET_USER

./commands/backup.ps1

Backup $BACKUP_INSTALLATION_FOLDER $BACKUP_NEW_ARCHIVE $BACKUP_EXCLUSIONS `
    $BACKUP_APPLICATION_USER $BACKUP_TARGET_SERVER

./commands/clean.ps1

Clean $CLEAN_TARGET_FOLDER $CLEAN_APPLICATION_USER $CLEAN_TARGET_SERVER

./commands/unpack.ps1

Unpack $UNPACK_ARCHIVE_NAME $UNPACK_INSTALLATION_FOLDER `
    $UNPACK_APPLICATION_USER $UNPACK_TARGET_SERVER

./commands/deploy.ps1

Invoke-Expression ".\commands\scp.ps1"
Invoke-Expression ".\commands\backup.ps1"
Invoke-Expression ".\commands\unpack.ps1"

./commands/deploy.ps1

Invoke-Expression ".\commands\scp.ps1"
Invoke-Expression ".\commands\backup.ps1"
Invoke-Expression ".\commands\unpack.ps1"

./commands/clean-deploy.ps1

Invoke-Expression ".\commands\scp.ps1"
Invoke-Expression ".\commands\backup.ps1"
Invoke-Expression ".\commands\clean.ps1"
Invoke-Expression ".\commands\unpack.ps1"
\$\endgroup\$

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Browse other questions tagged or ask your own question.