Functions/Private/Helpers.ps1

# Internal helper functions used by other module functions

Function New-DynamicParam {
    [cmdletbinding()]
    Param (
        [Parameter(Mandatory=$True)]
        [string]$ParamName,
        [Parameter(Mandatory=$True)]
        [string]$Prefix,
        [Parameter(Mandatory=$True)]
        [bool]$ParamRequired,
        [Parameter(Mandatory=$True)]
        [int]$Position        
    )
    
    Process {
        # Set the dynamic parameters' name
        New-Variable -Name "ParamName_$Prefix" -Value $ParamName -Scope Script -Force
        
        # Create the collection of attributes
        $AttribColl_XXX = New-Object System.Collections.ObjectModel.Collection[System.Attribute]
        
        # Create and set the parameters' attributes
        $ParamAttrib_XXX = New-Object System.Management.Automation.ParameterAttribute
        $ParamAttrib_XXX.Mandatory = $ParamRequired
        $ParamAttrib_XXX.Position = $Position
                
        # Add the attributes to the attributes collection
        $AttribColl_XXX.Add($ParamAttrib_XXX)
        
        New-Variable -Name "AttribColl_$Prefix" -Value $AttribColl_XXX -Scope Script -Force
    }
}


Function ConvertFrom-XMLElement {
    <#
    .Synopsis
        Converts named nodes of an element to properties of a PSObject, recursively.
 
    .Parameter Element
        The element to convert to a PSObject.
 
    .Parameter SelectXmlInfo
        Output from the Select-Xml cmdlet.
 
    .Inputs
        Microsoft.PowerShell.Commands.SelectXmlInfo output from Select-Xml.
 
    .Outputs
        System.Management.Automation.PSCustomObject object created from selected XML.
 
    .Link
        Select-Xml
 
    .Example
        Select-Xml /configuration/appSettings/add web.config |ConvertFrom-XmlElement
 
        key value
        --- -----
        webPages:Enabled false
    #>


    #Requires -Version 3
    [CmdletBinding()][OutputType([psobject])] Param(
    [Parameter(ParameterSetName='Element',Position=0,Mandatory=$true,ValueFromPipeline=$true)][Xml.XmlElement] $Element,
    [Parameter(ParameterSetName='SelectXmlInfo',Position=0,Mandatory=$true,ValueFromPipeline=$true)]
    [Microsoft.PowerShell.Commands.SelectXmlInfo]$SelectXmlInfo
    )
    Process
    {
        switch($PSCmdlet.ParameterSetName)
        {
            SelectXmlInfo { @($SelectXmlInfo | ForEach-Object { [Xml.XmlElement]$_.Node } | ConvertFrom-XmlElement) }
            Element
            {
                If(($Element.SelectNodes('*') | Group-Object Name | Measure-Object).Count -eq 1)
                {
                    @($Element.SelectNodes('*') |ConvertFrom-XmlElement)
                }
                Else
                {
                    $properties = @{}
                    $Element.Attributes | ForEach-Object { [void]$properties.Add($_.Name,$_.Value) }
                    foreach ($node in $Element.ChildNodes | Where-Object { $_.Name -and $_.Name -ne '#whitespace' } )
                    {
                        $subelements = $node.SelectNodes('*') | Group-Object Name
                        $value =
                            If($node.InnerText -and !$subelements)
                            {
                                $node.InnerText
                            }
                            ElseIf(($subelements | Measure-Object).Count -eq 1)
                            {
                                @($node.SelectNodes('*') | ConvertFrom-XmlElement)
                            }
                            Else
                            {
                                ConvertFrom-XmlElement $node
                            }
                        If(!$properties.Contains($node.Name))
                        { # new property
                            [void]$properties.Add($node.Name,$value)
                        }
                        Else
                        { # property name collision!
                            If($properties[$node.Name] -isnot [Collections.Generic.List[object]])
                            { $properties[$node.Name] = ([Collections.Generic.List[object]]@($properties[$node.Name],$value)) }
                            Else
                            { $properties[$node.Name].Add($value) }
                        }
                    }
                    New-Object PSObject -Property $properties
                }
            }
        }
    }    
}


Function New-EPCTestPointXML {
    [cmdletbinding()]
    Param (
        [Parameter(Mandatory=$True)]
        $Data        
    )

    $xmlData = '<testPointList>'
    ForEach ($Obj in $Data) {    
        $Properties = $Obj |  Get-Member -MemberType NoteProperty | Select-Object -ExpandProperty Name
        $xmlData += '<testPoint>'
        foreach ($Property in $Properties) {
            $xmlData += "<$Property>$($Obj.$Property)</$Property>"
        }
        $xmlData += '</testPoint>'
    }
    $xmlData += '</testPointList>'
    Return $xmlData    
}


