~speguero/powerpkg

69ed941925cb3852b1c329da05a6f12e75d623db — Steven Peguero 6 years ago 2d6c709
Fix indentation issues
2 files changed, 346 insertions(+), 346 deletions(-)

M example_package/powerpkg.ps1
M src/powerpkg.ps1
M example_package/powerpkg.ps1 => example_package/powerpkg.ps1 +173 -173
@@ 3,7 3,7 @@
	 ________  ________  ___       __   _______   ________  ________  ___  __    ________
	|\   __  \|\   __  \|\  \     |\  \|\  ___ \ |\   __  \|\   __  \|\  \|\  \ |\   ____\
	\ \  \|\  \ \  \|\  \ \  \    \ \  \ \   __/|\ \  \|\  \ \  \|\  \ \  \/  /|\ \  \___|
	 \ \   ____\ \  \\\  \ \  \  __\ \  \ \  \_|/_\ \   _  _\ \   ____\ \   ___  \ \  \  ___
	 \ \   ____\ \  \\\  \ \  \  __\ \  \ \  \_|/_\ \   _  _\ \   ____\ \   ___  \ \  \  ___  
	  \ \  \___|\ \  \\\  \ \  \|\__\_\  \ \  \_|\ \ \  \\  \\ \  \___|\ \  \\ \  \ \  \|\  \
	   \ \__\    \ \_______\ \____________\ \_______\ \__\\ _\\ \__\    \ \__\\ \__\ \_______\
	    \|__|     \|_______|\|____________|\|_______|\|__|\|__|\|__|     \|__| \|__|\|_______|


@@ 51,98 51,98 @@ $ErrorActionPreference = "Stop"

$Script = @{
	"Config" = @{
		"BlockHost"            = $Null
		"SuppressNotification" = $True
		"TotalImported"        = 0 # Retrieves number of imported package-specified script preferences.
		"ImportState"          = $Null # Reports whether or not package-specified script preferences were imported.
	}
	"CurrentDirectory"         = Split-Path -Parent -Path $MyInvocation.MyCommand.Definition
	"CurrentPSVersion"         = $Host.Version.Major
	"ExitCode"                 = 0
	"Output"                   = $Null # Used for capturing and printing general output.
		"BlockHost"				= $Null
		"SuppressNotification"	= $True
		"TotalImported"			= 0 # Retrieves number of imported package-specified script preferences.
		"ImportState"			= $Null # Reports whether or not package-specified script preferences were imported.
	}
	"CurrentDirectory"			= Split-Path -Parent -Path $MyInvocation.MyCommand.Definition
	"CurrentPSVersion"			= $Host.Version.Major
	"ExitCode"					= 0
	"Output"					= $Null # Used for capturing and printing general output.
}

$Machine = @{
	"UserspaceArchitecture" = [System.Environment]::GetEnvironmentVariable("Processor_Architecture")
	"OSVersion"             = [System.Environment]::OSVersion.Version.ToString()
	"Hostname"              = [System.Environment]::GetEnvironmentVariable("ComputerName")
	"Username"              = [System.Environment]::GetEnvironmentVariable("Username")
	"UserspaceArchitecture"	= [System.Environment]::GetEnvironmentVariable("Processor_Architecture")
	"OSVersion"				= [System.Environment]::OSVersion.Version.ToString()
	"Hostname"				= [System.Environment]::GetEnvironmentVariable("ComputerName")
	"Username"				= [System.Environment]::GetEnvironmentVariable("Username")
	"ProgramList" = @(

	                          # Registry paths that contain list of MSI program codes and version builds of installed applications:
							# Registry paths that contain list of MSI program codes and version builds of installed applications:

	                          "HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall", # x86 and AMD64 Programs
	                          "HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall" # x86 Programs (in AMD64 Userspace)
							"HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall", # x86 and AMD64 Programs
							"HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall" # x86 Programs (in AMD64 Userspace)
	)
}

