Diagnostic.psm1

<#
.SYNOPSIS
Generate a diagnostic file to help with debugging.
 
.DESCRIPTION
Generate a diagnostic file to help with debugging.
 
.PARAMETER outfilepath
The output file path for the diagnostic file.
  
.EXAMPLE
New-Diagnostic -outfilepath "C:\\temp\diagnostic.txt"
 
#>

function New-Diagnostic {
    [cmdletbinding()]
    Param(
        [string] $outfilepath = $(Throw "Enter the output file path for the diagnostic file.")
        
    )
    
    <#
    When the customer attempts to generate the encrypt credential file, the Security module makes use of a machine key store container.
    The machine key store file is located at [Environment]::GetFolderPath("CommonApplicationData").
    The Security module might encounter permission issue when trying to access this machine key store file.
    The file might already exists and that it was created with permissions for the user account that created that file and the system account only.
    Here we will attemp to search for this machine key store file and check its ACL. We then dump all these information into a output file.
    This will help customers and support team to determine permission issues.
    #>


    #The machine key container name
    $rsaMachineKeyContainerName = "SuperSecretProcessOnMachine"
    $maxUsernameLength = 56
    
    $rsaMachineKeyFileStoreACL = ""

    #Create a hash table
    $hashTable = @()
    $hashTable | Out-File $outfilepath
    $errors = New-Object System.Collections.ArrayList

    #Other special folder hers: Environment.SpecialFolder Enumeration
    #https://msdn.microsoft.com/en-us/library/system.environment.specialfolder(v=vs.110).aspx
    $commonAppDataDir = [Environment]::GetFolderPath("CommonApplicationData");
    $rsaMachineKeysDir = $commonAppDataDir + "\Microsoft\Crypto\RSA\MachineKeys"
    $contextUserName = [Environment]::UserName
    $contextUserDomainName = [Environment]::UserDomainName
    $powerShellVersion = $PSVERSIONTable.PSVersion.Major
    $powerShell64Bit = [Environment]::Is64BitProcess
    $machineName = [Environment]::MachineName
    $osVersion = [Environment]::OSVersion.VersionString
    
    #Track some properties
    $hashTable = $hashTable + @{"Machine Name"=$machineName}
    $hashTable = $hashTable + @{"OS Version"=$osVersion}
    $hashTable = $hashTable + @{"Common Application Data Directory"=$commonAppDataDir}
    $hashTable = $hashTable + @{"RSA Machine Keys Directory"=$rsaMachineKeysDir}
    $hashTable = $hashTable + @{"Context User"=$contextUserName}
    $hashTable = $hashTable + @{"Context User Domain"=$contextUserDomainName}
    $hashTable = $hashTable + @{"PowerShell Version"=$powerShellVersion}
    $hashTable = $hashTable + @{"PowerShell 64 Bit"=$powerShell64Bit}
    $hashTable = $hashTable + @{"Security Module Max Username Length (# of chars)"=$maxUsernameLength}
    
    $rsaMachineKeyStoreFile = $null

    #Look through the machine keys dir to search for our machine key store file. It's a file with the container name "SuperSecretProcessOnMachine" (a name used in Security module)
    foreach($file in Get-ChildItem $rsaMachineKeysDir) {
        $rsafilePath = ($rsaMachineKeysDir + "/" + $file)
        try {
            Write-Debug "Checking $rsafilePath for Security module machine key store." 
            $correctFileFound = Get-Content $rsafilePath -ErrorAction Stop | Select-String $rsaMachineKeyContainerName -quiet -casesensitive
        } catch {
            #If we fail (eg. permission), we track errors
            $correctFileFound = $false
            $errors.Add($_) | Out-Null
        }
        
        #We found it, stop looking.
        if ($correctFileFound -eq $true) {
            $rsaMachineKeyStoreFile = $rsafilePath
            Write-Debug "Found $rsafilePath as the Security module machine key store.";
            break;
        }
    }
    
    #If we found our machine key store, inspect the ACL on it
    if ($rsaMachineKeyStoreFile -ne $null) {
        $hashTable = $hashTable + @{"Security Module Machine Key File Store"=$rsaMachineKeyStoreFile}

        try {
            Write-Debug "Retrieving ACL on $rsaMachineKeyStoreFile"
            $rsaMachineKeyFileStoreACL = Get-Acl $rsaMachineKeyStoreFile -ErrorAction Stop | Format-List | Out-String
        } catch {
            #If we fail (eg. permission), we track errors
           $errors.Add("Retrieving ACL on " + $rsaMachineKeyStoreFile + " : " + $_)
        }
    } else {
        $hashTable = $hashTable + @{"Security Module Machine Key File Store"="Not found"}
    }

    $columnWidth = 55;

    #Generate name-value headers
    Add-Content $outfilepath (" {0,-$columnWidth} {1}" -F "Name", "Value")
    Add-Content $outfilepath (" {0,-$columnWidth} {1}" -F "====", "=====")

    #Dump out hashtable entries
    foreach ($key in $hashTable.keys) {
        $value = $($hashTable.$key)
        $entry = (" {0,-$columnWidth} : {1}" -F $key, $value)
        Add-Content $outfilepath $entry
    }

    Add-Content $outfilepath ("`n`nACL on machine key store file used by Security module")
    Add-Content $outfilepath ("`=====================================================")
    Add-Content $outfilepath $rsaMachineKeyFileStoreACL


    if ($errors.Count -gt 0) {
        Add-Content $outfilepath ("`n`nErrors retrieving ACL of machine key store files (it is normal not to have access to every files)")
        Add-Content $outfilepath ("=================================================================================================")
    }

    foreach ($error in $errors) {
         Add-Content $outfilepath $error
    }
}