PowerShell - Get-GuidFromHex

Lot of time I need to deal with conversion between different data structures. One most often I deal with is conversion from hex data to guid representation of it. I wrote simple function that handles that kind of conversion.

Since lot of guid representation stored in hex format uses little endian ordering, function has special switch -LittleEndian which will convert hex to guid using little endian byte ordering.

 function Get-GuidFromHex
{
    param
    (
        [string]
        $HexString,

        [switch]
        $LittleEndian
    )
    
    #region Perform hex string validation

    # if string provided is in raw hex format, remove heading '0x'
    if ($HexString.StartsWith('0x'))
    {
        $HexString = $HexString.Substring(2);
    }

    # guid expects 32 bits, if provided string is not 32 bits length,
    # we shouldn't bother with trying to convert it.
    if ($HexString.Length -ne 32)
    {
        throw "Hex string is not valid length. Current length is '$($HexString.Length)', expected length is 32 characters.";
    }

    # validate if all characters from provided string is expected
    # hex character.
    [regex]$hexAllowedCharacters = '[0-9a-fA-F]';

    foreach ($hexChar in $HexString.ToCharArray())
    {
        if (-not ($hexAllowedCharacters.Match($hexChar).Success))
        {
            throw "Unexpected character encountered '$hexChar'. Provided hex string is not in valid format";
        }
    }

    #endregion

    # if we are not using little endian ordering, just convert hex
    # to expected data type for guid
    if ( -not $LittleEndian.IsPresent)
    {
        $a = [uint32]('0x' + $HexString.Substring(0, 8));
        $b = [uint16]('0x' + $HexString.Substring(8, 4));
        $c = [uint16]('0x' + $HexString.Substring(12, 4));
    }
    # in case we selected little endian ordering, let's swap bits and reorder
    # them to match little endian structure
    else
    {
        $aArray = ($HexString.Substring(0, 8) -replace '.{2}(?=.)','$0,').Split(',');
        $a = [uint32](
                [string]::Format('0x{3}{2}{1}{0}',
                $aArray[0],
                $aArray[1],
                $aArray[2],
                $aArray[3]                
            )
        );

        $bArray = ($HexString.Substring(8,4) -replace '.{2}(?=.)','$0,').Split(',');
        $b = [uint16](
            [string]::Format('0x{1}{0}',
                $bArray[0],
                $bArray[1]
            )
        );

        $cArray = ($HexString.Substring(12,4) -replace '.{2}(?=.)','$0,').Split(',');
        $c = [uint16](
            [string]::Format('0x{1}{0}',
                $cArray[0],
                $cArray[1]
            )
        );
    }

    # instantiate guid - https://msdn.microsoft.com/en-us/library/system.guid%28v=vs.110%29.aspx
    return New-Object System.Guid($a, $b, $c,
            [byte]::Parse($HexString.Substring(16,2), 'HexNumber'),
            [byte]::Parse($HexString.Substring(18,2), 'HexNumber'),
            [byte]::Parse($HexString.Substring(20,2), 'HexNumber'),
            [byte]::Parse($HexString.Substring(22,2), 'HexNumber'),
            [byte]::Parse($HexString.Substring(24,2), 'HexNumber'),
            [byte]::Parse($HexString.Substring(26,2), 'HexNumber'),
            [byte]::Parse($HexString.Substring(28,2), 'HexNumber'),
            [byte]::Parse($HexString.Substring(30,2), 'HexNumber')
        )

}

I usually store all functions in my PowerShell profile which automatically loads them in memory every time I start PowerShell session and can use them straight away.

Here is simple scenario. If you use DirSync or AAD Connect to sync your on-premise identities to cloud, every object will use ImmutableId property to store mapping relation between cloud and on-premise user. By default, on-premise objectGuid property becomes cloud ImmutableId stored as Base64 string:

 Get-MsolUser -UserPrincipalName user@domain.com | Select-Object ImmutableId

ImmutableId
-----------
etyLZDkvKUKZzTogkajbig==

Now we need to convert Base64 string to hex:

 ([Convert]::FromBase64String("etyLZDkvKUKZzTogkajbig==") | ForEach-Object { "{0:x}" -f $_ }) -join ""
7adc8b64392f294299cd3a2091a8db8a

And finally we can use Get-GuidFromHex to get actual guid ( hex has little endian ordering ):

 Get-GuidFromHex -HexString "7adc8b64392f294299cd3a2091a8db8a" -LittleEndian

Guid
----
648bdc7a-2f39-4229-99cd-3a2091a8db8a