SkyWire-PSInstallerHelpers.psm1


<#
 .SYNOPSIS
  Parse a tag into a hashtable.
 
 .DESCRIPTION
  Parse a tag string into a hashtable which contains 'tagType' and 'version' where tagType is the pre-release tag or 'prod' and version is the version or ticket number.
 
 .PARAMETER tag
  The tag (ticket or version number) followed by pre-release tag, if applicable.
 
 .NOTES
  Production tags should omit the pre-release tag. If pre-release tag must be specified for production, use 'latest'.
 
 .EXAMPLE
  ConvertFrom-SWTag -tag POS-1337-beta
 
 .EXAMPLE
  ConvertFrom-SWTag -tag 4.25.0-rc
 
 .EXAMPLE
  ConvertFrom-SWTag -tag 4.25.0
#>

function ConvertFrom-SWTag {
  param(
    [Parameter(Mandatory=$true)]
    [string] $tag
  )

  if ($tag -match "^\w+-\d+$") {
    throw "Invalid tag. See help for more info."
  }

  # Match Ticket/Version number and pre release tag (ex: POS-1337-pre, 4.25.0-rc) or
  # match the version number for production release
  $regex = "^(.*)-(\w+)|\d+\.\d+\.\d+$"
  $result = [regex]::Match($tag, $regex)

  if (!$result.Success) {
    throw "Invalid tag: $tag"
  }

  try {

    $tagObject = @{
      tagType = "prod"
      version = $result.Value
    }

    # Results always contains 3 Groups. If the 3rd group has a value
    # then we have detected a pre-release tag.
    if ($result.Groups[2].Value) {
      $preReleaseTag = $result.Groups[2].Value
      
      # Support 'latest' tag
      $tagObject.tagType = if ($preReleaseTag -eq "latest") { "prod" } else { $preReleaseTag }
      $tagObject.version = $result.Groups[1].Value
    }

    Write-Output $tagObject
  } catch {
    Write-Error "An error occurred while evaluating a tag. This typically caused by a malformed tag string. Check your tags and try running this script again."
    throw $_.Exception
  }
}

<#
 .SYNOPSIS
  Removes all items from the SkyWire-PSInstaller local cache.
 
 .DESCRIPTION
  Permanently deletes all locally cached install files, forcing future installs to download needed packages from the server.
 
 .PARAMETER rootDir
  The root directory where all cached data resides (default: %PROGRAMDATA%\SkyWire-PSInstaller\.cache).
   
 .PARAMETER force
  If set, automatically remove the cache without prompting for confirmation.
#>

function Remove-SWCache {
  param(
    [Parameter(Mandatory=$false)]
    [string] $rootDir = "$env:PROGRAMDATA\SkyWire-PSInstaller",

    [Parameter(Mandatory=$false)]
    [switch] $force
  )

  try {
    Write-Host "Removing local SkyWire-PSInstaller cache" -ForegroundColor Green

    if ($force) {
      $confirmRemoval = 1
    } else {
      Write-Warning "This action cannot be undone!"
      $no = New-Object System.Management.Automation.Host.ChoiceDescription "&No", "Aborts the remove cache request, preserving cached files"
      $yes = New-Object System.Management.Automation.Host.ChoiceDescription "&Yes", "Removes all locally cached SkyWire-PSInstaller files"
      $options = [System.Management.Automation.Host.ChoiceDescription[]]($no, $yes)
      $promptMessage = "Are you sure you want to clear your local SkyWire-PSInstaller cache from $($rootDir)?"
      $confirmRemoval = $host.ui.PromptForChoice("Clear Local Cache", $promptMessage, $options, 0)
    }

    if ($confirmRemoval -eq 1) {
      Remove-Item -Path "$rootDir\*" -Recurse -Force -ErrorAction Ignore
    } else {
      Write-Warning "Cache removal request aborted!"
    }
  } catch {
    Write-Error "An error occurred while attempting to remove the cache. This can be caused by incorrect permissions. Try running this script as an administrator."
    throw $_.Exception
  }
}

<#
 .SYNOPSIS
  Remove a SkyWire IIS Application.
 
 .DESCRIPTION
  This is used to clean up IIS application and its related folders and files.
 
 .PARAMETER tag
  The tag (ticket number) or name for the container IIS application to be removed.
 
 .PARAMETER rootSite
  Specify the root IIS Site where the application resides (default: Default Web Site).
 
 .PARAMETER rootDir
  The parent directory of the application directory.
 
 .EXAMPLE
  Remove-SWApp -tag POS-1337-beta
 
 .EXAMPLE
  Remove-SWApp -tag POS-0000 -rootSite MyCustomSite -rootDir C:\myapps
#>

function Remove-SWApp {
  param(
    [Parameter(Mandatory=$true)]
    [string] $tag,

    [Parameter(Mandatory=$false)]
    [string] $rootSite = "Default Web Site",

    [Parameter(mandatory=$false)]
    [string] $rootDir = "C:\inetpub\wwwroot"
  )

  if (!$rootSite) {
    throw "The parameter rootSite must be provided."
  }

  if (!$rootDir) {
    throw "The parameter rootDir must be provided."
  }

  Try {
    Remove-Webapplication -name $tag -site $rootSite
  } catch {
    Write-Error "An error occurred while attempting to remove a web application. This typically occurs when you do not have permissions to access the IIS Metabase, or a specified parameter is incorrect."
    throw $_.Exception
  }
}

