From 12bbd4d158127cffe849bd5e5d727d2eb4c5f1d1 Mon Sep 17 00:00:00 2001 From: Florian Date: Mon, 15 Oct 2018 18:10:12 +0100 Subject: [PATCH 01/19] Public functions Test scenari for public functions and resources --- .gitignore | 1 - Tests/Compare-Hashtable.ps1 | 67 +++++++++++++++ Tests/ConvertTo-PDF.ps1 | 51 ++++++++++++ Tests/Copy-OrderedHashtable.ps1 | 68 ++++++++++++++++ Tests/Format-String.ps1 | 99 +++++++++++++++++++++++ Tests/res/Test Word 97-2003 Document.doc | Bin 0 -> 23040 bytes Tests/res/Test Word Document.docx | Bin 0 -> 12032 bytes Tests/res/properties.ini | 15 ++++ 8 files changed, 300 insertions(+), 1 deletion(-) create mode 100644 Tests/Compare-Hashtable.ps1 create mode 100644 Tests/ConvertTo-PDF.ps1 create mode 100644 Tests/Copy-OrderedHashtable.ps1 create mode 100644 Tests/Format-String.ps1 create mode 100644 Tests/res/Test Word 97-2003 Document.doc create mode 100644 Tests/res/Test Word Document.docx create mode 100644 Tests/res/properties.ini diff --git a/.gitignore b/.gitignore index 2bfa6a4..e69de29 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +0,0 @@ -tests/ 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/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/res/Test Word 97-2003 Document.doc b/Tests/res/Test Word 97-2003 Document.doc new file mode 100644 index 0000000000000000000000000000000000000000..a19672d120b1329ff1f4235250c7cb999d68b84b GIT binary patch literal 23040 zcmeHP30%$F_TQZbhe{z)q0_mgkOoPKW|Go~%yg>LTqma^A<8^lLm4xdgqtZchbB`I zi83?^5ke@5IB%`r>35~8dwK8u-{=4CJG;;KxAtCp@3r?{zx~_4z1DAc3T||Ika$G3 zlC%mIM3Q_FwkI+z_!1D;q0@?l^o2O`z6gavWNQN;Dh~fe8mLLmA||B06d|>pGw>!7 z0(TYylMptbBk>CK3e;%OXrQ>?T-r-16TOav*lxe>gJ6G?`^SV(sPH?ptvFraBo(n~ zCrTq-+Eo<)Gpa;a2Z}cVlUtkXeUHjP{$9vOO6Sk*Mu-5Wby{=1zoM&^u@ypE(#`;j<3zQ2<9&&qGB541d5PxO3qwOPGn z?%3P7Fj9f$?}qW>qea(i&L-8s$9d}ndNlxw^A7`E&Z&neeXO}You>0ex&Qi=mP<>b z?-#a%^ODvxP3iXOG)?LHZBe>hYf8^YnzmJ+=F{!d>9%O=cIY&%XFA;$rRCE1u{Hgx z{MP0FU1|D$wbk#o_;i1NmbN7i*JfFHn7=TI&0y9`lg@C(H;Op?|JT46Zn%Ioj>q?5 z4L0dNz`($WHH_yyBZM0&VCm@j@VxuLofU__u?Ap|rwY($-tX1FeDXIQ?B9E6a$Wr^ zWO9kJ{~RuCv^y$OsSx}2%y0lvqI0nf&6TA0`5*t@su7Q_r3T>L?B|2ifJo>P!&rX_ z_0$4=R_X(Djno8bHsF`o1ZhoxRw^OgQVST}0dlbgXwHbk-%10jG76+KcyVS*D3EsG z^T9ttq;Odv@qitFfkEIP`YS}hy!k7%CBp*hww3pP!QThtrR9R*^Lw1;wWg$}3KDYd;l;(pWOi-fO&veKms5YkO{!|L=4~opca5jiXwmo zFaTHq#CiWYy|i9eSkSbKgCubUxB;fwOGv?nP%0DvTSX}W0E!R?LOkCH89?PkK4IX= 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 o{zx3PH*gw_dGNb&)I*JNJ^DxF^h`GZ7c56skB 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 Date: Tue, 16 Oct 2018 15:11:45 +0100 Subject: [PATCH 02/19] Select-WriteHost Attempt to test Write-Log procedure Some additional unit tests --- PSTK.psm1 | 2 +- Private/Read-Property.ps1 | 8 +- Private/Select-WriteHost.ps1 | 140 ++++++++++++++++++++++++++++++++++ Public/Test-SQLConnection.ps1 | 14 +++- Public/Write-Log.ps1 | 19 ++++- Tests/Add-Offset.ps1 | 79 +++++++++++++++++++ Tests/ConvertTo-TitleCase.ps1 | 63 +++++++++++++++ Tests/Read-Properties.ps1 | 88 +++++++++++++++++++++ Tests/Read-Property.ps1 | 61 +++++++++++++++ Tests/Write-Log.ps1 | 71 +++++++++++++++++ 10 files changed, 541 insertions(+), 4 deletions(-) create mode 100644 Private/Select-WriteHost.ps1 create mode 100644 Tests/Add-Offset.ps1 create mode 100644 Tests/ConvertTo-TitleCase.ps1 create mode 100644 Tests/Read-Properties.ps1 create mode 100644 Tests/Read-Property.ps1 create mode 100644 Tests/Write-Log.ps1 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-Property.ps1 b/Private/Read-Property.ps1 index 4599a63..ee66d61 100644 --- a/Private/Read-Property.ps1 +++ b/Private/Read-Property.ps1 @@ -10,7 +10,7 @@ function Read-Property { Parse property content .PARAMETER Content - [String] The Content parameter should be the content of the property. + [System.String] The Content parameter should be the content of the property. .INPUTS None. @@ -24,6 +24,12 @@ function Read-Property { 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: 16/10/2018 #> [CmdletBinding ()] Param ( diff --git a/Private/Select-WriteHost.ps1 b/Private/Select-WriteHost.ps1 new file mode 100644 index 0000000..e7991db --- /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/Test-SQLConnection.ps1 b/Public/Test-SQLConnection.ps1 index 9151593..6499c37 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: 16/10/2018 + 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 ( diff --git a/Public/Write-Log.ps1 b/Public/Write-Log.ps1 index 241a9dd..9adc70f 100644 --- a/Public/Write-Log.ps1 +++ b/Public/Write-Log.ps1 @@ -10,10 +10,20 @@ 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: informational, warning, error, 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 four different available types: + - CHECK: checkpoint, used to confirm a status. + - 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. @@ -51,7 +61,14 @@ function Write-Log { green in the host. .NOTES - TODO Add locale variable + File name: Write-Log.ps1 + Author: Florian Carrier + Creation date: 15/10/2018 + Last modified: 16/10/2018 + TODO Add locale variable + + .LINK + https://github.com/Akaizoku/PSTK #> [CmdletBinding ()] # Inputs 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/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/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 From d9025b553db3378f6a4b7751f79c5d535de23a37 Mon Sep 17 00:00:00 2001 From: Florian Date: Fri, 26 Oct 2018 12:35:15 +0100 Subject: [PATCH 03/19] Explicit naming Added explicit parameter name in Write-Log --- Public/Write-Log.ps1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Public/Write-Log.ps1 b/Public/Write-Log.ps1 index 9adc70f..146537c 100644 --- a/Public/Write-Log.ps1 +++ b/Public/Write-Log.ps1 @@ -64,7 +64,7 @@ function Write-Log { File name: Write-Log.ps1 Author: Florian Carrier Creation date: 15/10/2018 - Last modified: 16/10/2018 + Last modified: 19/10/2018 TODO Add locale variable .LINK @@ -107,5 +107,5 @@ function Write-Log { # Format log $Log = "$Time`t$Type`t$Message" # Output - Write-Host $Log -ForegroundColor $Colour.$Type + Write-Host -Object $Log -ForegroundColor $Colour.$Type } From 9db6d51dbdae31be0a42396423d8dd75f6cc1387 Mon Sep 17 00:00:00 2001 From: Florian Carrier Date: Tue, 23 Apr 2019 12:49:18 +0100 Subject: [PATCH 04/19] 1.2.0 pre-release First draft of the 1.2.0 release --- CHANGELOG.md | 31 +++++ PSTK.psd1 | 26 +++- Private/Resolve-Array.ps1 | 54 +++++++++ Public/Compare-Hashtable.ps1 | 41 ++++--- Public/Compare-Properties.ps1 | 26 ++-- Public/Complete-RelativePath.ps1 | 5 +- Public/Convert-FileEncoding.ps1 | 3 + Public/Expand-CompressedFile.ps1 | 85 +++++++++++++ Public/Find-Key.ps1 | 83 +++++++++++++ Public/Get-CallerPreference.ps1 | 165 ++++++++++++++++++++++++++ Public/Get-EnvironmentVariable.ps1 | 34 ++++++ Public/Get-HTTPStatus.ps1 | 46 +++++++ Public/Get-KeyValue.ps1 | 83 +++++++++++++ Public/Get-Path.ps1 | 49 ++++++++ Public/Get-Properties.ps1 | 82 +++++++++---- Public/Remove-EnvironmentVariable.ps1 | 34 ++++++ Public/Resolve-URI.ps1 | 27 +++++ Public/Select-XMLNode.ps1 | 64 ++++++++++ Public/Set-EnvironmentVariable.ps1 | 41 +++++++ Public/Test-EnvironmentVariable.ps1 | 33 ++++++ Public/Test-SQLConnection.ps1 | 51 ++++---- Public/Test-Service.ps1 | 59 +++++++++ Public/Update-File.ps1 | 53 +++++++++ Public/Write-ErrorLog.ps1 | 37 ++++++ Public/Write-Log.ps1 | 54 ++++++--- README.md | 7 ++ 26 files changed, 1178 insertions(+), 95 deletions(-) create mode 100644 Private/Resolve-Array.ps1 create mode 100644 Public/Expand-CompressedFile.ps1 create mode 100644 Public/Find-Key.ps1 create mode 100644 Public/Get-CallerPreference.ps1 create mode 100644 Public/Get-EnvironmentVariable.ps1 create mode 100644 Public/Get-HTTPStatus.ps1 create mode 100644 Public/Get-KeyValue.ps1 create mode 100644 Public/Get-Path.ps1 create mode 100644 Public/Remove-EnvironmentVariable.ps1 create mode 100644 Public/Resolve-URI.ps1 create mode 100644 Public/Select-XMLNode.ps1 create mode 100644 Public/Set-EnvironmentVariable.ps1 create mode 100644 Public/Test-EnvironmentVariable.ps1 create mode 100644 Public/Test-Service.ps1 create mode 100644 Public/Update-File.ps1 create mode 100644 Public/Write-ErrorLog.ps1 diff --git a/CHANGELOG.md b/CHANGELOG.md index 6efde87..439055b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,37 @@ 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). +## Unreleased + +### Added +The following functions have been added: +- Expand-CompressedFile +- Find-Key +- Get-CallerPreference +- Get-EnvironmentVariable +- Get-HTTPStatus +- Get-KeyValue +- Get-Path +- Remove-EnvironmentVariable +- Resolve-Array +- Resolve-URI +- Select-XMLNode +- Set-EnvironmentVariable +- Test-EnvironmentVariable +- Test-Service +- Update-File +- Write-ErrorLog + +### Changed +The following functions have been updated: +- Compare-Hashtable +- Compare-Properties +- Complete-RelativePath +- Convert-FileEncoding +- Get-Properties +- Test-SQLConnection +- Write-Log + ## [1.1.0](https://github.com/Akaizoku/PSTK/releases/tag/1.1.0) - 2018-10-15 ### Added diff --git a/PSTK.psd1 b/PSTK.psd1 index 45ae0c8..c517700 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' @@ -77,15 +77,30 @@ FunctionsToExport = @( "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", "New-DynamicParameter", + "Remove-EnvironmentVariable", "Rename-NumberedFile", + "Resolve-URI", + "Select-XMLNode", + "Set-EnvironmentVariable", "Set-Tags", "Start-Script", "Stop-Script", + "Test-EnvironmentVariable", + "Test-Service", "Test-SQLConnection", + "Update-File", + "Write-ErrorLog", "Write-Log" ) @@ -126,6 +141,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/Private/Resolve-Array.ps1 b/Private/Resolve-Array.ps1 new file mode 100644 index 0000000..61c2d8b --- /dev/null +++ b/Private/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/Compare-Hashtable.ps1 b/Public/Compare-Hashtable.ps1 index da188d9..923ec21 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 vrariables + 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..1d52a96 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 vrariables + 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..2f1e5c6 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 vrariables + 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/Convert-FileEncoding.ps1 b/Public/Convert-FileEncoding.ps1 index d1a5e07..1833dcb 100644 --- a/Public/Convert-FileEncoding.ps1 +++ b/Public/Convert-FileEncoding.ps1 @@ -65,6 +65,8 @@ function Convert-FileEncoding { $Exclude = $null ) Begin { + # Get global preference vrariables + 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 @@ -80,6 +82,7 @@ function Convert-FileEncoding { $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" -Message "$NewFile" $Count += 1 } if ($Count -gt 0) { diff --git a/Public/Expand-CompressedFile.ps1 b/Public/Expand-CompressedFile.ps1 new file mode 100644 index 0000000..d0be54a --- /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 vrariables + 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..0025619 --- /dev/null +++ b/Public/Get-EnvironmentVariable.ps1 @@ -0,0 +1,34 @@ +function Get-EnvironmentVariable { + [CmdletBinding()] + Param ( + [Parameter ( + Position = 1, + Mandatory = $true, + HelpMessage = "Name of the environment variable" + )] + [ValidateNotNullOrEmpty()] + [String] + $Variable, + [Parameter ( + Position = 2, + Mandatory = $false, + HelpMessage = "Scope of the environment variable" + )] + [ValidateSet ("Machine", "Process", "User")] + [String] + $Scope = "Machine" + ) + Begin { + # Get global preference vrariables + Get-CallerPreference -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState + } + Process { + # Check if variable is defined + $Value = [Environment]::GetEnvironmentVariable($Variable, $Scope) + if ($Value) { + Write-Log -Type "DEBUG" -Message "$Scope`t$Variable=$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..2a4399e --- /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 vrariables + 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-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..885fdf5 100644 --- a/Public/Get-Properties.ps1 +++ b/Public/Get-Properties.ps1 @@ -75,40 +75,70 @@ 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 vrariables + 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 + 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 + $CustomPath = Join-Path -Path $CustomDirectory -ChildPath $Custom + if (Test-Path -Path $CustomPath) { + # 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" + } } + } 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" -Message "$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" -Message "$MissingProperties $Grammar not defined" + Stop-Script 1 } - } else { - Write-Log -Type "WARN" -Message "$Custom not found in directory $CustomDirectory" } + return $Properties + } else { + Write-Log -Type "ERROR" -Message "$File not found in directory $Directory" + Stop-Script 1 } - return $Properties - } else { - Write-Log -Type "ERROR" -Message "$File not found in directory $Directory" - Stop-Script 1 } } diff --git a/Public/Remove-EnvironmentVariable.ps1 b/Public/Remove-EnvironmentVariable.ps1 new file mode 100644 index 0000000..de11dd9 --- /dev/null +++ b/Public/Remove-EnvironmentVariable.ps1 @@ -0,0 +1,34 @@ +function Remove-EnvironmentVariable { + [CmdletBinding()] + Param ( + [Parameter ( + Position = 1, + Mandatory = $true, + HelpMessage = "Name of the environment variable" + )] + [ValidateNotNullOrEmpty()] + [String] + $Variable, + [Parameter ( + Position = 2, + Mandatory = $false, + HelpMessage = "Scope of the environment variable" + )] + [ValidateSet ("Machine", "Process", "User")] + [String] + $Scope = "Machine" + ) + Begin { + # Get global preference vrariables + Get-CallerPreference -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState + } + Process { + # Check if variable is defined + if (Test-EnvironmentVariable -Variable $Variable -Scope $Scope) { + Write-Log -Type "INFO" -Message "Removing $Variable environment variable in $Scope scope" + [Environment]::SetEnvironmentVariable($Variable, "", $Scope) + } else { + Write-Log -Type "WARN" -Message "$Variable environment variable is not defined in $Scope scope" + } + } +} 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..4cb1eea --- /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 vrariables + 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..3c9b1d5 --- /dev/null +++ b/Public/Set-EnvironmentVariable.ps1 @@ -0,0 +1,41 @@ +function Set-EnvironmentVariable { + [CmdletBinding()] + Param ( + [Parameter ( + Position = 1, + Mandatory = $true, + HelpMessage = "Name of the environment variable" + )] + [ValidateNotNullOrEmpty()] + [String] + $Variable, + [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 vrariables + Get-CallerPreference -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState + } + Process { + # Check if variable is defined + if (Test-EnvironmentVariable -Variable $Variable -Scope $Scope) { + Write-Log -Type "WARN" -Message "Overwriting existing $Variable environment variable in $Scope scope" + } + Write-Log -Type "DEBUG" -Message "$Scope`t$Variable=$Value" + [Environment]::SetEnvironmentVariable($Variable, $Value, $Scope) + } +} diff --git a/Public/Test-EnvironmentVariable.ps1 b/Public/Test-EnvironmentVariable.ps1 new file mode 100644 index 0000000..4179420 --- /dev/null +++ b/Public/Test-EnvironmentVariable.ps1 @@ -0,0 +1,33 @@ +function Test-EnvironmentVariable { + [CmdletBinding()] + Param ( + [Parameter ( + Position = 1, + Mandatory = $true, + HelpMessage = "Name of the environment variable" + )] + [ValidateNotNullOrEmpty()] + [String] + $Variable, + [Parameter ( + Position = 2, + Mandatory = $false, + HelpMessage = "Scope of the environment variable" + )] + [ValidateSet ("Machine", "Process", "User")] + [String] + $Scope = "Machine" + ) + Begin { + # Get global preference vrariables + Get-CallerPreference -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState + } + Process { + # Check if variable is defined + if (Get-EnvironmentVariable -Variable $Variable -Scope $Scope) { + return $true + } else { + return $false + } + } +} diff --git a/Public/Test-SQLConnection.ps1 b/Public/Test-SQLConnection.ps1 index 6499c37..ae8e773 100644 --- a/Public/Test-SQLConnection.ps1 +++ b/Public/Test-SQLConnection.ps1 @@ -106,29 +106,36 @@ function Test-SQLConnection { [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 vrariables + Get-CallerPreference -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState + } + Process { + # Break-down connection info + if ($Security) { + Write-Log -Type "DEBUG" -Message "SQL Server authentication" + 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 + $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..a999c64 --- /dev/null +++ b/Public/Test-Service.ps1 @@ -0,0 +1,59 @@ +# ------------------------------------------------------------------------------ +# 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()] + [String] + $Service + ) + Begin { + # Get global preference vrariables + Get-CallerPreference -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState + } + Process { + try { + if (Get-Service -Name $Service -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..a044168 --- /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: 08/12/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 = "New string to replace old with" + )] + [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..6d3149c --- /dev/null +++ b/Public/Write-ErrorLog.ps1 @@ -0,0 +1,37 @@ +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" + )] + [String] + $Message, + [Parameter ( + Position = 3, + Mandatory = $false, + HelpMessage = "Error code" + )] + [Int] + $ErrorCode + ) + Begin { + # Get global preference vrariables + Get-CallerPreference -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState + } + Process { + Write-Log -Type "DEBUG" -Message $Path + $Message | Out-File -FilePath $Path -Append -Force + if ($PSBoundParameters["ErrorCode"]) { + Stop-Script -ErrorCode $ErrorCode + } + } +} diff --git a/Public/Write-Log.ps1 b/Public/Write-Log.ps1 index 146537c..e5cdf08 100644 --- a/Public/Write-Log.ps1 +++ b/Public/Write-Log.ps1 @@ -11,18 +11,19 @@ function Write-Log { ed manner with respective colour code. It takes two parameters: - - Type of output: informational, warning, error, or checkpoint. + - 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 four different available types: - - CHECK: checkpoint, used to confirm a status. - - 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. + 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. @@ -60,11 +61,17 @@ 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 File name: Write-Log.ps1 Author: Florian Carrier Creation date: 15/10/2018 - Last modified: 19/10/2018 + Last modified: 22/04/2018 TODO Add locale variable .LINK @@ -80,6 +87,7 @@ function Write-Log { )] [ValidateSet ( "CHECK", + "DEBUG", "ERROR", "INFO", "WARN" @@ -96,16 +104,26 @@ function Write-Log { [String] $Message ) - # 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 vrariables + 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" + } + } + Process { + # Format log + $Log = "$Time`t$Type`t$Message" + # Output + if ($Type -eq "DEBUG") { + Write-Debug -Message $Log + } else { + Write-Host -Object $Log -ForegroundColor $Colour.$Type + } } - # Format log - $Log = "$Time`t$Type`t$Message" - # Output - Write-Host -Object $Log -ForegroundColor $Colour.$Type } diff --git a/README.md b/README.md index c424571..5de3866 100644 --- a/README.md +++ b/README.md @@ -29,15 +29,22 @@ Get-Command -Module PSTK | Function | ConvertTo-NaturalSort | 1.0.0 | PSTK | | Function | ConvertTo-PDF | 1.0.0 | PSTK | | Function | Copy-OrderedHashtable | 1.0.0 | PSTK | +| Function | Expand-CompressedFile | 1.0.0 | PSTK | +| Function | Find-Key | 1.0.0 | PSTK | | Function | Format-String | 1.0.0 | PSTK | +| Function | Get-HTTPStatus | 1.0.0 | PSTK | +| Function | Get-KeyValue | 1.0.0 | PSTK | | Function | Get-Object | 1.0.0 | PSTK | +| Function | Get-Path | 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 | Resolve-URI | 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 | Update-File | 1.0.0 | PSTK | | Function | Write-Log | 1.0.0 | PSTK | ## Dependencies From bf854f01d199ec9815ea8e70e5a7b4cdc5584a49 Mon Sep 17 00:00:00 2001 From: Florian Carrier Date: Tue, 23 Apr 2019 17:37:10 +0100 Subject: [PATCH 05/19] Fix debug log format --- Public/Write-Log.ps1 | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Public/Write-Log.ps1 b/Public/Write-Log.ps1 index e5cdf08..0e37e52 100644 --- a/Public/Write-Log.ps1 +++ b/Public/Write-Log.ps1 @@ -117,12 +117,12 @@ function Write-Log { } } Process { - # Format log - $Log = "$Time`t$Type`t$Message" - # Output if ($Type -eq "DEBUG") { - Write-Debug -Message $Log + Write-Debug -Message $Message } else { + # Format log + $Log = "$Time`t$Type`t$Message" + # Output Write-Host -Object $Log -ForegroundColor $Colour.$Type } } From 54553c065ff050544b6e113203e9f0e858f6f929 Mon Sep 17 00:00:00 2001 From: Florian Carrier Date: Tue, 4 Jun 2019 13:57:31 +0100 Subject: [PATCH 06/19] Update Write-Log.ps1 Add exit code to error log --- Public/Write-Log.ps1 | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/Public/Write-Log.ps1 b/Public/Write-Log.ps1 index 0e37e52..5e2620b 100644 --- a/Public/Write-Log.ps1 +++ b/Public/Write-Log.ps1 @@ -28,6 +28,11 @@ function Write-Log { .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. @@ -54,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." @@ -71,7 +83,7 @@ function Write-Log { File name: Write-Log.ps1 Author: Florian Carrier Creation date: 15/10/2018 - Last modified: 22/04/2018 + Last modified: 29/04/2019 TODO Add locale variable .LINK @@ -102,7 +114,14 @@ function Write-Log { [ValidateNotNullOrEmpty ()] [Alias ("Output", "Log")] [String] - $Message + $Message, + [Parameter ( + Position = 3, + Mandatory = $false, + HelpMessage = "Error code" + )] + [Int] + $ErrorCode ) Begin { # Get global preference vrariables @@ -125,5 +144,8 @@ function Write-Log { # Output Write-Host -Object $Log -ForegroundColor $Colour.$Type } + if ($PSBoundParameters["ErrorCode"]) { + Stop-Script -ErrorCode $ErrorCode + } } } From 52361b0446aef653c079d97f0b0f7422a57f69b1 Mon Sep 17 00:00:00 2001 From: Florian Carrier Date: Fri, 14 Jun 2019 15:30:05 +0100 Subject: [PATCH 07/19] Create Confirm-Prompt.ps1 --- Public/Confirm-Prompt.ps1 | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 Public/Confirm-Prompt.ps1 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 + } + } + } +} From 32e3966ba2ca02cb1280ec3ea9b53eb1bdbcfe3b Mon Sep 17 00:00:00 2001 From: Florian Carrier Date: Fri, 14 Jun 2019 15:30:10 +0100 Subject: [PATCH 08/19] Create Set-RelativePath.ps1 --- Public/Set-RelativePath.ps1 | 41 +++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 Public/Set-RelativePath.ps1 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 + } +} From 7fa68f2c87c0da31f8e7fc5cfaa658caf8fb0730 Mon Sep 17 00:00:00 2001 From: Florian Carrier Date: Fri, 14 Jun 2019 15:30:14 +0100 Subject: [PATCH 09/19] Create Out-Hashtable.ps1 --- Public/Out-Hashtable.ps1 | 57 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 Public/Out-Hashtable.ps1 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 + } +} From d5d493de3db2011e22437e1215bb85343dd2ebf1 Mon Sep 17 00:00:00 2001 From: Florian Carrier Date: Fri, 14 Jun 2019 15:30:17 +0100 Subject: [PATCH 10/19] Create Remove-Object.ps1 --- Public/Remove-Object.ps1 | 77 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 Public/Remove-Object.ps1 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 + } + } + } +} From bb1606b973935d8091c7cb6a5e7455b75c851cf9 Mon Sep 17 00:00:00 2001 From: Florian Carrier Date: Fri, 14 Jun 2019 15:30:29 +0100 Subject: [PATCH 11/19] Create Test-Object.ps1 --- Public/Test-Object.ps1 | 50 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 Public/Test-Object.ps1 diff --git a/Public/Test-Object.ps1 b/Public/Test-Object.ps1 new file mode 100644 index 0000000..ad1c3c5 --- /dev/null +++ b/Public/Test-Object.ps1 @@ -0,0 +1,50 @@ +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] { + Write-Log -Type "DEBUG" -Object $Error[0].Exception + $Outcome = $true + } + # Output test result + if ($PSBoundParameters.ContainsKey("NotFound")) { + return (-Not $Outcome) + } else { + return $Outcome + } + } +} From 06582c964a02ea832761309261ac651f66cf1ab7 Mon Sep 17 00:00:00 2001 From: Florian Carrier Date: Fri, 14 Jun 2019 15:30:41 +0100 Subject: [PATCH 12/19] Update LICENSE --- LICENSE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 From ac483001240137c1cd93921809b79c1da8d0ac8d Mon Sep 17 00:00:00 2001 From: Florian Carrier Date: Fri, 14 Jun 2019 15:31:02 +0100 Subject: [PATCH 13/19] Update Write-Log.ps1 --- Public/Write-Log.ps1 | 32 +++++++++++++++++++++++++------- 1 file changed, 25 insertions(+), 7 deletions(-) diff --git a/Public/Write-Log.ps1 b/Public/Write-Log.ps1 index 5e2620b..a6b52b4 100644 --- a/Public/Write-Log.ps1 +++ b/Public/Write-Log.ps1 @@ -83,7 +83,7 @@ function Write-Log { File name: Write-Log.ps1 Author: Florian Carrier Creation date: 15/10/2018 - Last modified: 29/04/2019 + Last modified: 06/06/2019 TODO Add locale variable .LINK @@ -112,19 +112,27 @@ function Write-Log { HelpMessage = "Message to output" )] [ValidateNotNullOrEmpty ()] - [Alias ("Output", "Log")] - [String] - $Message, + [Alias ("Message", "Output", "Log")] + [Object] + $Object, [Parameter ( Position = 3, Mandatory = $false, HelpMessage = "Error code" )] [Int] - $ErrorCode + $ErrorCode, + [Parameter ( + Position = 4, + Mandatory = $false, + HelpMessage = "Path to an optional output file" + )] + [Alias ("Path")] + [String] + $File ) Begin { - # Get global preference vrariables + # Get global preference variables Get-CallerPreference -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState # Variables $Time = Get-Date -Format "yyyy-MM-dd HH:mm:ss" @@ -134,6 +142,12 @@ function Write-Log { "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") { @@ -144,7 +158,11 @@ function Write-Log { # Output Write-Host -Object $Log -ForegroundColor $Colour.$Type } - if ($PSBoundParameters["ErrorCode"]) { + 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 } } From da0e887b6f22b5ba3bf3d6ca5af72ccb5233075f Mon Sep 17 00:00:00 2001 From: Florian Carrier Date: Fri, 14 Jun 2019 15:31:27 +0100 Subject: [PATCH 14/19] Update Update-File.ps1 Fix help message for encoding parameter --- Public/Update-File.ps1 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Public/Update-File.ps1 b/Public/Update-File.ps1 index a044168..9c414e0 100644 --- a/Public/Update-File.ps1 +++ b/Public/Update-File.ps1 @@ -7,13 +7,13 @@ function Update-File { Replaces a specified string in a text file by a given value. .PARAMETER Path - The path parameter corresponds + The path parameter corresponds .NOTES File name: Update-File.ps1 Author: Florian Carrier Creation date: 08/12/2018 - Last modified: 08/12/2018 + Last modified: 14/06/2018 #> [CmdletBinding ()] Param ( @@ -43,7 +43,7 @@ function Update-File { [Parameter ( Position = 4, Mandatory = $false, - HelpMessage = "New string to replace old with" + HelpMessage = "Encoding" )] [String] $Encoding = "ASCII" From 000a51438b248614daad57803093426520f19530 Mon Sep 17 00:00:00 2001 From: Florian Carrier Date: Fri, 14 Jun 2019 15:33:17 +0100 Subject: [PATCH 15/19] Fix typos --- Private/Read-Properties.ps1 | 4 ++-- Public/Compare-Hashtable.ps1 | 2 +- Public/Compare-Properties.ps1 | 2 +- Public/Complete-RelativePath.ps1 | 2 +- Public/Convert-FileEncoding.ps1 | 15 ++++++++++----- Public/Expand-CompressedFile.ps1 | 2 +- Public/Get-EnvironmentVariable.ps1 | 9 +++++---- Public/Get-HTTPStatus.ps1 | 2 +- Public/Get-Properties.ps1 | 2 +- Public/Remove-EnvironmentVariable.ps1 | 13 +++++++------ Public/Select-XMLNode.ps1 | 2 +- Public/Set-EnvironmentVariable.ps1 | 13 +++++++------ Public/Test-EnvironmentVariable.ps1 | 7 ++++--- Public/Test-SQLConnection.ps1 | 10 +++++----- Public/Test-Service.ps1 | 2 +- Public/Write-ErrorLog.ps1 | 15 +++++++++++---- 16 files changed, 59 insertions(+), 43 deletions(-) diff --git a/Private/Read-Properties.ps1 b/Private/Read-Properties.ps1 index 37045ac..327a9d2 100644 --- a/Private/Read-Properties.ps1 +++ b/Private/Read-Properties.ps1 @@ -59,8 +59,8 @@ function Read-Properties { ) # 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 + $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) { diff --git a/Public/Compare-Hashtable.ps1 b/Public/Compare-Hashtable.ps1 index 923ec21..96ba038 100644 --- a/Public/Compare-Hashtable.ps1 +++ b/Public/Compare-Hashtable.ps1 @@ -43,7 +43,7 @@ function Compare-Hashtable { $Difference ) Begin { - # Get global preference vrariables + # Get global preference variables Get-CallerPreference -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState # Variables $Check = $true diff --git a/Public/Compare-Properties.ps1 b/Public/Compare-Properties.ps1 index 1d52a96..9c3412f 100644 --- a/Public/Compare-Properties.ps1 +++ b/Public/Compare-Properties.ps1 @@ -47,7 +47,7 @@ function Compare-Properties { $Required ) Begin { - # Get global preference vrariables + # Get global preference variables Get-CallerPreference -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState # Variables $Missing = New-Object -TypeName System.Collections.ArrayList diff --git a/Public/Complete-RelativePath.ps1 b/Public/Complete-RelativePath.ps1 index 2f1e5c6..d8b33a1 100644 --- a/Public/Complete-RelativePath.ps1 +++ b/Public/Complete-RelativePath.ps1 @@ -35,7 +35,7 @@ function Complete-RelativePath { $WorkingDirectory ) Begin { - # Get global preference vrariables + # Get global preference variables Get-CallerPreference -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState # Test working directory path if (-Not (Test-Path -Path $WorkingDirectory)) { diff --git a/Public/Convert-FileEncoding.ps1 b/Public/Convert-FileEncoding.ps1 index 1833dcb..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,7 +66,7 @@ function Convert-FileEncoding { $Exclude = $null ) Begin { - # Get global preference vrariables + # Get global preference variables Get-CallerPreference -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState # Check parameters and instantiate variables # $Path = Resolve-Path -Path $Path @@ -77,20 +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" -Message "$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 index d0be54a..404b443 100644 --- a/Public/Expand-CompressedFile.ps1 +++ b/Public/Expand-CompressedFile.ps1 @@ -53,7 +53,7 @@ function Expand-CompressedFile { $Force ) Begin { - # Get global preference vrariables + # Get global preference variables Get-CallerPreference -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState } Process { diff --git a/Public/Get-EnvironmentVariable.ps1 b/Public/Get-EnvironmentVariable.ps1 index 0025619..8983472 100644 --- a/Public/Get-EnvironmentVariable.ps1 +++ b/Public/Get-EnvironmentVariable.ps1 @@ -7,8 +7,9 @@ function Get-EnvironmentVariable { HelpMessage = "Name of the environment variable" )] [ValidateNotNullOrEmpty()] + [Alias ("Variable")] [String] - $Variable, + $Name, [Parameter ( Position = 2, Mandatory = $false, @@ -19,14 +20,14 @@ function Get-EnvironmentVariable { $Scope = "Machine" ) Begin { - # Get global preference vrariables + # Get global preference variables Get-CallerPreference -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState } Process { # Check if variable is defined - $Value = [Environment]::GetEnvironmentVariable($Variable, $Scope) + $Value = [Environment]::GetEnvironmentVariable($Name, $Scope) if ($Value) { - Write-Log -Type "DEBUG" -Message "$Scope`t$Variable=$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 index 2a4399e..c152712 100644 --- a/Public/Get-HTTPStatus.ps1 +++ b/Public/Get-HTTPStatus.ps1 @@ -29,7 +29,7 @@ function Get-HTTPStatus { $URI ) Begin { - # Get global preference vrariables + # Get global preference variables Get-CallerPreference -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState } Process { diff --git a/Public/Get-Properties.ps1 b/Public/Get-Properties.ps1 index 885fdf5..e34a10a 100644 --- a/Public/Get-Properties.ps1 +++ b/Public/Get-Properties.ps1 @@ -86,7 +86,7 @@ function Get-Properties { $Section ) Begin { - # Get global preference vrariables + # Get global preference variables Get-CallerPreference -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState } Process { diff --git a/Public/Remove-EnvironmentVariable.ps1 b/Public/Remove-EnvironmentVariable.ps1 index de11dd9..0fe3e5a 100644 --- a/Public/Remove-EnvironmentVariable.ps1 +++ b/Public/Remove-EnvironmentVariable.ps1 @@ -7,8 +7,9 @@ function Remove-EnvironmentVariable { HelpMessage = "Name of the environment variable" )] [ValidateNotNullOrEmpty()] + [Alias ("Variable")] [String] - $Variable, + $Name, [Parameter ( Position = 2, Mandatory = $false, @@ -19,16 +20,16 @@ function Remove-EnvironmentVariable { $Scope = "Machine" ) Begin { - # Get global preference vrariables + # Get global preference variables Get-CallerPreference -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState } Process { # Check if variable is defined - if (Test-EnvironmentVariable -Variable $Variable -Scope $Scope) { - Write-Log -Type "INFO" -Message "Removing $Variable environment variable in $Scope scope" - [Environment]::SetEnvironmentVariable($Variable, "", $Scope) + 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 "$Variable environment variable is not defined in $Scope scope" + Write-Log -Type "WARN" -Message "$Name environment variable is not defined in $Scope scope" } } } diff --git a/Public/Select-XMLNode.ps1 b/Public/Select-XMLNode.ps1 index 4cb1eea..6c8b016 100644 --- a/Public/Select-XMLNode.ps1 +++ b/Public/Select-XMLNode.ps1 @@ -27,7 +27,7 @@ function Select-XMLNode { $Namespace = $XML.DocumentElement.NamespaceURI ) Begin { - # Get global preference vrariables + # Get global preference variables Get-CallerPreference -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState } Process { diff --git a/Public/Set-EnvironmentVariable.ps1 b/Public/Set-EnvironmentVariable.ps1 index 3c9b1d5..7588578 100644 --- a/Public/Set-EnvironmentVariable.ps1 +++ b/Public/Set-EnvironmentVariable.ps1 @@ -7,8 +7,9 @@ function Set-EnvironmentVariable { HelpMessage = "Name of the environment variable" )] [ValidateNotNullOrEmpty()] + [Alias ("Variable")] [String] - $Variable, + $Name, [Parameter ( Position = 2, Mandatory = $true, @@ -27,15 +28,15 @@ function Set-EnvironmentVariable { $Scope = "Machine" ) Begin { - # Get global preference vrariables + # Get global preference variables Get-CallerPreference -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState } Process { # Check if variable is defined - if (Test-EnvironmentVariable -Variable $Variable -Scope $Scope) { - Write-Log -Type "WARN" -Message "Overwriting existing $Variable environment variable in $Scope scope" + 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$Variable=$Value" - [Environment]::SetEnvironmentVariable($Variable, $Value, $Scope) + Write-Log -Type "DEBUG" -Message "$Scope`t$Name=$Value" + [Environment]::SetEnvironmentVariable($Name, $Value, $Scope) } } diff --git a/Public/Test-EnvironmentVariable.ps1 b/Public/Test-EnvironmentVariable.ps1 index 4179420..a50471b 100644 --- a/Public/Test-EnvironmentVariable.ps1 +++ b/Public/Test-EnvironmentVariable.ps1 @@ -7,8 +7,9 @@ function Test-EnvironmentVariable { HelpMessage = "Name of the environment variable" )] [ValidateNotNullOrEmpty()] + [Alias ("Variable")] [String] - $Variable, + $Name, [Parameter ( Position = 2, Mandatory = $false, @@ -19,12 +20,12 @@ function Test-EnvironmentVariable { $Scope = "Machine" ) Begin { - # Get global preference vrariables + # Get global preference variables Get-CallerPreference -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState } Process { # Check if variable is defined - if (Get-EnvironmentVariable -Variable $Variable -Scope $Scope) { + if (Get-EnvironmentVariable -Name $Name -Scope $Scope) { return $true } else { return $false diff --git a/Public/Test-SQLConnection.ps1 b/Public/Test-SQLConnection.ps1 index ae8e773..31d3292 100644 --- a/Public/Test-SQLConnection.ps1 +++ b/Public/Test-SQLConnection.ps1 @@ -52,7 +52,7 @@ function Test-SQLConnection { File name: Test-SQLConnection.ps1 Author: Florian Carrier Creation date: 15/10/2018 - Last modified: 16/10/2018 + Last modified: 12/06/2019 Dependencies: Test-SQLConnection requires the SQLServer module TODO Add secured password handling @@ -102,18 +102,17 @@ function Test-SQLConnection { Mandatory = $false, HelpMessage = "Password" )] - [Alias ("PW")] + [Alias ("Pw")] [String] $Password ) Begin { - # Get global preference vrariables + # Get global preference variables Get-CallerPreference -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState } Process { # Break-down connection info if ($Security) { - Write-Log -Type "DEBUG" -Message "SQL Server authentication" if ($Username) { $ConnectionString = "Server=$Server; Database=$Database; Integrated Security=False; User ID=$Username; Password=$Password; Connect Timeout=3;" } else { @@ -127,7 +126,8 @@ function Test-SQLConnection { $ConnectionString = "Server=$Server; Database=$Database; Integrated Security=True; Connect Timeout=3;" } # Create connection object - $Connection = New-Object -TypeName System.Data.SqlClient.SqlConnection -ArgumentList $ConnectionString + Write-Log -Type "DEBUG" -Object $ConnectionString + $Connection = New-Object -TypeName "System.Data.SqlClient.SqlConnection" -ArgumentList $ConnectionString # Try to open the connection try { $Connection.Open() diff --git a/Public/Test-Service.ps1 b/Public/Test-Service.ps1 index a999c64..b85f4aa 100644 --- a/Public/Test-Service.ps1 +++ b/Public/Test-Service.ps1 @@ -44,7 +44,7 @@ function Test-Service { $Service ) Begin { - # Get global preference vrariables + # Get global preference variables Get-CallerPreference -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState } Process { diff --git a/Public/Write-ErrorLog.ps1 b/Public/Write-ErrorLog.ps1 index 6d3149c..36d4ea7 100644 --- a/Public/Write-ErrorLog.ps1 +++ b/Public/Write-ErrorLog.ps1 @@ -13,8 +13,9 @@ function Write-ErrorLog { Mandatory = $true, HelpMessage = "Message to log" )] - [String] - $Message, + [Alias ("Message")] + [Object] + $Object, [Parameter ( Position = 3, Mandatory = $false, @@ -24,13 +25,19 @@ function Write-ErrorLog { $ErrorCode ) Begin { - # Get global preference vrariables + # 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["ErrorCode"]) { + if ($PSBoundParameters.ContainsKey("ErrorCode")) { Stop-Script -ErrorCode $ErrorCode } } From 162308a6fa6aee7336561aee5f14ff5a49246233 Mon Sep 17 00:00:00 2001 From: Florian Carrier Date: Fri, 14 Jun 2019 15:38:30 +0100 Subject: [PATCH 16/19] Export new functions Add new public functions to manifest --- PSTK.psd1 | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/PSTK.psd1 b/PSTK.psd1 index c517700..41cfd84 100644 --- a/PSTK.psd1 +++ b/PSTK.psd1 @@ -73,6 +73,7 @@ FunctionsToExport = @( "Compare-Hashtable", "Compare-Properties", "Complete-RelativePath", + "Confirm-Prompt", "Convert-FileEncoding", "ConvertTo-NaturalSort", "ConvertTo-PDF", @@ -88,15 +89,19 @@ FunctionsToExport = @( "Get-Path", "Get-Properties", "New-DynamicParameter", + "Out-Hashtable", "Remove-EnvironmentVariable", + "Remove-Object", "Rename-NumberedFile", "Resolve-URI", "Select-XMLNode", "Set-EnvironmentVariable", + "Set-RelativePath", "Set-Tags", "Start-Script", "Stop-Script", "Test-EnvironmentVariable", + "Test-Object", "Test-Service", "Test-SQLConnection", "Update-File", From 484d956fc7c3f94a9d2882468045b6f32df20a3a Mon Sep 17 00:00:00 2001 From: Florian Carrier Date: Wed, 19 Jun 2019 10:26:37 +0100 Subject: [PATCH 17/19] Add properties import functions Added functions to import valuesfrom properties and CSV files --- CHANGELOG.md | 12 ++- PSTK.psd1 | 4 + Private/Read-Properties.ps1 | 136 ++++++++++++++++---------------- Private/Read-Property.ps1 | 49 +++++++----- Private/Select-WriteHost.ps1 | 2 +- Public/Get-Object.ps1 | 17 +++- Public/Import-CSVProperties.ps1 | 62 +++++++++++++++ Public/Import-Properties.ps1 | 118 +++++++++++++++++++++++++++ Public/Resolve-Boolean.ps1 | 39 +++++++++ Public/Resolve-Tags.ps1 | 74 +++++++++++++++++ Public/Test-Object.ps1 | 7 +- 11 files changed, 427 insertions(+), 93 deletions(-) create mode 100644 Public/Import-CSVProperties.ps1 create mode 100644 Public/Import-Properties.ps1 create mode 100644 Public/Resolve-Boolean.ps1 create mode 100644 Public/Resolve-Tags.ps1 diff --git a/CHANGELOG.md b/CHANGELOG.md index 439055b..d9269be 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added The following functions have been added: +- Confirm-Prompt - Expand-CompressedFile - Find-Key - Get-CallerPreference @@ -15,12 +16,20 @@ The following functions have been added: - 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 @@ -31,7 +40,8 @@ The following functions have been updated: - Compare-Properties - Complete-RelativePath - Convert-FileEncoding -- Get-Properties +- Get-Object +- Test-Object - Test-SQLConnection - Write-Log diff --git a/PSTK.psd1 b/PSTK.psd1 index 41cfd84..a0edee2 100644 --- a/PSTK.psd1 +++ b/PSTK.psd1 @@ -88,11 +88,15 @@ FunctionsToExport = @( "Get-Object", "Get-Path", "Get-Properties", + "Import-CSVProperties", + "Import-Properties", "New-DynamicParameter", "Out-Hashtable", "Remove-EnvironmentVariable", "Remove-Object", "Rename-NumberedFile", + "Resolve-Boolean", + "Resolve-Tags", "Resolve-URI", "Select-XMLNode", "Set-EnvironmentVariable", diff --git a/Private/Read-Properties.ps1 b/Private/Read-Properties.ps1 index 327a9d2..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 ee66d61..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 - [System.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,7 +20,7 @@ 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". @@ -29,27 +29,38 @@ function Read-Property { File name: Read-Property.ps1 Author: Florian Carrier Creation date: 15/10/2018 - Last modified: 16/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 index e7991db..789b239 100644 --- a/Private/Select-WriteHost.ps1 +++ b/Private/Select-WriteHost.ps1 @@ -96,7 +96,7 @@ function Select-WriteHost { } 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") + $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) { 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/Import-CSVProperties.ps1 b/Public/Import-CSVProperties.ps1 new file mode 100644 index 0000000..1778d4f --- /dev/null +++ b/Public/Import-CSVProperties.ps1 @@ -0,0 +1,62 @@ +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 = ',' + ) + 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 + $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 the property is not the specified key + if ($Header -ne $Key) { + # Add property to list + $RowProperties.Add($Header, $Row.$Header) + } + } + # 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..03e16cd --- /dev/null +++ b/Public/Import-Properties.ps1 @@ -0,0 +1,118 @@ +# ------------------------------------------------------------------------------ +# 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 + System.Collections.Specialized.OrderedDictionary. Import-Properties returns an + ordered hash table containing the names and values of the properties listed + in the property files. + + .EXAMPLE + Import-Properties -File "default.ini" -Directory ".\conf" -Custom "custom.ini" -CustomDirectory "\\shared" + + 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 name" + )] + [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 + if ($Section) { + $Properties = Read-Properties -Path $Path -Section + } else { + $Properties = Read-Properties -Path $Path + } + # 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 "WARN" -Object "Path not found $Custom" + } + } + # 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" + Stop-Script 1 + } + } + return $Properties + } else { + Write-Log -Type "ERROR" -Object "Path not found $Path" -ErrorCode 1 + } + } +} 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/Test-Object.ps1 b/Public/Test-Object.ps1 index ad1c3c5..ce46eef 100644 --- a/Public/Test-Object.ps1 +++ b/Public/Test-Object.ps1 @@ -37,7 +37,12 @@ function Test-Object { try { $Outcome = Test-Path -Path $Path -ErrorAction "Stop" } catch [System.UnauthorizedAccessException] { - Write-Log -Type "DEBUG" -Object $Error[0].Exception + 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 From 04d869c384ddcf40be7d1b20b6d466866b029d9b Mon Sep 17 00:00:00 2001 From: Florian Carrier Date: Wed, 19 Jun 2019 10:43:38 +0100 Subject: [PATCH 18/19] Update property functions Clean-up import property functions --- Public/Get-Properties.ps1 | 20 +++++++------------- Public/Import-Properties.ps1 | 22 +++++++++------------- 2 files changed, 16 insertions(+), 26 deletions(-) diff --git a/Public/Get-Properties.ps1 b/Public/Get-Properties.ps1 index e34a10a..ceafa39 100644 --- a/Public/Get-Properties.ps1 +++ b/Public/Get-Properties.ps1 @@ -94,28 +94,24 @@ function Get-Properties { $Path = Join-Path -Path $Directory -ChildPath $File if (Test-Path -Path $Path) { # 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 - } + $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 -File $Custom -Directory $CustomDirectory + $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" -Message "The ""$Property"" property defined in $Custom is unknown" + Write-Log -Type "WARN" -Object "The ""$Property"" property defined in $Custom is unknown" } } } else { - Write-Log -Type "WARN" -Message "$Custom not found in directory $CustomDirectory" + Write-Log -Type "WARN" -Object "$Custom not found in directory $CustomDirectory" } } # If some items are mandatory @@ -124,21 +120,19 @@ function Get-Properties { foreach ($Item in $ValidateSet) { # Check that the property has been defined if (-Not $Properties.$Item) { - Write-Log -Type "WARN" -Message "$Item property is missing from $File" + 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" -Message "$MissingProperties $Grammar not defined" - Stop-Script 1 + Write-Log -Type "ERROR" -Object "$MissingProperties $Grammar not defined" -ErrorCode 1 } } return $Properties } else { - Write-Log -Type "ERROR" -Message "$File not found in directory $Directory" - Stop-Script 1 + Write-Log -Type "ERROR" -Object "$File not found in directory $Directory" -ErrorCode 1 } } } diff --git a/Public/Import-Properties.ps1 b/Public/Import-Properties.ps1 index 03e16cd..360d048 100644 --- a/Public/Import-Properties.ps1 +++ b/Public/Import-Properties.ps1 @@ -16,15 +16,15 @@ function Import-Properties { The Custom parameter should be the name of the custom property file. .OUTPUTS - System.Collections.Specialized.OrderedDictionary. Import-Properties returns an + Import-Properties returns an ordered hash table containing the names and values of the properties listed in the property files. .EXAMPLE - Import-Properties -File "default.ini" -Directory ".\conf" -Custom "custom.ini" -CustomDirectory "\\shared" + 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 + 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. @@ -45,7 +45,7 @@ function Import-Properties { [Parameter ( Position = 2, Mandatory = $false, - HelpMessage = "Path to the custom property file name" + HelpMessage = "Path to the custom property file" )] [String] $Custom, @@ -58,7 +58,7 @@ function Import-Properties { $ValidateSet, [Parameter ( HelpMessage = "Define if section headers should be used to group properties or be ignored" - )] + )] [Switch] $Section ) @@ -70,11 +70,7 @@ function Import-Properties { # Check that specified file exists if (Test-Path -Path $Path) { # Parse properties with or without section split - if ($Section) { - $Properties = Read-Properties -Path $Path -Section - } else { - $Properties = Read-Properties -Path $Path - } + $Properties = Read-Properties -Path $Path -Section:$Section # Check if a custom file is provided if ($PSBoundParameters.ContainsKey("Custom")) { # Make sure said file does exists @@ -90,7 +86,8 @@ function Import-Properties { } } } else { - Write-Log -Type "WARN" -Object "Path not found $Custom" + 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 @@ -106,8 +103,7 @@ function Import-Properties { if ($MissingProperties -ge 1) { if ($MissingProperties -eq 1) { $Grammar = "property is" } else { $Grammar = "properties are" } - Write-Log -Type "ERROR" -Object "$MissingProperties $Grammar not defined" - Stop-Script 1 + Write-Log -Type "ERROR" -Object "$MissingProperties $Grammar not defined" -ErrorCode 1 } } return $Properties From 7807237a6a28e7f11b534c00977802893b21d36d Mon Sep 17 00:00:00 2001 From: Florian Carrier Date: Thu, 12 Sep 2019 10:53:42 +0100 Subject: [PATCH 19/19] 1.2.0 Release of version 1.2.0 --- CHANGELOG.md | 2 +- PSTK.psd1 | 1 + Public/Import-CSVProperties.ps1 | 39 +++++++++++++-- {Private => Public}/Resolve-Array.ps1 | 0 Public/Test-Service.ps1 | 5 +- Public/Write-Log.ps1 | 2 +- README.md | 70 +++++++++++++++++---------- 7 files changed, 85 insertions(+), 34 deletions(-) rename {Private => Public}/Resolve-Array.ps1 (100%) diff --git a/CHANGELOG.md b/CHANGELOG.md index d9269be..41263ef 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ 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). -## Unreleased +## [1.2.0](https://github.com/Akaizoku/PSTK/releases/tag/1.1.0) - 2019-09-12 ### Added The following functions have been added: diff --git a/PSTK.psd1 b/PSTK.psd1 index a0edee2..32a91eb 100644 --- a/PSTK.psd1 +++ b/PSTK.psd1 @@ -95,6 +95,7 @@ FunctionsToExport = @( "Remove-EnvironmentVariable", "Remove-Object", "Rename-NumberedFile", + "Resolve-Array", "Resolve-Boolean", "Resolve-Tags", "Resolve-URI", diff --git a/Public/Import-CSVProperties.ps1 b/Public/Import-CSVProperties.ps1 index 1778d4f..1fb9dfb 100644 --- a/Public/Import-CSVProperties.ps1 +++ b/Public/Import-CSVProperties.ps1 @@ -23,7 +23,19 @@ function Import-CSVProperties { HelpMessage = "Specifies the delimiter that separates the property values in the CSV file. The default is a comma (,)." )] [String] - $Delimiter = ',' + $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 @@ -33,6 +45,7 @@ function Import-CSVProperties { } 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 @@ -47,10 +60,28 @@ function Import-CSVProperties { $RowProperties = New-Object -TypeName "System.Collections.Specialized.OrderedDictionary" # Loop through each property foreach ($Header in $Headers) { - # If the property is not the specified key - if ($Header -ne $Key) { + # 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, $Row.$Header) + $RowProperties.Add($Header, $Value) } } # Add row to the property list diff --git a/Private/Resolve-Array.ps1 b/Public/Resolve-Array.ps1 similarity index 100% rename from Private/Resolve-Array.ps1 rename to Public/Resolve-Array.ps1 diff --git a/Public/Test-Service.ps1 b/Public/Test-Service.ps1 index b85f4aa..6068e1e 100644 --- a/Public/Test-Service.ps1 +++ b/Public/Test-Service.ps1 @@ -40,8 +40,9 @@ function Test-Service { HelpMessage = "Name of the service" )] [ValidateNotNullOrEmpty()] + [Alias ("Service")] [String] - $Service + $Name ) Begin { # Get global preference variables @@ -49,7 +50,7 @@ function Test-Service { } Process { try { - if (Get-Service -Name $Service -ErrorAction "Stop") { + if (Get-Service -Name $Name -ErrorAction "Stop") { return $true } } catch { diff --git a/Public/Write-Log.ps1 b/Public/Write-Log.ps1 index a6b52b4..7ea2804 100644 --- a/Public/Write-Log.ps1 +++ b/Public/Write-Log.ps1 @@ -111,7 +111,7 @@ function Write-Log { Mandatory = $true, HelpMessage = "Message to output" )] - [ValidateNotNullOrEmpty ()] + [ValidateNotNull ()] [Alias ("Message", "Output", "Log")] [Object] $Object, diff --git a/README.md b/README.md index 5de3866..769d5ef 100644 --- a/README.md +++ b/README.md @@ -20,32 +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 | Expand-CompressedFile | 1.0.0 | PSTK | -| Function | Find-Key | 1.0.0 | PSTK | -| Function | Format-String | 1.0.0 | PSTK | -| Function | Get-HTTPStatus | 1.0.0 | PSTK | -| Function | Get-KeyValue | 1.0.0 | PSTK | -| Function | Get-Object | 1.0.0 | PSTK | -| Function | Get-Path | 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 | Resolve-URI | 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 | Update-File | 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