Function Get-JSONErrorStream {
    <#
        .SYNOPSIS
        Returns the error text of a JSON stream
 
        .DESCRIPTION
        Returns the error text of a JSON stream
         
        .PARAMETER JSONResponse
        The error response
             
        .EXAMPLE
        Get-JSONErrorStream $_
        Returns the error message from a JSON stream that errored out.
         
        .NOTES
        Version 1.0
    #>

    Param(
        [Parameter(Mandatory=$true, ValueFromPipeline=$true)]
        $JSONResponse
    )

    $ResponseStream = $JSONResponse.Exception.Response.GetResponseStream()
    $Reader = New-Object System.IO.StreamReader($ResponseStream)
    $ResponseBody = $Reader.ReadToEnd() | ConvertFrom-Json
    Write-Host -ForegroundColor Red -BackgroundColor Black $ResponseBody.errorMessage
    Write-Host
    Write-Host
}


Function New-SAMLInteractive {
    [CmdletBinding()]
    Param(
        [Parameter(Mandatory=$true)]
        [string] $LoginIDP
    )

    Begin{
        $RegEx = '(?i)name="SAMLResponse"(?: type="hidden")? value=\"(.*?)\"(?:.*)?\/>'
        Add-Type -AssemblyName System.Windows.Forms 
        Add-Type -AssemblyName System.Web
    }
    Process{
        # create window for embedded browser
        $form = New-Object Windows.Forms.Form
        $form.StartPosition = [System.Windows.Forms.FormStartPosition]::CenterScreen;
        $form.Width = 640
        $form.Height = 700
        $form.showIcon = $false
        $form.TopMost = $true
    
        $web = New-Object Windows.Forms.WebBrowser
        $web.Size = $form.ClientSize
        $web.Anchor = "Left,Top,Right,Bottom"
        $web.ScriptErrorsSuppressed = $true

        $form.Controls.Add($web)

        $web.Navigate($LoginIDP)
        
        $web.add_Navigating({
            If ($web.DocumentText -match "SAMLResponse"){
                $_.cancel = $true

                If ($web.DocumentText -match $RegEx){
                    $form.Close()
                    $Script:SAMLResponse = $(($Matches[1] -replace '&#x2b;', '+') -replace '&#x3d;', '=')
                }
            }
        })
    
        # show browser window, waits for window to close
        If ([system.windows.forms.application]::run($form) -ne "OK") {
            If ($null -ne $Script:SAMLResponse){
                Write-Output $Script:SAMLResponse
                $form.Close()
                Remove-Variable -Name SAMLResponse -Scope Script -ErrorAction SilentlyContinue
            }
            Else {
                throw "SAMLResponse not matched"
            }
        }
    }
    End{
        $form.Dispose()
    }
}


Function ParseBool {
    [CmdletBinding()]
    param(
        [Parameter(Position=0)]
        [System.String]$inputVal
    )
    switch -regex ($inputVal.Trim()) {
        "^(1|true|yes|on|enabled)$" { Return $True }
        default { Return $False }
    }
}


Function Invoke-RestMethod_ErrorHandling {
    <#
        .SYNOPSIS
        Invoke-RestMethod but with better handling of 4xx errors
 
        .DESCRIPTION
        The default error output for 4xx errors is a very unhelpful and generic error. This modification returns better error messages
    #>


    [CmdletBinding()]
    Param(
        [Parameter(Mandatory=$true)]
        $Method,
        [Parameter(Mandatory=$true)]
        $URI,
        [Parameter(Mandatory=$true)]
        $Headers,
        [Parameter(Mandatory=$false)]
        $Body,
        [Parameter(Mandatory=$false)]
        $ContentType
    )

    Try {
        $Response = Invoke-RestMethod @PSBoundParameters -ErrorAction Stop
        Return $Response
    } 
    Catch [System.Net.WebException] { 
        # This is the only way to return the content of the response
        $RespStream = $_.Exception.Response.GetResponseStream()
        $Reader = New-Object System.IO.StreamReader($RespStream)
        $RespBody = $Reader.ReadToEnd() | ConvertFrom-Json
        Throw $RespBody.errorMessage
    }
}