diff --git a/.gitignore b/.gitignore index 2bfa6a4..e69de29 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +0,0 @@ -tests/ diff --git a/CHANGELOG.md b/CHANGELOG.md index 6efde87..41263ef 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,47 @@ All notable changes to the [PSTK](https://github.com/Akaizoku/PSTK) project will The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [1.2.0](https://github.com/Akaizoku/PSTK/releases/tag/1.1.0) - 2019-09-12 + +### Added +The following functions have been added: +- Confirm-Prompt +- Expand-CompressedFile +- Find-Key +- Get-CallerPreference +- Get-EnvironmentVariable +- Get-HTTPStatus +- Get-KeyValue +- Get-Path +- Import-CSVProperties +- Import-Properties +- Out-Hashtable +- Remove-EnvironmentVariable +- Remove-Object +- Resolve-Array +- Resolve-Boolean +- Resolve-Tags +- Resolve-URI +- Select-XMLNode +- Set-EnvironmentVariable +- Set-RelativePath +- Test-EnvironmentVariable +- Test-Object +- Test-Service +- Update-File +- Write-ErrorLog + +### Changed +The following functions have been updated: +- Compare-Hashtable +- Compare-Properties +- Complete-RelativePath +- Convert-FileEncoding +- Get-Object +- Test-Object +- Test-SQLConnection +- Write-Log + ## [1.1.0](https://github.com/Akaizoku/PSTK/releases/tag/1.1.0) - 2018-10-15 ### Added diff --git a/LICENSE b/LICENSE index 4f6ee46..e98fa9c 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2018 Florian Carrier +Copyright (c) 2019 Florian Carrier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/PSTK.psd1 b/PSTK.psd1 index 45ae0c8..32a91eb 100644 --- a/PSTK.psd1 +++ b/PSTK.psd1 @@ -11,8 +11,8 @@ # Script module or binary module file associated with this manifest. RootModule = 'PSTK.psm1' -# Version number of this module. -ModuleVersion = '1.0.0' +# Version number of this module.s +ModuleVersion = '1.2.0' # Supported PSEditions # CompatiblePSEditions = @() @@ -27,7 +27,7 @@ Author = 'Florian Carrier' # CompanyName = 'Florian Carrier' # Copyright statement for this module -Copyright = '(c) 2018 Florian Carrier. All rights reserved.' +Copyright = '(c) 2019 Florian Carrier. All rights reserved.' # Description of the functionality provided by this module Description = 'Collection of useful functions and procedures for PowerShell scripting' @@ -73,19 +73,44 @@ FunctionsToExport = @( "Compare-Hashtable", "Compare-Properties", "Complete-RelativePath", + "Confirm-Prompt", "Convert-FileEncoding", "ConvertTo-NaturalSort", "ConvertTo-PDF", "Copy-OrderedHashtable", + "Expand-CompressedFile", + "Find-Key", "Format-String", + "Get-CallerPreference", + "Get-EnvironmentVariable", + "Get-HTTPStatus", + "Get-KeyValue", "Get-Object", + "Get-Path", "Get-Properties", + "Import-CSVProperties", + "Import-Properties", "New-DynamicParameter", + "Out-Hashtable", + "Remove-EnvironmentVariable", + "Remove-Object", "Rename-NumberedFile", + "Resolve-Array", + "Resolve-Boolean", + "Resolve-Tags", + "Resolve-URI", + "Select-XMLNode", + "Set-EnvironmentVariable", + "Set-RelativePath", "Set-Tags", "Start-Script", "Stop-Script", + "Test-EnvironmentVariable", + "Test-Object", + "Test-Service", "Test-SQLConnection", + "Update-File", + "Write-ErrorLog", "Write-Log" ) @@ -126,6 +151,11 @@ PrivateData = @{ # ReleaseNotes of this module ReleaseNotes = @' +## 1.2.0 +Added support for global preferences +Expanded existing functions +Added new features + ## 1.1.0 Updated folder structure Added about_help diff --git a/PSTK.psm1 b/PSTK.psm1 index 94bf8fd..95b0ea3 100644 --- a/PSTK.psm1 +++ b/PSTK.psm1 @@ -13,7 +13,7 @@ Creation date: 23/08/2018 Last modified: 15/10/2018 Repository: https://github.com/Akaizoku/PSTK - Depndencies: Test-SQLConnection requires the SQLServer module + Dependencies: Test-SQLConnection requires the SQLServer module .LINK https://github.com/Akaizoku/PSTK diff --git a/Private/Read-Properties.ps1 b/Private/Read-Properties.ps1 index 37045ac..f22338e 100644 --- a/Private/Read-Properties.ps1 +++ b/Private/Read-Properties.ps1 @@ -9,12 +9,8 @@ function Read-Properties { .DESCRIPTION Parse properties file to generate configuration variables - .PARAMETER File - [String] The File parameter should be the name of the property file. - - .PARAMETER Directory - [String] The Directory parameter should be the path to the directory containing the - property file. + .PARAMETER Path + The patch parameter corresponds to the path to the property file to read. .PARAMETER Section [Switch] The Section parameter indicates if properties should be grouped depending on @@ -25,7 +21,7 @@ function Read-Properties { ordered hash table containing the content of the property file. .EXAMPLE - Read-Properties -File "default.ini" -Directory ".\conf" -Section + Read-Properties -Path ".\conf\default.ini" -Section In this example, Read-Properties will parse the default.ini file contained in the .\conf directory and generate an ordered hashtable containing the @@ -36,19 +32,11 @@ function Read-Properties { [Parameter ( Position = 1, Mandatory = $true, - HelpMessage = "Property file name" - )] - [ValidateNotNullOrEmpty ()] - [String] - $File, - [Parameter ( - Position = 2, - Mandatory = $true, - HelpMessage = "Path to the directory containing the property file" + HelpMessage = "Path to the property file" )] [ValidateNotNullOrEmpty ()] [String] - $Directory, + $Path, [Parameter ( Position = 3, Mandatory = $false, @@ -57,64 +45,78 @@ function Read-Properties { [Switch] $Section ) - # Properties variables - $PropertyFile = Join-Path -Path $Directory -ChildPath $File - $Properties = New-Object -TypeName System.Collections.Specialized.OrderedDictionary - $Sections = New-Object -TypeName System.Collections.Specialized.OrderedDictionary - $Header = $null - # Check that the file exists - if (Test-Path -Path $PropertyFile) { - $FileContent = Get-Content -Path $PropertyFile - $LineNumber = 0 - # Read the property file line by line - foreach ($Content in $FileContent) { - $LineNumber += 1 - # If properties have to be grouped by section - if ($Section) { - # If end of file and section is open - if ($LineNumber -eq $FileContent.Count -And $Header) { - if ($Content[0] -ne "#" -And $Content[0] -ne ";" -And $Content -ne "") { - $Property = Read-Property -Content $Content + Begin { + # Get global preference variables + Get-CallerPreference -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState + # Instantiate variables + $Properties = New-Object -TypeName "System.Collections.Specialized.OrderedDictionary" + $Sections = New-Object -TypeName "System.Collections.Specialized.OrderedDictionary" + $Header = $null + $errors = 0 + } + Process { + # Check that the file exists + if (Test-Path -Path $Path) { + $ListOfProperties = Get-Content -Path $Path + $LineNumber = 0 + # Read the property file line by line + foreach ($Property in $ListOfProperties) { + $LineNumber += 1 + # If properties have to be grouped by section + if ($Section) { + # If end of file and section is open + if ($LineNumber -eq $ListOfProperties.Count -And $Header) { + if ($Property[0] -ne "#" -And $Property[0] -ne ";" -And $Property -ne "") { + $Property = Read-Property -Property $Property + if ($Property.Count -gt 0) { + $Sections.Add($Property.Key, $Property.Value) + } else { + Write-Log -Type "WARN" -Message "Unable to process line $LineNumber from $Path" + } + } + $Clone = Copy-OrderedHashtable -Hashtable $Sections -Deep + $Properties.Add($Header, $Clone) + } elseif ($Property[0] -eq "[") { + # If previous section exists add it to the property list + if ($Header) { + $Clone = Copy-OrderedHashtable -Hashtable $Sections -Deep + $Properties.Add($Header, $Clone) + } + # Create new property group + $Header = $Property.Substring(1, $Property.Length - 2) + $Sections.Clear() + } elseif ($Header -And $Property[0] -ne "#" -And $Property[0] -ne ";" -And $Property -ne "") { + $Property = Read-Property -Property $Property if ($Property.Count -gt 0) { $Sections.Add($Property.Key, $Property.Value) } else { - Write-Log -Type "WARN" -Message "Unable to process line $LineNumber from $PropertyFile" + Write-Log -Type "WARN" -Message "Unable to process line $LineNumber from $Path" } } - $Clone = Copy-OrderedHashtable -Hashtable $Sections -Deep - $Properties.Add($Header, $Clone) - } elseif ($Content[0] -eq "[") { - # If previous section exists add it to the property list - if ($Header) { - $Clone = Copy-OrderedHashtable -Hashtable $Sections -Deep - $Properties.Add($Header, $Clone) - } - # Create new property group - $Header = $Content.Substring(1, $Content.Length - 2) - $Sections.Clear() - } elseif ($Header -And $Content[0] -ne "#" -And $Content[0] -ne ";" -And $Content -ne "") { - $Property = Read-Property -Content $Content - if ($Property.Count -gt 0) { - $Sections.Add($Property.Key, $Property.Value) - } else { - Write-Log -Type "WARN" -Message "Unable to process line $LineNumber from $PropertyFile" - } - } - } else { - # Ignore comments, sections, and blank lines - if ($Content[0] -ne "#" -And $Content[0] -ne ";" -And $Content[0] -ne "[" -And $Content -ne "") { - $Property = Read-Property -Content $Content - if ($Property.Count -gt 0) { - $Properties.Add($Property.Key, $Property.Value) - } else { - Write-Log -Type "WARN" -Message "Unable to process line $LineNumber from $PropertyFile" + } else { + # Ignore comments, sections, and blank lines + if ($Property[0] -ne "#" -And $Property[0] -ne ";" -And $Property[0] -ne "[" -And $Property -ne "") { + $Property = Read-Property -Property $Property + if ($Property.Count -gt 0) { + try { + $Properties.Add($Property.Key, $Property.Value) + } catch { + Write-Log -Type "WARN" -Object "Two distinct definitions of the property $($Property.Key) have been found in the configuration file" + $Errors += 1 + } + } else { + Write-Log -Type "WARN" -Message "Unable to process line $LineNumber from $Path" + } } } } + } else { + # Alert that configuration file does not exist at specified location + Write-Log -Type "ERROR" -Message "Path not found $Path" -ErrorCode 1 + } + if ($Errors -gt 0) { + Write-Log -Type "ERROR" -Object "Unable to proceed. Resolve the issues in $Path" -ErrorCode 1 } - } else { - # Alert that configuration file does not exists at specified location - Write-Log -Type "ERROR" -Message "The $File file cannot be found under $(Resolve-Path $Directory)" + return $Properties } - return $Properties } diff --git a/Private/Read-Property.ps1 b/Private/Read-Property.ps1 index 4599a63..f995a88 100644 --- a/Private/Read-Property.ps1 +++ b/Private/Read-Property.ps1 @@ -4,13 +4,13 @@ function Read-Property { <# .SYNOPSIS - Parse property content + Parse property .DESCRIPTION - Parse property content + Parse property content to output key-value pair - .PARAMETER Content - [String] The Content parameter should be the content of the property. + .PARAMETER Property + The property parameter corresponds to the property to read. .INPUTS None. @@ -20,30 +20,47 @@ function Read-Property { ordered hashtable containing the name and value of a given property. .EXAMPLE - Read-Property -Content "Key = Value" + Read-Property -Property "Key = Value" In this example, Read-Property will parse the content and assign the value "Value" to the property "Key". + + .NOTES + File name: Read-Property.ps1 + Author: Florian Carrier + Creation date: 15/10/2018 + Last modified: 17/06/2019 #> [CmdletBinding ()] Param ( [Parameter ( - Position = 1, - Mandatory = $true, - HelpMessage = "Property content" + Position = 1, + Mandatory = $true, + ValueFromPipeline = $true, + HelpMessage = "Property content" )] [ValidateNotNullOrEmpty ()] [String] - $Content + $Property ) - $Property = New-Object -TypeName System.Collections.Specialized.OrderedDictionary - $Index = $Content.IndexOf("=") - if ($Index -gt 0) { - $Offset = 1 - $Key = $Content.Substring(0, $Index) - $Value = $Content.Substring($Index + $Offset, $Content.Length - $Index - $Offset) - $Property.Add("Key" , $Key.Trim()) - $Property.Add("Value" , $Value.Trim()) + Begin { + # Get global preference variables + Get-CallerPreference -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState + # Instantiate variables + $KeyValuePair = New-Object -TypeName "System.Collections.Specialized.OrderedDictionary" + $Index = $Property.IndexOf("=") + } + Process { + # Check that format is valid + if ($Index -gt 0) { + $Offset = 1 + $Key = $Property.Substring(0, $Index) + $Value = $Property.Substring($Index + $Offset, $Property.Length - $Index - $Offset) + # Generate key-value pair + $KeyValuePair.Add("Key" , $Key.Trim()) + $KeyValuePair.Add("Value" , $Value.Trim()) + } + # Output key-value pair + return $KeyValuePair } - return $Property } diff --git a/Private/Select-WriteHost.ps1 b/Private/Select-WriteHost.ps1 new file mode 100644 index 0000000..789b239 --- /dev/null +++ b/Private/Select-WriteHost.ps1 @@ -0,0 +1,140 @@ +function Select-WriteHost { + <# + .SYNOPSIS + Captures Write-Host output to standard output stream + + .DESCRIPTION + Creates a proxy function for Write-Host and redirects the console output to + the standard output stream. + + .PARAMETER InputObject + The input object parameter corresponds to in-line values passed from the pi- + peline. + + .PARAMETER ScriptBlock + The script block parameter corresponds to the script to capture. + + .PARAMETER Quiet + The quiet parameter is a flag whether the Write-Host output should be sup- + pressed or kept. + + .INPUTS + [System.Objects] Select-WriteHost accepts objects from the pipeline. + + .OUTPUTS + [System.String] Select-WriteHost returns a string. + + .EXAMPLE + $Output = 1..10 | % { Write-Host $_ } | Select-WriteHost + + In this example, Select-WriteHost will store the list of numbers from one + to 10 into the $Output variable but still output the results to the console. + + .EXAMPLE + $Output = 1..10 | % { Write-Host $_ } | Select-WriteHost -Quiet + + In this example, Select-WriteHost will store the list of numbers from one + to 10 into the $Output variable and suppress the console output. + + .EXAMPLE + $Output = Select-WriteHost -ScriptBlock { 1..10 | % { Write-Host $_ } } -OutputFile "test" + + In this example, Select-WriteHost will store the list of numbers from one + to 10 into the $Output variable but still output the results to the console. + + .EXAMPLE + $Output = Select-WriteHost -ScriptBlock { 1..10 | % { Write-Host $_ } } -OutputFile "test" -Quiet + + In this example, Select-WriteHost will store the list of numbers from one + to 10 into the $Output variable and suppress the console output. + + .NOTES + File name: Select-WriteHost.ps1 + Author: Florian Carrier + Creation date: 16/10/2018 + Last modified: 16/10/2018 + Credit: @LincolnAtkinson + + .LINK + http://www.latkin.org/blog/2012/04/25/how-to-capture-or-redirect-write-host-output-in-powershell/ + #> + [CmdletBinding (DefaultParameterSetName = "FromPipeline")] + Param ( + [Parameter ( + ValueFromPipeline = $true, + ParameterSetName = "FromPipeline", + HelpMessage = "In-line script input to capture" + )] + [Object] + $InputObject, + [Parameter ( + Position = 1, + Mandatory = $true, + ParameterSetName = "FromScriptblock", + HelpMessage = "Script to capture" + )] + [ScriptBlock] + $ScriptBlock, + [Parameter ( + Position = 2, + Mandatory = $false, + ParameterSetName = "FromScriptblock", + HelpMessage = "Path to the output file" + )] + [String] + $OutputFile, + [Parameter ( + HelpMessage = "Define if console output has to be suppressed" + )] + [Switch] + $Quiet + ) + Begin { + function Unregister-WriteHost { + # Clear out the proxy version of Write-Host + Remove-Item Function:Write-Host -ErrorAction 0 + } + function Edit-WriteHost ([String] $Scope, [Switch] $Quiet) { + # Create a proxy for Write-Host + $MetaData = New-Object -TypeName "System.Management.Automation.CommandMetaData" (Get-Command -Name "Microsoft.PowerShell.Utility\Write-Host") + $Proxy = [System.Management.Automation.ProxyCommand]::Create($MetaData) + # Amend its behaviour + $Content = if ($Quiet) { + # In quiet mode, whack the entire function body, simply pass input di- + # rectly to the pipeline + $Proxy -replace '(?s)\bbegin\b.+', '$Object' + } else { + # In default mode, pass input to the pipeline, but allow real Write-Host + # to process as well + $Proxy -replace '($SteppablePipeline.Process)', '$Object; $1' + } + # load our version into the specified scope + Invoke-Expression "Function ${Scope}:Write-Host { $Content }" + } + Unregister-WriteHost + # If we are running at the end of a pipeline, need to immediately inject the + # proxy into global scope, so that everybody else in the pipeline uses it. + # This works great, but dangerous if we don't clean up properly. + if ($PSCmdlet.ParameterSetName -eq "FromPipeline") { + Edit-WriteHost -Scope "global" -Quiet:$Quiet + } + } + Process { + if ($PSCmdlet.ParameterSetName -eq "FromScriptBlock") { + # If a scriptblock was passed to us, then we can declare the proxy as + # local scope and let the runtime take it out of scope for us. + # The scriptblock will inherit the proxy automatically as it's in a child + # scope. + . Edit-WriteHost -Scope "local" -Quiet:$Quiet + if ($OutputFile) { & $Scriptblock | Out-File -FilePath $OutputFile -Encoding "UTF8" -Append } + else { & $Scriptblock } + } else { + # In pipeline scenario, just pass input along + if ($OutputFile) { $InputObject | Out-File -FilePath $OutputFile -Encoding "UTF8" -Append } + else { $InputObject } + } + } + End { + Unregister-WriteHost + } +} diff --git a/Public/Compare-Hashtable.ps1 b/Public/Compare-Hashtable.ps1 index da188d9..96ba038 100644 --- a/Public/Compare-Hashtable.ps1 +++ b/Public/Compare-Hashtable.ps1 @@ -42,27 +42,34 @@ function Compare-Hashtable { # [System.Collections.Specialized.OrderedDictionary] $Difference ) - $Check = $true - # Check that hashtables are of the same size - if ($Reference.Count -ne $Difference.Count) { - $Check = $false - } else { - # Loop through tables - foreach ($Key in $Reference.Keys) { - # Check that they contain the same keys - if ($Difference.$Key) { - # Check that they contain the same values - if ($Difference.$Key -ne $Reference.$Key) { + Begin { + # Get global preference variables + Get-CallerPreference -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState + # Variables + $Check = $true + } + Process { + # Check that hashtables are of the same size + if ($Reference.Count -ne $Difference.Count) { + $Check = $false + } else { + # Loop through tables + foreach ($Key in $Reference.Keys) { + # Check that they contain the same keys + if ($Difference.$Key) { + # Check that they contain the same values + if ($Difference.$Key -ne $Reference.$Key) { + $Check = $false + Write-Log -Type "DEBUG" -Message "$($Difference.$Key) does not exists in reference hashtable" + break + } + } else { $Check = $false - Write-Debug "$($Difference.$Key) does not exists in reference hashtable" + Write-Log -Type "DEBUG" -Message "$Key does not exists in difference hashtable" break } - } else { - $Check = $false - Write-Debug "$Key does not exists in difference hashtable" - break } } + return $Check } - return $Check } diff --git a/Public/Compare-Properties.ps1 b/Public/Compare-Properties.ps1 index 1d3c3b5..9c3412f 100644 --- a/Public/Compare-Properties.ps1 +++ b/Public/Compare-Properties.ps1 @@ -46,14 +46,24 @@ function Compare-Properties { [String[]] $Required ) - $Missing = New-Object -TypeName System.Collections.ArrayList - $Parameters = $Required.Split(",") - foreach ($Parameter in $Parameters) { - $Property = $Parameter.Trim() - if ($Property -ne "" -And !$Properties.$Property) { - [Void]$Missing.Add($Property) + Begin { + # Get global preference variables + Get-CallerPreference -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState + # Variables + $Missing = New-Object -TypeName System.Collections.ArrayList + $Parameters = $Required.Split(",") + } + Process { + # Loop through parameters + foreach ($Parameter in $Parameters) { + $Property = $Parameter.Trim() + # Check if property exists + if ($Property -ne "" -And !$Properties.$Property) { + Write-Log -Type "DEBUG" -Message "$Property is missing" + [Void]$Missing.Add($Property) + } } + # Force array-list format + return @($Missing) } - # Force array-list format - return @($Missing) } diff --git a/Public/Complete-RelativePath.ps1 b/Public/Complete-RelativePath.ps1 index 1df04a5..d8b33a1 100644 --- a/Public/Complete-RelativePath.ps1 +++ b/Public/Complete-RelativePath.ps1 @@ -30,11 +30,14 @@ function Complete-RelativePath { HelpMessage = "Root directory to pre-prend to relative path" )] [ValidateNotNullOrEmpty ()] - [Alias ("Directory")] + [Alias ("Directory", "Root")] [String] $WorkingDirectory ) Begin { + # Get global preference variables + Get-CallerPreference -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState + # Test working directory path if (-Not (Test-Path -Path $WorkingDirectory)) { Write-Log -Type "ERROR" -Message "$WorkingDirectory does not exists." Stop-Script 1 diff --git a/Public/Confirm-Prompt.ps1 b/Public/Confirm-Prompt.ps1 new file mode 100644 index 0000000..071fdfe --- /dev/null +++ b/Public/Confirm-Prompt.ps1 @@ -0,0 +1,27 @@ +function Confirm-Prompt { + [CmdletBinding ()] + Param ( + [Parameter ( + Position = 1, + Mandatory = $true, + HelpMessage = "Prompt message" + )] + [String] + $Prompt + ) + Begin { + $ConfirmPrompt = $Prompt + " ([Y] Yes | [N] No)" + } + Process { + $Answer = Read-Host -Prompt $ConfirmPrompt + switch -RegEx ($Answer) { + # Switch is case insensitive + '\Ayes\Z|\Ay\Z|\A1\Z|\Atrue\Z|\At\Z' { return $true } + '\Ano\Z|\An\Z|\A0\Z|\Afalse\Z|\Af\Z' { return $false } + default { + Write-Log -Type "ERROR" -Object "Unable to process answer. Please enter either [Y] Yes or [N] No" + Confirm-Prompt -Prompt $Prompt + } + } + } +} diff --git a/Public/Convert-FileEncoding.ps1 b/Public/Convert-FileEncoding.ps1 index d1a5e07..a84f60b 100644 --- a/Public/Convert-FileEncoding.ps1 +++ b/Public/Convert-FileEncoding.ps1 @@ -47,6 +47,7 @@ function Convert-FileEncoding { Mandatory = $true, HelpMessage = "Encoding" )] + [ValidateSet ("ASCII", "BigEndianUnicode", "OEM", "Unicode", "UTF7", "UTF8", "UTF8BOM", "UTF8NoBOM", "UTF32")] [String] $Encoding, [Parameter ( @@ -65,6 +66,8 @@ function Convert-FileEncoding { $Exclude = $null ) Begin { + # Get global preference variables + Get-CallerPreference -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState # Check parameters and instantiate variables # $Path = Resolve-Path -Path $Path $Files = Get-Object -Path $Path -Type "File" -Filter $Filter -Exclude $Exclude @@ -75,19 +78,24 @@ function Convert-FileEncoding { Process { try { foreach ($File in $Files) { - Write-Log -Type "INFO" -Message "Converting ""$($File.Name)"" to $Encoding" + Write-Log -Type "INFO" -Object "Converting ""$($File.Name)"" to $Encoding" $Filename = "$($File.BaseName)_$Encoding$($File.Extension)" $FilePath = Join-Path -Path $Path -ChildPath $File $NewFile = Join-Path -Path $Path -ChildPath $Filename Get-Content -Path $FilePath | Out-File -Encoding $Encoding $NewFile + Write-Log -Type "DEBUG" -Object "$NewFile" $Count += 1 } if ($Count -gt 0) { $Output = $true } - Write-Log -Type "CHECK" -Message "$Count files were converted to $Encoding" + Write-Log -Type "CHECK" -Object "$Count files were converted to $Encoding" } catch { - Write-Log -Type "ERROR" -Message "$($Error[0].Exception)" + if ($Error[0].Exception) { + Write-Log -Type "ERROR" -Object "$($Error[0].Exception)" + } else { + Write-Log -Type "ERROR" -Object "An unknown error occurred" + } } return $Output } diff --git a/Public/Expand-CompressedFile.ps1 b/Public/Expand-CompressedFile.ps1 new file mode 100644 index 0000000..404b443 --- /dev/null +++ b/Public/Expand-CompressedFile.ps1 @@ -0,0 +1,85 @@ +function Expand-CompressedFile { + <# + .SYNOPSIS + Expand compressed file + + .DESCRIPTION + Expand a compressed file with the "best" method available depending on the + PowerShell version used. + + .PARAMETER Path + The path parameter corresponds to the path to the compressed file to expand. + + .PARAMETER DestinationPath + The destination path parameter corresponds to the target path for the expan- + sion. + If not specified, the file will be expanded in the same location under a dir- + ectory with the same name of the file (without the extension). + + .PARAMETER Force + The force switch defines if the target should be overwritten in case it al- + ready exists. + + .EXAMPLE + Expand-CompressedFile -Path "C:\archive.zip" -DestinationPath "C:\archive" + + In this example, + + .NOTES + File name: Expand-CompressedFile.ps1 + Author: Florian Carrier + Creation date: 08/12/2018 + Last modified: 08/12/2018 + #> + [CmdletBinding ()] + Param( + [Parameter ( + Position = 1, + Mandatory = $true, + HelpMessage = "Path to the compressed file" + )] + [ValidateNotNullOrEmpty ()] + [String] + $Path, + [Parameter ( + Position = 2, + Mandatory = $false, + HelpMessage = "Destination where to extract the contents" + )] + [ValidateNotNullOrEmpty ()] + [String] + $DestinationPath, + [Switch] + $Force + ) + Begin { + # Get global preference variables + Get-CallerPreference -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState + } + Process { + # Check if destination path has been specified + if (-Not $DestinationPath) { + $FilePath = Get-ChildItem -Path $Path + $DestinationPath = Join-Path -Path $FilePath.DirectoryName -ChildPath $FilePath.BaseName + } + # Check PowerShell version to determine method to be used for decompressing + if ($PSVersionTable.PSVersion.Major -ge 5) { + # If PowerShell version greater than 5 then use more efficien Expand-Archive + Write-Log -Type "DEBUG" -Message "Using native PowerShell v5.0 Expand-Archive function" + Expand-Archive -Path $Path -DestinationPath $DestinationPath -Force:$Force + } else { + # Else copy files "manually" + Write-Log -Type "DEBUG" -Message "PowerShell version lower than 5.0" + Write-Log -Type "DEBUG" -Message "Copying objects out of the compressed file" + $Shell = New-Object -ComObject Shell.Application + $File = $Shell.NameSpace($Path) + foreach($Item in $File.Items()) { + if ($Force) { + $Shell.Namespace($DestinationPath).CopyHere($Item, 0x14) + } else { + $Shell.Namespace($DestinationPath).CopyHere($Item) + } + } + } + } +} diff --git a/Public/Find-Key.ps1 b/Public/Find-Key.ps1 new file mode 100644 index 0000000..2fa8bcb --- /dev/null +++ b/Public/Find-Key.ps1 @@ -0,0 +1,83 @@ +function Find-Key { + <# + .SYNOPSIS + Check if a key exists in a hashtable + + .DESCRIPTION + Check if a specified key exists in a hashtable with or without regards to + the case. + + .PARAMETER Hashtable + The hastable parameter corresponds to the hastable in which to look for the + key. + + .PARAMETER Key + The key parameter corresponds to the key to search for. + + .PARAMETER CaseSensitive + The case sensitive switch defines is the search should be case sensitive. + + .OUTPUTS + [System.Boolean] The function returns a boolean. + + .EXAMPLE + Find-Key -Hashtable @{"key"="value"} -Key "KEY" + + In this example, the function returns true. + + .EXAMPLE + Find-Key -Hashtable @{"key"="value"} -Key "KEY" -CaseSensitive + + In this example, the function returns false. + + .NOTES + File name: Find-Key.ps1 + Author: Florian Carrier + Creation date: 08/12/2018 + Last modified: 08/12/2018 + #> + [CmdletBinding ()] + Param ( + [Parameter ( + Position = 1, + Mandatory = $true, + HelpMessage = "Hashtable" + )] + [ValidateNotNullOrEmpty ()] + [System.Collections.Specialized.OrderedDictionary] + $Hashtable, + [Parameter ( + Position = 2, + Mandatory = $true, + HelpMessage = "Key to search for" + )] + [ValidateNotNullOrEmpty ()] + [String] + $Key, + [Parameter ( + HelpMessage = "Define if match should be case sensitive" + )] + [Switch] + $CaseSensitive + ) + Process { + $Check = $false + if ($Hashtable.$Key) { + $Check = $true + } elseif (-Not $CaseSensitive) { + $UpperCaseKey = Format-String -String $Key -Format "UpperCase" + if ($Hashtable.$UpperCaseKey) { + $Check = $true + } else { + $FormattedKey = Format-String -String $Key -Format "lowercase" + foreach ($Item in $Hashtable.Keys) { + $FormattedItem = Format-String -String $Item -Format "lowercase" + if ($FormattedItem -eq $FormattedKey) { + $Check = $true + } + } + } + } + return $Check + } +} diff --git a/Public/Get-CallerPreference.ps1 b/Public/Get-CallerPreference.ps1 new file mode 100644 index 0000000..4866be0 --- /dev/null +++ b/Public/Get-CallerPreference.ps1 @@ -0,0 +1,165 @@ +#requires -Version 2.0 + +function Get-CallerPreference +{ + <# + .Synopsis + Fetches "Preference" variable values from the caller's scope. + .DESCRIPTION + Script module functions do not automatically inherit their caller's variables, but they can be + obtained through the $PSCmdlet variable in Advanced Functions. This function is a helper function + for any script module Advanced Function; by passing in the values of $ExecutionContext.SessionState + and $PSCmdlet, Get-CallerPreference will set the caller's preference variables locally. + .PARAMETER Cmdlet + The $PSCmdlet object from a script module Advanced Function. + .PARAMETER SessionState + The $ExecutionContext.SessionState object from a script module Advanced Function. This is how the + Get-CallerPreference function sets variables in its callers' scope, even if that caller is in a different + script module. + .PARAMETER Name + Optional array of parameter names to retrieve from the caller's scope. Default is to retrieve all + Preference variables as defined in the about_Preference_Variables help file (as of PowerShell 4.0) + This parameter may also specify names of variables that are not in the about_Preference_Variables + help file, and the function will retrieve and set those as well. + .EXAMPLE + Get-CallerPreference -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState + + Imports the default PowerShell preference variables from the caller into the local scope. + .EXAMPLE + Get-CallerPreference -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState -Name 'ErrorActionPreference','SomeOtherVariable' + + Imports only the ErrorActionPreference and SomeOtherVariable variables into the local scope. + .EXAMPLE + 'ErrorActionPreference','SomeOtherVariable' | Get-CallerPreference -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState + + Same as Example 2, but sends variable names to the Name parameter via pipeline input. + .INPUTS + String + .OUTPUTS + None. This function does not produce pipeline output. + .LINK + about_Preference_Variables + + .NOTES + Author: David Wyatt + Updated: 02/07/2014 + License: MICROSOFT LIMITED PUBLIC LICENSE + Link: https://gallery.technet.microsoft.com/scriptcenter/Inherit-Preference-82343b9d + #> + + [CmdletBinding(DefaultParameterSetName = 'AllVariables')] + param ( + [Parameter(Mandatory = $true)] + [ValidateScript({ $_.GetType().FullName -eq 'System.Management.Automation.PSScriptCmdlet' })] + $Cmdlet, + + [Parameter(Mandatory = $true)] + [System.Management.Automation.SessionState] + $SessionState, + + [Parameter(ParameterSetName = 'Filtered', ValueFromPipeline = $true)] + [string[]] + $Name + ) + + begin + { + $filterHash = @{} + } + + process + { + if ($null -ne $Name) + { + foreach ($string in $Name) + { + $filterHash[$string] = $true + } + } + } + + end + { + # List of preference variables taken from the about_Preference_Variables help file in PowerShell version 4.0 + + $vars = @{ + 'ErrorView' = $null + 'FormatEnumerationLimit' = $null + 'LogCommandHealthEvent' = $null + 'LogCommandLifecycleEvent' = $null + 'LogEngineHealthEvent' = $null + 'LogEngineLifecycleEvent' = $null + 'LogProviderHealthEvent' = $null + 'LogProviderLifecycleEvent' = $null + 'MaximumAliasCount' = $null + 'MaximumDriveCount' = $null + 'MaximumErrorCount' = $null + 'MaximumFunctionCount' = $null + 'MaximumHistoryCount' = $null + 'MaximumVariableCount' = $null + 'OFS' = $null + 'OutputEncoding' = $null + 'ProgressPreference' = $null + 'PSDefaultParameterValues' = $null + 'PSEmailServer' = $null + 'PSModuleAutoLoadingPreference' = $null + 'PSSessionApplicationName' = $null + 'PSSessionConfigurationName' = $null + 'PSSessionOption' = $null + + 'ErrorActionPreference' = 'ErrorAction' + 'DebugPreference' = 'Debug' + 'ConfirmPreference' = 'Confirm' + 'WhatIfPreference' = 'WhatIf' + 'VerbosePreference' = 'Verbose' + 'WarningPreference' = 'WarningAction' + } + + + foreach ($entry in $vars.GetEnumerator()) + { + if (([string]::IsNullOrEmpty($entry.Value) -or -not $Cmdlet.MyInvocation.BoundParameters.ContainsKey($entry.Value)) -and + ($PSCmdlet.ParameterSetName -eq 'AllVariables' -or $filterHash.ContainsKey($entry.Name))) + { + $variable = $Cmdlet.SessionState.PSVariable.Get($entry.Key) + + if ($null -ne $variable) + { + if ($SessionState -eq $ExecutionContext.SessionState) + { + Set-Variable -Scope 1 -Name $variable.Name -Value $variable.Value -Force -Confirm:$false -WhatIf:$false + } + else + { + $SessionState.PSVariable.Set($variable.Name, $variable.Value) + } + } + } + } + + if ($PSCmdlet.ParameterSetName -eq 'Filtered') + { + foreach ($varName in $filterHash.Keys) + { + if (-not $vars.ContainsKey($varName)) + { + $variable = $Cmdlet.SessionState.PSVariable.Get($varName) + + if ($null -ne $variable) + { + if ($SessionState -eq $ExecutionContext.SessionState) + { + Set-Variable -Scope 1 -Name $variable.Name -Value $variable.Value -Force -Confirm:$false -WhatIf:$false + } + else + { + $SessionState.PSVariable.Set($variable.Name, $variable.Value) + } + } + } + } + } + + } # end + +} # function Get-CallerPreference diff --git a/Public/Get-EnvironmentVariable.ps1 b/Public/Get-EnvironmentVariable.ps1 new file mode 100644 index 0000000..8983472 --- /dev/null +++ b/Public/Get-EnvironmentVariable.ps1 @@ -0,0 +1,35 @@ +function Get-EnvironmentVariable { + [CmdletBinding()] + Param ( + [Parameter ( + Position = 1, + Mandatory = $true, + HelpMessage = "Name of the environment variable" + )] + [ValidateNotNullOrEmpty()] + [Alias ("Variable")] + [String] + $Name, + [Parameter ( + Position = 2, + Mandatory = $false, + HelpMessage = "Scope of the environment variable" + )] + [ValidateSet ("Machine", "Process", "User")] + [String] + $Scope = "Machine" + ) + Begin { + # Get global preference variables + Get-CallerPreference -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState + } + Process { + # Check if variable is defined + $Value = [Environment]::GetEnvironmentVariable($Name, $Scope) + if ($Value) { + Write-Log -Type "DEBUG" -Message "$Scope`t$Name=$Value" + } + # If variable does not exists, the value will be null + return $Value + } +} diff --git a/Public/Get-HTTPStatus.ps1 b/Public/Get-HTTPStatus.ps1 new file mode 100644 index 0000000..c152712 --- /dev/null +++ b/Public/Get-HTTPStatus.ps1 @@ -0,0 +1,46 @@ +# ------------------------------------------------------------------------------ +# Get HTTP/HTTPS status +# ------------------------------------------------------------------------------ +function Get-HTTPStatus { + <# + .SYNOPSIS + Returns the status of an HTTP request + + .DESCRIPTION + Queries a server and returns the status of the request + + .PARAMETER URI + The URI parameter corresponds to the URI (Uniform Resource Identifier) of + the server to query + + .NOTES + File name: Get-HTTPStatus.ps1 + Author: Florian Carrier + Creation date: 15/01/2019 + Last modified: 17/01/2019 + #> + Param( + [Parameter( + Position = 1, + Mandatory = $true, + HelpMessage = "URI to check" + )] + [String] + $URI + ) + Begin { + # Get global preference variables + Get-CallerPreference -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState + } + Process { + try { + # Query server + Write-Debug -Message $URI + $Status = Invoke-WebRequest -URI $URI | Select-Object -ExpandProperty "StatusCode" + } catch { + # If server is offline + $Status = 0 + } + return $Status + } +} diff --git a/Public/Get-KeyValue.ps1 b/Public/Get-KeyValue.ps1 new file mode 100644 index 0000000..a3f25d0 --- /dev/null +++ b/Public/Get-KeyValue.ps1 @@ -0,0 +1,83 @@ +function Get-KeyValue { + <# + .SYNOPSIS + Returns a value from a hashtable + + .DESCRIPTION + Returns a value corresponding to a specified key in a hashtable with or + without regards to the case. + + .PARAMETER Hashtable + The hastable parameter corresponds to the hastable in which to look for the + key. + + .PARAMETER Key + The key parameter corresponds to the key to search for. + + .PARAMETER CaseSensitive + The case sensitive switch defines is the search should be case sensitive. + + .OUTPUTS + [System.Boolean] The function returns a boolean. + + .EXAMPLE + Find-Key -Hashtable @{"key"="value"} -Key "KEY" + + In this example, the function returns the value "value". + + .EXAMPLE + Find-Key -Hashtable @{"key"="value"} -Key "KEY" -CaseSensitive + + In this example, the function returns a null value. + + .NOTES + File name: Get-KeyValue.ps1 + Author: Florian Carrier + Creation date: 08/12/2018 + Last modified: 08/12/2018 + #> + [CmdletBinding ()] + Param ( + [Parameter ( + Position = 1, + Mandatory = $true, + HelpMessage = "Hashtable" + )] + [ValidateNotNullOrEmpty ()] + [System.Collections.Specialized.OrderedDictionary] + $Hashtable, + [Parameter ( + Position = 2, + Mandatory = $true, + HelpMessage = "Key to search for" + )] + [ValidateNotNullOrEmpty ()] + [String] + $Key, + [Parameter ( + HelpMessage = "Define if match should be case sensitive" + )] + [Switch] + $CaseSensitive + ) + Process { + if (Find-Key -Hashtable $Hashtable -Key $Key -CaseSensitive:$CaseSensitive) { + if ($CaseSensitive) { + $Value = $Hashtable.$Key + } else { + $FormattedKey = Format-String -String $Key -Format "lowercase" + foreach ($Item in $Hashtable.GetEnumerator()) { + $FormattedItem = Format-String -String $Item.Key -Format "lowercase" + if ($FormattedItem -eq $FormattedKey) { + $Value = $Item.Value + } + } + } + return $Value + } else { + # If key does not exists, returns null + Write-Log -Type "WARN" -Message """$Key"" was not found" + return $null + } + } +} diff --git a/Public/Get-Object.ps1 b/Public/Get-Object.ps1 index 453e9a7..186b72e 100644 --- a/Public/Get-Object.ps1 +++ b/Public/Get-Object.ps1 @@ -4,10 +4,10 @@ function Get-Object { <# .SYNOPSIS - Convert file to specified encoding + Get objects .DESCRIPTION - Create a copy of a given file and convert the encoding as specified. + Get list of objects matching specifications .PARAMETER Path [String] The path parameter corresponds to the path to the directory or object to @@ -103,7 +103,12 @@ function Get-Object { HelpMessage = "Pattern to exclude" )] [String] - $Exclude = $null + $Exclude = $null, + [Parameter ( + HelpMessage = "Stop script if no results are found" + )] + [Switch] + $StopScript ) Begin { $Path = Resolve-Path -Path $Path @@ -143,7 +148,11 @@ function Get-Object { } else { Write-Log -Type "ERROR" -Message "No $($ObjectType.$Type) were found in $Path." } - Stop-Script 1 + if ($PSBoundParameters.ContainsKey("StopScript")) { + Stop-Script 1 + } else { + return $false + } } else { return $Objects } diff --git a/Public/Get-Path.ps1 b/Public/Get-Path.ps1 new file mode 100644 index 0000000..407753b --- /dev/null +++ b/Public/Get-Path.ps1 @@ -0,0 +1,49 @@ +function Get-Path { + [CmdletBinding ()] + Param ( + [Parameter ( + Position = 1, + Mandatory = $true, + HelpMessage = "List of paths to resolve" + )] + [ValidateNotNullOrEmpty ()] + [String[]] + $PathToResolve, + [Parameter ( + Position = 2, + Mandatory = $true, + HelpMessage = "Hashtable containing the paths" + )] + [ValidateNotNullOrEmpty ()] + [System.Collections.Specialized.OrderedDictionary] + $Hashtable, + [Parameter ( + Position = 3, + Mandatory = $false, + HelpMessage = "Root for relative path" + )] + [ValidateNotNullOrEmpty ()] + [String] + $Root = $PSScriptRoot + ) + Process { + $Paths = Resolve-Array -Array $PathToResolve -Delimiter "," + foreach ($Path in $Paths) { + $Pathway = $Hashtable.$Path + # If path is relative + if ($Pathway -match "^.*\\") { + $RelativePath = $Pathway -replace "^.*\\", "" + $AbsolutePath = Join-Path -Path $Root -ChildPath $RelativePath + if (Test-Path -Path $AbsolutePath) { + $Hashtable.$Path = $AbsolutePath + } else { + Write-Log -Type "INFO" -Message "Creating directory: $AbsolutePath" + New-item -ItemType "Directory" -Path "$AbsolutePath" | Out-Null + } + } elseif (-Not (Test-Path -Path $Pathway)) { + Write-Log -Type "ERROR" -Message "Path not found: $Pathway" + } + } + return $Hashtable + } +} diff --git a/Public/Get-Properties.ps1 b/Public/Get-Properties.ps1 index 56eae86..ceafa39 100644 --- a/Public/Get-Properties.ps1 +++ b/Public/Get-Properties.ps1 @@ -75,40 +75,64 @@ function Get-Properties { [Parameter ( Position = 5, Mandatory = $false, + HelpMessage = "List of properties to check" + )] + [String[]] + $ValidateSet, + [Parameter ( HelpMessage = "Define if section headers should be used to group properties or be ignored" )] [Switch] $Section ) - # Check that specified file exists - if (Test-Path -Path "$Directory\$File") { - # Parse properties with or without section split - if ($Section) { - $Properties = Read-Properties -File $File -Directory $Directory -Section - } else { - $Properties = Read-Properties -File $File -Directory $Directory - } - # Check if a custom file is provided - if ($Custom) { - # Make sure said file does exists - if (Test-Path -Path "$CustomDirectory\$Custom") { - # Override default properties with custom ones - $Customs = Read-Properties -File $Custom -Directory $CustomDirectory - foreach ($Property in $Customs.Keys) { - # Override default with custom - if ($Properties.$Property) { - $Properties.$Property = $Customs.$Property - } else { - Write-Log -Type "WARN" -Message "The ""$Property"" property defined in $Custom is unknown" + Begin { + # Get global preference variables + Get-CallerPreference -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState + } + Process { + # Check that specified file exists + $Path = Join-Path -Path $Directory -ChildPath $File + if (Test-Path -Path $Path) { + # Parse properties with or without section split + $Properties = Read-Properties -Path $Path -Section:$Section + # Check if a custom file is provided + if ($Custom) { + # Make sure said file does exists + $CustomPath = Join-Path -Path $CustomDirectory -ChildPath $Custom + if (Test-Path -Path $CustomPath) { + # Override default properties with custom ones + $Customs = Read-Properties -Path $CustomPath + foreach ($Property in $Customs.Keys) { + # Override default with custom + if ($Properties.$Property) { + $Properties.$Property = $Customs.$Property + } else { + Write-Log -Type "WARN" -Object "The ""$Property"" property defined in $Custom is unknown" + } } + } else { + Write-Log -Type "WARN" -Object "$Custom not found in directory $CustomDirectory" } - } else { - Write-Log -Type "WARN" -Message "$Custom not found in directory $CustomDirectory" } + # If some items are mandatory + if ($PSBoundParameters.ContainsKey("ValidateSet")) { + $MissingProperties = 0 + foreach ($Item in $ValidateSet) { + # Check that the property has been defined + if (-Not $Properties.$Item) { + Write-Log -Type "WARN" -Object "$Item property is missing from $File" + $MissingProperties += 1 + } + } + if ($MissingProperties -ge 1) { + if ($MissingProperties -eq 1) { $Grammar = "property is" } + else { $Grammar = "properties are" } + Write-Log -Type "ERROR" -Object "$MissingProperties $Grammar not defined" -ErrorCode 1 + } + } + return $Properties + } else { + Write-Log -Type "ERROR" -Object "$File not found in directory $Directory" -ErrorCode 1 } - return $Properties - } else { - Write-Log -Type "ERROR" -Message "$File not found in directory $Directory" - Stop-Script 1 } } diff --git a/Public/Import-CSVProperties.ps1 b/Public/Import-CSVProperties.ps1 new file mode 100644 index 0000000..1fb9dfb --- /dev/null +++ b/Public/Import-CSVProperties.ps1 @@ -0,0 +1,93 @@ +function Import-CSVProperties { + [CmdletBinding ()] + Param ( + [Parameter ( + Position = 1, + Mandatory = $true, + ValueFromPipeline = $true, + HelpMessage = "Path to the CSV file" + )] + [ValidateNotNullOrEmpty ()] + [String] + $Path, + [Parameter ( + Position = 2, + Mandatory = $false, + HelpMessage = "Name of the property to use as key" + )] + [String] + $Key, + [Parameter ( + Position = 3, + Mandatory = $false, + HelpMessage = "Specifies the delimiter that separates the property values in the CSV file. The default is a comma (,)." + )] + [String] + $Delimiter = ',', + [Parameter ( + Position = 4, + Mandatory = $false, + HelpMessage = "Specifies the value to set for empty columns. The default is a null value (NULL)." + )] + [String] + $NullValue = $null, + [Parameter ( + HelpMessage = "Specifies that the key will not be added to the corresponding list of properties" + )] + [Switch] + $IgnoreKey + ) + Begin { + # Get global preference variables + Get-CallerPreference -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState + # Instantiate variables + $Properties = New-Object -TypeName "System.Collections.Specialized.OrderedDictionary" + } + Process { + # Import values from CSV file + Write-Log -Type "DEBUG" -Object $Path + $CSVProperties = Import-CSV -Path $Path -Delimiter $Delimiter + # Grab list of headers + $Headers = ($CSVProperties | Get-Member -MemberType "NoteProperty").Name + # Check key parameter + if ($PSBoundParameters.ContainsKey('Key')) { + if ($Headers -NotContains $Key) { + Write-Log -Type "ERROR" -Object "$Key property was not found in $Path" -ErrorCode 1 + } + } + # Loop through CSV rows + foreach ($Row in $CSVProperties) { + $RowProperties = New-Object -TypeName "System.Collections.Specialized.OrderedDictionary" + # Loop through each property + foreach ($Header in $Headers) { + # If key has to be ignored + if ($IgnoreKey) { + # If the property is not the specified key + if ($Header -ne $Key) { + # Handle null or empty values + if ([String]::IsNullOrEmpty($Row.$Header)) { + $Value = $NullValue + } else { + $Value = $Row.$Header + } + # Add property to list + $RowProperties.Add($Header, $Value) + } + } else { + # Handle null or empty values + if ([String]::IsNullOrEmpty($Row.$Header)) { + $Value = $NullValue + } else { + $Value = $Row.$Header + } + # Add property to list + $RowProperties.Add($Header, $Value) + } + } + # Add row to the property list + $Properties.Add($Row.$Key, $RowProperties) + } + # Return properties hashtable + return $Properties + } +} diff --git a/Public/Import-Properties.ps1 b/Public/Import-Properties.ps1 new file mode 100644 index 0000000..360d048 --- /dev/null +++ b/Public/Import-Properties.ps1 @@ -0,0 +1,114 @@ +# ------------------------------------------------------------------------------ +# Properties setting function +# ------------------------------------------------------------------------------ +function Import-Properties { + <# + .SYNOPSIS + Import properties from configuration files + + .DESCRIPTION + Import properties from configuration files + + .PARAMETER Path + The path parameter should be the name of the property file. + + .PARAMETER Custom + The Custom parameter should be the name of the custom property file. + + .OUTPUTS + Import-Properties returns an + ordered hash table containing the names and values of the properties listed + in the property files. + + .EXAMPLE + Import-Properties -Path "\conf\default.ini" -Custom "\\shared\custom.ini" + + In this example, Import-Properties will read properties from the default.ini + file contained in the \conf directory, then read the properties from + in the custom.ini file contained in the \\shared directory, and override the + default ones with the custom ones. + + .NOTES + Import-Properties does not currently allow the use of sections to group proper- + ties in custom files + #> + [CmdletBinding ()] + Param ( + [Parameter ( + Position = 1, + Mandatory = $true, + HelpMessage = "Path to the property file" + )] + [ValidateNotNullOrEmpty ()] + [String] + $Path, + [Parameter ( + Position = 2, + Mandatory = $false, + HelpMessage = "Path to the custom property file" + )] + [String] + $Custom, + [Parameter ( + Position = 3, + Mandatory = $false, + HelpMessage = "List of properties to check" + )] + [String[]] + $ValidateSet, + [Parameter ( + HelpMessage = "Define if section headers should be used to group properties or be ignored" + )] + [Switch] + $Section + ) + Begin { + # Get global preference variables + Get-CallerPreference -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState + } + Process { + # Check that specified file exists + if (Test-Path -Path $Path) { + # Parse properties with or without section split + $Properties = Read-Properties -Path $Path -Section:$Section + # Check if a custom file is provided + if ($PSBoundParameters.ContainsKey("Custom")) { + # Make sure said file does exists + if (Test-Path -Path $Custom) { + # Override default properties with custom ones + $CustomProperties = Read-Properties -Path $Custom + foreach ($Property in $CustomProperties.Keys) { + # Override default with custom + if ($Properties.$Property) { + $Properties.$Property = $CustomProperties.$Property + } else { + Write-Log -Type "WARN" -Object "The ""$Property"" property defined in $Custom is unknown" + } + } + } else { + Write-Log -Type "ERROR" -Object "Path not found $Custom" + Write-Log -Type "WARN" -Object "No custom configuration could be retrieved" + } + } + # If some items are mandatory + if ($PSBoundParameters.ContainsKey("ValidateSet")) { + $MissingProperties = 0 + foreach ($Item in $ValidateSet) { + # Check that the property has been defined + if (-Not $Properties.$Item) { + Write-Log -Type "WARN" -Object "$Item property is missing from configuration file" + $MissingProperties += 1 + } + } + if ($MissingProperties -ge 1) { + if ($MissingProperties -eq 1) { $Grammar = "property is" } + else { $Grammar = "properties are" } + Write-Log -Type "ERROR" -Object "$MissingProperties $Grammar not defined" -ErrorCode 1 + } + } + return $Properties + } else { + Write-Log -Type "ERROR" -Object "Path not found $Path" -ErrorCode 1 + } + } +} diff --git a/Public/Out-Hashtable.ps1 b/Public/Out-Hashtable.ps1 new file mode 100644 index 0000000..d50091a --- /dev/null +++ b/Public/Out-Hashtable.ps1 @@ -0,0 +1,57 @@ +function Out-Hashtable { + [CmdletBinding ()] + Param ( + [Parameter ( + Position = 1, + Mandatory = $true, + HelpMessage = "Hashtable to output" + )] + [ValidateNotNullOrEmpty ()] + [System.Collections.Specialized.OrderedDictionary] + $Hashtable, + [Parameter ( + Position = 2, + Mandatory = $true, + HelpMessage = "Path" + )] + [ValidateNotNullOrEmpty ()] + [String] + $Path, + [Parameter ( + Position = 3, + Mandatory = $false, + HelpMessage = "Encoding" + )] + [ValidateSet ("ASCII", "BigEndianUnicode", "OEM", "Unicode", "UTF7", "UTF8", "UTF8BOM", "UTF8NoBOM", "UTF32")] + [String] + $Encoding = "UTF8", + [Parameter ( + HelpMessage = "Adds the output to the end of an existing file" + )] + [Switch] + $Append, + [Parameter ( + HelpMessage = "Prompts you for confirmation before running the cmdlet" + )] + [Switch] + $Confirm, + [Parameter ( + HelpMessage = "Prevents an existing file from being overwritten and displays a message that the file already exists" + )] + [Switch] + $NoClobber, + [Parameter ( + HelpMessage = "Specifies that the content written to the file does not end with a newline character" + )] + [Switch] + $NoNewLine, + [Parameter ( + HelpMessage = "Shows what would happen if the cmdlet runs" + )] + [Switch] + $WhatIf + ) + Process { + $Hashtable.GetEnumerator() | % { "$($_.Name)=$($_.Value)"} | Out-File -FilePath $Path -Encoding $Encoding -Append:$Append -Confirm:$Confirm -NoClobber:$NoClobber -NoNewline:$NoNewLine -WhatIf:$WhatIf + } +} diff --git a/Public/Remove-EnvironmentVariable.ps1 b/Public/Remove-EnvironmentVariable.ps1 new file mode 100644 index 0000000..0fe3e5a --- /dev/null +++ b/Public/Remove-EnvironmentVariable.ps1 @@ -0,0 +1,35 @@ +function Remove-EnvironmentVariable { + [CmdletBinding()] + Param ( + [Parameter ( + Position = 1, + Mandatory = $true, + HelpMessage = "Name of the environment variable" + )] + [ValidateNotNullOrEmpty()] + [Alias ("Variable")] + [String] + $Name, + [Parameter ( + Position = 2, + Mandatory = $false, + HelpMessage = "Scope of the environment variable" + )] + [ValidateSet ("Machine", "Process", "User")] + [String] + $Scope = "Machine" + ) + Begin { + # Get global preference variables + Get-CallerPreference -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState + } + Process { + # Check if variable is defined + if (Test-EnvironmentVariable -Variable $Name -Scope $Scope) { + Write-Log -Type "INFO" -Message "Removing $Name environment variable in $Scope scope" + [Environment]::SetEnvironmentVariable($Name, "", $Scope) + } else { + Write-Log -Type "WARN" -Message "$Name environment variable is not defined in $Scope scope" + } + } +} diff --git a/Public/Remove-Object.ps1 b/Public/Remove-Object.ps1 new file mode 100644 index 0000000..57fac90 --- /dev/null +++ b/Public/Remove-Object.ps1 @@ -0,0 +1,77 @@ +# ------------------------------------------------------------------------------ +# Generic Remove-Item with checks +# ------------------------------------------------------------------------------ +function Remove-Object { + <# + .SYNOPSIS + Remove objects + + .DESCRIPTION + Remove list of objects matching specifications + + .NOTES + /!\ Exclude is currently not supported in Windows PowerShell + See https://github.com/PowerShell/PowerShell/issues/6865 + #> + [CmdletBinding ()] + Param ( + [Parameter ( + Position = 1, + Mandatory = $true, + HelpMessage = "Path to the items" + )] + [ValidateNotNullOrEmpty ()] + [String] + $Path, + [Parameter ( + Position = 2, + Mandatory = $false, + HelpMessage = "Type of item" + )] + [ValidateSet ( + "All", + "File", + "Folder" + )] + [String] + $Type = "All", + [Parameter ( + Position = 3, + Mandatory = $false, + HelpMessage = "Filter to apply" + )] + [String] + $Filter = "*", + [Parameter ( + Position = 4, + Mandatory = $false, + HelpMessage = "Pattern to exclude" + )] + [String] + $Exclude = $null + ) + Begin { + $Path = Resolve-Path -Path $Path + if (-Not (Test-Path -Path $Path)) { + Write-Log -Type "ERROR" -Message "$Path does not exists." + Stop-Script 1 + } + } + Process { + $Objects = New-Object -TypeName System.Collections.ArrayList + # Check PowerShell version to prevent issue + $PSVersion = $PSVersionTable.PSVersion | Select-Object -ExpandProperty "Major" + if ($PSVersion -lt 6) { + $Objects = Get-Object -Path $Path -Type $Type -Filter $Filter + } else { + $Objects = Get-Object -Path $Path -Type $Type -Filter $Filter -Exclude $Exclude + } + # If objects are found + if ($Objects. Count -gt 0) { + foreach ($Object in $Objects) { + Write-Log -Type "DEBUG" -Object $Object + Remove-Item -Path $Path -Recurse -Force + } + } + } +} diff --git a/Public/Resolve-Array.ps1 b/Public/Resolve-Array.ps1 new file mode 100644 index 0000000..61c2d8b --- /dev/null +++ b/Public/Resolve-Array.ps1 @@ -0,0 +1,54 @@ +function Resolve-Array { + <# + .SYNOPSIS + Creates an array from string + + .DESCRIPTION + Creates an array from a string containing items delimited by a specified delimiter + + .PARAMETER Array + The array parameter corresponds to the string to transform to an array. + + .PARAMETER Delimiter + The delimiter parameters corresponds to the character delimiting the items + in the string. + The default value is a comma (","). + + .EXAMPLE + Resolve-Array -Array "a, b, c" -Delimiter "," + + In this example, the function will return an array containing the values a, + b, and c. + + .NOTES + File name: Resolve-Array.ps1 + Author: Florian Carrier + Creation date: 08/12/2018 + Last modified: 08/12/2018 + #> + [CmdletBinding ()] + Param ( + [Parameter ( + Position = 1, + Mandatory = $true, + HelpMessage = "Array to resolve" + )] + [ValidateNotNullOrEmpty ()] + [String[]] + $Array, + [Parameter ( + Position = 2, + Mandatory = $false, + HelpMessage = "Item delimiter" + )] + [ValidateNotNullOrEmpty ()] + [String] + $Delimiter = "," + ) + Process { + if ($Array.Count -eq 1) { + $Array = $Array.Split($Delimiter).Trim() + } + return $Array + } +} diff --git a/Public/Resolve-Boolean.ps1 b/Public/Resolve-Boolean.ps1 new file mode 100644 index 0000000..f728388 --- /dev/null +++ b/Public/Resolve-Boolean.ps1 @@ -0,0 +1,39 @@ +function Resolve-Boolean { + [CmdletBinding ()] + Param ( + [Parameter ( + Position = 1, + Mandatory = $true, + HelpMessage = "Boolean values" + )] + [String] + $Boolean, + [Parameter ( + Position = 2, + Mandatory = $true, + HelpMessage = "Hashtable containing the values" + )] + [ValidateNotNullOrEmpty ()] + [System.Collections.Specialized.OrderedDictionary] + $Hashtable + ) + Begin { + # Get global preference variables + Get-CallerPreference -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState + # Instantiate variables + $BooleanValues = Resolve-Array -Array $Boolean -Delimiter "," + } + Process { + # Loop through values + foreach ($BooleanValue in $BooleanValues) { + if (($Hashtable.$BooleanValue -eq $true) -Or ($Hashtable.$BooleanValue -eq 1)) { + $Hashtable.$BooleanValue = $true + } elseif (($Hashtable.$BooleanValue -eq $false) -Or ($Hashtable.$BooleanValue -eq 0)) { + $Hashtable.$BooleanValue = $false + } else { + Write-Log -Type "WARN" -Object "$($Hashtable.$BooleanValue) could not be parsed as a boolean" + } + } + return $Hashtable + } +} diff --git a/Public/Resolve-Tags.ps1 b/Public/Resolve-Tags.ps1 new file mode 100644 index 0000000..97c0fa6 --- /dev/null +++ b/Public/Resolve-Tags.ps1 @@ -0,0 +1,74 @@ +# ------------------------------------------------------------------------------ +# Prepare tags for Set-Tags +# ------------------------------------------------------------------------------ +function Resolve-Tags { + <# + .SYNOPSIS + Prepare tags for Set-Tags + + .DESCRIPTION + Transform hashtable of variables into list of tags usable by Set-Tags + + .PARAMETER Tags + The tags parameter cor- + responds to the list of variables to be replaced with their corresponding va- + lues. + + It has to be in the following format: + + $Tags = [Ordered]@{ + Variable1 = Value1, + Variable2 = Value2, + } + + .OUTPUTS + Resolve-Tags returns a formatted hashtable. + + .EXAMPLE + Resolve-Tags-Tags $Tags + #> + [CmdletBinding ()] + Param ( + [Parameter ( + Position = 1, + Mandatory = $true, + HelpMessage = "Tags" + )] + [ValidateNotNullOrEmpty ()] + [System.Collections.Specialized.OrderedDictionary] + $Tags, + [Parameter ( + Position = 2, + Mandatory = $false, + HelpMessage = "Prefix to append to the tag name" + )] + [String] + $Prefix, + [Parameter ( + Position = 3, + Mandatory = $false, + HelpMessage = "Suffix to append to the tag name" + )] + [String] + $Suffix + ) + Begin { + # Get global preference variables + Get-CallerPreference -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState + # Instantiate variables + $FormattedTags = New-Object -TypeName "System.Collections.Specialized.OrderedDictionary" + } + Process { + # Loop through each tags + foreach ($Tag in $Tags.GetEnumerator()) { + # Generate token-value pair + $FormattedTag = [Ordered]@{ + "Token" = [System.String]::Concat($Prefix, $Tag.Name, $Suffix) + "Value" = $Tag.Value + } + $FormattedTags.Add($Tag.Name, $FormattedTag) + } + # Return formatted tag list + return $FormattedTags + } +} diff --git a/Public/Resolve-URI.ps1 b/Public/Resolve-URI.ps1 new file mode 100644 index 0000000..2b9d9bb --- /dev/null +++ b/Public/Resolve-URI.ps1 @@ -0,0 +1,27 @@ +function Resolve-URI { + [CmdletBinding ()] + Param ( + [Parameter ( + Position = 1, + Mandatory = $true, + HelpMessage = "URI to resolve" + )] + [String] + $URI + ) + Begin { + # List of restricted characters + $RestrictedCharacters = [Ordered]@{ + "\" = "/" + " " = "%20" + } + } + Process { + # Encode URI + foreach ($RestrictedCharacter in $RestrictedCharacters.GetEnumerator()) { + $URI = $URI.Replace($RestrictedCharacter.Key, $RestrictedCharacter.Value) + } + # Return encoded URI + return $URI + } +} diff --git a/Public/Select-XMLNode.ps1 b/Public/Select-XMLNode.ps1 new file mode 100644 index 0000000..6c8b016 --- /dev/null +++ b/Public/Select-XMLNode.ps1 @@ -0,0 +1,64 @@ +function Select-XMLNode { + [CmdletBinding()] + Param ( + [Parameter ( + Position = 1, + Mandatory = $true, + HelpMessage = "XML content" + )] + [ValidateNotNullOrEmpty()] + [System.XML.XMLDocument] + $XML, + [Parameter ( + Position = 2, + Mandatory = $true, + HelpMessage = "XPath corresponding to the node" + )] + [ValidateNotNullOrEmpty()] + [String] + $XPath, + [Parameter ( + Position = 3, + Mandatory = $false, + HelpMessage = "Namespace" + )] + [ValidateNotNullOrEmpty()] + [String] + $Namespace = $XML.DocumentElement.NamespaceURI + ) + Begin { + # Get global preference variables + Get-CallerPreference -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState + } + Process { + # Variables + $Delimiter = "/" + $Alias = "x" + $SpecialCharacters = [RegEx]::New('^[/.@]*') + if ($XPath -match $SpecialCharacters) { + $Prefix = $Matches[0] + $XPath = $XPath -replace $SpecialCharacters, '' + } + # Get namespace + $NamespaceManager = New-Object -TypeName "Xml.XmlNamespaceManager" -ArgumentList $XML.NameTable + $NamespaceManager.AddNamespace($Alias, $Namespace) + # Split XPath to identify nodes + $Nodes = $XPath.Split($Delimiter) + $PrefixedNodes = New-Object -TypeName "System.Collections.ArrayList" + # Prefix nodes with namespace (alias) + foreach($Node in $Nodes) { + if ($Node) { + [Void]$PrefixedNodes.Add("${Alias}:${Node}") + } + } + # Join prefixed-nodes to create new XPath with namespace + $XPathWithNamespace = $PrefixedNodes -join $Delimiter + # Check XPath prefix + if ($Prefix) { + $XPathWithNamespace = $Prefix + "" + $XPathWithNamespace + } + # Select and return nodes + $SelectedNodes = $XML.SelectNodes($XPathWithNamespace, $NamespaceManager) + return $SelectedNodes + } +} diff --git a/Public/Set-EnvironmentVariable.ps1 b/Public/Set-EnvironmentVariable.ps1 new file mode 100644 index 0000000..7588578 --- /dev/null +++ b/Public/Set-EnvironmentVariable.ps1 @@ -0,0 +1,42 @@ +function Set-EnvironmentVariable { + [CmdletBinding()] + Param ( + [Parameter ( + Position = 1, + Mandatory = $true, + HelpMessage = "Name of the environment variable" + )] + [ValidateNotNullOrEmpty()] + [Alias ("Variable")] + [String] + $Name, + [Parameter ( + Position = 2, + Mandatory = $true, + HelpMessage = "Value of the environment variable" + )] + [ValidateNotNullOrEmpty()] + [String] + $Value, + [Parameter ( + Position = 3, + Mandatory = $false, + HelpMessage = "Scope of the environment variable" + )] + [ValidateSet ("Machine", "Process", "User")] + [String] + $Scope = "Machine" + ) + Begin { + # Get global preference variables + Get-CallerPreference -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState + } + Process { + # Check if variable is defined + if (Test-EnvironmentVariable -Variable $Name -Scope $Scope) { + Write-Log -Type "WARN" -Message "Overwriting existing $Name environment variable in $Scope scope" + } + Write-Log -Type "DEBUG" -Message "$Scope`t$Name=$Value" + [Environment]::SetEnvironmentVariable($Name, $Value, $Scope) + } +} diff --git a/Public/Set-RelativePath.ps1 b/Public/Set-RelativePath.ps1 new file mode 100644 index 0000000..7234ea2 --- /dev/null +++ b/Public/Set-RelativePath.ps1 @@ -0,0 +1,41 @@ +function Set-RelativePath { + [CmdletBinding ()] + Param ( + [Parameter ( + Position = 1, + Mandatory = $true, + HelpMessage = "List of paths to resolve" + )] + [ValidateNotNullOrEmpty ()] + [String[]] + $Path, + [Parameter ( + Position = 2, + Mandatory = $true, + HelpMessage = "Hashtable containing the paths" + )] + [ValidateNotNullOrEmpty ()] + [System.Collections.Specialized.OrderedDictionary] + $Hashtable, + [Parameter ( + Position = 3, + Mandatory = $true, + HelpMessage = "Root for relative paths" + )] + [ValidateNotNullOrEmpty ()] + [String] + $Root + ) + Begin { + # Get global preference variables + Get-CallerPreference -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState + } + Process { + $RelativePaths = Resolve-Array -Array $Path -Delimiter "," + foreach ($RelativePath in $RelativePaths) { + # Write-Log -Type "DEBUG" -Object $Hashtable.$RelativePath + $Hashtable.$RelativePath = Join-Path -Path $Root -ChildPath $Hashtable.$RelativePath + } + return $Hashtable + } +} diff --git a/Public/Test-EnvironmentVariable.ps1 b/Public/Test-EnvironmentVariable.ps1 new file mode 100644 index 0000000..a50471b --- /dev/null +++ b/Public/Test-EnvironmentVariable.ps1 @@ -0,0 +1,34 @@ +function Test-EnvironmentVariable { + [CmdletBinding()] + Param ( + [Parameter ( + Position = 1, + Mandatory = $true, + HelpMessage = "Name of the environment variable" + )] + [ValidateNotNullOrEmpty()] + [Alias ("Variable")] + [String] + $Name, + [Parameter ( + Position = 2, + Mandatory = $false, + HelpMessage = "Scope of the environment variable" + )] + [ValidateSet ("Machine", "Process", "User")] + [String] + $Scope = "Machine" + ) + Begin { + # Get global preference variables + Get-CallerPreference -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState + } + Process { + # Check if variable is defined + if (Get-EnvironmentVariable -Name $Name -Scope $Scope) { + return $true + } else { + return $false + } + } +} diff --git a/Public/Test-Object.ps1 b/Public/Test-Object.ps1 new file mode 100644 index 0000000..ce46eef --- /dev/null +++ b/Public/Test-Object.ps1 @@ -0,0 +1,55 @@ +function Test-Object { + <# + .SYNOPSIS + Wrapper for Test-Path + + .DESCRIPTION + Wrapper function for Test-Path to catch access permission issues + + .INPUTS Path + The path parameter corresponds to the path to the object to test + + .INPUTS NotFound + The not found flag reverse the valid outcome of the test to allow for negative testing + #> + [CmdletBinding ()] + Param ( + [Parameter ( + Position = 1, + Mandatory = $true, + HelpMessage = "Path to the object" + )] + [ValidateNotNUllOrEmpty ()] + [String] + $Path, + [Parameter ( + HelpMessage = "Reverse valid outcome of the test" + )] + [Switch] + $NotFound + ) + Begin { + # Get global preference variables + Get-CallerPreference -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState + } + Process { + # Test path + try { + $Outcome = Test-Path -Path $Path -ErrorAction "Stop" + } catch [System.UnauthorizedAccessException] { + if (-Not $Error[0].Exception) { + $ErrorMessage = "Access is denied ($Path)" + } else { + $ErrorMessage = $Error[0].Exception + } + Write-Log -Type "DEBUG" -Object $ErrorMessage + $Outcome = $true + } + # Output test result + if ($PSBoundParameters.ContainsKey("NotFound")) { + return (-Not $Outcome) + } else { + return $Outcome + } + } +} diff --git a/Public/Test-SQLConnection.ps1 b/Public/Test-SQLConnection.ps1 index 9151593..31d3292 100644 --- a/Public/Test-SQLConnection.ps1 +++ b/Public/Test-SQLConnection.ps1 @@ -49,7 +49,19 @@ function Test-SQLConnection { the "password" password. .NOTES - TODO Add secured password handling + File name: Test-SQLConnection.ps1 + Author: Florian Carrier + Creation date: 15/10/2018 + Last modified: 12/06/2019 + Dependencies: Test-SQLConnection requires the SQLServer module + TODO Add secured password handling + + .LINK + https://github.com/Akaizoku/PSTK + + .LINK + https://docs.microsoft.com/en-us/sql/powershell/download-sql-server-ps-module + #> [CmdletBinding ()] Param ( @@ -90,33 +102,40 @@ function Test-SQLConnection { Mandatory = $false, HelpMessage = "Password" )] - [Alias ("PW")] + [Alias ("Pw")] [String] $Password ) - # Break-down connection info - if ($Security) { - Write-Debug "SQL Server authentication" - if ($Username) { - $ConnectionString = "Server=$Server; Database=$Database; Integrated Security=False; User ID=$Username; Password=$Password; Connect Timeout=3;" + Begin { + # Get global preference variables + Get-CallerPreference -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState + } + Process { + # Break-down connection info + if ($Security) { + if ($Username) { + $ConnectionString = "Server=$Server; Database=$Database; Integrated Security=False; User ID=$Username; Password=$Password; Connect Timeout=3;" + } else { + Write-Log -Type "ERROR" -Message "Please provide a valid username" + Write-Log -Type "DEBUG" -Message "$Username" + Stop-Script 1 + } } else { - Write-Log -Type "ERROR" -Message "Please provide a valid username" - Write-Debug "$Username" - Stop-Script 1 + # Else default to integrated security + Write-Log -Type "DEBUG" -Message "Integrated Security" + $ConnectionString = "Server=$Server; Database=$Database; Integrated Security=True; Connect Timeout=3;" + } + # Create connection object + Write-Log -Type "DEBUG" -Object $ConnectionString + $Connection = New-Object -TypeName "System.Data.SqlClient.SqlConnection" -ArgumentList $ConnectionString + # Try to open the connection + try { + $Connection.Open() + $Connection.Close() + return $true + } catch { + Write-Log -Type "DEBUG" -Message "Unable to connect to $ConnectionString" + return $false } - } else { - Write-Debug "Integrated Secutiry" - $ConnectionString = "Server=$Server; Database=$Database; Integrated Security=True; Connect Timeout=3;" - } - # Create connection object - $Connection = New-Object -TypeName System.Data.SqlClient.SqlConnection -ArgumentList $ConnectionString - # Try to open the connection - try { - $Connection.Open() - $Connection.Close() - return $true - } catch { - Write-Debug "Unable to connect to $ConnectionString" - return $false } } diff --git a/Public/Test-Service.ps1 b/Public/Test-Service.ps1 new file mode 100644 index 0000000..6068e1e --- /dev/null +++ b/Public/Test-Service.ps1 @@ -0,0 +1,60 @@ +# ------------------------------------------------------------------------------ +# Check service +# ------------------------------------------------------------------------------ +function Test-Service { + <# + .SYNOPSIS + Check if service exists + + .DESCRIPTION + Check if a service with the provided name exists + + .PARAMETER Service + The service parameter corresponds to the name of the service to look for + + .INPUTS + This function accepts strings as input values from pipeline. + + .OUTPUTS + Returns boolean value: + - true if service exists + - false if service is not found + + .EXAMPLE + Test-Service -Service "WildFly" + + This example returns true if you have installed WildFly as a service. + + .NOTES + File name: Test-Service.ps1 + Author: Florian Carrier + Creation date: 22/01/2019 + Last modified: 22/01/2019 + #> + [CmdletBinding()] + Param ( + [Parameter ( + Position = 1, + Mandatory = $true, + ValueFromPipeline = $true, + HelpMessage = "Name of the service" + )] + [ValidateNotNullOrEmpty()] + [Alias ("Service")] + [String] + $Name + ) + Begin { + # Get global preference variables + Get-CallerPreference -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState + } + Process { + try { + if (Get-Service -Name $Name -ErrorAction "Stop") { + return $true + } + } catch { + return $false + } + } +} diff --git a/Public/Update-File.ps1 b/Public/Update-File.ps1 new file mode 100644 index 0000000..9c414e0 --- /dev/null +++ b/Public/Update-File.ps1 @@ -0,0 +1,53 @@ +function Update-File { + <# + .SYNOPSIS + Replaces a string value in a file + + .DESCRIPTION + Replaces a specified string in a text file by a given value. + + .PARAMETER Path + The path parameter corresponds + + .NOTES + File name: Update-File.ps1 + Author: Florian Carrier + Creation date: 08/12/2018 + Last modified: 14/06/2018 + #> + [CmdletBinding ()] + Param ( + [Parameter ( + Position = 1, + Mandatory = $true, + HelpMessage = "Path to the file to update" + )] + [ValidateNotNullOrEmpty ()] + [String] + $Path, + [Parameter ( + Position = 2, + Mandatory = $true, + HelpMessage = "Old string to replace" + )] + [ValidateNotNullOrEmpty ()] + [String] + $OldString, + [Parameter ( + Position = 3, + Mandatory = $true, + HelpMessage = "New string to replace old with" + )] + [String] + $NewString, + [Parameter ( + Position = 4, + Mandatory = $false, + HelpMessage = "Encoding" + )] + [String] + $Encoding = "ASCII" + ) + $FileContent = Get-Content -Path $Path + $FileContent -replace $OldString, $NewString | Out-File -FilePath $Path -Encoding $Encoding +} diff --git a/Public/Write-ErrorLog.ps1 b/Public/Write-ErrorLog.ps1 new file mode 100644 index 0000000..36d4ea7 --- /dev/null +++ b/Public/Write-ErrorLog.ps1 @@ -0,0 +1,44 @@ +function Write-ErrorLog { + [CmdletBinding()] + Param ( + [Parameter ( + Position = 1, + Mandatory = $true, + HelpMessage = "Path to the output file" + )] + [String] + $Path, + [Parameter ( + Position = 2, + Mandatory = $true, + HelpMessage = "Message to log" + )] + [Alias ("Message")] + [Object] + $Object, + [Parameter ( + Position = 3, + Mandatory = $false, + HelpMessage = "Error code" + )] + [Int] + $ErrorCode + ) + Begin { + # Get global preference variables + Get-CallerPreference -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState + # Ensure message is a string + if ($Object.GetType() -ne "String") { + $Message = ($Object | Out-String).Trim() + } else { + $Message = $Object.Trim() + } + } + Process { + Write-Log -Type "DEBUG" -Message $Path + $Message | Out-File -FilePath $Path -Append -Force + if ($PSBoundParameters.ContainsKey("ErrorCode")) { + Stop-Script -ErrorCode $ErrorCode + } + } +} diff --git a/Public/Write-Log.ps1 b/Public/Write-Log.ps1 index 241a9dd..7ea2804 100644 --- a/Public/Write-Log.ps1 +++ b/Public/Write-Log.ps1 @@ -10,13 +10,29 @@ function Write-Log { The Write-Log function outputs the time and type of a message in a formatt- ed manner with respective colour code. + It takes two parameters: + - Type of output: information, warning, error, debug, or checkpoint. + - Message: output content. + .PARAMETER Type The Type parameter defines the level of importance of the message and will influence the colour of the output. + There are five available types: + - CHECK: checkpoint, used to confirm a status. + - DEBUG: debug message, used to debug scripts. + - ERROR: error message, used to provide detail on an issue. + - INFO: information, used to convey a message. + - WARN: warnign, used to detail a non-blocking issue. + .PARAMETER Message The Message parameter corresponds to the desired output to be logged. + .PARAMETER ErrorCode + The error code parameter acts as a switch. If specified, the script exe- + cution is terminated and the value corresponds to the error code to throw + when terminating the script. + .INPUTS None. You cannot pipe objects to Write-Log. @@ -43,6 +59,13 @@ function Write-Log { and the specified message itself. The message will be displayed in red in the host. + .EXAMPLE + Write-Log -Type "ERROR" -Message "This is an error message." -ErrorCode 1 + + This example outputs an error message with the timestamp, the "ERROR" tag, + and the specified message itself. The script will terminate with the exit + code 1. + .EXAMPLE Write-Log -Type "CHECK" -Message "This is a checkpoint message." @@ -50,8 +73,21 @@ function Write-Log { tag, and the specified message itself. The message will be displayed in green in the host. + .EXAMPLE + Write-Log -Type "DEBUG" -Message "This is a debug message." + + This example outputs a message through the default DEBUG PowerShell chanel, + if the -DEBUG flag is enabled. + .NOTES - TODO Add locale variable + File name: Write-Log.ps1 + Author: Florian Carrier + Creation date: 15/10/2018 + Last modified: 06/06/2019 + TODO Add locale variable + + .LINK + https://github.com/Akaizoku/PSTK #> [CmdletBinding ()] # Inputs @@ -63,6 +99,7 @@ function Write-Log { )] [ValidateSet ( "CHECK", + "DEBUG", "ERROR", "INFO", "WARN" @@ -74,21 +111,59 @@ function Write-Log { Mandatory = $true, HelpMessage = "Message to output" )] - [ValidateNotNullOrEmpty ()] - [Alias ("Output", "Log")] + [ValidateNotNull ()] + [Alias ("Message", "Output", "Log")] + [Object] + $Object, + [Parameter ( + Position = 3, + Mandatory = $false, + HelpMessage = "Error code" + )] + [Int] + $ErrorCode, + [Parameter ( + Position = 4, + Mandatory = $false, + HelpMessage = "Path to an optional output file" + )] + [Alias ("Path")] [String] - $Message + $File ) - # Variables - $Time = Get-Date -Format "yyyy-MM-dd HH:mm:ss" - $Colour = [Ordered]@{ - "CHECK" = "Green" - "ERROR" = "Red" - "INFO" = "White" - "WARN" = "Yellow" + Begin { + # Get global preference variables + Get-CallerPreference -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState + # Variables + $Time = Get-Date -Format "yyyy-MM-dd HH:mm:ss" + $Colour = [Ordered]@{ + "CHECK" = "Green" + "ERROR" = "Red" + "INFO" = "White" + "WARN" = "Yellow" + } + # Ensure message is a string + if ($Object.GetType() -ne "String") { + $Message = ($Object | Out-String).Trim() + } else { + $Message = $Object.Trim() + } + } + Process { + if ($Type -eq "DEBUG") { + Write-Debug -Message $Message + } else { + # Format log + $Log = "$Time`t$Type`t$Message" + # Output + Write-Host -Object $Log -ForegroundColor $Colour.$Type + } + if ($PSBoundParameters.ContainsKey("File")) { + Write-Log -Type "DEBUG" -Message $Path + $Message | Out-File -FilePath $Path -Append -Force + } + if ($PSBoundParameters.ContainsKey("ErrorCode")) { + Stop-Script -ErrorCode $ErrorCode + } } - # Format log - $Log = "$Time`t$Type`t$Message" - # Output - Write-Host $Log -ForegroundColor $Colour.$Type } diff --git a/README.md b/README.md index c424571..769d5ef 100644 --- a/README.md +++ b/README.md @@ -20,25 +20,50 @@ Import-Module -Name PSTK Get-Command -Module PSTK ``` -| CommandType | Name | Version | Source | -| ----------- | --------------------- | ------- | ------ | -| Function | Compare-Hashtable | 1.0.0 | PSTK | -| Function | Compare-Properties | 1.0.0 | PSTK | -| Function | Complete-RelativePath | 1.0.0 | PSTK | -| Function | Convert-FileEncoding | 1.0.0 | PSTK | -| Function | ConvertTo-NaturalSort | 1.0.0 | PSTK | -| Function | ConvertTo-PDF | 1.0.0 | PSTK | -| Function | Copy-OrderedHashtable | 1.0.0 | PSTK | -| Function | Format-String | 1.0.0 | PSTK | -| Function | Get-Object | 1.0.0 | PSTK | -| Function | Get-Properties | 1.0.0 | PSTK | -| Function | New-DynamicParameter | 1.0.0 | PSTK | -| Function | Rename-NumberedFile | 1.0.0 | PSTK | -| Function | Set-Tags | 1.0.0 | PSTK | -| Function | Start-Script | 1.0.0 | PSTK | -| Function | Stop-Script | 1.0.0 | PSTK | -| Function | Test-SQLConnection | 1.0.0 | PSTK | -| Function | Write-Log | 1.0.0 | PSTK | +| CommandType | Name | Version | Source | +| ----------- | -------------------------- | ------- | ------ | +| Function | Compare-Hashtable | 1.2.0 | PSTK | +| Function | Compare-Properties | 1.2.0 | PSTK | +| Function | Complete-RelativePath | 1.2.0 | PSTK | +| Function | Confirm-Prompt | 1.2.0 | PSTK | +| Function | Convert-FileEncoding | 1.2.0 | PSTK | +| Function | ConvertTo-NaturalSort | 1.2.0 | PSTK | +| Function | ConvertTo-PDF | 1.2.0 | PSTK | +| Function | Copy-OrderedHashtable | 1.2.0 | PSTK | +| Function | Expand-CompressedFile | 1.2.0 | PSTK | +| Function | Find-Key | 1.2.0 | PSTK | +| Function | Format-String | 1.2.0 | PSTK | +| Function | Get-CallerPreference | 1.2.0 | PSTK | +| Function | Get-EnvironmentVariable | 1.2.0 | PSTK | +| Function | Get-HTTPStatus | 1.2.0 | PSTK | +| Function | Get-KeyValue | 1.2.0 | PSTK | +| Function | Get-Object | 1.2.0 | PSTK | +| Function | Get-Path | 1.2.0 | PSTK | +| Function | Get-Properties | 1.2.0 | PSTK | +| Function | Import-CSVProperties | 1.2.0 | PSTK | +| Function | Import-Properties | 1.2.0 | PSTK | +| Function | New-DynamicParameter | 1.2.0 | PSTK | +| Function | Out-Hashtable | 1.2.0 | PSTK | +| Function | Remove-EnvironmentVariable | 1.2.0 | PSTK | +| Function | Remove-Object | 1.2.0 | PSTK | +| Function | Rename-NumberedFile | 1.2.0 | PSTK | +| Function | Resolve-Array | 1.2.0 | PSTK | +| Function | Resolve-Boolean | 1.2.0 | PSTK | +| Function | Resolve-Tags | 1.2.0 | PSTK | +| Function | Resolve-URI | 1.2.0 | PSTK | +| Function | Select-XMLNode | 1.2.0 | PSTK | +| Function | Set-EnvironmentVariable | 1.2.0 | PSTK | +| Function | Set-RelativePath | 1.2.0 | PSTK | +| Function | Set-Tags | 1.2.0 | PSTK | +| Function | Start-Script | 1.2.0 | PSTK | +| Function | Stop-Script | 1.2.0 | PSTK | +| Function | Test-EnvironmentVariable | 1.2.0 | PSTK | +| Function | Test-Object | 1.2.0 | PSTK | +| Function | Test-Service | 1.2.0 | PSTK | +| Function | Test-SQLConnection | 1.2.0 | PSTK | +| Function | Update-File | 1.2.0 | PSTK | +| Function | Write-ErrorLog | 1.2.0 | PSTK | +| Function | Write-Log | 1.2.0 | PSTK | ## Dependencies diff --git a/Tests/Add-Offset.ps1 b/Tests/Add-Offset.ps1 new file mode 100644 index 0000000..9d307b4 --- /dev/null +++ b/Tests/Add-Offset.ps1 @@ -0,0 +1,79 @@ +<# + .SYNOPSIS + Add-Offset Unit Testing + + .DESCRIPTION + Unit Test for Add-Offset function from PSTK module + + .NOTES + File name: Add-Offset.ps1 + Author: Florian Carrier + Creation date: 15/10/2018 + Last modified: 15/10/2018 +#> + +# ------------------------------------------------------------------------------ +# Initialisation +# ------------------------------------------------------------------------------ +$Path = Split-Path -Path (Split-Path -Path $MyInvocation.MyCommand.Definition) -Parent +$Repository = Join-Path -Path $Path -ChildPath "Private" +# Import functions +$Scripts = @( + $MyInvocation.MyCommand.Name, + "Test-Alphanumeric.ps1" +) +foreach ($Script in $Scripts) { + $Link = Join-Path -Path $Repository -ChildPath $Script + . $Link +} +# ------------------------------------------------------------------------------ +# Test objects +# ------------------------------------------------------------------------------ +# Reference +$Reference = @( + "a1", + "b2", + "g7", + "z26" +) + +# + 2 +$Positive = @( + "a3", + "b4", + "g9", + "z28" +) + +# - 3 +$Negative = @( + "a0", + "b1", + "g6", + "z25" +) + +# ------------------------------------------------------------------------------ +# Test +# ------------------------------------------------------------------------------ +$PositiveCounter = 0 +$NegativeCounter = 0 +for ($i=0; $i -lt $Reference.Length; $i++) { + $NewPositiveValue = Add-Offset -Alphanumeric $Reference[$i] -Offset 2 + if ($NewPositiveValue -eq $Positive[$i]) { $PositiveCounter += 1 } + $NewNegativeValue = Add-Offset -Alphanumeric $Reference[$i] -Offset -1 + if ($NewNegativeValue -eq $Negative[$i]) { $NegativeCounter += 1 } +} +if ($PositiveCounter -eq $Reference.Length) { $CheckPositive = $true } +else { $CheckPositive = $false } +if ($NegativeCounter -eq $Reference.Length) { $CheckNegative = $true } +else { $CheckNegative = $false } + +# ------------------------------------------------------------------------------ +# Check outcome +# ------------------------------------------------------------------------------ +if ($CheckPositive -And $CheckNegative) { + return $true +} else { + return $false +} diff --git a/Tests/Compare-Hashtable.ps1 b/Tests/Compare-Hashtable.ps1 new file mode 100644 index 0000000..3758d17 --- /dev/null +++ b/Tests/Compare-Hashtable.ps1 @@ -0,0 +1,67 @@ +<# + .SYNOPSIS + Compare-Hashtable Unit Testing + + .DESCRIPTION + Unit Test for Compare-Hashtable function from PSTK module + + .NOTES + File name: Compare-Hashtable.ps1 + Author: Florian Carrier + Creation date: 31/08/2018 + Last modified: 04/10/2018 +#> + +# ------------------------------------------------------------------------------ +# Initialisation +# ------------------------------------------------------------------------------ +$Path = Split-Path $MyInvocation.MyCommand.Definition +$Repository = Split-Path $Path -Parent +# Import toolbox +Import-Module "$Repository\PSTK" -Force + +# ------------------------------------------------------------------------------ +# Test objects +# ------------------------------------------------------------------------------ +# Reference +$Reference = [ordered]@{ + Property1 = 1 + Property2 = 2 + Property3 = 3 + Property4 = 4 + Property5 = 5 +} +# Exact match +$Exact = [ordered]@{ + Property1 = 1 + Property2 = 2 + Property3 = 3 + Property4 = 4 + Property5 = 5 +} +# No match +$Inexact = [ordered]@{ + Property1 = 5 + Property2 = 4 + Property3 = 3 + Property4 = 2 + Property5 = 1 +} +# Empty hashtable +$Empty = [ordered]@{} + +# ------------------------------------------------------------------------------ +# Test +# ------------------------------------------------------------------------------ +$CheckExact = Compare-Hashtable -Reference $Reference -Difference $Exact +$CheckInexact = Compare-Hashtable -Reference $Reference -Difference $Inexact +$CheckEmpty = Compare-Hashtable -Reference $Reference -Difference $Empty + +# ------------------------------------------------------------------------------ +# Check outcome +# ------------------------------------------------------------------------------ +if ($CheckExact -And !$CheckInexact -And !$CheckEmpty) { + return $true +} else { + return $false +} diff --git a/Tests/ConvertTo-PDF.ps1 b/Tests/ConvertTo-PDF.ps1 new file mode 100644 index 0000000..4479f3d --- /dev/null +++ b/Tests/ConvertTo-PDF.ps1 @@ -0,0 +1,51 @@ +<# + .SYNOPSIS + ConvertTo-PDF Unit Testing + + .DESCRIPTION + Unit Test for ConvertTo-PDF function from PSTK module + + .NOTES + File name: ConvertTo-PDF.ps1 + Author: Florian Carrier + Creation date: 26/09/2018 + Last modified: 26/09/2018 +#> + +# ------------------------------------------------------------------------------ +# Initialisation +# ------------------------------------------------------------------------------ +$Path = Split-Path $MyInvocation.MyCommand.Definition +$Repository = Split-Path $Path -Parent +# Import toolbox +Import-Module "$Repository\PSTK" -Force + +# ------------------------------------------------------------------------------ +# Test objects +# ------------------------------------------------------------------------------ +$DocumentPath = Join-Path -Path $Path -ChildPath ".\res" +$PDF = "*.pdf" +$Expected = @("Test Word 97-2003 Document.pdf", "Test Word Document.pdf") + +# ------------------------------------------------------------------------------ +# Test +# ------------------------------------------------------------------------------ +$Check1 = ConvertTo-PDF -Path $DocumentPath +$Generated = Get-ChildItem -Path $DocumentPath -Filter $PDF | Select -Expand "Name" +$Compare = Compare-Object -ReferenceObject $Generated -DifferenceObject $Expected -PassThru +if ($Compare -eq $null) { + $Check2 = $true +} else { + $Check2 = $false +} +# Clean-up +Remove-Item -Path "$DocumentPath\*" -Filter $PDF + +# ------------------------------------------------------------------------------ +# Check outcome +# ------------------------------------------------------------------------------ +if ($Check1 -And $Check2) { + return $true +} else { + return $false +} diff --git a/Tests/ConvertTo-TitleCase.ps1 b/Tests/ConvertTo-TitleCase.ps1 new file mode 100644 index 0000000..970385d --- /dev/null +++ b/Tests/ConvertTo-TitleCase.ps1 @@ -0,0 +1,63 @@ +<# + .SYNOPSIS + ConvertTo-TitleCase Unit Testing + + .DESCRIPTION + Unit Test for ConvertTo-TitleCase function from PSTK module + + .NOTES + File name: ConvertTo-TitleCase.ps1 + Author: Florian Carrier + Creation date: 05/10/2018 + Last modified: 16/10/2018 +#> + +# ------------------------------------------------------------------------------ +# Initialisation +# ------------------------------------------------------------------------------ +$Path = Split-Path -Path (Split-Path -Path $MyInvocation.MyCommand.Definition) -Parent +$Repository = Join-Path -Path $Path -ChildPath "Private" +# Import functions +$Scripts = @( + $MyInvocation.MyCommand.Name +) +foreach ($Script in $Scripts) { + $Link = Join-Path -Path $Repository -ChildPath $Script + . $Link +} + +# ------------------------------------------------------------------------------ +# Test objects +# ------------------------------------------------------------------------------ +$Strings = [Ordered]@{ + Letter = "L" + Word = "Lorem" + Sentence = "Lorem ipsum dolor sit amet, consectetur adipiscing elit." + Test = "lOrEm" +} +$Expected = [Ordered]@{ + Letter = "L" + Word = "Lorem" + Sentence = "Lorem Ipsum Dolor Sit Amet, Consectetur Adipiscing Elit." + Test = "Lorem" +} + + +# ------------------------------------------------------------------------------ +# Test +# ------------------------------------------------------------------------------ +$Check = $true +foreach ($Entry in $Strings.GetEnumerator()) { + $Key = $Entry.Key + $String = $Entry.Value + $FormattedString = ConvertTo-TitleCase -String $String + if ($FormattedString -ne $Expected.$Key) { + $Check = $false + break + } +} + +# ------------------------------------------------------------------------------ +# Check outcome +# ------------------------------------------------------------------------------ +return $Check diff --git a/Tests/Copy-OrderedHashtable.ps1 b/Tests/Copy-OrderedHashtable.ps1 new file mode 100644 index 0000000..40ef29f --- /dev/null +++ b/Tests/Copy-OrderedHashtable.ps1 @@ -0,0 +1,68 @@ +<# + .SYNOPSIS + Copy-OrderedHashtable Unit Testing + + .DESCRIPTION + Unit Test for Copy-OrderedHashtable function from PSTK module + + .NOTES + File name: Copy-OrderedHashtable.ps1 + Author: Florian Carrier + Creation date: 31/08/2018 + Last modified: 04/10/2018 +#> + +# ------------------------------------------------------------------------------ +# Initialisation +# ------------------------------------------------------------------------------ +$Path = Split-Path $MyInvocation.MyCommand.Definition +$Repository = Split-Path $Path -Parent +# Import toolbox +Import-Module "$Repository\PSTK" -Force + +# ------------------------------------------------------------------------------ +# Test objects +# ------------------------------------------------------------------------------ +# Simple +$Simple = [ordered]@{ + Property1 = 1 + Property2 = 2 + Property3 = 3 + Property4 = 4 + Property5 = 5 +} +# Complex +$Complex = [ordered]@{ + Section1 = [ordered]@{ + Property1 = 1 + Property2 = 2 + } + Property3 = 3 + Section2 = [ordered]@{ + Property4 = 4 + Property5 = 5 + } +} +# Empty hashtable +$Empty = [ordered]@{} + +# ------------------------------------------------------------------------------ +# Test +# ------------------------------------------------------------------------------ +$CloneSimple = Copy-OrderedHashtable -Hashtable $Simple +$CheckSimple = Compare-Hashtable -Reference $CloneSimple -Difference $Simple + +$CloneComplex = Copy-OrderedHashtable -Hashtable $Complex +$CheckComplex = Compare-Hashtable -Reference $CloneComplex -Difference $Complex + +$CloneEmpty = Copy-OrderedHashtable -Hashtable $Empty +$CheckEmpty = Compare-Hashtable -Reference $CloneEmpty -Difference $Empty + +# ------------------------------------------------------------------------------ +# Check outcome +# ------------------------------------------------------------------------------ +if ($CloneSimple -And $CheckComplex -And $CheckEmpty) { + return $true +} else { + return $false +} diff --git a/Tests/Format-String.ps1 b/Tests/Format-String.ps1 new file mode 100644 index 0000000..9a87573 --- /dev/null +++ b/Tests/Format-String.ps1 @@ -0,0 +1,99 @@ +<# + .SYNOPSIS + Format-String Unit Testing + + .DESCRIPTION + Unit Test for Format-String function from PSTK module + + .NOTES + File name: Format-String.ps1 + Author: Florian Carrier + Creation date: 05/10/2018 + Last modified: 05/10/2018 +#> + +# ------------------------------------------------------------------------------ +# Initialisation +# ------------------------------------------------------------------------------ +$Path = Split-Path $MyInvocation.MyCommand.Definition +$Repository = Split-Path $Path -Parent +# Import toolbox +Import-Module "$Repository\PSTK" -Force + +# ------------------------------------------------------------------------------ +# Test objects +# ------------------------------------------------------------------------------ +$Strings = [Ordered]@{ + Letter = "L" + Word = "Lorem" + Sentence = "Lorem ipsum dolor sit amet, consectetur adipiscing elit." + Test = "lOrEm" +} +$Expected = [Ordered]@{ + Letter = [Ordered]@{ + CamelCase = "l" + KebabCase = "l" + LowerCase = "l" + PaslcalCase = "L" + SentenceCase = "L" + SnakeCase = "l" + TitleCase = "L" + TrainCase = "L" + UpperCase = "L" + } + Word = [Ordered]@{ + CamelCase = "lorem" + KebabCase = "lorem" + LowerCase = "lorem" + PaslcalCase = "Lorem" + SentenceCase = "Lorem" + SnakeCase = "lorem" + TitleCase = "Lorem" + TrainCase = "Lorem" + UpperCase = "LOREM" + } + Sentence = [Ordered]@{ + CamelCase = "loremIpsumDolorSitAmetConsecteturAdipiscingElit" + KebabCase = "lorem-ipsum-dolor-sit-amet-consectetur-adipiscing-elit" + LowerCase = "lorem ipsum dolor sit amet, consectetur adipiscing elit." + PaslcalCase = "LoremIpsumDolorSitAmetConsecteturAdipiscingElit" + SentenceCase = "Lorem ipsum dolor sit amet, consectetur adipiscing elit." + SnakeCase = "lorem_ipsum_dolor_sit_amet_consectetur_adipiscing_elit" + TitleCase = "Lorem Ipsum Dolor Sit Amet, Consectetur Adipiscing Elit." + TrainCase = "Lorem_Ipsum_Dolor_Sit_Amet_Consectetur_Adipiscing_Elit" + UpperCase = "LOREM IPSUM DOLOR SIT AMET, CONSECTETUR ADIPISCING ELIT." + } + Test = [Ordered]@{ + CamelCase = "lorem" + KebabCase = "lorem" + LowerCase = "lorem" + PaslcalCase = "Lorem" + SentenceCase = "Lorem" + SnakeCase = "lorem" + TitleCase = "Lorem" + TrainCase = "Lorem" + UpperCase = "LOREM" + } +} + + +# ------------------------------------------------------------------------------ +# Test +# ------------------------------------------------------------------------------ +$Check = $true +foreach ($Entry in $Strings.GetEnumerator()) { + $Key = $Entry.Key + $String = $Entry.Value + foreach ($Format in $Expected.$Key.Keys) { + $FormattedString = Format-String -String $String -Format $Format + if ($FormattedString -ne $Expected.$Key.$Format) { + $Check = $false + break + } + } +} + +# ------------------------------------------------------------------------------ +# Check outcome +# ------------------------------------------------------------------------------ +return $Check diff --git a/Tests/Read-Properties.ps1 b/Tests/Read-Properties.ps1 new file mode 100644 index 0000000..55460ef --- /dev/null +++ b/Tests/Read-Properties.ps1 @@ -0,0 +1,88 @@ +<# + .SYNOPSIS + Read-Properties Unit Testing + + .DESCRIPTION + Unit Test for Read-Properties function from PSTK module + + .NOTES + File name: Read-Properties.ps1 + Author: Florian Carrier + Creation date: 31/08/2018 + Last modified: 16/10/2018 + TODO Fix +#> + +# ------------------------------------------------------------------------------ +# Initialisation +# ------------------------------------------------------------------------------ +$Path = Split-Path -Path $MyInvocation.MyCommand.Definition +$Repository = Split-Path -Path $Path -Parent +$PrivateDirectory = Join-Path -Path $Repository -ChildPath "Private" +# Import module and private functions +Import-Module -Name "$Repository\PSTK" -Force +$Scripts = @( + $MyInvocation.MyCommand.Name, + "Read-Property.ps1" +) +foreach ($Script in $Scripts) { + $Link = Join-Path -Path $PrivateDirectory -ChildPath $Script + . $Link +} +# ------------------------------------------------------------------------------ +# Expected results +# ------------------------------------------------------------------------------ +$PropertyFile = "properties.ini" +# Without Sections +$Expected1 = [Ordered]@{ + "Property1" = "1" + "Property2" = "2" + "Property3" = "3" + "Property4" = "4" + "Property5" = "5" + "Property6" = "6" + "Property7" = "7" + "Property8" = "8" + "Property9" = "9" + "Property10" = "10" +} + +# With Sections +$Expected2 = [Ordered]@{ + "Section1" = [Ordered]@{ + "Property1" = "1" + "Property2" = "2" + "Property3" = "3" + "Property4" = "4" + "Property5" = "5" + } + "Section2" = [Ordered]@{ + "Property6" = "6" + "Property7" = "7" + "Property8" = "8" + "Property9" = "9" + "Property10" = "10" + } +} + +# ------------------------------------------------------------------------------ +# Test +# ------------------------------------------------------------------------------ +# Without Sections +$Properties1 = Read-Properties -File $PropertyFile -Directory "$Path\res" +$Check1 = Compare-Hashtable -Reference $Expected1 -Difference $Properties1 + +# With Sections +$Properties2 = Read-Properties -File $PropertyFile -Directory "$Path\res" -Section +$Check2 = Compare-Hashtable -Reference $Expected2 -Difference $Properties2 + +# ------------------------------------------------------------------------------ +# Check outcome +# ------------------------------------------------------------------------------ +if ($Check1 -And $Check2) { + return $true +} else { + return $false +} + +# Y U NO WORK diff --git a/Tests/Read-Property.ps1 b/Tests/Read-Property.ps1 new file mode 100644 index 0000000..4184b27 --- /dev/null +++ b/Tests/Read-Property.ps1 @@ -0,0 +1,61 @@ +<# + .SYNOPSIS + Read-Property Unit Testing + + .DESCRIPTION + Unit Test for Read-Property function from PSTK module + + .NOTES + File name: Read-Property.ps1 + Author: Florian Carrier + Creation date: 31/08/2018 + Last modified: 16/10/2018 +#> + +# ------------------------------------------------------------------------------ +# Initialisation +# ------------------------------------------------------------------------------ +$Path = Split-Path -Path (Split-Path -Path $MyInvocation.MyCommand.Definition) -Parent +$Repository = Join-Path -Path $Path -ChildPath "Private" +# Import functions +$Scripts = @( + $MyInvocation.MyCommand.Name +) +foreach ($Script in $Scripts) { + $Link = Join-Path -Path $Repository -ChildPath $Script + . $Link +} + +# ------------------------------------------------------------------------------ +# Expected results +# ------------------------------------------------------------------------------ +$Expected = [ordered]@{ + Key = "Property Name" + Value = "Property Value" +} + +# ------------------------------------------------------------------------------ +# Test +# ------------------------------------------------------------------------------ +$Property1 = Read-Property -Content "Property Name = Property Value" +$Property2 = Read-Property -Content "Property Name Property Value" +$Property3 = Read-Property -Content "Property Name = Property=Value" +$Property4 = Read-Property -Content "Property Name =Property Value" +$Property5 = Read-Property -Content "Property Name=Property Value" +$Property6 = Read-Property -Content "Property Name= Property Value" + +$Check1 = Compare-Hashtable -Reference $Expected -Difference $Property1 +$Check2 = Compare-Hashtable -Reference $Expected -Difference $Property2 +$Check3 = Compare-Hashtable -Reference $Expected -Difference $Property3 +$Check4 = Compare-Hashtable -Reference $Expected -Difference $Property4 +$Check5 = Compare-Hashtable -Reference $Expected -Difference $Property5 +$Check6 = Compare-Hashtable -Reference $Expected -Difference $Property6 + +# ------------------------------------------------------------------------------ +# Check outcome +# ------------------------------------------------------------------------------ +if ($Check1 -And !$Check2 -And !$Check3 -And $Check4 -And $Check5 -And $Check6) { + return $true +} else { + return $false +} diff --git a/Tests/Write-Log.ps1 b/Tests/Write-Log.ps1 new file mode 100644 index 0000000..e35045a --- /dev/null +++ b/Tests/Write-Log.ps1 @@ -0,0 +1,71 @@ +<# + .SYNOPSIS + Write-Log Unit Testing + + .DESCRIPTION + Unit Test for Write-Log procedure from PSTK module + + .NOTES + File name: Write-Log.ps1 + Author: Florian Carrier + Creation date: 16/10/2018 + Last modified: 16/10/2018 + TODO Does not work, find workaround +#> + +# ------------------------------------------------------------------------------ +# Initialisation +# ------------------------------------------------------------------------------ +$Path = Split-Path -Path (Split-Path -Path $MyInvocation.MyCommand.Definition) -Parent +$Repository = Join-Path -Path $Path -ChildPath "Private" +# Import toolbox +Import-Module "$Path\PSTK" -Force +# Import functions +$Scripts = @( + "Select-WriteHost.ps1" +) +foreach ($Script in $Scripts) { + $Link = Join-Path -Path $Repository -ChildPath $Script + . $Link +} + +# ------------------------------------------------------------------------------ +# Test objects +# ------------------------------------------------------------------------------ +$File = "$($MyInvocation.MyCommand.Name).log" +$Outputs = [Ordered]@{ + "CHECK" = "This is a checkpoint message." + "ERROR" = "This is an error message." + "INFO" = "This is an informational message." + "WARN" = "This is a warning message." +} +# Expected output +$Timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss" +$Expected = @" +$Timestamp`tCHECK`tThis is a checkpoint message. +$Timestamp`tERROR`tThis is an error message. +$Timestamp`tINFO`tThis is an informational message. +$Timestamp`tWARN`tThis is a warning message. +"@ + +# ------------------------------------------------------------------------------ +# Test +# ------------------------------------------------------------------------------ +# Generate log +foreach ($Output in $Outputs.GetEnumerator()) { + $null = Select-WriteHost -ScriptBlock { Write-Log -Type $Output.Name -Message $Output.Value } -OutputFile $File -Quiet +} +# Check output +$FileContent = Get-Content -Path $File -Raw +if ($FileContent -eq $Expected) { + $Check = $true +} else { + $Check = $false +} +# Clean-up +Remove-Item -Path $File + +# ------------------------------------------------------------------------------ +# Check outcome +# ------------------------------------------------------------------------------ +return $Check diff --git a/Tests/res/Test Word 97-2003 Document.doc b/Tests/res/Test Word 97-2003 Document.doc new file mode 100644 index 0000000..a19672d Binary files /dev/null and b/Tests/res/Test Word 97-2003 Document.doc differ diff --git a/Tests/res/Test Word Document.docx b/Tests/res/Test Word Document.docx new file mode 100644 index 0000000..2329e2e Binary files /dev/null and b/Tests/res/Test Word Document.docx differ diff --git a/Tests/res/properties.ini b/Tests/res/properties.ini new file mode 100644 index 0000000..e83d9c4 --- /dev/null +++ b/Tests/res/properties.ini @@ -0,0 +1,15 @@ +# Some properties +[Section1] +Property1 = 1 +Property2 = 2 +Property3 = 3 +Property4 = 4 +Property5 = 5 + +; Some more properties +[Section2] +Property6 = 6 +Property7 = 7 +Property8 = 8 +Property9 = 9 +Property10 = 10