<#
  .SYNOPSIS
  Retrieves an install pacakge file from the cache
 
  .DESCRIPTION
  This is used to pull an install package file from the local cache, or add it to the cache if it does not exist.
 
  .PARAMETER app
  The app to retrieve an install package for. Valid values: POS, WebConfig, SRDM, Reporting, Gateway, POSDAC, SRDMDAC, REPORTINGDAC, BACPAC
 
  .PARAMETER tag
  The version of Gateway.
 
  .PARAMETER rootDir
  Specify the root directory where all data will reside for the current deployment (default: %PROGRAMDATA%\SkyWire-PSInstaller).
 
  .PARAMETER bacpacUrl
  If the app parameter is "BACPAC", the URL to download the .bacpac file from
 
  .PARAMETER refresh
  Indicates that any currently cached copy of this file should be refreshed
 
  .EXAMPLE
  Get-SWInstallPacakge -app Gateway -tag POS-1337-beta
 
  .EXAMPLE
  Get-SWInstallPacakge -app Gateway -tag POS-1337-beta -refresh
#>

function Get-SWInstallPackage {
  param(
    [Parameter(Mandatory=$true)]
    [ValidateSet("POS","WebConfig","SRDM","Reporting","Gateway","POSDAC","SRDMDAC","REPORTINGDAC","BACPAC")]
    [string] $app,

    [Parameter(Mandatory=$true)]
    [string] $tag,

    [Parameter(Mandatory=$false)]
    [string] $rootDir = "$env:PROGRAMDATA\SkyWire-PSInstaller",

    [Parameter(Mandatory=$false)]
    [string] $bacpacUrl,

    [Parameter(Mandatory=$false)]
    [switch] $refresh
  )
  try {
    if (!(Test-Path $rootDir)) {
      New-Item -Path $rootDir -ItemType Directory > $null
    }

    $tagDetail = ConvertFrom-SWTag -tag $tag

    $packageUrl = switch($app) {
      "POS" { "$env:SW_POS_DOWNLOAD_URL/$($tagDetail.tagType)/$($tagDetail.version)/Setup.exe" }
      "POSDAC" { "$env:SW_POS_DOWNLOAD_URL/$($tagDetail.tagType)/$($tagDetail.version)/SkyWire.Pos.Database/bin/Release/SkyWire.Pos.Database.dacpac" }
      "WebConfig" { "$env:SW_WEBCONFIG_DOWNLOAD_URL/$($tagDetail.tagType)/$($tagDetail.version)/WebConfig_DeployPackages.zip" }
      "Gateway" { "$env:SW_GATEWAY_DOWNLOAD_URL/$($tagDetail.tagType)/$($tagDetail.version)/SkyWire.Gateway.WebApi.zip" }
      "SRDM" { "$env:SW_WEBCONFIG_DOWNLOAD_URL/$($tagDetail.tagType)/$($tagDetail.version)/SkyWire.SRDM.WebApi.zip" }
      "SRDMDAC" { "$env:SW_POS_DOWNLOAD_URL/$($tagDetail.tagType)/$($tagDetail.version)/SkyWire.SRDM.Database/bin/Release/SkyWire.SRDM.Database.dacpac" }
      "BACPAC" { "$bacpacUrl" }
      #TODO: POS API, Reporting, ReportingDAC
    }

    $fileName = Split-Path $packageUrl -Leaf

    # Ensure Powershell can handle newer versions of TLS for web requests
    [Net.ServicePointManager]::SecurityProtocol = "tls12, tls11, tls"

    if ($app -eq "BACPAC") {
      $package = "$rootDir\$fileName"

      if ($refresh -eq $true) {
        Remove-Item $package -Force -ErrorAction Ignore
      }

      if (!(Test-Path $package)) {
        Invoke-WebRequest -Uri $packageUrl -OutFile $package
      }

    }
    elseif ($tagDetail.tagType -eq "beta") {

      $packageDir = "$rootDir\$tag"
      if (!(Test-Path $packageDir)) {
        New-Item $packageDir -ItemType Directory > $null
      }

      #If there's an old version, erase it and pull down the latest
      $package = "$packageDir\$fileName"
      Remove-Item $package -Force -ErrorAction Ignore
      Invoke-WebRequest -Uri $packageUrl -OutFile $package

    } else {
      #Does the file exist in cache?
      $cacheDir = "$rootDir\.cache"
      $packageDir = "$cacheDir\$app\$($tagDetail.version)"
      $package = "$packageDir\$fileName"

      if ($refresh -eq $true) {
        Remove-Item $packageDir -Recurse -Force -ErrorAction Ignore
      }

      if (!(Test-Path $packageDir)) {
        New-Item $packageDir -ItemType Directory > $null
      }

      if (!(Test-Path $package -PathType Leaf)) {
        Invoke-WebRequest -Uri $packageUrl -OutFile $package
      }
    }

    if ($app -eq "WebConfig") {
      #Package is contained in Zip Archive with SRDM files. Extract the archive and return just the WebDeploy package
      Expand-Archive -Path $package -DestinationPath "$packageDir\WebConfig_DeployPackages" -Force
      $package = "$packageDir\WebConfig_DeployPackages\SkyWire.WebConfig.zip"
    }
      
    Write-Output $package
  } catch {
    Write-Error "An error occurred while attempting to retrieve an install package. This typically due to permissions or a missing environment variable. Please review your configuration and try again."
    throw $_.Exception
  }
}