$Package = @{
	"Name"                             = $MyInvocation.MyCommand.Definition.Split("\")[-2] # Name of directory this script is located in.
	"Name"								= $MyInvocation.MyCommand.Definition.Split("\")[-2] # Name of directory this script is located in.
	"Content" = @{
		"All"                          = $Null
		"Configuration"                = $Null
		"TaskEntry"                    = $Null
		"All"							= $Null
		"Configuration"					= $Null
		"TaskEntry"						= $Null
	}
	"Path"                             = ("{0}\package.xml" -f $Script.CurrentDirectory)
	"Delimiter"                        = "," # Character used for separating individual values specified in task entires.
	"Path"								= ("{0}\package.xml" -f $Script.CurrentDirectory)
	"Delimiter"							= "," # Character used for separating individual values specified in task entires.
	"TaskEntryStatus" = @{
		"Index"                        = 0
		"Successful"                   = 0
		"Unsuccessful"                 = 0
		"TotalProcessed"               = 0
		"TotalFailedButContinued"      = 0
		"Index"							= 0
		"Successful"					= 0
		"Unsuccessful"					= 0
		"TotalProcessed"				= 0
		"TotalFailedButContinued"		= 0
	}
	"TaskEntrySubparameterRegexp" = @{
		"Executable" = @{
			"Package"                  = "(\[)Package(\])" # Regexp that replaces matching string with path of package directory.
			"Package"					= "(\[)Package(\])" # Regexp that replaces matching string with path of package directory.
			"Sanitizer" = (
			                             # Regexps that remove arbitrary commands:
										# Regexps that remove arbitrary commands:

			                             "\;(.*)$",
			                             "\&(.*)$",
			                             "\|(.*)$",
			                             "(\s+)$"
										"\;(.*)$",
										"\&(.*)$",
										"\|(.*)$",
										"(\s+)$"
			)
		}
		"VerifyInstall" = @{
			                             # NOTE:
			                             #
			                             # Arg_Build cannot parse uncommonly used, non-alphanumeric characters, such as commas, on
			                             # PowerShell 2.0. Upgrade to 3.0+ to circumvent this issue.

			"Arg_Build"                = "\[Build:(.*)\]$"
			"Type_Hotfix"              = "^(\[)Hotfix(\])"
			"Type_Path"                = "^(\[)Path(\])"
			"Type_Version_FileInfo"    = "^(\[)Vers_File(\])"
			"Type_Version_ProductInfo" = "^(\[)Vers_Product(\])"
			"Type_Program"             = "^(\[)Program(\])"
			"Value_MSIGUID"            = "^\{(.*)\}$"
										# NOTE:
										#
										# Arg_Build cannot parse uncommonly used, non-alphanumeric characters, such as commas, on
										# PowerShell 2.0. Upgrade to 3.0+ to circumvent this issue.

			"Arg_Build"					= "\[Build:(.*)\]$"
			"Type_Hotfix"				= "^(\[)Hotfix(\])"
			"Type_Path"					= "^(\[)Path(\])"
			"Type_Version_FileInfo"		= "^(\[)Vers_File(\])"
			"Type_Version_ProductInfo"	= "^(\[)Vers_Product(\])"
			"Type_Program"				= "^(\[)Program(\])"
			"Value_MSIGUID"				= "^\{(.*)\}$"
		}
	}
	"Variable" = @{
		"TerminateProcess" = @{
			"AlreadyPrompted"          = $False # Ensures to only display TerminateMessage prompt once, if terminating more than one process.
			"AlreadyPrompted"			= $False # Ensures to only display TerminateMessage prompt once, if terminating more than one process.
		}
		"VerifyInstall" = @{
			"SpecifiedBuild"           = $Null
			"DiscoveredBuild"          = $Null
			"Existence"                = $Null
			"ProgramReference"         = $Null
			"SpecifiedBuild"			= $Null
			"DiscoveredBuild"			= $Null
			"Existence"					= $Null
			"ProgramReference"			= $Null
		}
	}
}

$TaskEntry = @{
	"TaskName"         = $Null
	"Executable"       = $Null
	"OperatingSystem"  = $Null
	"Architecture"     = $Null
	"TerminateProcess" = $Null
	"TerminateMessage" = $Null
	"VerifyInstall"    = $Null
	"SuccessExitCode"  = $Null
	"ContinueIfFail"   = $Null
	"SkipProcessCount" = $Null
	"TaskName"			= $Null
	"Executable"		= $Null
	"OperatingSystem"	= $Null
	"Architecture"		= $Null
	"TerminateProcess"	= $Null
	"TerminateMessage"	= $Null
	"VerifyInstall"		= $Null
	"SuccessExitCode"	= $Null
	"ContinueIfFail"	= $Null
	"SkipProcessCount"	= $Null
}

# <<<< PROPERTIES ----


@@ 158,26 158,26 @@ function Get-EnvironmentVariableValue
	)

	$Function = @{
		"EnvironmentVariableSyntax" = @{
			"Before"                = "^(\$)env(\:)"
			"After"                 = "env:\"
		"EnvironmentVariableSyntax"	= @{
			"Before"				= "^(\$)env(\:)"
			"After"					= "env:\"
		}
		"Path"                      = $Path
		"Result"                    = $Null
		"Path"						= $Path
		"Result"					= $Null
	}

	foreach ($Item in $Function.Path -split "\\")
    {
	{
		if ($Item -match $Function.EnvironmentVariableSyntax.Before)
        {
		{
			$Item = $Item -replace ($Function.EnvironmentVariableSyntax.Before, $Function.EnvironmentVariableSyntax.After)

			try
            {
			{
				$Item = (Get-Content $Item -ErrorAction Stop)
			}
			catch [Exception]
            {
			{
				continue
			}
		}


@@ 199,28 199,28 @@ function Invoke-Executable
	)

	$Invocation = @{
		"Input"                  = $Path
		"Input"						= $Path
		"Executable" = @{
			"Value"              = $Null
			"Quoted"             = "^(\"")(.*)(\"")"
			"Unquoted"           = "^(\S+)"
			"Value"					= $Null
			"Quoted"				= "^(\"")(.*)(\"")"
			"Unquoted"				= "^(\S+)"
		}
		"Arguments" = @{
			"Value"              = $Null
			"LeftwardWhitespace" = "^(\s+)(.*)"
			"Value"					= $Null
			"LeftwardWhitespace"	= "^(\s+)(.*)"
		}
	}

	# Split executable and its arguments:

	if ($Invocation.Input -match $Invocation.Executable.Quoted)
    {
	{
		$Invocation.Executable.Value = $Invocation.Input -match $Invocation.Executable.Quoted
		$Invocation.Executable.Value = $Matches[2]
		$Invocation.Arguments.Value  = $Invocation.Input -replace ($Invocation.Executable.Quoted, "")
	}
	else
    {
	{
		$Invocation.Executable.Value = $Invocation.Input -match $Invocation.Executable.Unquoted
		$Invocation.Executable.Value = $Matches[1]
		$Invocation.Arguments.Value  = $Invocation.Input -replace ($Invocation.Executable.Unquoted, "")


@@ 229,14 229,14 @@ function Invoke-Executable
	# Remove potential whitespace between executable and its arguments:

	if ($Invocation.Arguments.Value -match $Invocation.Arguments.LeftwardWhitespace)
    {
	{
		$Invocation.Arguments.Value = $Invocation.Arguments.Value -match $Invocation.Arguments.LeftwardWhitespace
		$Invocation.Arguments.Value = $Matches[2]
	}
	else {}

	try
    {
	{
		$ProcessStartInfo                        = New-Object System.Diagnostics.ProcessStartInfo
		$ProcessStartInfo.FileName               = $Invocation.Executable.Value
		$ProcessStartInfo.RedirectStandardError  = $True


@@ 257,7 257,7 @@ function Invoke-Executable
		return $Result
	}
	catch [Exception]
    {
	{
		throw
	}
}


@@ 293,7 293,7 @@ function Show-BalloonTip
	Add-Type -AssemblyName System.Windows.Forms

	if ($Script:Balloon -eq $Null)
    {
	{
		$Script:Balloon = New-Object System.Windows.Forms.NotifyIcon
	}



@@ 346,20 346,20 @@ function Write-Result
	[String]$Result = ""

	if ($Output -notmatch "^$")
    {
	{
		if ($AddNewLine)
        {
		{
			$Result += ("{0}`n`n" -f $Output)
		}
		else
        {
		{
			$Result += ("{0}`n" -f $Output)
		}
	}
	else {}

	if ($Code -notmatch "^$")
    {
	{
		$Code = (": ({0})" -f $Code)
	}



@@ 377,13 377,13 @@ function Write-Result
try
{
	if (Test-Path $Package.Path)
    {
	{
		[XML]$Package.Content.All      = Get-Content $Package.Path
		$Package.Content.Configuration = $Package.Content.All.Package.Configuration
		$Package.Content.TaskEntry     = $Package.Content.All.Package.TaskEntry
	}
	else
    {
	{
		throw "No package file was present within the package directory."
	}
}


@@ 449,13 449,13 @@ $Package += @{
foreach ($ImportedHostname in $Script.Config.BlockHost)
{
	if ($Machine.Hostname -match $ImportedHostname -and $ImportedHostname -notmatch "^$")
    {
	{
		Write-Host -ForegroundColor Red ("`nERROR: Package ""{0}"" will not be processed, as this host is blocked.`n" -f $Package.Name)

		[Environment]::Exit(4)
	}
	else
    {
	{
		pass
	}
}


@@ 477,7 477,7 @@ Write-Host -ForegroundColor Cyan (
foreach ($Item in $Package.Content.TaskEntry)
{
	try
    {
	{
		$TaskEntry.TaskName         = $Item.TaskName
		$TaskEntry.Executable       = $Item.Executable
		$TaskEntry.OperatingSystem  = $Item.OperatingSystem


@@ 490,7 490,7 @@ foreach ($Item in $Package.Content.TaskEntry)
		$TaskEntry.SkipProcessCount = $Item.SkipProcessCount
	}
	catch [Exception]
    {
	{
		$Script.Output = ("`nTask Entry ({0}): {1}" -f $TaskEntry.TaskName, $Error[0])
		Write-Host -ForegroundColor Red (Write-Result -Status "ERROR" -Code 3 -Output $Script.Output -AddNewLine)



@@ 503,7 503,7 @@ foreach ($Item in $Package.Content.TaskEntry)
	$Package.TaskEntryStatus.Index = $Package.TaskEntryStatus.Index + 1

	if ($TaskEntry.TaskName -match "^$" -or $TaskEntry.TaskName -match "^(\s+)$")
    {
	{
		$Script.Output = ("`nTaskName: Specification is required for ""{0}"" at Task Entry {1}." -f $TaskEntry.Executable, [String]$Package.TaskEntryStatus.Index)
		Write-Host -ForegroundColor Red (Write-Result -Status "ERROR" -Code 7 -Output $Script.Output -AddNewLine)



@@ 511,18 511,18 @@ foreach ($Item in $Package.Content.TaskEntry)
		break
	}
	elseif ($TaskEntry.TaskName -match "^\#")
    {
	{
		continue
	}
	else
    {
	{
		pass
	}

	# ---- Executable Parameter >>>>

	if ($TaskEntry.Executable -match "^$" -or $TaskEntry.Executable -match "^(\s+)$")
    {
	{
		$Script.Output = ("`nExecutable: Specification is required for ""{0}"" at Task Entry {1}." -f $TaskEntry.TaskName, [String]$Package.TaskEntryStatus.Index)
		Write-Host -ForegroundColor Red (Write-Result -Status "ERROR" -Code 7 -Output $Script.Output -AddNewLine)



@@ 530,18 530,18 @@ foreach ($Item in $Package.Content.TaskEntry)
		break
	}
	elseif ($TaskEntry.Executable -match $Package.TaskEntrySubparameterRegexp.Executable.Package)
    {
	{
		$TaskEntry.Executable = $TaskEntry.Executable -Replace ($Package.TaskEntrySubparameterRegexp.Executable.Package, ("{0}\" -f $Script.CurrentDirectory))
	}
	else
    {
	{
		pass
	}

	# The following loop prevents execution of arbitrary commands:

	foreach ($Item in $Package.TaskEntrySubparameterRegexp.Executable.Sanitizer)
    {
	{
		$TaskEntry.Executable = $TaskEntry.Executable -replace ($Item, "")
	}



@@ 553,15 553,15 @@ foreach ($Item in $Package.Content.TaskEntry)
	# ---- OperatingSystem Parameter >>>>

	if ($TaskEntry.OperatingSystem -match "^$")
    {
	{
		pass
	}
	elseif ($Machine.OSVersion -match $TaskEntry.OperatingSystem)
    {
	{
		pass
	}
	else
    {
	{
		$Script.Output = ("OperatingSystem: ""{0}"" is a requirement." -f $TaskEntry.OperatingSystem)

		Write-Host -ForegroundColor Yellow (Write-Result -Status "SKIP" -Output $Script.Output -AddNewLine)


@@ 571,15 571,15 @@ foreach ($Item in $Package.Content.TaskEntry)
	# ---- Architecture Parameter >>>>

	if ($TaskEntry.Architecture -match "^$")
    {
	{
		pass
	}
	elseif ($TaskEntry.Architecture -match $Machine.UserspaceArchitecture)
    {
	{
		pass
	}
	else
    {
	{
		$Script.Output = ("Architecture: ""{0}"" is a requirement." -f $TaskEntry.Architecture)

		Write-Host -ForegroundColor Yellow (Write-Result -Status "SKIP" -Output $Script.Output -AddNewLine)


@@ 591,19 591,19 @@ foreach ($Item in $Package.Content.TaskEntry)
	# ---- VerifyInstall Parameter (Type_Hotfix Subparameter) >>>>

	if ($TaskEntry.VerifyInstall -match $Package.TaskEntrySubparameterRegexp.VerifyInstall.Type_Hotfix)
    {
	{
		$TaskEntry.VerifyInstall                  = $TaskEntry.VerifyInstall -replace ($Package.TaskEntrySubparameterRegexp.VerifyInstall.Type_Hotfix, "")
		$Package.Variable.VerifyInstall.Existence = Get-Hotfix | ? {$_.HotfixID -eq $TaskEntry.VerifyInstall}

		if ($Package.Variable.VerifyInstall.Existence -ne $Null)
        {
		{
			$Script.Output = ("VerifyInstall: [Hotfix] ""{0}"" exists." -f $TaskEntry.VerifyInstall)

			Write-Host -ForegroundColor Yellow (Write-Result -Status "SKIP" -Output $Script.Output -AddNewLine)
			continue
		}
		else
        {
		{
			pass
		}
	}


@@ 611,20 611,20 @@ foreach ($Item in $Package.Content.TaskEntry)
	# ---- VerifyInstall Parameter (Type_Path Subparameter) >>>>

	elseif ($TaskEntry.VerifyInstall -match $Package.TaskEntrySubparameterRegexp.VerifyInstall.Type_Path)
    {
	{
		$TaskEntry.VerifyInstall                  = $TaskEntry.VerifyInstall -replace ($Package.TaskEntrySubparameterRegexp.VerifyInstall.Type_Path, "")
		$TaskEntry.VerifyInstall                  = Get-EnvironmentVariableValue -Path $TaskEntry.VerifyInstall
		$Package.Variable.VerifyInstall.Existence = Test-Path $TaskEntry.VerifyInstall

		if ($Package.Variable.VerifyInstall.Existence -eq $True)
        {
		{
			$Script.Output = ("VerifyInstall: [Path] ""{0}"" exists." -f $TaskEntry.VerifyInstall)

			Write-Host -ForegroundColor Yellow (Write-Result -Status "SKIP" -Output $Script.Output -AddNewLine)
			continue
		}
		else
        {
		{
			pass
		}
	}


@@ 632,11 632,11 @@ foreach ($Item in $Package.Content.TaskEntry)
	# ---- VerifyInstall Parameter (Version_FileInfo Subparameter) >>>>

	elseif ($TaskEntry.VerifyInstall -match $Package.TaskEntrySubparameterRegexp.VerifyInstall.Type_Version_FileInfo)
    {
	{
		$TaskEntry.VerifyInstall = $TaskEntry.VerifyInstall -replace ($Package.TaskEntrySubparameterRegexp.VerifyInstall.Type_Version_FileInfo, "")

		try
        {
		{
			# Separates Arg_Build (version/build number) and VerifyInstall value (file path):

			$TaskEntry.VerifyInstall -match $Package.TaskEntrySubparameterRegexp.VerifyInstall.Arg_Build | Out-Null


@@ 649,19 649,19 @@ foreach ($Item in $Package.Content.TaskEntry)
			# Determines as to whether or not both Arg_Build and version/build number of VerifyInstall value (file path) match:

			if ($Package.Variable.VerifyInstall.SpecifiedBuild -eq $Package.Variable.VerifyInstall.DiscoveredBuild)
            {
			{
				$Script.Output = ("VerifyInstall: [Vers_File] ""{0}"" exists." -f $Package.Variable.VerifyInstall.SpecifiedBuild)

				Write-Host -ForegroundColor Yellow (Write-Result -Status "SKIP" -Output $Script.Output -AddNewLine)
				continue
			}
			else
            {
			{
				throw
			}
		}
		catch [Exception]
        {
		{
			pass
		}
	}


@@ 669,11 669,11 @@ foreach ($Item in $Package.Content.TaskEntry)
	# ---- VerifyInstall Parameter (Version_ProductInfo Subparameter) >>>>

	elseif ($TaskEntry.VerifyInstall -match $Package.TaskEntrySubparameterRegexp.VerifyInstall.Type_Version_ProductInfo)
    {
	{
		$TaskEntry.VerifyInstall = $TaskEntry.VerifyInstall -replace ($Package.TaskEntrySubparameterRegexp.VerifyInstall.Type_Version_ProductInfo, "")

		try
        {
		{
			# Separates Arg_Build (version/build number) and VerifyInstall value (file path):

			$TaskEntry.VerifyInstall -match $Package.TaskEntrySubparameterRegexp.VerifyInstall.Arg_Build | Out-Null


@@ 686,19 686,19 @@ foreach ($Item in $Package.Content.TaskEntry)
			# Determines as to whether or not both Arg_Build and version/build number of VerifyInstall value (file path) match:

			if ($Package.Variable.VerifyInstall.SpecifiedBuild -eq $Package.Variable.VerifyInstall.DiscoveredBuild)
            {
			{
				$Script.Output = ("VerifyInstall: [Vers_Product] ""{0}"" exists." -f $Package.Variable.VerifyInstall.SpecifiedBuild)

				Write-Host -ForegroundColor Yellow (Write-Result -Status "SKIP" -Output $Script.Output -AddNewLine)
				continue
			}
			else
            {
			{
				throw
			}
		}
		catch [Exception]
        {
		{
			pass
		}
	}


@@ 706,37 706,37 @@ foreach ($Item in $Package.Content.TaskEntry)
	# ---- VerifyInstall Parameter (Type_Program Subparameter) >>>>

	elseif ($TaskEntry.VerifyInstall -match $Package.TaskEntrySubparameterRegexp.VerifyInstall.Type_Program)
    {
	{
		$TaskEntry.VerifyInstall = $TaskEntry.VerifyInstall -replace ($Package.TaskEntrySubparameterRegexp.VerifyInstall.Type_Program, "")

		try
        {
		{
			if ($TaskEntry.VerifyInstall -notmatch $Package.TaskEntrySubparameterRegexp.VerifyInstall.Arg_Build) # If the VerifyInstall value does not contain the Arg_Build argument.
            {
			{

				# Determines whether or not VerifyInstall value is a MSI GUID, in order to reference the correct property:

				if ($TaskEntry.VerifyInstall -match $Package.TaskEntrySubparameterRegexp.VerifyInstall.Value_MSIGUID)
                {
				{
					$Package.Variable.VerifyInstall.ProgramReference = "PSChildName"
				}
				else
                {
				{
					$Package.Variable.VerifyInstall.ProgramReference = "DisplayName"
				}

				# Searches the registry for possible program name or MSI GUID that matches VerifyInstall value:

				foreach ($Path in $Machine.ProgramList)
                {
				{
					if (Test-Path $Path)
                    {
					{
						$Package.Variable.VerifyInstall.Existence += @(
							Get-ChildItem $Path | % {Get-ItemProperty $_.PSPath} | ? {$_.$($Package.Variable.VerifyInstall.ProgramReference) -eq $TaskEntry.VerifyInstall} | % {$_.DisplayName}
						)
					}
					else
                    {
					{
						pass
					}
				}


@@ 744,19 744,19 @@ foreach ($Item in $Package.Content.TaskEntry)
				# Determines as to whether or not a matching program code or MSI GUID was found:

				if ($Package.Variable.VerifyInstall.Existence -ne $Null)
                {
				{
					$Script.Output = ("VerifyInstall: [Program] ""{0}"" exists." -f $TaskEntry.VerifyInstall)

					Write-Host -ForegroundColor Yellow (Write-Result -Status "SKIP" -Output $Script.Output -AddNewLine)
					continue
				}
				else
                {
				{
					throw
				}
			}
			else
            {
			{
				# Separates Arg_Build (version/build number) and VerifyInstall value (program name/MSI GUID):

				$TaskEntry.VerifyInstall -match $Package.TaskEntrySubparameterRegexp.VerifyInstall.Arg_Build | Out-Null


@@ 766,26 766,26 @@ foreach ($Item in $Package.Content.TaskEntry)
				# Determines whether or not VerifyInstall value is a MSI GUID, in order to reference the correct property:

				if ($TaskEntry.VerifyInstall -match $Package.TaskEntrySubparameterRegexp.VerifyInstall.Value_MSIGUID)
                {
				{
					$Package.Variable.VerifyInstall.ProgramReference = "PSChildName"
				}
				else
                {
				{
					$Package.Variable.VerifyInstall.ProgramReference = "DisplayName"
				}

				# Searches the registry for possible program name/MSI GUID that matches VerifyInstall value:

				foreach ($Path in $Machine.ProgramList)
                {
				{
					if (Test-Path $Path)
                    {
					{
						$Package.Variable.VerifyInstall.DiscoveredBuild += @(
							Get-ChildItem $Path | % {Get-ItemProperty $_.PSPath} | ? {$_.$($Package.Variable.VerifyInstall.ProgramReference) -eq $TaskEntry.VerifyInstall} | % {$_.DisplayVersion}
						)
					}
					else
                    {
					{
						pass
					}
				}


@@ 793,25 793,25 @@ foreach ($Item in $Package.Content.TaskEntry)
				# Determines whether or not there is a match between a discovered program name/MSI GUID's respective version/build number and Arg_Build:

				if ($Package.Variable.VerifyInstall.DiscoveredBuild -contains $Package.Variable.VerifyInstall.SpecifiedBuild)
                {
				{
					$Script.Output = ("VerifyInstall: [Program] ""{0}"" exists." -f $Package.Variable.VerifyInstall.SpecifiedBuild)

					Write-Host -ForegroundColor Yellow (Write-Result -Status "SKIP" -Output $Script.Output -AddNewLine)
					continue
				}
				else
                {
				{
					throw
				}
			}
		}
		catch [Exception]
        {
		{
			pass
		}
	}
	else
    {
	{
		pass
	}



@@ 820,53 820,53 @@ foreach ($Item in $Package.Content.TaskEntry)
	# ---- TerminateProcess Parameter >>>>

	if ($TaskEntry.TerminateProcess -notmatch "^$")
    {
	{
		$TaskEntry.TerminateProcess = $TaskEntry.TerminateProcess -split ($Package.Delimiter)

		foreach ($Process in $TaskEntry.TerminateProcess)
        {
		{
			try
            {
			{
				if (Get-Process $Process)
                {
				{
					pass
				}
				else
                {
				{
					continue
				}

				if ($TaskEntry.TerminateMessage -notmatch "^$" -and $Package.Variable.TerminateProcess.AlreadyPrompted -eq $False)
                {
				{
					Show-DialogBox -Title $Package.Name -Message $TaskEntry.TerminateMessage | Out-Null
					$Package.Variable.TerminateProcess.AlreadyPrompted = $True
				}
				else
                {
				{
					pass
				}

				Get-Process $Process | Stop-Process -Force
			}
			catch [Exception]
            {
			{
				continue
			}
		}
	}
	else
    {
	{
		pass
	}

	# ---- SuccessExitCode Parameter >>>>

	if ($TaskEntry.SuccessExitCode -eq $Null)
    {
	{
		$TaskEntry.SuccessExitCode = 0
	}
	else
    {
	{
		$TaskEntry.SuccessExitCode  = $TaskEntry.SuccessExitCode -split ($Package.Delimiter)
		$TaskEntry.SuccessExitCode += 0
	}


@@ 874,68 874,68 @@ foreach ($Item in $Package.Content.TaskEntry)
	# ---- Executable Invocation Processing >>>>

	try
    {
	{
		$Script.Output = (Invoke-Executable -Path $TaskEntry.Executable)

		if ($TaskEntry.SuccessExitCode -contains $Script.Output.ExitCode)
        {
		{
			Write-Host -ForegroundColor Green (Write-Result -Status "OK" -Code $Script.Output.ExitCode -Output $Script.Output.Output)

			if ($TaskEntry.SkipProcessCount -ne "true")
            {
			{
				$Package.TaskEntryStatus.Successful++
			}
			else
            {
			{
				continue
			}
		}
		else
        {
		{
			Write-Host -ForegroundColor Red (Write-Result -Status "WARN" -Code $Script.Output.ExitCode -Output $Script.Output.Output)

			if ($TaskEntry.SkipProcessCount -ne "true")
            {
			{
				$Package.TaskEntryStatus.Unsuccessful++
			}
			else
            {
			{
				pass
			}

			if ($TaskEntry.ContinueIfFail -ne "true")
            {
			{
				$Script.ExitCode = 1
				break
			}
			else
            {
			{
				$Package.TaskEntryStatus.TotalFailedButContinued++
				continue
			}
		}
	}
	catch [Exception]
    {
	{
		$Script.Output = ("Executable Invocation: {0}" -f $Error[0])
		Write-Host -ForegroundColor Red (Write-Result -Status "ERROR" -Code 2 -Output $Script.Output -AddNewLine)

		if ($TaskEntry.SkipProcessCount -ne "true")
        {
		{
			$Package.TaskEntryStatus.Unsuccessful++
		}
		else
        {
		{
			pass
		}

		if ($TaskEntry.ContinueIfFail -ne "true")
        {
		{
			$Script.ExitCode = 2
			break
		}
		else
        {
		{
			$Package.TaskEntryStatus.TotalFailedButContinued++
			continue
		}


@@ 949,11 949,11 @@ if ($Package.TaskEntryStatus.Successful -eq 0 -and $Package.TaskEntryStatus.Unsu
	Write-Host -ForegroundColor Red "`nWARN: No task entries were processed.`n"

	if ($Script.ExitCode -eq 0)
    {
	{
		$Script.ExitCode = 6
	}
	else
    {
	{
		pass
	}
}


@@ 965,7 965,7 @@ else
		"`nTasks Processed : " + $Package.TaskEntryStatus.TotalProcessed + `
		"`n  ^"                                                          + `
		"`n  |"                                                          + `
		"`n  |---- Success : " + $Package.TaskEntryStatus.Successful     + `
		"`n  |---- Success : " + $Package.TaskEntryStatus.Successful	 + `
		"`n  +---- Failure : " + $Package.TaskEntryStatus.Unsuccessful   + `
		"`n"
	)


@@ 973,22 973,22 @@ else
	Write-Host ("`nPackage Results ({0}):" -f $Package.Name)

	if ($Script.ExitCode -eq 0)
    {
	{
		$Script.Output += ("`nOK: ({0})`n" -f $Script.ExitCode)

		Write-Host -ForegroundColor Green $Script.Output

		if ($Script.Config.SuppressNotification -eq $False)
        {
		{
			Show-BalloonTip -Title $Package.Notification.Header -Text $Package.Notification.Footer | Out-Null
		}
		else
        {
		{
			pass
		}
	}
	else
    {
	{
		$Script.Output += ("`nERROR: ({0})`n" -f $Script.ExitCode)

		Write-Host -ForegroundColor Red $Script.Output

M src/powerpkg.ps1 => src/powerpkg.ps1 +173 -173
@@ 3,7 3,7 @@
	 ________  ________  ___       __   _______   ________  ________  ___  __    ________
	|\   __  \|\   __  \|\  \     |\  \|\  ___ \ |\   __  \|\   __  \|\  \|\  \ |\   ____\
	\ \  \|\  \ \  \|\  \ \  \    \ \  \ \   __/|\ \  \|\  \ \  \|\  \ \  \/  /|\ \  \___|
	 \ \   ____\ \  \\\  \ \  \  __\ \  \ \  \_|/_\ \   _  _\ \   ____\ \   ___  \ \  \  ___
	 \ \   ____\ \  \\\  \ \  \  __\ \  \ \  \_|/_\ \   _  _\ \   ____\ \   ___  \ \  \  ___  
	  \ \  \___|\ \  \\\  \ \  \|\__\_\  \ \  \_|\ \ \  \\  \\ \  \___|\ \  \\ \  \ \  \|\  \
	   \ \__\    \ \_______\ \____________\ \_______\ \__\\ _\\ \__\    \ \__\\ \__\ \_______\
	    \|__|     \|_______|\|____________|\|_______|\|__|\|__|\|__|     \|__| \|__|\|_______|


@@ 51,98 51,98 @@ $ErrorActionPreference = "Stop"

$Script = @{
	"Config" = @{
		"BlockHost"            = $Null
		"SuppressNotification" = $True
		"TotalImported"        = 0 # Retrieves number of imported package-specified script preferences.
		"ImportState"          = $Null # Reports whether or not package-specified script preferences were imported.
	}
	"CurrentDirectory"         = Split-Path -Parent -Path $MyInvocation.MyCommand.Definition
	"CurrentPSVersion"         = $Host.Version.Major
	"ExitCode"                 = 0
	"Output"                   = $Null # Used for capturing and printing general output.
		"BlockHost"				= $Null
		"SuppressNotification"	= $True
		"TotalImported"			= 0 # Retrieves number of imported package-specified script preferences.
		"ImportState"			= $Null # Reports whether or not package-specified script preferences were imported.
	}
	"CurrentDirectory"			= Split-Path -Parent -Path $MyInvocation.MyCommand.Definition
	"CurrentPSVersion"			= $Host.Version.Major
	"ExitCode"					= 0
	"Output"					= $Null # Used for capturing and printing general output.
}

$Machine = @{
	"UserspaceArchitecture" = [System.Environment]::GetEnvironmentVariable("Processor_Architecture")
	"OSVersion"             = [System.Environment]::OSVersion.Version.ToString()
	"Hostname"              = [System.Environment]::GetEnvironmentVariable("ComputerName")
	"Username"              = [System.Environment]::GetEnvironmentVariable("Username")
	"UserspaceArchitecture"	= [System.Environment]::GetEnvironmentVariable("Processor_Architecture")
	"OSVersion"				= [System.Environment]::OSVersion.Version.ToString()
	"Hostname"				= [System.Environment]::GetEnvironmentVariable("ComputerName")
	"Username"				= [System.Environment]::GetEnvironmentVariable("Username")
	"ProgramList" = @(

	                          # Registry paths that contain list of MSI program codes and version builds of installed applications:
							# Registry paths that contain list of MSI program codes and version builds of installed applications:

	                          "HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall", # x86 and AMD64 Programs
	                          "HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall" # x86 Programs (in AMD64 Userspace)
							"HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall", # x86 and AMD64 Programs
							"HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall" # x86 Programs (in AMD64 Userspace)
	)
}

$Package = @{
	"Name"                             = $MyInvocation.MyCommand.Definition.Split("\")[-2] # Name of directory this script is located in.
	"Name"								= $MyInvocation.MyCommand.Definition.Split("\")[-2] # Name of directory this script is located in.
	"Content" = @{
		"All"                          = $Null
		"Configuration"                = $Null
		"TaskEntry"                    = $Null
		"All"							= $Null
		"Configuration"					= $Null
		"TaskEntry"						= $Null
	}
	"Path"                             = ("{0}\package.xml" -f $Script.CurrentDirectory)
	"Delimiter"                        = "," # Character used for separating individual values specified in task entires.
	"Path"								= ("{0}\package.xml" -f $Script.CurrentDirectory)
	"Delimiter"							= "," # Character used for separating individual values specified in task entires.
	"TaskEntryStatus" = @{
		"Index"                        = 0
		"Successful"                   = 0
		"Unsuccessful"                 = 0
		"TotalProcessed"               = 0
		"TotalFailedButContinued"      = 0
		"Index"							= 0
		"Successful"					= 0
		"Unsuccessful"					= 0
		"TotalProcessed"				= 0
		"TotalFailedButContinued"		= 0
	}
	"TaskEntrySubparameterRegexp" = @{
		"Executable" = @{
			"Package"                  = "(\[)Package(\])" # Regexp that replaces matching string with path of package directory.
			"Package"					= "(\[)Package(\])" # Regexp that replaces matching string with path of package directory.
			"Sanitizer" = (
			                             # Regexps that remove arbitrary commands:
										# Regexps that remove arbitrary commands:

			                             "\;(.*)$",
			                             "\&(.*)$",
			                             "\|(.*)$",
			                             "(\s+)$"
										"\;(.*)$",
										"\&(.*)$",
										"\|(.*)$",
										"(\s+)$"
			)
		}
		"VerifyInstall" = @{
			                             # NOTE:
			                             #
			                             # Arg_Build cannot parse uncommonly used, non-alphanumeric characters, such as commas, on
			                             # PowerShell 2.0. Upgrade to 3.0+ to circumvent this issue.

			"Arg_Build"                = "\[Build:(.*)\]$"
			"Type_Hotfix"              = "^(\[)Hotfix(\])"
			"Type_Path"                = "^(\[)Path(\])"
			"Type_Version_FileInfo"    = "^(\[)Vers_File(\])"
			"Type_Version_ProductInfo" = "^(\[)Vers_Product(\])"
			"Type_Program"             = "^(\[)Program(\])"
			"Value_MSIGUID"            = "^\{(.*)\}$"
										# NOTE:
										#
										# Arg_Build cannot parse uncommonly used, non-alphanumeric characters, such as commas, on
										# PowerShell 2.0. Upgrade to 3.0+ to circumvent this issue.

			"Arg_Build"					= "\[Build:(.*)\]$"
			"Type_Hotfix"				= "^(\[)Hotfix(\])"
			"Type_Path"					= "^(\[)Path(\])"
			"Type_Version_FileInfo"		= "^(\[)Vers_File(\])"
			"Type_Version_ProductInfo"	= "^(\[)Vers_Product(\])"
			"Type_Program"				= "^(\[)Program(\])"
			"Value_MSIGUID"				= "^\{(.*)\}$"
		}
	}
	"Variable" = @{
		"TerminateProcess" = @{
			"AlreadyPrompted"          = $False # Ensures to only display TerminateMessage prompt once, if terminating more than one process.
			"AlreadyPrompted"			= $False # Ensures to only display TerminateMessage prompt once, if terminating more than one process.
		}
		"VerifyInstall" = @{
			"SpecifiedBuild"           = $Null
			"DiscoveredBuild"          = $Null
			"Existence"                = $Null
			"ProgramReference"         = $Null
			"SpecifiedBuild"			= $Null
			"DiscoveredBuild"			= $Null
			"Existence"					= $Null
			"ProgramReference"			= $Null
		}
	}
}

$TaskEntry = @{
	"TaskName"         = $Null
	"Executable"       = $Null
	"OperatingSystem"  = $Null
	"Architecture"     = $Null
	"TerminateProcess" = $Null
	"TerminateMessage" = $Null
	"VerifyInstall"    = $Null
	"SuccessExitCode"  = $Null
	"ContinueIfFail"   = $Null
	"SkipProcessCount" = $Null
	"TaskName"			= $Null
	"Executable"		= $Null
	"OperatingSystem"	= $Null
	"Architecture"		= $Null
	"TerminateProcess"	= $Null
	"TerminateMessage"	= $Null
	"VerifyInstall"		= $Null
	"SuccessExitCode"	= $Null
	"ContinueIfFail"	= $Null
	"SkipProcessCount"	= $Null
}

# <<<< PROPERTIES ----


@@ 158,26 158,26 @@ function Get-EnvironmentVariableValue
	)

	$Function = @{
		"EnvironmentVariableSyntax" = @{
			"Before"                = "^(\$)env(\:)"
			"After"                 = "env:\"
		"EnvironmentVariableSyntax"	= @{
			"Before"				= "^(\$)env(\:)"
			"After"					= "env:\"
		}
		"Path"                      = $Path
		"Result"                    = $Null
		"Path"						= $Path
		"Result"					= $Null
	}

	foreach ($Item in $Function.Path -split "\\")
    {
	{
		if ($Item -match $Function.EnvironmentVariableSyntax.Before)
        {
		{
			$Item = $Item -replace ($Function.EnvironmentVariableSyntax.Before, $Function.EnvironmentVariableSyntax.After)

			try
            {
			{
				$Item = (Get-Content $Item -ErrorAction Stop)
			}
			catch [Exception]
            {
			{
				continue
			}
		}


@@ 199,28 199,28 @@ function Invoke-Executable
	)

	$Invocation = @{
		"Input"                  = $Path
		"Input"						= $Path
		"Executable" = @{
			"Value"              = $Null
			"Quoted"             = "^(\"")(.*)(\"")"
			"Unquoted"           = "^(\S+)"
			"Value"					= $Null
			"Quoted"				= "^(\"")(.*)(\"")"
			"Unquoted"				= "^(\S+)"
		}
		"Arguments" = @{
			"Value"              = $Null
			"LeftwardWhitespace" = "^(\s+)(.*)"
			"Value"					= $Null
			"LeftwardWhitespace"	= "^(\s+)(.*)"
		}
	}

	# Split executable and its arguments:

	if ($Invocation.Input -match $Invocation.Executable.Quoted)
    {
	{
		$Invocation.Executable.Value = $Invocation.Input -match $Invocation.Executable.Quoted
		$Invocation.Executable.Value = $Matches[2]
		$Invocation.Arguments.Value  = $Invocation.Input -replace ($Invocation.Executable.Quoted, "")
	}
	else
    {
	{
		$Invocation.Executable.Value = $Invocation.Input -match $Invocation.Executable.Unquoted
		$Invocation.Executable.Value = $Matches[1]
		$Invocation.Arguments.Value  = $Invocation.Input -replace ($Invocation.Executable.Unquoted, "")


@@ 229,14 229,14 @@ function Invoke-Executable
	# Remove potential whitespace between executable and its arguments:

	if ($Invocation.Arguments.Value -match $Invocation.Arguments.LeftwardWhitespace)
    {
	{
		$Invocation.Arguments.Value = $Invocation.Arguments.Value -match $Invocation.Arguments.LeftwardWhitespace
		$Invocation.Arguments.Value = $Matches[2]
	}
	else {}

	try
    {
	{
		$ProcessStartInfo                        = New-Object System.Diagnostics.ProcessStartInfo
		$ProcessStartInfo.FileName               = $Invocation.Executable.Value
		$ProcessStartInfo.RedirectStandardError  = $True


@@ 257,7 257,7 @@ function Invoke-Executable
		return $Result
	}
	catch [Exception]
    {
	{
		throw
	}
}


@@ 293,7 293,7 @@ function Show-BalloonTip
	Add-Type -AssemblyName System.Windows.Forms

	if ($Script:Balloon -eq $Null)
    {
	{
		$Script:Balloon = New-Object System.Windows.Forms.NotifyIcon
	}



@@ 346,20 346,20 @@ function Write-Result
	[String]$Result = ""

	if ($Output -notmatch "^$")
    {
	{
		if ($AddNewLine)
        {
		{
			$Result += ("{0}`n`n" -f $Output)
		}
		else
        {
		{
			$Result += ("{0}`n" -f $Output)
		}
	}
	else {}

	if ($Code -notmatch "^$")
    {
	{
		$Code = (": ({0})" -f $Code)
	}



@@ 377,13 377,13 @@ function Write-Result
try
{
	if (Test-Path $Package.Path)
    {
	{
		[XML]$Package.Content.All      = Get-Content $Package.Path
		$Package.Content.Configuration = $Package.Content.All.Package.Configuration
		$Package.Content.TaskEntry     = $Package.Content.All.Package.TaskEntry
	}
	else
    {
	{
		throw "No package file was present within the package directory."
	}
}


@@ 449,13 449,13 @@ $Package += @{
foreach ($ImportedHostname in $Script.Config.BlockHost)
{
	if ($Machine.Hostname -match $ImportedHostname -and $ImportedHostname -notmatch "^$")
    {
	{
		Write-Host -ForegroundColor Red ("`nERROR: Package ""{0}"" will not be processed, as this host is blocked.`n" -f $Package.Name)

		[Environment]::Exit(4)
	}
	else
    {
	{
		pass
	}
}


@@ 477,7 477,7 @@ Write-Host -ForegroundColor Cyan (
foreach ($Item in $Package.Content.TaskEntry)
{
	try
    {
	{
		$TaskEntry.TaskName         = $Item.TaskName
		$TaskEntry.Executable       = $Item.Executable
		$TaskEntry.OperatingSystem  = $Item.OperatingSystem


@@ 490,7 490,7 @@ foreach ($Item in $Package.Content.TaskEntry)
		$TaskEntry.SkipProcessCount = $Item.SkipProcessCount
	}
	catch [Exception]
    {
	{
		$Script.Output = ("`nTask Entry ({0}): {1}" -f $TaskEntry.TaskName, $Error[0])
		Write-Host -ForegroundColor Red (Write-Result -Status "ERROR" -Code 3 -Output $Script.Output -AddNewLine)



@@ 503,7 503,7 @@ foreach ($Item in $Package.Content.TaskEntry)
	$Package.TaskEntryStatus.Index = $Package.TaskEntryStatus.Index + 1

	if ($TaskEntry.TaskName -match "^$" -or $TaskEntry.TaskName -match "^(\s+)$")
    {
	{
		$Script.Output = ("`nTaskName: Specification is required for ""{0}"" at Task Entry {1}." -f $TaskEntry.Executable, [String]$Package.TaskEntryStatus.Index)
		Write-Host -ForegroundColor Red (Write-Result -Status "ERROR" -Code 7 -Output $Script.Output -AddNewLine)



@@ 511,18 511,18 @@ foreach ($Item in $Package.Content.TaskEntry)
		break
	}
	elseif ($TaskEntry.TaskName -match "^\#")
    {
	{
		continue
	}
	else
    {
	{
		pass
	}

	# ---- Executable Parameter >>>>

	if ($TaskEntry.Executable -match "^$" -or $TaskEntry.Executable -match "^(\s+)$")
    {
	{
		$Script.Output = ("`nExecutable: Specification is required for ""{0}"" at Task Entry {1}." -f $TaskEntry.TaskName, [String]$Package.TaskEntryStatus.Index)
		Write-Host -ForegroundColor Red (Write-Result -Status "ERROR" -Code 7 -Output $Script.Output -AddNewLine)



@@ 530,18 530,18 @@ foreach ($Item in $Package.Content.TaskEntry)
		break
	}
	elseif ($TaskEntry.Executable -match $Package.TaskEntrySubparameterRegexp.Executable.Package)
    {
	{
		$TaskEntry.Executable = $TaskEntry.Executable -Replace ($Package.TaskEntrySubparameterRegexp.Executable.Package, ("{0}\" -f $Script.CurrentDirectory))
	}
	else
    {
	{
		pass
	}

	# The following loop prevents execution of arbitrary commands:

	foreach ($Item in $Package.TaskEntrySubparameterRegexp.Executable.Sanitizer)
    {
	{
		$TaskEntry.Executable = $TaskEntry.Executable -replace ($Item, "")
	}



@@ 553,15 553,15 @@ foreach ($Item in $Package.Content.TaskEntry)
	# ---- OperatingSystem Parameter >>>>

	if ($TaskEntry.OperatingSystem -match "^$")
    {
	{
		pass
	}
	elseif ($Machine.OSVersion -match $TaskEntry.OperatingSystem)
    {
	{
		pass
	}
	else
    {
	{
		$Script.Output = ("OperatingSystem: ""{0}"" is a requirement." -f $TaskEntry.OperatingSystem)

		Write-Host -ForegroundColor Yellow (Write-Result -Status "SKIP" -Output $Script.Output -AddNewLine)


@@ 571,15 571,15 @@ foreach ($Item in $Package.Content.TaskEntry)
	# ---- Architecture Parameter >>>>

	if ($TaskEntry.Architecture -match "^$")
    {
	{
		pass
	}
	elseif ($TaskEntry.Architecture -match $Machine.UserspaceArchitecture)
    {
	{
		pass
	}
	else
    {
	{
		$Script.Output = ("Architecture: ""{0}"" is a requirement." -f $TaskEntry.Architecture)

		Write-Host -ForegroundColor Yellow (Write-Result -Status "SKIP" -Output $Script.Output -AddNewLine)


@@ 591,19 591,19 @@ foreach ($Item in $Package.Content.TaskEntry)
	# ---- VerifyInstall Parameter (Type_Hotfix Subparameter) >>>>

	if ($TaskEntry.VerifyInstall -match $Package.TaskEntrySubparameterRegexp.VerifyInstall.Type_Hotfix)
    {
	{
		$TaskEntry.VerifyInstall                  = $TaskEntry.VerifyInstall -replace ($Package.TaskEntrySubparameterRegexp.VerifyInstall.Type_Hotfix, "")
		$Package.Variable.VerifyInstall.Existence = Get-Hotfix | ? {$_.HotfixID -eq $TaskEntry.VerifyInstall}

		if ($Package.Variable.VerifyInstall.Existence -ne $Null)
        {
		{
			$Script.Output = ("VerifyInstall: [Hotfix] ""{0}"" exists." -f $TaskEntry.VerifyInstall)

			Write-Host -ForegroundColor Yellow (Write-Result -Status "SKIP" -Output $Script.Output -AddNewLine)
			continue
		}
		else
        {
		{
			pass
		}
	}


@@ 611,20 611,20 @@ foreach ($Item in $Package.Content.TaskEntry)
	# ---- VerifyInstall Parameter (Type_Path Subparameter) >>>>

	elseif ($TaskEntry.VerifyInstall -match $Package.TaskEntrySubparameterRegexp.VerifyInstall.Type_Path)
    {
	{
		$TaskEntry.VerifyInstall                  = $TaskEntry.VerifyInstall -replace ($Package.TaskEntrySubparameterRegexp.VerifyInstall.Type_Path, "")
		$TaskEntry.VerifyInstall                  = Get-EnvironmentVariableValue -Path $TaskEntry.VerifyInstall
		$Package.Variable.VerifyInstall.Existence = Test-Path $TaskEntry.VerifyInstall

		if ($Package.Variable.VerifyInstall.Existence -eq $True)
        {
		{
			$Script.Output = ("VerifyInstall: [Path] ""{0}"" exists." -f $TaskEntry.VerifyInstall)

			Write-Host -ForegroundColor Yellow (Write-Result -Status "SKIP" -Output $Script.Output -AddNewLine)
			continue
		}
		else
        {
		{
			pass
		}
	}


@@ 632,11 632,11 @@ foreach ($Item in $Package.Content.TaskEntry)
	# ---- VerifyInstall Parameter (Version_FileInfo Subparameter) >>>>

	elseif ($TaskEntry.VerifyInstall -match $Package.TaskEntrySubparameterRegexp.VerifyInstall.Type_Version_FileInfo)
    {
	{
		$TaskEntry.VerifyInstall = $TaskEntry.VerifyInstall -replace ($Package.TaskEntrySubparameterRegexp.VerifyInstall.Type_Version_FileInfo, "")

		try
        {
		{
			# Separates Arg_Build (version/build number) and VerifyInstall value (file path):

			$TaskEntry.VerifyInstall -match $Package.TaskEntrySubparameterRegexp.VerifyInstall.Arg_Build | Out-Null


@@ 649,19 649,19 @@ foreach ($Item in $Package.Content.TaskEntry)
			# Determines as to whether or not both Arg_Build and version/build number of VerifyInstall value (file path) match:

			if ($Package.Variable.VerifyInstall.SpecifiedBuild -eq $Package.Variable.VerifyInstall.DiscoveredBuild)
            {
			{
				$Script.Output = ("VerifyInstall: [Vers_File] ""{0}"" exists." -f $Package.Variable.VerifyInstall.SpecifiedBuild)

				Write-Host -ForegroundColor Yellow (Write-Result -Status "SKIP" -Output $Script.Output -AddNewLine)
				continue
			}
			else
            {
			{
				throw
			}
		}
		catch [Exception]
        {
		{
			pass
		}
	}


@@ 669,11 669,11 @@ foreach ($Item in $Package.Content.TaskEntry)
	# ---- VerifyInstall Parameter (Version_ProductInfo Subparameter) >>>>

	elseif ($TaskEntry.VerifyInstall -match $Package.TaskEntrySubparameterRegexp.VerifyInstall.Type_Version_ProductInfo)
    {
	{
		$TaskEntry.VerifyInstall = $TaskEntry.VerifyInstall -replace ($Package.TaskEntrySubparameterRegexp.VerifyInstall.Type_Version_ProductInfo, "")

		try
        {
		{
			# Separates Arg_Build (version/build number) and VerifyInstall value (file path):

			$TaskEntry.VerifyInstall -match $Package.TaskEntrySubparameterRegexp.VerifyInstall.Arg_Build | Out-Null


@@ 686,19 686,19 @@ foreach ($Item in $Package.Content.TaskEntry)
			# Determines as to whether or not both Arg_Build and version/build number of VerifyInstall value (file path) match:

			if ($Package.Variable.VerifyInstall.SpecifiedBuild -eq $Package.Variable.VerifyInstall.DiscoveredBuild)
            {
			{
				$Script.Output = ("VerifyInstall: [Vers_Product] ""{0}"" exists." -f $Package.Variable.VerifyInstall.SpecifiedBuild)

				Write-Host -ForegroundColor Yellow (Write-Result -Status "SKIP" -Output $Script.Output -AddNewLine)
				continue
			}
			else
            {
			{
				throw
			}
		}
		catch [Exception]
        {
		{
			pass
		}
	}


@@ 706,37 706,37 @@ foreach ($Item in $Package.Content.TaskEntry)
	# ---- VerifyInstall Parameter (Type_Program Subparameter) >>>>

	elseif ($TaskEntry.VerifyInstall -match $Package.TaskEntrySubparameterRegexp.VerifyInstall.Type_Program)
    {
	{
		$TaskEntry.VerifyInstall = $TaskEntry.VerifyInstall -replace ($Package.TaskEntrySubparameterRegexp.VerifyInstall.Type_Program, "")

		try
        {
		{
			if ($TaskEntry.VerifyInstall -notmatch $Package.TaskEntrySubparameterRegexp.VerifyInstall.Arg_Build) # If the VerifyInstall value does not contain the Arg_Build argument.
            {
			{

				# Determines whether or not VerifyInstall value is a MSI GUID, in order to reference the correct property:

				if ($TaskEntry.VerifyInstall -match $Package.TaskEntrySubparameterRegexp.VerifyInstall.Value_MSIGUID)
                {
				{
					$Package.Variable.VerifyInstall.ProgramReference = "PSChildName"
				}
				else
                {
				{
					$Package.Variable.VerifyInstall.ProgramReference = "DisplayName"
				}

				# Searches the registry for possible program name or MSI GUID that matches VerifyInstall value:

				foreach ($Path in $Machine.ProgramList)
                {
				{
					if (Test-Path $Path)
                    {
					{
						$Package.Variable.VerifyInstall.Existence += @(
							Get-ChildItem $Path | % {Get-ItemProperty $_.PSPath} | ? {$_.$($Package.Variable.VerifyInstall.ProgramReference) -eq $TaskEntry.VerifyInstall} | % {$_.DisplayName}
						)
					}
					else
                    {
					{
						pass
					}
				}


@@ 744,19 744,19 @@ foreach ($Item in $Package.Content.TaskEntry)
				# Determines as to whether or not a matching program code or MSI GUID was found:

				if ($Package.Variable.VerifyInstall.Existence -ne $Null)
                {
				{
					$Script.Output = ("VerifyInstall: [Program] ""{0}"" exists." -f $TaskEntry.VerifyInstall)

					Write-Host -ForegroundColor Yellow (Write-Result -Status "SKIP" -Output $Script.Output -AddNewLine)
					continue
				}
				else
                {
				{
					throw
				}
			}
			else
            {
			{
				# Separates Arg_Build (version/build number) and VerifyInstall value (program name/MSI GUID):

				$TaskEntry.VerifyInstall -match $Package.TaskEntrySubparameterRegexp.VerifyInstall.Arg_Build | Out-Null


@@ 766,26 766,26 @@ foreach ($Item in $Package.Content.TaskEntry)
				# Determines whether or not VerifyInstall value is a MSI GUID, in order to reference the correct property:

				if ($TaskEntry.VerifyInstall -match $Package.TaskEntrySubparameterRegexp.VerifyInstall.Value_MSIGUID)
                {
				{
					$Package.Variable.VerifyInstall.ProgramReference = "PSChildName"
				}
				else
                {
				{
					$Package.Variable.VerifyInstall.ProgramReference = "DisplayName"
				}

				# Searches the registry for possible program name/MSI GUID that matches VerifyInstall value:

				foreach ($Path in $Machine.ProgramList)
                {
				{
					if (Test-Path $Path)
                    {
					{
						$Package.Variable.VerifyInstall.DiscoveredBuild += @(
							Get-ChildItem $Path | % {Get-ItemProperty $_.PSPath} | ? {$_.$($Package.Variable.VerifyInstall.ProgramReference) -eq $TaskEntry.VerifyInstall} | % {$_.DisplayVersion}
						)
					}
					else
                    {
					{
						pass
					}
				}


@@ 793,25 793,25 @@ foreach ($Item in $Package.Content.TaskEntry)
				# Determines whether or not there is a match between a discovered program name/MSI GUID's respective version/build number and Arg_Build:

				if ($Package.Variable.VerifyInstall.DiscoveredBuild -contains $Package.Variable.VerifyInstall.SpecifiedBuild)
                {
				{
					$Script.Output = ("VerifyInstall: [Program] ""{0}"" exists." -f $Package.Variable.VerifyInstall.SpecifiedBuild)

					Write-Host -ForegroundColor Yellow (Write-Result -Status "SKIP" -Output $Script.Output -AddNewLine)
					continue
				}
				else
                {
				{
					throw
				}
			}
		}
		catch [Exception]
        {
		{
			pass
		}
	}
	else
    {
	{
		pass
	}



@@ 820,53 820,53 @@ foreach ($Item in $Package.Content.TaskEntry)
	# ---- TerminateProcess Parameter >>>>

	if ($TaskEntry.TerminateProcess -notmatch "^$")
    {
	{
		$TaskEntry.TerminateProcess = $TaskEntry.TerminateProcess -split ($Package.Delimiter)

		foreach ($Process in $TaskEntry.TerminateProcess)
        {
		{
			try
            {
			{
				if (Get-Process $Process)
                {
				{
					pass
				}
				else
                {
				{
					continue
				}

				if ($TaskEntry.TerminateMessage -notmatch "^$" -and $Package.Variable.TerminateProcess.AlreadyPrompted -eq $False)
                {
				{
					Show-DialogBox -Title $Package.Name -Message $TaskEntry.TerminateMessage | Out-Null
					$Package.Variable.TerminateProcess.AlreadyPrompted = $True
				}
				else
                {
				{
					pass
				}

				Get-Process $Process | Stop-Process -Force
			}
			catch [Exception]
            {
			{
				continue
			}
		}
	}
	else
    {
	{
		pass
	}

	# ---- SuccessExitCode Parameter >>>>

	if ($TaskEntry.SuccessExitCode -eq $Null)
    {
	{
		$TaskEntry.SuccessExitCode = 0
	}
	else
    {
	{
		$TaskEntry.SuccessExitCode  = $TaskEntry.SuccessExitCode -split ($Package.Delimiter)
		$TaskEntry.SuccessExitCode += 0
	}


@@ 874,68 874,68 @@ foreach ($Item in $Package.Content.TaskEntry)
	# ---- Executable Invocation Processing >>>>

	try
    {
	{
		$Script.Output = (Invoke-Executable -Path $TaskEntry.Executable)

		if ($TaskEntry.SuccessExitCode -contains $Script.Output.ExitCode)
        {
		{
			Write-Host -ForegroundColor Green (Write-Result -Status "OK" -Code $Script.Output.ExitCode -Output $Script.Output.Output)

			if ($TaskEntry.SkipProcessCount -ne "true")
            {
			{
				$Package.TaskEntryStatus.Successful++
			}
			else
            {
			{
				continue
			}
		}
		else
        {
		{
			Write-Host -ForegroundColor Red (Write-Result -Status "WARN" -Code $Script.Output.ExitCode -Output $Script.Output.Output)

			if ($TaskEntry.SkipProcessCount -ne "true")
            {
			{
				$Package.TaskEntryStatus.Unsuccessful++
			}
			else
            {
			{
				pass
			}

			if ($TaskEntry.ContinueIfFail -ne "true")
            {
			{
				$Script.ExitCode = 1
				break
			}
			else
            {
			{
				$Package.TaskEntryStatus.TotalFailedButContinued++
				continue
			}
		}
	}
	catch [Exception]
    {
	{
		$Script.Output = ("Executable Invocation: {0}" -f $Error[0])
		Write-Host -ForegroundColor Red (Write-Result -Status "ERROR" -Code 2 -Output $Script.Output -AddNewLine)

		if ($TaskEntry.SkipProcessCount -ne "true")
        {
		{
			$Package.TaskEntryStatus.Unsuccessful++
		}
		else
        {
		{
			pass
		}

		if ($TaskEntry.ContinueIfFail -ne "true")
        {
		{
			$Script.ExitCode = 2
			break
		}
		else
        {
		{
			$Package.TaskEntryStatus.TotalFailedButContinued++
			continue
		}


@@ 949,11 949,11 @@ if ($Package.TaskEntryStatus.Successful -eq 0 -and $Package.TaskEntryStatus.Unsu
	Write-Host -ForegroundColor Red "`nWARN: No task entries were processed.`n"

	if ($Script.ExitCode -eq 0)
    {
	{
		$Script.ExitCode = 6
	}
	else
    {
	{
		pass
	}
}


@@ 965,7 965,7 @@ else
		"`nTasks Processed : " + $Package.TaskEntryStatus.TotalProcessed + `
		"`n  ^"                                                          + `
		"`n  |"                                                          + `
		"`n  |---- Success : " + $Package.TaskEntryStatus.Successful     + `
		"`n  |---- Success : " + $Package.TaskEntryStatus.Successful	 + `
		"`n  +---- Failure : " + $Package.TaskEntryStatus.Unsuccessful   + `
		"`n"
	)


@@ 973,22 973,22 @@ else
	Write-Host ("`nPackage Results ({0}):" -f $Package.Name)

	if ($Script.ExitCode -eq 0)
    {
	{
		$Script.Output += ("`nOK: ({0})`n" -f $Script.ExitCode)

		Write-Host -ForegroundColor Green $Script.Output

		if ($Script.Config.SuppressNotification -eq $False)
        {
		{
			Show-BalloonTip -Title $Package.Notification.Header -Text $Package.Notification.Footer | Out-Null
		}
		else
        {
		{
			pass
		}
	}
	else
    {
	{
		$Script.Output += ("`nERROR: ({0})`n" -f $Script.ExitCode)

		Write-Host -ForegroundColor Red $Script.Output