diff --git a/.editorconfig b/.editorconfig deleted file mode 100644 index d97e1a5..0000000 --- a/.editorconfig +++ /dev/null @@ -1,6 +0,0 @@ -root = true - -[*] -indent_style = space -indent_size = 4 -tab_width = 4 diff --git a/Convert-EmlFile.ps1 b/Convert-EmlFile.ps1 index 2bbb674..29f2e9f 100644 --- a/Convert-EmlFile.ps1 +++ b/Convert-EmlFile.ps1 @@ -3,16 +3,16 @@ <# .SYNOPSIS Function will parse an eml files. - + .DESCRIPTION Function will parse eml file and return a normalized object that can be used to extract infromation from the encoded file. - + .PARAMETER EmlFileName A string representing the eml file to parse. - + .EXAMPLE PS C:\> Convert-EmlFile -EmlFileName 'C:\Test\test.eml' - + .OUTPUTS System.Object #> @@ -25,21 +25,21 @@ [string] $EmlFileName ) - + # Instantiate new ADODB Stream object $adoStream = New-Object -ComObject 'ADODB.Stream' - + # Open stream $adoStream.Open() - + # Load file $adoStream.LoadFromFile($EmlFileName) - + # Instantiate new CDO Message Object $cdoMessageObject = New-Object -ComObject 'CDO.Message' - + # Open object and pass stream $cdoMessageObject.DataSource.OpenObject($adoStream, '_Stream') - + return $cdoMessageObject } \ No newline at end of file diff --git a/Export-Registry.ps1 b/Export-Registry.ps1 index c9952f1..d99af54 100644 --- a/Export-Registry.ps1 +++ b/Export-Registry.ps1 @@ -3,38 +3,38 @@ function Export-Registry <# .SYNOPSIS Export registry item properties. - + .DESCRIPTION Export item properties for a given registry key. - + By default results will be written to the pipeline unless the -ExportFormat parameter is used. - + .PARAMETER KeyPath A string representing the Key(s) to export in the PsDrive format IE: HKCU:\SOFTWARE\TestSoftware - + .PARAMETER ExportFormat A string representing the format to use for the export. - - Possible values are: - + + Possible values are: + - CSV - XML - + PArameter is used in conjunction with the ExportPath paramter. - + .PARAMETER ExportPath A string representing the path where keys should be exported. - + .PARAMETER NoBinaryData When parameter is specified any binary data present in the registry key is removed. - + .EXAMPLE PS C:\> Export-RegistryNew -KeyPath 'HKCU:\SOFTWARE\TestSoftware' - + .NOTES Additional information about the function. #> - + [CmdletBinding(DefaultParameterSetName = 'PrintOnly')] param ( @@ -65,13 +65,13 @@ function Export-Registry [switch] $NoBinaryData ) - + begin { - # Initialize results array + # Initialize results array [System.Collections.ArrayList]$returnData = @() } - + process { # Go through all paths @@ -80,25 +80,25 @@ function Export-Registry if ((Test-IsRegistryKey -KeyPath $path) -eq $true) { Write-Verbose "Getting properties for key: $path" - + # Get registry item $paramGetItem = @{ Path = $path ErrorAction = 'Stop' } - + [Microsoft.Win32.RegistryKey]$regItem = Get-Item @paramGetItem - + # Get key properties [array]$regItemProperties = $regItem.'Property' - + if ($regItemProperties.Count -gt 0) { # Enumerate properties foreach ($property in $regItemProperties) { Write-Verbose "Exporting $property" - + # Append data to return array [void]($returnData.Add([pscustomobject]@{ 'Path' = $regItem @@ -124,12 +124,12 @@ function Export-Registry else { Write-Warning -Message "Key $path does not exist" - + continue } } } - + end { # Check we have results @@ -143,11 +143,11 @@ function Export-Registry if ($PSBoundParameters.ContainsKey('NoBinaryData')) { Write-Verbose -Message 'Removing binary data from return values' - + # Remove binary data $returnData = $returnData | Where-Object { $_.Type -ne 'Binary' } } - + switch ($ExportFormat) { 'csv' @@ -161,14 +161,14 @@ function Export-Registry $returnData | Export-Clixml -Path $ExportPath -Force } } - + Write-Verbose -Message "Data written to $ExportPath" } - + default { Write-Verbose -Message 'No data will be exported' - + # Print on screen only $returnData } diff --git a/Get-ElapsedTime.ps1 b/Get-ElapsedTime.ps1 index 148d596..821c3a2 100644 --- a/Get-ElapsedTime.ps1 +++ b/Get-ElapsedTime.ps1 @@ -1,183 +1,183 @@ function Get-ElapsedTime { - <# - .SYNOPSIS - Will return information about elapsed time for the given StopWatch. - - .DESCRIPTION - Function requires a [System.Diagnostics.Stopwatch] object as input and will output information about elapsed time. - - By default a [TimeSpan] object is returned containing all information about elapsed time. - - If any other parameter like -Days is used function will return an Int or Double instead depending on the switch used. - - .PARAMETER ElapsedTime - Will return a [TimeSpan] object representing the elapsed time for the given stopwatch. - - .PARAMETER Days - Will return an [Int] object representing the number of days since the stopwatch was started. - - .PARAMETER Hours - Will return an [Int] object representing the number of hours since the stopwatch was started. - - .PARAMETER Minutes - Will return an [Int] object representing the number of minutes since the stopwatch was started. - - .PARAMETER Seconds - Will return an [Int] object representing the number of seconds since the stopwatch was started. - - .PARAMETER TotalDays - Will return a [Double] object representing the number of TotalDays since the stopwatch was started. - - .PARAMETER TotalHours - Will return a [Double] object representing the number of TotalHours since the stopwatch was started. - - .PARAMETER TotalMinutes - Will return a [Double] object representing the number of TotalMinutes since the stopwatch was started. - - .PARAMETER TotalSeconds - Will return a [Double] object representing the number of TotalSeconds since the stopwatch was started. - - .PARAMETER TotalMilliseconds - Will return a [Double] object representing the number of TotalMilliseconds since the stopwatch was started. - - .EXAMPLE - PS C:\> Get-ElapsedTime -ElapsedTime $ElapsedTime -Days - - .OUTPUTS - System.TimeSpan, System.Double, System.Int32 - #> - - [CmdletBinding(DefaultParameterSetName = 'FullOutput', - ConfirmImpact = 'High', - SupportsPaging = $false, - SupportsShouldProcess = $false)] - [OutputType([timespan], ParameterSetName = 'FullOutput')] - [OutputType([int], ParameterSetName = 'Days')] - [OutputType([int], ParameterSetName = 'Hours')] - [OutputType([int], ParameterSetName = 'Minutes')] - [OutputType([int], ParameterSetName = 'Seconds')] - [OutputType([double], ParameterSetName = 'TotalDays')] - [OutputType([double], ParameterSetName = 'TotalHours')] - [OutputType([double], ParameterSetName = 'TotalMinutes')] - [OutputType([double], ParameterSetName = 'TotalSeconds')] - [OutputType([double], ParameterSetName = 'TotalMilliseconds')] - [OutputType([timespan])] - param - ( - [Parameter(ParameterSetName = 'FullOutput', - Mandatory = $true)] - [Parameter(ParameterSetName = 'Days')] - [Parameter(ParameterSetName = 'Hours')] - [Parameter(ParameterSetName = 'Minutes')] - [Parameter(ParameterSetName = 'Seconds')] - [Parameter(ParameterSetName = 'TotalDays')] - [Parameter(ParameterSetName = 'TotalHours')] - [Parameter(ParameterSetName = 'TotalMilliseconds')] - [Parameter(ParameterSetName = 'TotalMinutes')] - [Parameter(ParameterSetName = 'TotalSeconds')] - [System.Diagnostics.Stopwatch] - $ElapsedTime, - [Parameter(ParameterSetName = 'Days')] - [switch] - $Days, - [Parameter(ParameterSetName = 'Hours')] - [switch] - $Hours, - [Parameter(ParameterSetName = 'Minutes')] - [switch] - $Minutes, - [Parameter(ParameterSetName = 'Seconds')] - [switch] - $Seconds, - [Parameter(ParameterSetName = 'TotalDays')] - [switch] - $TotalDays, - [Parameter(ParameterSetName = 'TotalHours')] - [switch] - $TotalHours, - [Parameter(ParameterSetName = 'TotalMinutes')] - [switch] - $TotalMinutes, - [Parameter(ParameterSetName = 'TotalSeconds')] - [switch] - $TotalSeconds, - [Parameter(ParameterSetName = 'TotalMilliseconds')] - [switch] - $TotalMilliseconds - ) - - switch ($PsCmdlet.ParameterSetName) - { - 'FullOutput' - { - # Return full timespan object - return $ElapsedTime.Elapsed - - break - } - 'Days' - { - # Return days with no decimals - return $ElapsedTime.Elapsed.Days - - break - } - 'Hours' - { - # Return hours with no decimals - return $ElapsedTime.Elapsed.Hours - - break - } - 'Minutes' - { - # Return minutes with no decimals - return $ElapsedTime.Elapsed.Minutes - - break - } - 'Seconds' - { - # Return seconds with no decimals - return $ElapsedTime.Elapsed.Seconds - - break - } - 'TotalDays' - { - # Return days with double precision - return $ElapsedTime.Elapsed.TotalDays - - break - } - 'TotalHours' - { - # Return hours with double precision - return $ElapsedTime.Elapsed.TotalHours - - break - } - 'TotalMinutes' - { - # Return minutes with double precision - return $ElapsedTime.Elapsed.TotalMinutes - - break - } - 'TotalSeconds' - { - # Return seconds with double precision - return $ElapsedTime.Elapsed.TotalSeconds - - break - } - 'TotalMilliseconds' - { - # Return milliseconds with double precision - return $ElapsedTime.Elapsed.TotalMilliseconds - - break - } - } + <# + .SYNOPSIS + Will return information about elapsed time for the given StopWatch. + + .DESCRIPTION + Function requires a [System.Diagnostics.Stopwatch] object as input and will output information about elapsed time. + + By default a [TimeSpan] object is returned containing all information about elapsed time. + + If any other parameter like -Days is used function will return an Int or Double instead depending on the switch used. + + .PARAMETER ElapsedTime + Will return a [TimeSpan] object representing the elapsed time for the given stopwatch. + + .PARAMETER Days + Will return an [Int] object representing the number of days since the stopwatch was started. + + .PARAMETER Hours + Will return an [Int] object representing the number of hours since the stopwatch was started. + + .PARAMETER Minutes + Will return an [Int] object representing the number of minutes since the stopwatch was started. + + .PARAMETER Seconds + Will return an [Int] object representing the number of seconds since the stopwatch was started. + + .PARAMETER TotalDays + Will return a [Double] object representing the number of TotalDays since the stopwatch was started. + + .PARAMETER TotalHours + Will return a [Double] object representing the number of TotalHours since the stopwatch was started. + + .PARAMETER TotalMinutes + Will return a [Double] object representing the number of TotalMinutes since the stopwatch was started. + + .PARAMETER TotalSeconds + Will return a [Double] object representing the number of TotalSeconds since the stopwatch was started. + + .PARAMETER TotalMilliseconds + Will return a [Double] object representing the number of TotalMilliseconds since the stopwatch was started. + + .EXAMPLE + PS C:\> Get-ElapsedTime -ElapsedTime $ElapsedTime -Days + + .OUTPUTS + System.TimeSpan, System.Double, System.Int32 + #> + + [CmdletBinding(DefaultParameterSetName = 'FullOutput', + ConfirmImpact = 'High', + SupportsPaging = $false, + SupportsShouldProcess = $false)] + [OutputType([timespan], ParameterSetName = 'FullOutput')] + [OutputType([int], ParameterSetName = 'Days')] + [OutputType([int], ParameterSetName = 'Hours')] + [OutputType([int], ParameterSetName = 'Minutes')] + [OutputType([int], ParameterSetName = 'Seconds')] + [OutputType([double], ParameterSetName = 'TotalDays')] + [OutputType([double], ParameterSetName = 'TotalHours')] + [OutputType([double], ParameterSetName = 'TotalMinutes')] + [OutputType([double], ParameterSetName = 'TotalSeconds')] + [OutputType([double], ParameterSetName = 'TotalMilliseconds')] + [OutputType([timespan])] + param + ( + [Parameter(ParameterSetName = 'FullOutput', + Mandatory = $true)] + [Parameter(ParameterSetName = 'Days')] + [Parameter(ParameterSetName = 'Hours')] + [Parameter(ParameterSetName = 'Minutes')] + [Parameter(ParameterSetName = 'Seconds')] + [Parameter(ParameterSetName = 'TotalDays')] + [Parameter(ParameterSetName = 'TotalHours')] + [Parameter(ParameterSetName = 'TotalMilliseconds')] + [Parameter(ParameterSetName = 'TotalMinutes')] + [Parameter(ParameterSetName = 'TotalSeconds')] + [System.Diagnostics.Stopwatch] + $ElapsedTime, + [Parameter(ParameterSetName = 'Days')] + [switch] + $Days, + [Parameter(ParameterSetName = 'Hours')] + [switch] + $Hours, + [Parameter(ParameterSetName = 'Minutes')] + [switch] + $Minutes, + [Parameter(ParameterSetName = 'Seconds')] + [switch] + $Seconds, + [Parameter(ParameterSetName = 'TotalDays')] + [switch] + $TotalDays, + [Parameter(ParameterSetName = 'TotalHours')] + [switch] + $TotalHours, + [Parameter(ParameterSetName = 'TotalMinutes')] + [switch] + $TotalMinutes, + [Parameter(ParameterSetName = 'TotalSeconds')] + [switch] + $TotalSeconds, + [Parameter(ParameterSetName = 'TotalMilliseconds')] + [switch] + $TotalMilliseconds + ) + + switch ($PsCmdlet.ParameterSetName) + { + 'FullOutput' + { + # Return full timespan object + return $ElapsedTime.Elapsed + + break + } + 'Days' + { + # Return days with no decimals + return $ElapsedTime.Elapsed.Days + + break + } + 'Hours' + { + # Return hours with no decimals + return $ElapsedTime.Elapsed.Hours + + break + } + 'Minutes' + { + # Return minutes with no decimals + return $ElapsedTime.Elapsed.Minutes + + break + } + 'Seconds' + { + # Return seconds with no decimals + return $ElapsedTime.Elapsed.Seconds + + break + } + 'TotalDays' + { + # Return days with double precision + return $ElapsedTime.Elapsed.TotalDays + + break + } + 'TotalHours' + { + # Return hours with double precision + return $ElapsedTime.Elapsed.TotalHours + + break + } + 'TotalMinutes' + { + # Return minutes with double precision + return $ElapsedTime.Elapsed.TotalMinutes + + break + } + 'TotalSeconds' + { + # Return seconds with double precision + return $ElapsedTime.Elapsed.TotalSeconds + + break + } + 'TotalMilliseconds' + { + # Return milliseconds with double precision + return $ElapsedTime.Elapsed.TotalMilliseconds + + break + } + } } diff --git a/Get-TimerStatus.ps1 b/Get-TimerStatus.ps1 index cf7998e..ede44b3 100644 --- a/Get-TimerStatus.ps1 +++ b/Get-TimerStatus.ps1 @@ -1,29 +1,29 @@ function Get-TimerStatus { - <# - .SYNOPSIS - Will return boolean value representing status of an existing stopwatch. - - .DESCRIPTION - Function requires a [System.Diagnostics.Stopwatch] object as input and will return $True if stopwatch is running or $False otherwise. - - .PARAMETER Timer - A [System.Diagnostics.Stopwatch] object representing the StopWatch to check status for. - - .EXAMPLE - PS C:\> Get-TimerStatus -Timer $Timer - - .OUTPUTS - System.Boolean - #> - - [OutputType([bool])] - param - ( - [Parameter(Mandatory = $true)] - [System.Diagnostics.Stopwatch] - $Timer - ) - - return $Timer.IsRunning + <# + .SYNOPSIS + Will return boolean value representing status of an existing stopwatch. + + .DESCRIPTION + Function requires a [System.Diagnostics.Stopwatch] object as input and will return $True if stopwatch is running or $False otherwise. + + .PARAMETER Timer + A [System.Diagnostics.Stopwatch] object representing the StopWatch to check status for. + + .EXAMPLE + PS C:\> Get-TimerStatus -Timer $Timer + + .OUTPUTS + System.Boolean + #> + + [OutputType([bool])] + param + ( + [Parameter(Mandatory = $true)] + [System.Diagnostics.Stopwatch] + $Timer + ) + + return $Timer.IsRunning } \ No newline at end of file diff --git a/Get-UniqueUPN.ps1 b/Get-UniqueUPN.ps1 index 58a9ece..a7562e1 100644 --- a/Get-UniqueUPN.ps1 +++ b/Get-UniqueUPN.ps1 @@ -3,50 +3,50 @@ function Get-UniqueUPN <# .SYNOPSIS Cmdlet will generate a forest wide unique UPN. - + .DESCRIPTION Cmdlet will generate a forest wide unique UPN according to generation rules defined by the user. - + Cmdlet accept different types of objects to generate the UPN to allow greater flexibility - + ADObject - For example and object from Get-AdUser cmdlet Strings - Representing First Name, Last Name etc. DirectoryService Objects - For example when using native .Net methods to retrieve the identity - + .PARAMETER ADObject An ADObject for example output of the Get-ADUser cmdlet - + .PARAMETER FirstName A string representing the First Name of the user - + .PARAMETER LastName A string representing the Last Name of the user - + .PARAMETER MiddleName A string representing the Middle Name of the user, parameter is optional. - + .PARAMETER UPNSuffix A string representing the UPN suffix to be used. - + .PARAMETER FirstNameFormat A string representing the format to be for the First Name part of the UPN. - + .PARAMETER LastNameFormat A string representing the format to be for the Last Name part of the UPN. - + .PARAMETER IncludeMiddleName When paramenter is specified user Middle Name, if present, will be included in the UPN generation process. - + .PARAMETER ADServer A string representing the name of the AD Domain Controller that will be used to query Active Directory. - + If no server is specified the closest Global Catalog will be automatically selected. - + .PARAMETER Separator A string representing the separator to be used between UPN parts, defaults to a '.'. #> - + [CmdletBinding(DefaultParameterSetName = 'Strings')] param ( @@ -79,7 +79,7 @@ function Get-UniqueUPN [ValidateNotNullOrEmpty()] [string]$Separator = '.' ) - + if ($PSCmdlet.ParameterSetName -eq 'ADObject') { switch ($ADObject.GetType().FullName) @@ -89,7 +89,7 @@ function Get-UniqueUPN [string]$firstName = $ADObject.GivenName [string]$lastName = $ADObject.Surname [string]$middleName = $ADObject.MiddleName - + break } 'System.DirectoryServices.DirectoryEntry' @@ -97,7 +97,7 @@ function Get-UniqueUPN [string]$firstName = $ADObject.Properties['givenName'][0] [string]$lastName = $ADObject.Properties['sn'][0] [string]$middleName = $ADObject.Properties['middleName'][0] - + break } 'System.DirectoryServices.SearchResult' @@ -105,7 +105,7 @@ function Get-UniqueUPN [string]$firstName = $ADObject.Properties['givenName'][0] [string]$lastName = $ADObject.Properties['sn'][0] [string]$middleName = $ADObject.Properties['middleName'][0] - + break } default @@ -120,7 +120,7 @@ function Get-UniqueUPN [string]$lastName = $LastName [string]$middleName = $MiddleName } - + # Format first name $firstName = switch ($FirstNameFormat) { @@ -133,7 +133,7 @@ function Get-UniqueUPN $firstName.Substring(0, 1) } } - + # Format last name $LastName = switch ($FirstNameFormat) { @@ -146,24 +146,24 @@ function Get-UniqueUPN $LastName.Substring(0, 1) } } - + # Use middle name [string]$middleNamePart = if ($IncludeMiddleName -and $MiddleName) { '{0}{1}' -f $Separator, $MiddleName } - + # Setup required attributes [string]$baseUPN = ('{0}{1}{2}{3}@{4}' -f $FirstName, $middleNamePart, $Separator, $LastName, $UPNSuffix).ToLower() [string]$uniqueUPN = $baseUPN [int]$counter = 1 - + while (Test-UPNExist -UPN $uniqueUPN -Server $ADServer) { $uniqueUPN = '{0}{1}@{2}' -f ($baseUPN.Split('@')[0]), $counter, $UPNSuffix - + $counter++ } - + return $uniqueUPN } \ No newline at end of file diff --git a/New-ApiRequest.ps1 b/New-ApiRequest.ps1 index e3a907b..09d0651 100644 --- a/New-ApiRequest.ps1 +++ b/New-ApiRequest.ps1 @@ -1,121 +1,121 @@ function New-ApiRequest { <# - .SYNOPSIS - Function will query data from an URL API. - - .DESCRIPTION - Function is intended as a wrapper around PowerShell built-in Invoke-RestMethod cmdlet allowing user to quickly generate web requests to APIs requiring OAuth2 authentication. - - .PARAMETER ApiKey - A string representing the API key to be used. - - .PARAMETER ApiSecret - A string representing the API key secret. - - .PARAMETER ApiUrl - A string representing the API endpoint URL - - .PARAMETER GrantType - A string representing the Grant Type supported by the API. - - If not specified it will default to client_credentials. - - .PARAMETER ContentType - A string representing the content type to send as part of the request in case request needs to be crafted with a special ContentType. - - .PARAMETER Method - A string representing the method to be used in the web request. - - If not specified it will default to GET. - - .PARAMETER Headers - A string representing custom headers to send as part of the webrequest. - - .EXAMPLE - PS C:\> New-ApiRequest -ApiKey 'Value1' -ApiUrl 'Value2' -ApiSecret 'MySecret' - - .NOTES - Additional information about the function. + .SYNOPSIS + Function will query data from an URL API. + + .DESCRIPTION + Function is intended as a wrapper around PowerShell built-in Invoke-RestMethod cmdlet allowing user to quickly generate web requests to APIs requiring OAuth2 authentication. + + .PARAMETER ApiKey + A string representing the API key to be used. + + .PARAMETER ApiSecret + A string representing the API key secret. + + .PARAMETER ApiUrl + A string representing the API endpoint URL + + .PARAMETER GrantType + A string representing the Grant Type supported by the API. + + If not specified it will default to client_credentials. + + .PARAMETER ContentType + A string representing the content type to send as part of the request in case request needs to be crafted with a special ContentType. + + .PARAMETER Method + A string representing the method to be used in the web request. + + If not specified it will default to GET. + + .PARAMETER Headers + A string representing custom headers to send as part of the webrequest. + + .EXAMPLE + PS C:\> New-ApiRequest -ApiKey 'Value1' -ApiUrl 'Value2' -ApiSecret 'MySecret' + + .NOTES + Additional information about the function. #> - - [CmdletBinding()] - [OutputType([pscustomobject])] - param - ( - [Parameter(Mandatory = $true)] - [string] - $ApiKey, - [string] - $ApiSecret, - [Parameter(Mandatory = $true)] - [ValidateNotNullOrEmpty()] - [string] - $ApiUrl, - [ValidateNotNullOrEmpty()] - [string] - $GrantType = 'client_credentials', - [ValidateNotNullOrEmpty()] - [string] - $ContentType, - [ValidateNotNullOrEmpty()] - [ValidateSet('GET', 'POST', IgnoreCase = $true)] - [string] - $Method = 'GET', - [ValidateNotNullOrEmpty()] - [string] - $Headers - ) - - Process - { - # Generate request post data - [hashtable]$requestBody = @{ - 'client_id' = $ApiKey; - 'grant_type' = $GrantType - } - - switch ($PSBoundParameters.Keys) - { - 'ContentType' - { - $requestBody.Add('ContentType', $ContentType) - - break - } - 'ApiSecret' - { - $requestBody.Add('client_secret', $ApiSecret) - - break - } - } - - # Define splat command - $paramInvokeWebRequest = @{ - Uri = $ApiUrl - Body = $requestBody - } - - # Get passed parameters - switch ($PSBoundParameters.Keys) - { - 'Headers' - { - # Add custom header - $paramInvokeWebRequest.Add('Headers', $Headers) - - break - } - 'Method' - { - # Use custom method - $paramInvokeWebRequest.Add('Method', $Method) - - break - } - } - - return Invoke-RestMethod @paramInvokeWebRequest - } + + [CmdletBinding()] + [OutputType([pscustomobject])] + param + ( + [Parameter(Mandatory = $true)] + [string] + $ApiKey, + [string] + $ApiSecret, + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [string] + $ApiUrl, + [ValidateNotNullOrEmpty()] + [string] + $GrantType = 'client_credentials', + [ValidateNotNullOrEmpty()] + [string] + $ContentType, + [ValidateNotNullOrEmpty()] + [ValidateSet('GET', 'POST', IgnoreCase = $true)] + [string] + $Method = 'GET', + [ValidateNotNullOrEmpty()] + [string] + $Headers + ) + + Process + { + # Generate request post data + [hashtable]$requestBody = @{ + 'client_id' = $ApiKey; + 'grant_type' = $GrantType + } + + switch ($PSBoundParameters.Keys) + { + 'ContentType' + { + $requestBody.Add('ContentType', $ContentType) + + break + } + 'ApiSecret' + { + $requestBody.Add('client_secret', $ApiSecret) + + break + } + } + + # Define splat command + $paramInvokeWebRequest = @{ + Uri = $ApiUrl + Body = $requestBody + } + + # Get passed parameters + switch ($PSBoundParameters.Keys) + { + 'Headers' + { + # Add custom header + $paramInvokeWebRequest.Add('Headers', $Headers) + + break + } + 'Method' + { + # Use custom method + $paramInvokeWebRequest.Add('Method', $Method) + + break + } + } + + return Invoke-RestMethod @paramInvokeWebRequest + } } \ No newline at end of file diff --git a/New-LogEntry/CHANGELOG.md b/New-LogEntry/CHANGELOG.md index e9e5583..fa6fc69 100644 --- a/New-LogEntry/CHANGELOG.md +++ b/New-LogEntry/CHANGELOG.md @@ -1,14 +1,5 @@ # New-LogEntry - Change History -## Maintenance parity with IT-ToolBox (unreleased) - -- Synchronize public logging implementation and private helpers with IT-ToolBox after PR #9. -- Treat redaction replacements literally, preventing matched secrets from being reinserted. -- Serialize buffer flushes, retain entries after failures and preserve appended entries. -- Resolve default paths from the caller, falling back to the interactive working directory. -- Reject conflicting buffered severity switches. -- Add regression and concurrent-process tests alongside the dot-source loader tests. - ## Version 2.1.0 - Refactored message formatting, console output, file writes, and buffer handling to remove duplicated logic diff --git a/New-LogEntry/New-LogEntry.ps1 b/New-LogEntry/New-LogEntry.ps1 index 250e145..f7240af 100644 --- a/New-LogEntry/New-LogEntry.ps1 +++ b/New-LogEntry/New-LogEntry.ps1 @@ -18,7 +18,6 @@ $script:NewLogEntrySourceFiles = @( 'Private/Add-NewLogEntryBuffer.ps1' 'Private/Get-NewLogEntryBuffer.ps1' 'Private/Clear-NewLogEntryBuffer.ps1' - 'Private/Flush-NewLogEntryBuffer.ps1' 'Public/New-LogEntry.ps1' ) diff --git a/New-LogEntry/Private/ConvertTo-NewLogEntryRedactedMessage.ps1 b/New-LogEntry/Private/ConvertTo-NewLogEntryRedactedMessage.ps1 index 9838c4a..e0426f1 100644 --- a/New-LogEntry/Private/ConvertTo-NewLogEntryRedactedMessage.ps1 +++ b/New-LogEntry/Private/ConvertTo-NewLogEntryRedactedMessage.ps1 @@ -18,8 +18,7 @@ function ConvertTo-NewLogEntryRedactedMessage continue } - # Escape dollar signs so replacement syntax cannot reinsert matched secrets. - $redactedMessage = [regex]::Replace($redactedMessage, $item, $Replacement.Replace('$', '$$')) + $redactedMessage = [regex]::Replace($redactedMessage, $item, $Replacement) } return $redactedMessage diff --git a/New-LogEntry/Private/Flush-NewLogEntryBuffer.ps1 b/New-LogEntry/Private/Flush-NewLogEntryBuffer.ps1 deleted file mode 100644 index 705d582..0000000 --- a/New-LogEntry/Private/Flush-NewLogEntryBuffer.ps1 +++ /dev/null @@ -1,38 +0,0 @@ -function Flush-NewLogEntryBuffer -{ - param( - [string]$Path, - [ValidateRange(1, 86400)] - [int]$LockTimeoutSeconds = 30 - ) - - Initialize-NewLogEntryState - - # Serialize snapshot, write, and removal with other operations on this buffer. - # A failed write leaves entries available for a later retry. - [System.Threading.Monitor]::Enter($script:NewLogEntryBufferLock) - try - { - $lines = $script:NewLogEntryBuffer.ToArray() - if ($lines.Count -eq 0) - { - return - } - - Write-NewLogEntryLines -Lines $lines -Path $Path -LockTimeoutSeconds $LockTimeoutSeconds - - # Remove only the written prefix, preserving any reentrant append during writing. - $script:NewLogEntryBuffer.RemoveRange(0, $lines.Count) - $script:messageBuffer = $script:NewLogEntryBuffer -join [Environment]::NewLine - if ($script:messageBuffer.Length -gt 0) - { - $script:messageBuffer += [Environment]::NewLine - } - } - finally - { - [System.Threading.Monitor]::Exit($script:NewLogEntryBufferLock) - } - - return $lines -} diff --git a/New-LogEntry/Private/Resolve-NewLogEntryPath.ps1 b/New-LogEntry/Private/Resolve-NewLogEntryPath.ps1 index df27c19..e69de29 100644 --- a/New-LogEntry/Private/Resolve-NewLogEntryPath.ps1 +++ b/New-LogEntry/Private/Resolve-NewLogEntryPath.ps1 @@ -1,34 +0,0 @@ -function Resolve-NewLogEntryPath -{ - param([string]$Path, [string]$CallerScriptPath) - - if (-not [string]::IsNullOrWhiteSpace($Path)) - { - return $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($Path) - } - - $basePath = if (-not [string]::IsNullOrWhiteSpace($CallerScriptPath)) - { - $CallerScriptPath - } - else - { - Join-Path -Path (Get-Location).ProviderPath -ChildPath 'PowerShell' - } - - $directory = Split-Path -Path $basePath -Parent - $fileName = Split-Path -Path $basePath -Leaf - - if ([string]::IsNullOrWhiteSpace($directory)) - { - $directory = (Get-Location).ProviderPath - } - - if ([string]::IsNullOrWhiteSpace($fileName)) - { - $fileName = 'PowerShell' - } - - $safeTimestamp = [DateTime]::Now.ToString('yyyyMMdd-HHmmss') - return Join-Path -Path $directory -ChildPath ('{0}-LogFile-{1}.log' -f $fileName, $safeTimestamp) -} diff --git a/New-LogEntry/Tests/LoggerParity.Tests.ps1 b/New-LogEntry/Tests/LoggerParity.Tests.ps1 deleted file mode 100644 index 9a14978..0000000 --- a/New-LogEntry/Tests/LoggerParity.Tests.ps1 +++ /dev/null @@ -1,195 +0,0 @@ -BeforeAll { - $loaderPath = (Resolve-Path (Join-Path $PSScriptRoot '../New-LogEntry.ps1')).Path - New-Module -Name StandaloneNewLogEntry -ArgumentList $loaderPath -ScriptBlock { - param($Loader) - . $Loader - } | Import-Module -Force -} -AfterAll { Remove-Module StandaloneNewLogEntry } - -Describe 'New-LogEntry audit regressions' { - BeforeEach { - New-LogEntry -ClearBuffer - } - - It 'treats regex replacement expressions as literal text: ' -ForEach @( - @{ Replacement = '$0' } - @{ Replacement = '$1' } - @{ Replacement = '$&' } - @{ Replacement = '$$' } - @{ Replacement = '${secret}' } - ) { - $path = Join-Path $TestDrive ([guid]::NewGuid().ToString() + '.log') - $line = New-LogEntry -LogMessage 'token=do-not-disclose' -RedactSecrets -RedactionText $Replacement -LogFilePath $path -NoConsole -PassThru - $line | Should -Not -Match 'do-not-disclose' - $line.EndsWith($Replacement) | Should -BeTrue - (Get-Content -LiteralPath $path) | Should -Be $line - New-LogEntry -LogMessage 'token=do-not-disclose' -BufferOnly -RedactPattern '(?token=\S+)' -RedactionText $Replacement - (New-LogEntry -GetBuffer).EndsWith($Replacement) | Should -BeTrue - } - - It 'rejects conflicting buffered severity switches: and ' -ForEach @( - @{ First = 'BufferOnlyInfo'; Second = 'BufferOnlyWarning' } - @{ First = 'BufferOnlyInfo'; Second = 'BufferOnlyError' } - @{ First = 'BufferOnlyWarning'; Second = 'BufferOnlyError' } - ) { - $flags = @{ $First = $true; $Second = $true } - { New-LogEntry -LogMessage 'conflict' @flags } | Should -Throw '*Use only one*' - @(New-LogEntry -GetBuffer).Count | Should -Be 0 - } - - It 'still accepts individual buffered severity switches: ' -ForEach @( - @{ Flag = 'BufferOnlyInfo'; Tag = 'INFO' } - @{ Flag = 'BufferOnlyWarning'; Tag = 'WARNING' } - @{ Flag = 'BufferOnlyError'; Tag = 'ERROR' } - ) { - $flags = @{ $Flag = $true } - New-LogEntry -LogMessage 'message' -BufferOnly @flags - (New-LogEntry -GetBuffer) | Should -Match "\[$Tag\]: message$" - } - - It 'preserves an entry appended while the snapshot is being written' { - InModuleScope StandaloneNewLogEntry { - Mock Write-NewLogEntryLines { - [System.Threading.Monitor]::IsEntered($script:NewLogEntryBufferLock) | Should -BeTrue - Add-NewLogEntryBuffer -Lines 'arrived-during-flush' - } - New-LogEntry -LogMessage 'original' -BufferOnly - $flushed = @(New-LogEntry -FlushBuffer -LogFilePath 'unused.log' -NoConsole -PassThru) - $flushed.Count | Should -Be 1 - $flushed[0] | Should -Match ': original$' - @(New-LogEntry -GetBuffer).Count | Should -Be 1 - (New-LogEntry -GetBuffer) | Should -Be 'arrived-during-flush' - $script:messageBuffer | Should -Be ("arrived-during-flush" + [Environment]::NewLine) - } - } - - It 'retains all entries when writing fails and releases the buffer lock' { - InModuleScope StandaloneNewLogEntry { - Mock Write-NewLogEntryLines { throw 'write failed' } - New-LogEntry -LogMessage 'retry-me' -BufferOnly - { New-LogEntry -FlushBuffer -LogFilePath 'unused.log' -NoConsole } | Should -Throw '*write failed*' - (New-LogEntry -GetBuffer) | Should -Match ': retry-me$' - [System.Threading.Monitor]::IsEntered($script:NewLogEntryBufferLock) | Should -BeFalse - New-LogEntry -LogMessage 'next' -BufferOnly - @(New-LogEntry -GetBuffer).Count | Should -Be 2 - } - } - - It 'writes buffered entries exactly once across successive flushes' { - $path = Join-Path $TestDrive 'once.log' - New-LogEntry -LogMessage 'once' -BufferOnly - New-LogEntry -FlushBuffer -LogFilePath $path -NoConsole - New-LogEntry -FlushBuffer -LogFilePath $path -NoConsole - @(Get-Content -LiteralPath $path).Count | Should -Be 1 - @(New-LogEntry -GetBuffer).Count | Should -Be 0 - } - - It 'resolves interactive defaults in the current directory' { - InModuleScope StandaloneNewLogEntry -Parameters @{ Directory = $TestDrive } { - param($Directory) - Push-Location $Directory - try { - $path = Resolve-NewLogEntryPath - (Split-Path $path -Parent) | Should -Be $Directory - (Split-Path $path -Leaf) | Should -Match '^PowerShell-LogFile-\d{8}-\d{6}\.log$' - } - finally { Pop-Location } - } - } - - It 'writes default logs beside a calling script, including buffer flushes' { - $scriptDirectory = Join-Path $TestDrive 'caller' - New-Item -ItemType Directory $scriptDirectory | Out-Null - $caller = Join-Path $scriptDirectory 'automation.ps1' - @' -New-LogEntry -LogMessage 'direct-default' -NoConsole -New-LogEntry -LogMessage 'buffer-default' -BufferOnly -New-LogEntry -FlushBuffer -NoConsole -'@ | Set-Content -LiteralPath $caller - & $caller - $logs = @(Get-ChildItem $scriptDirectory -Filter 'automation.ps1-LogFile-*.log') - $logs.Count | Should -BeGreaterThan 0 - $lines = @(Get-Content -LiteralPath $logs.FullName) - $lines.Count | Should -Be 2 - $lines[0] | Should -Match ': direct-default$' - $lines[1] | Should -Match ': buffer-default$' - } -} - - -Describe 'New-LogEntry file-write integration' { - BeforeEach { New-LogEntry -ClearBuffer } - - It 'can retry buffered entries after a real filesystem failure' { - New-LogEntry -LogMessage 'retained-after-failure' -BufferOnly - { New-LogEntry -FlushBuffer -LogFilePath $TestDrive -NoConsole -ErrorAction Stop } | Should -Throw - $before = @(New-LogEntry -GetBuffer) - $before.Count | Should -Be 1 - $path = Join-Path $TestDrive 'retried.log' - $written = @(New-LogEntry -FlushBuffer -LogFilePath $path -NoConsole -PassThru) - $written[0] | Should -Be $before[0] - (Get-Content -LiteralPath $path) | Should -Be $before[0] - @(New-LogEntry -GetBuffer).Count | Should -Be 0 - } - - It 'preserves every direct and buffered line from concurrent processes' { - $path = Join-Path $TestDrive 'concurrent.log' - $manifest = $loaderPath - $jobs = @() - try { - $jobs = @(1..3 | ForEach-Object { - Start-Job -ArgumentList $manifest, $path, $_ -ScriptBlock { - param($Manifest, $Path, $Worker) - . $Manifest - foreach ($i in 1..20) { - New-LogEntry -LogMessage "worker-$Worker-direct-$i" -LogFilePath $Path -NoConsole -ErrorAction Stop - New-LogEntry -LogMessage "worker-$Worker-buffer-$i" -BufferOnly -ErrorAction Stop - } - New-LogEntry -FlushBuffer -LogFilePath $Path -NoConsole -ErrorAction Stop - } - }) - $jobs | Receive-Job -Wait -ErrorAction Stop - foreach ($job in $jobs) { $job.State | Should -Be 'Completed' } - $lines = @(Get-Content -LiteralPath $path) - $lines.Count | Should -Be 120 - $messages = @($lines | ForEach-Object { $_ -replace '^.*\[INFO\]: ', '' }) - @($messages | Select-Object -Unique).Count | Should -Be 120 - foreach ($worker in 1..3) { - foreach ($i in 1..20) { - $messages | Should -Contain "worker-$worker-direct-$i" - $messages | Should -Contain "worker-$worker-buffer-$i" - } - } - } - finally { - if ($jobs.Count -gt 0) { $jobs | Remove-Job -Force } - } - } -} - - -Describe 'Interactive default log path' { - It 'writes in the current directory when called without a script file' { - $manifest = $loaderPath - $job = Start-Job -ArgumentList $manifest, $TestDrive -ScriptBlock { - param($Manifest, $Directory) - . $Manifest - Set-Location $Directory - New-LogEntry -LogMessage 'interactive-default' -NoConsole -ErrorAction Stop - New-LogEntry -LogMessage 'interactive-buffer' -BufferOnly - New-LogEntry -FlushBuffer -NoConsole -ErrorAction Stop - } - try { - $job | Receive-Job -Wait -ErrorAction Stop - $job.State | Should -Be 'Completed' - $logs = @(Get-ChildItem $TestDrive -Filter 'PowerShell-LogFile-*.log') - $logs.Count | Should -BeGreaterThan 0 - $lines = @(Get-Content -LiteralPath $logs.FullName) - $lines.Count | Should -Be 2 - $lines[0] | Should -Match ': interactive-default$' - $lines[1] | Should -Match ': interactive-buffer$' - } - finally { $job | Remove-Job -Force } - } -}