JhcUtil.psm1

#Requires -Version 3
#
# Collection a variety of useful tools.

#---searches down through a users org given a passed in AAD user serach string
#

function Invoke-JhcUtilScriptBlock
{
    [CmdletBinding()]
    param (
        [Parameter(Mandatory)]
        [System.String]
        $ScriptBlockXml,
        [Parameter(Mandatory=$false)]
        [System.String[]]
        $ArgumentList
    )
    
    $sbstr = Import-Clixml -Path $ScriptBlockXml
    $sb = [scriptblock]::Create($sbstr)

    if($ArgumentList){
        Invoke-Command -ScriptBlock $sb -ArgumentList $ArgumentList
    }
    else {
        Invoke-Command -ScriptBlock $sb
    }

}

function Update-JhcUtilWindowTitle
{
    [CmdletBinding()]
    param (
        [Parameter(Mandatory)]
        [System.String]
        $Title
    )

    $Host.UI.RawUI.WindowTitle = $Title
}

function Search-JhcUtilAadUserOrg
{
    param (
        [Parameter(Mandatory,ValueFromPipelineByPropertyName,Position = 0)]
        [System.String]
        $MailNickName
    )
    
    begin{
        $mn = 'AzureAD'
        $m = Get-Module -Name $mn -ListAvailable
        $ext = $false
        $l = @()
        $mnn = ''

        # Check for AAD module. If not there, report and exit.
        #
        if($null -ne $m){
            Write-Information -MessageData "Found $($m.Name) module. Version: $($m.Version)"
        }
        else{
            Write-Error -Message "Cmdlet requires $mn module. Exiting..."
            $ext = $true
        }

        # Check whether you need to auth to AAD. If so, report adn exit
        #
        if(-not $ext)
        {
            try {
                Get-AzureADUser -Top 1 -ErrorAction SilentlyContinue | Out-Null 
            }
            catch {
                Write-Error -Message 'You must call the Connect-AzureAD cmdlet before running this cmdlet.'
                $ext = $true
            }
        }

        # Grab mail nickname to test whether it is unique
        #
        if(-not $ext)
        {
            $mnn =  Get-AzureADUser -SearchString $MailNickName
        }

        # Report if it was not uniqe and exit
        #
        if($mnn.Length -gt 1)
        {
            Write-Error -Message "MailNickName: `"$MailNickName`" produced more than one result ($($mnn.Length)). It must be a unique user. Exiting..."
            $ext = $true
        }
    }

    process{
        # Traverse though AAD
        #
        if(-not $ext)
        {
            $l = orgtrav($MailNickName)
        }
    }
    
    end
    {
        $mgr = ''
        $k = $null

        if($ext)
        {
            return $null
        }
        
        $mgr = Get-AzureADUserManager -ObjectId $mnn.ObjectId
        $k = $mnn | select-object -property @{name = 'Manager'; expression = {$mgr.DisplayName}}, @{name = 'ManagerMailNickName'; expression = {$mgr.MailNickName}}, Displayname, MailNickName, JobTitle, Department, PhysicalDeliveryOfficeName

        if($l.Count -gt 0)
        {
            $l += $k
            
            ($l.Count-1)..0 |
                ForEach-Object{ $i = $_; $l[$i] }
        }
        else
        {
            $k
        }
    }
}

#---internal function for Search-JhcUtilAadUserOrg
#
function orgtrav
{
    param (
        [String]
        $MailNickName
    )

    $a = @()

    if($MailNickName -eq $null)
    {
        return $null
    }

    foreach( $o in Get-AzureADUser -SearchString $MailNickName | Get-AzureADUserDirectReport )
    {
        $ct++

        Write-Progress -Activity "Looking up AAD user $($o.Displayname)" -Status "Searching $('.' * $a.count)"
        $m = Get-AzureADUserManager -ObjectId $o.ObjectId
        $a += $o | select-object -property @{name = 'Manager'; expression = {$m.DisplayName}}, @{name = 'ManagerMailNickName'; expression = {$m.MailNickName}}, Displayname, MailNickName, JobTitle, Department, PhysicalDeliveryOfficeName
        orgtrav($o.MailNickName)
    }

    return $a
}


#---converts base 64 bstring into a plain string
#
function Convertfrom-JhcUtilBase64String
{
    param(
            [Parameter(Mandatory, ValueFromPipeline=$true, Position = 0)]
            [System.String]
            $String,

            [Parameter(Position = 1)]
            [ValidateSet("ASCII", "Unicode", "UTF32", "UTF7", "UTF8", "BigEndianUnicode")]
            [System.String]
            $Encoding = 'ASCII'
    )

    begin{}

    process
    {
        foreach($psb in $PSBoundParameters)
        {
            [System.Text.Encoding]::$Encoding.GetString([System.Convert]::FromBase64String($psb['String']))
        }
    }

    end{}
}

#---converts string to Base64 String
#
function ConvertTo-JhcUtilBase64String
{
    param(
            [Parameter(Mandatory, ValueFromPipeline=$true, Position = 0)]
            [System.String]
            $String,

            [Parameter(Position = 1)]
            [ValidateSet("ASCII", "Unicode", "UTF32", "UTF7", "UTF8", "BigEndianUnicode")]
            [System.String]
            $Encoding = 'ASCII'
    )

    begin{}

    process
    {
        foreach($psb in $PSBoundParameters)
        {
            $b = [System.Text.Encoding]::$Encoding.GetBytes($psb['String'])
            
            [System.Convert]::ToBase64String($b)
        }
    }

    end{}
}

#---Converts Secure String into plain string
#
function Unprotect-JhcUtilSecureString
{
    param
    (
        [parameter(Mandatory=$true, ValueFromPipeline=$true, Position=0)]
        [System.Security.SecureString]
        $SecureString
    )

    begin{}

    process
    {
        foreach($ss in $SecureString)
        {
            if($null -eq $ss)
            {
                throw "Passed-in secure string was null."
            }

            try
            {
                $us = [runtime.interopservices.Marshal]::SecureStringToGlobalAllocUnicode($ss)

                return [runtime.interopservices.Marshal]::PtrToStringAuto($us)
            }
            finally
            {
                [runtime.interopservices.Marshal]::ZeroFreeGlobalAllocUnicode($us)
            }
        }
    }

    end{}
}

#---Simple long term history retriever
#
function Get-JhcUtilLongTermHistory
{
    [CmdletBinding()]
    param (
        [Parameter(Mandatory=$false)]
        [int]
        $Last
    )
    
    begin
    {
        $histfile = $env:APPDATA + '\Microsoft\Windows\PowerShell\PSReadLine\ConsoleHost_history.txt'
    }
    process
    {
        if(Test-Path -Path $histfile)
        {
            $i = 0
            
            if($Last) {
                Get-Content -Path $histfile |
                    ForEach-Object { $l = $_; $i++; New-Object -TypeName pscustomobject -Property @{'Id' = $i; 'CommandLine' = $l} } |
                        Select-Object -Last $Last
            }
            else {
                Get-Content -Path $histfile |
                    ForEach-Object { $l = $_; $i++; New-Object -TypeName pscustomobject -Property @{'Id' = $i; 'CommandLine' = $l} }               
            }
        }
        else
        {
            write-Error -Message "History file not found. $histfile"  
        }
    }
    end{}
}

#---This cmdlet requires Excel is installed
#
function Convert-JhcUtilXlsxToCsv
{
    param
    (
        [Parameter(Mandatory, ParameterSetName="Path", Position = 0)]
        [System.String[]]
        $Path,

        [Parameter(Mandatory, ParameterSetName="LiteralPath", ValueFromPipelineByPropertyName = $true)]
        [Alias("PSPath")]
        [System.String[]]
        $LiteralPath,

        [Parameter(Mandatory = $false, ValueFromPipelineByPropertyName = $false)]
        [switch]
        $Force = $false
    )

    begin
    {
        $ex = New-Object -ComObject Excel.Application

        $ex.Visible = $false

        $ex.DisplayAlerts = $false

        $wb = $null
        $i = $null
    }

    process
    {
        $PathsToProcess = @()

        if($PSCmdlet.ParameterSetName -eq 'Path')
        {
            $PathsToProcess += Resolve-Path -Path $Path |
            
                ForEach-Object ProviderPath
        }
        else
        {
            $PathsToProcess += Resolve-Path -LiteralPath $LiteralPath |

                ForEach-Object ProviderPath
        }

        foreach( $filepath in $PathsToProcess )
        {
            $fp = Get-Item -Path $filepath

            try
            {
                $wb = $ex.Workbooks.Open($fp.FullName)
            }
            catch
            {
                Write-Error $_

                continue
            }

            $i = 0
            
            try
            {
                
                foreach( $ws in $wb.Worksheets )
                {
                    $cf = "$($fp.DirectoryName)\$($fp.BaseName)_$($i).csv"                    

                    if( (-not (Test-Path -Path $cf -PathType Leaf)) -or $Force )
                    {
                        Write-Verbose -Message "Saving $cf"

                        $ws.SaveAs($cf,6)
                    }
                    else
                    {
                        Write-Error -Message "$cf file already exists."
                    }
                    
                    $i++
                }
            }
            catch
            {
                Write-Error $_
            }
        }

    }

    end
    {
        $ex.Quit()
    }
}

#--- returns encoding of the passed in file
#
function Show-JhcUtilFileEncoding
{
    param(
            [Parameter(Mandatory, ParameterSetName="Path", Position = 0)]
            [System.String[]]
            $Path,

            [Parameter(Mandatory, ParameterSetName="LiteralPath", ValueFromPipelineByPropertyName = $true)]
            [Alias("PSPath")]
            [System.String[]]
            $LiteralPath,

            [Parameter(Mandatory=$false)]
            [switch]
            $ExtendedOutput = $false
        )

    begin
    {
        $rc = 4
        $tc = 4
        $enc = 'Byte'

        $header = 'Encoding', 'Path'
        $Extheader = 'Encoding', 'Path', 'Byte0', 'Byte1', 'Byte2', 'Byte3'

        $l = $null
    }

    process
    {
        $pathsToProcess = @()
        
        if($PSCmdlet.ParameterSetName  -eq "LiteralPath")
        {
            $pathsToProcess += Resolve-Path -LiteralPath $LiteralPath | Foreach-Object ProviderPath
        }
        
        if($PSCmdlet.ParameterSetName -eq "Path")
        {
            $pathsToProcess += Resolve-Path $Path | Foreach-Object ProviderPath
        }

        foreach($filePath in $pathsToProcess)
        {
            if(Test-Path -LiteralPath $filePath -PathType Container)
            {
                continue
            }
        
            [byte[]]$byte = get-content -Encoding $enc -ReadCount $rc -TotalCount $tc -Path $filePath
            
            $l = $null

            if ( $byte[0] -eq 0xef -and $byte[1] -eq 0xbb -and $byte[2] -eq 0xbf )
            {
                $l = "UTF8"
            }
            elseif( $byte[0] -eq 0xff -and $byte[1] -eq 0xfe -and $byte[2] -eq 0 -and $byte[3] -eq 0 )
            {
                $l = "UTF32"
            }
            elseif( $byte[0] -eq 0xff -and $byte[1] -eq 0xfe )
            {
                $l = "Unicode"
            }
            elseif( $byte[0] -eq 0xfe -and $byte[1] -eq 0xff )
            {
                $l = "BigEndianUnicode"
            }
            else 
            {
                $l = "ASCII"
            }
            
            $l += ",$($filePath)"

            if($ExtendedOutput)
            {
                $l += ",$($byte[0]),$($byte[1]),$($byte[2]),$($byte[3])"
                
                $l | ConvertFrom-Csv -Header $Extheader
            }
            else
            {
                $l | ConvertFrom-Csv -Header $header
            }
        }
    }

    end{}
}

function Get-JhcUtilStockSp
{
    param
    (
        [Parameter(Mandatory = $true, Position = 0, ValueFromPipelineByPropertyName = $true)]
        [System.Security.SecureString]
        $apiKey,
        [parameter(Mandatory = $true, Position = 1, ValueFromPipelineByPropertyName=$true)]
        [System.String]
        $symbol
    )

    begin
    {
        $aak = Unprotect-JhcUtilSecureString -SecureString $apiKey

        #-- Ref: https://www.alphavantage.co/documentation/
        #
        $uri = "https://www.alphavantage.co/query?function=GLOBAL_QUOTE&datatype=csv&apikey=$($aak)&symbol"
    }

    process
    {
        $o = Invoke-RestMethod -Uri "$($uri)=$($symbol)" | ConvertFrom-Csv

        if($o)
        {
            $o.open = [Double]$o.open
            $o.high = [Double]$o.high
            $o.low = [Double]$o.low
            $o.price = [Double]$o.price
            $o.volume = [System.Int32]$o.volume
            $o.latestDay = [datetime]$o.latestDay
            $o.previousClose = [Double]$o.previousClose
            $o.change = [Double]$o.change
            $o.changePercent = [Double]($o.changePercent -replace '\%', '')
        }

        $o

    }

    end{}
}