PSCustomObject - Cycle through hashtable?

Hi All,

I am not sure I am using the right terminology but here goes, I have written a function to retrieve a URL report from VirusTotal, the function I have written converts from the returned JSON to a PSCustomObject and I would like to get a certain element of the output, here is the code so far (I have my own API Key stored in $APIKey):

function VT-RetrieveReport($URLToSend='https://test.co.uk'){
    $URL = 'http://www.virustotal.com/vtapi/v2/url/report'
    $PostParams = @{apikey="$APIKey";resource="$URLToSend"}
    $TheResponse = Invoke-WebRequest -Uri $URL -Method Post -Body $PostParams | ConvertFrom-Json
    return $TheResponse.Scans
    }

This function returns a list like this:

ZCloudsec : @{detected=False; result=clean site}
PhishLabs : @{detected=False; result=unrated site}
Zerofox : @{detected=True; result=clean site}

These are note properties of a PSCustomObject, what I would like to do is get the ‘detected=False’ part of each blacklist name but I do not know how to achieve this, so far I can get the Name property which is just:

ZCloudsec
PhishLabs
Zerofox

by doing

VT-RetrieveReport | Get-Member -MemberType NoteProperty | Select -ExpandProperty Name

How do I access the key/value pairs of each of the Names? Ideally I would like to throw away any name that is detected=false and just keep the ones that say detected=true, so the output would look something like:

Zerofox = True

I hope that explains it, thanks again for any further help

function VT-RetrieveReport($URLToSend='https://test.co.uk'){
    $URL = 'http://www.virustotal.com/vtapi/v2/url/report'
    $PostParams = @{apikey="$APIKey";resource="$URLToSend"}
    $TheResponse = Invoke-WebRequest -Uri $URL -Method Post -Body $PostParams | ConvertFrom-Json
    
    foreach ($BlackList in ($TheResponse.Scans | Get-Member -MemberType NoteProperty).Name) {
        if ($TheResponse.Scans.$BlackList.Detected) {
            [PSCustomObject]@{ $BlackList = $true }
        }
    } 
}

Thanks very much Sam.

Hi again,

I am trying to improve the function by outputting to a CSV file but I am struggling with the additional fields for the blacklists that show as something is malicious - the additional fields do not show on the CSV file ‘Blacklist Name’,'Detected#,‘Reason’:

function VT-RetrieveReport($URLToSend=@('http://test2.co.uk','http://www.test.co.uk','https://test3.co.uk','http://www.test4.co.uk')){
$URL = 'http://www.virustotal.com/vtapi/v2/url/report'
$CustomOutput = 
ForEach ($URLToRetrieveReportFor in $URLToSend) {
        $PostParams = @{apikey="$APIKey";resource="$URLToRetrieveReportFor"}
        $TheResponse = Invoke-WebRequest -Uri $URL -Method Post -Body $PostParams | ConvertFrom-Json
        [hashtable]$ObjectProperty = @{}
        $ObjectProperty.Add('URL',$URLToRetrieveReportFor)
        $ObjectProperty.Add('ScanTime',[datetime]$TheResponse.scan_date)
        $ObjectProperty.Add('Blacklists Checked',$TheResponse.total)

    ForEach ($BlackList in ($TheResponse.Scans | Get-Member -MemberType NoteProperty).Name) {
        if ($TheResponse.Scans.$BlackList.Detected) {
            $ObjectProperty.Add('Blacklist Name',$BlackList)
            $ObjectProperty.Add('Detected',$TheResponse.Scans.$BlackList.Detected)
            $ObjectProperty.Add('Reason',$TheResponse.Scans.$BlackList.Result)
            }
            
        }
        New-Object -TypeName PSObject -Property $ObjectProperty 
    }
    $CustomOutput | Export-Csv C:\users\me\desktop\test.csv -Append -NoTypeInformation
}

Thanks for any further help

Is there a way to run the function, store all of the processing output and then send it to CSV?

Nested objects are nested objects for a reason, so the won’t always parse cleanly. Anytime that you are trying to flatten something, you’re making assumptions that the data your getting will cooperate. For instance, if there are additional scans or more information in the “Scans”, the loop will catch them but I’m not sure it would add that property to the export because it’s not in every object.

My question is why put it into a CSV? You can do a ton of analysis without ever needing to stare at a spreadsheet.

To answer you last question, Export-CSV will export to a CSV, but it’s probably not going to look how you expect.

"scan_date","total","scans"
"8/6/2018 9:24:50 AM","3","@{ZCloudsec=System.Collections.Hashtable; PhishLabs=System.Collections.Hashtable; Zerofox=System.Collections.Hashtable}"

You do need to do a parse to make it flat, so here is goes. With out having the JSON and from what you posted, I created a mock object from the response. The key is the hash values are all the same, so you’re going to need to do some concatenation to get a unique value for detected and result. Take a look at this code which should get you pretty close to what you are attempting:

$urls = 'http://test2.co.uk','http://www.test.co.uk','https://test3.co.uk','http://www.test4.co.uk'

foreach ($url in $urls) {
    $theResponse = [pscustomobject]@{
        scan_date = (Get-Date)
        total = 3
        scans = [pscustomobject]@{
            ZCloudsec = @{detected=$false; result='clean site'}
            PhishLabs = @{detected=$false; result='unrated site'}
            Zerofox = @{detected=$true; result='clean site'}
        }
    }
    
    $props = @{}
    $props.Add('Url', $url)
    $props.Add('Scan Time', $theResponse.scan_date)
    $props.Add('Blacklists Checked', $theResponse.total)

    foreach ($scan in $theResponse.Scans.PSObject.Properties) {
        foreach ($hash in $scan.Value.GetEnumerator()) {
            $props.Add(('{0}_{1}' -f $scan.Name, $hash.Name), $hash.Value)
        }
    }
    
    New-Object -TypeName PSObject -Property $props
}

Output:

Url                : http://test2.co.uk
Blacklists Checked : 3
PhishLabs_detected : False
Zerofox_result     : clean site
Zerofox_detected   : True
Scan Time          : 8/6/2018 9:24:49 AM
ZCloudsec_detected : False
ZCloudsec_result   : clean site
PhishLabs_result   : unrated site

Url                : http://www.test.co.uk
Blacklists Checked : 3
PhishLabs_detected : False
Zerofox_result     : clean site
Zerofox_detected   : True
Scan Time          : 8/6/2018 9:24:50 AM
ZCloudsec_detected : False
ZCloudsec_result   : clean site
PhishLabs_result   : unrated site

Url                : https://test3.co.uk
Blacklists Checked : 3
PhishLabs_detected : False
Zerofox_result     : clean site
Zerofox_detected   : True
Scan Time          : 8/6/2018 9:24:50 AM
ZCloudsec_detected : False
ZCloudsec_result   : clean site
PhishLabs_result   : unrated site

Url                : http://www.test4.co.uk
Blacklists Checked : 3
PhishLabs_detected : False
Zerofox_result     : clean site
Zerofox_detected   : True
Scan Time          : 8/6/2018 9:24:50 AM
ZCloudsec_detected : False
ZCloudsec_result   : clean site
PhishLabs_result   : unrated site

Thanks very much Rob, just what I was after. Things like this really knock confidence when I cannot figure something out for myself.

Your welcome!! That is what a community is supposed to do, help!!