{"id":7559,"date":"2020-06-19T10:01:05","date_gmt":"2020-06-19T14:01:05","guid":{"rendered":"https:\/\/jdhitsolutions.com\/blog\/?p=7559"},"modified":"2020-06-19T10:01:05","modified_gmt":"2020-06-19T14:01:05","slug":"an-expanded-powershell-scripting-inventory-tool","status":"publish","type":"post","link":"https:\/\/jdhitsolutions.com\/blog\/powershell\/7559\/an-expanded-powershell-scripting-inventory-tool\/","title":{"rendered":"An Expanded PowerShell Scripting Inventory Tool"},"content":{"rendered":"<p>The other day<a href=\"https:\/\/jdhitsolutions.com\/blog\/powershell\/7549\/building-a-powershell-inventory\/\" target=\"_blank\" rel=\"noopener noreferrer\"> I shared my code<\/a> that I worked up to solve an Iron Scripter PowerShell challenge. One of the shortcomings was that I didn't address a challenge to include a property that would indicate what file was using a given command. I also felt I could do better with performance in Windows PowerShell on large file sets. I'm trying to process almost 5000 PowerShell files.<\/p>\n<h2>A Module is Born<\/h2>\n<p>The first thing I did was re-organize my code into a PowerShell module. This allowed me to break out some code into separate functions like this.<\/p>\n<pre class=\"lang:ps mark:0 decode:true\">Function Get-ASTToken {\n    [CmdletBinding()]\n\n    Param (\n        [Parameter(Mandatory, Position = 0)]\n        [string]$Path\n    )\n\n    Begin {\n        Write-Verbose \"[BEGIN  ] Starting: $($MyInvocation.Mycommand)\"\n        New-Variable astTokens -force\n        New-Variable astErr -force\n    } #begin\n\n    Process {\n        $cPath = Convert-Path $Path\n        Write-Verbose \"[PROCESS] Getting AST Tokens from $cpath\"\n        $AST = [System.Management.Automation.Language.Parser]::ParseFile($cPath, [ref]$astTokens, [ref]$astErr)\n        [void]$AST.FindAll({$args[0] -is [System.Management.Automation.Language.CommandAst]}, $true)\n        $astTokens\n    } #process\n\n    End {\n        Write-Verbose \"[END    ] Ending: $($MyInvocation.Mycommand)\"\n    } #end\n}\n<\/pre>\n<p>One advantage is that I can now build a Pester test. You can't Pester test code that invokes the .NET Framework, but I can test if a PowerShell function is called. Were I to build tests, i could mock a result from this function.<\/p>\n<p>I also am now defining a class for my custom object.<\/p>\n<pre class=\"lang:ps mark:0 decode:true\">class PSInventory {\n    [string]$Name\n    [int32]$Total\n    [string[]]$Files\n    hidden [datetime]$Date = (Get-Date)\n    hidden [string]$Computername = [System.Environment]::MachineName\n\n    PSInventory([string]$Name) {\n        $this.Name = $Name\n    }\n}\n<\/pre>\n<p>In my code, I can create a result like this:<\/p>\n<pre class=\"lang:ps mark:0 decode:true\">$item = [PSInventory]::new($TextInfo.ToTitleCase($Value.tolower()))\n<\/pre>\n<p>As an aside, one of the things that bugged me with my original code was that the command name was displayed as it was found. I have not been consistent over the years so that meant seeing output like 'Write-verbose' and 'select-Object'.\u00a0 My solution was to use a trick my friend Gladys Kravitz taught me using a TextInfo object.<\/p>\n<pre class=\"lang:ps mark:0 decode:true\">$TextInfo = (Get-Culture).TextInfo\n<\/pre>\n<p>This object has a ToTitleCase() method which I am invoking. To ensure consistency, I turn the $Value variable, which contains the derived command name, to lower case which is then turned to TitleCase. Nice.<\/p>\n<h2>Parallel and Jobs<\/h2>\n<p>The module also contains a controller function, although it could also have been a script, that uses the module commands to process a given folder.<\/p>\n<pre class=\"lang:ps mark:0 decode:true\">Function Get-PSScriptInventory {\n    [cmdletbinding()]\n    [alias(\"psi\")]\n    [OutputType(\"PSInventory\")]\n\n    Param(\n        [Parameter(Position = 0, Mandatory, HelpMessage = \"Specify the root folder path\")]\n        [string]$Path,\n        [Parameter(HelpMessage = \"Recurse for files\")]\n        [switch]$Recurse,\n        [Parameter(HelpMessage = \"Specify the number of files to batch process. A value of 0 means do not run in batches or parallel.\")]\n        [ValidateSet(0,50,100,250,500)]\n        [int]$BatchSize = 0\n    )\n<\/pre>\n<p>The function includes a parameter to indicate if you want to \"batch process\" the folder. This only makes sense if you have a very large number of files.<\/p>\n<p>If you are running PowerShell 7, the files will be divided into batches and each batch processed in parallel.<\/p>\n<pre class=\"lang:ps mark:0 decode:true\">if ($IsCoreCLR) {\n    Write-Verbose \"[PROCESS] Processing in parallel\"\n    $files = Get-PSFile -Path $Path -Recurse:$Recurse\n    $totalfilecount = $files.count\n    $sets = @{}\n    $c = 0\n    for ($i = 0 ; $i -lt $files.count; $i += $batchsize) {\n        $c++\n        $start = $i\n        $end = $i + ($BatchSize-1)\n        $sets.Add(\"Set$C\", @($files[$start..$end]))\n    }\n    $results = $sets.GetEnumerator() | ForEach-Object -Parallel {\n        Write-Host \"[$(Get-Date -format 'hh:mm:ss.ffff')] Processing $($_.name) in parallel\" -ForegroundColor cyan\n        Write-Information \"Processing $($_.name)\" -tags meta\n        $_.value | Measure-ScriptFile\n    }\n} #coreCLR\n<\/pre>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-7560\" src=\"https:\/\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2020\/06\/get-psscriptinventory.png\" alt=\"get-psscriptinventory\" width=\"1669\" height=\"235\" srcset=\"https:\/\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2020\/06\/get-psscriptinventory.png 1669w, https:\/\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2020\/06\/get-psscriptinventory-300x42.png 300w, https:\/\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2020\/06\/get-psscriptinventory-1024x144.png 1024w, https:\/\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2020\/06\/get-psscriptinventory-768x108.png 768w, https:\/\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2020\/06\/get-psscriptinventory-1536x216.png 1536w, https:\/\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2020\/06\/get-psscriptinventory-850x120.png 850w\" sizes=\"auto, (max-width: 1669px) 100vw, 1669px\" \/>You can see in the code snippet that I'm using Write-Information. I can look at the tags later and see exactly how long this process took.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-7561\" src=\"https:\/\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2020\/06\/get-psscriptinventory-information.png\" alt=\"get-psscriptinventory-information\" width=\"1089\" height=\"98\" srcset=\"https:\/\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2020\/06\/get-psscriptinventory-information.png 1089w, https:\/\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2020\/06\/get-psscriptinventory-information-300x27.png 300w, https:\/\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2020\/06\/get-psscriptinventory-information-1024x92.png 1024w, https:\/\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2020\/06\/get-psscriptinventory-information-768x69.png 768w, https:\/\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2020\/06\/get-psscriptinventory-information-850x76.png 850w\" sizes=\"auto, (max-width: 1089px) 100vw, 1089px\" \/>In Windows PowerShell, I'm dividing the files into batches than then spinning of a thread job.<\/p>\n<pre class=\"lang:ps mark:0 decode:true\">ForEach-Object -begin {\n    $totalfilecount = 0\n    $tmp = [System.Collections.Generic.List[object]]::new()\n    $jobs = @()\n\n    #define the scriptblock to run in a thread job\n    $sb = {\n        Param([object[]]$Files)\n        $files | Measure-ScriptFile\n    }\n} -process {\n    if ($tmp.Count -ge $batchsize) {\n        Write-Host \"[$(Get-Date -format 'hh:mm:ss.ffff')] Processing set of $($tmp.count) files\" -ForegroundColor cyan\n        Write-Information \"Starting threadjob\" -Tags meta\n        $jobs += Start-ThreadJob -ScriptBlock $sb -ArgumentList @(, $tmp.ToArray()) -Name tmpJob\n        $tmp.Clear()\n    }\n    $totalfilecount++\n    $tmp.Add($_)\n} -end {\n    #use the remaining objects\n    Write-Host \"[$(Get-Date -format 'hh:mm:ss.ffff')] Processing remaining set of $($tmp.count) of files\" -ForegroundColor cyan\n    $jobs += Start-ThreadJob -ScriptBlock $sb -ArgumentList @(, $tmp.ToArray()) -name tmpJob\n}\n#wait for jobs to complete\nWrite-Verbose \"[PROCESS] Waiting for $($jobs.count) jobs to complete\"\n$results = $jobs | Wait-Job | Receive-Job\n<\/pre>\n<p>This is similar to a background job, but appears to run a bit faster. This requires the ThreadJob module from the PowerShell Gallery. Even so, this is still slower in Windows PowerShell. I'm betting that improvements were made to the AST in PowerShell 7, in addition to other performance improvements. Still, you can look at this code a a proof of concept.<\/p>\n<p>In fact, this entire module, which I hope you'll try out, is intended for educational purposes and to serve as a proof-of-concept platform. I hope you'll look at how I structured the module. How I handle cross-platform needs. How I used commands like Write-Information. How I used a PowerShell class.<\/p>\n<p>You can find this code on Github at <a href=\"https:\/\/github.com\/jdhitsolutions\/PSScriptingInventory\" target=\"_blank\" rel=\"noopener noreferrer\">https:\/\/github.com\/jdhitsolutions\/PSScriptingInventory<\/a>. If you have comments, questions or problems feel free to post an issue. I also hope you'll try your hand at the <a href=\"https:\/\/ironscripter.us\" target=\"_blank\" rel=\"noopener noreferrer\">Iron Scripter<\/a> challenges.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>The other day I shared my code that I worked up to solve an Iron Scripter PowerShell challenge. One of the shortcomings was that I didn&#8217;t address a challenge to include a property that would indicate what file was using a given command. I also felt I could do better with performance in Windows PowerShell&#8230;<\/p>\n","protected":false},"author":1,"featured_media":7560,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"New: An Expanded #PowerShell Scripting Inventory Tool","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":true,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"font":"","enabled":false},"version":2}},"categories":[4,610,8],"tags":[447,516,618,612,534,631],"class_list":["post-7559","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-powershell","category-powershell-7","category-scripting","tag-ast","tag-classes","tag-iron-scripter","tag-parallel","tag-powershell","tag-threadjob"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v27.4 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>An Expanded PowerShell Scripting Inventory Tool &#8226; The Lonely Administrator<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/jdhitsolutions.com\/blog\/powershell\/7559\/an-expanded-powershell-scripting-inventory-tool\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"An Expanded PowerShell Scripting Inventory Tool &#8226; The Lonely Administrator\" \/>\n<meta property=\"og:description\" content=\"The other day I shared my code that I worked up to solve an Iron Scripter PowerShell challenge. One of the shortcomings was that I didn&#039;t address a challenge to include a property that would indicate what file was using a given command. I also felt I could do better with performance in Windows PowerShell...\" \/>\n<meta property=\"og:url\" content=\"https:\/\/jdhitsolutions.com\/blog\/powershell\/7559\/an-expanded-powershell-scripting-inventory-tool\/\" \/>\n<meta property=\"og:site_name\" content=\"The Lonely Administrator\" \/>\n<meta property=\"article:published_time\" content=\"2020-06-19T14:01:05+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2020\/06\/get-psscriptinventory.png\" \/>\n\t<meta property=\"og:image:width\" content=\"1669\" \/>\n\t<meta property=\"og:image:height\" content=\"235\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"author\" content=\"Jeffery Hicks\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@JeffHicks\" \/>\n<meta name=\"twitter:site\" content=\"@JeffHicks\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Jeffery Hicks\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"4 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/jdhitsolutions.com\\\/blog\\\/powershell\\\/7559\\\/an-expanded-powershell-scripting-inventory-tool\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/jdhitsolutions.com\\\/blog\\\/powershell\\\/7559\\\/an-expanded-powershell-scripting-inventory-tool\\\/\"},\"author\":{\"name\":\"Jeffery Hicks\",\"@id\":\"https:\\\/\\\/jdhitsolutions.com\\\/blog\\\/#\\\/schema\\\/person\\\/d0258030b41f07fd745f4078bdf5b6c9\"},\"headline\":\"An Expanded PowerShell Scripting Inventory Tool\",\"datePublished\":\"2020-06-19T14:01:05+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/jdhitsolutions.com\\\/blog\\\/powershell\\\/7559\\\/an-expanded-powershell-scripting-inventory-tool\\\/\"},\"wordCount\":534,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\\\/\\\/jdhitsolutions.com\\\/blog\\\/#\\\/schema\\\/person\\\/d0258030b41f07fd745f4078bdf5b6c9\"},\"image\":{\"@id\":\"https:\\\/\\\/jdhitsolutions.com\\\/blog\\\/powershell\\\/7559\\\/an-expanded-powershell-scripting-inventory-tool\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/jdhitsolutions.com\\\/blog\\\/wp-content\\\/uploads\\\/2020\\\/06\\\/get-psscriptinventory.png\",\"keywords\":[\"AST\",\"Classes\",\"Iron Scripter\",\"parallel\",\"PowerShell\",\"ThreadJob\"],\"articleSection\":[\"PowerShell\",\"PowerShell 7\",\"Scripting\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\\\/\\\/jdhitsolutions.com\\\/blog\\\/powershell\\\/7559\\\/an-expanded-powershell-scripting-inventory-tool\\\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/jdhitsolutions.com\\\/blog\\\/powershell\\\/7559\\\/an-expanded-powershell-scripting-inventory-tool\\\/\",\"url\":\"https:\\\/\\\/jdhitsolutions.com\\\/blog\\\/powershell\\\/7559\\\/an-expanded-powershell-scripting-inventory-tool\\\/\",\"name\":\"An Expanded PowerShell Scripting Inventory Tool &#8226; The Lonely Administrator\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/jdhitsolutions.com\\\/blog\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/jdhitsolutions.com\\\/blog\\\/powershell\\\/7559\\\/an-expanded-powershell-scripting-inventory-tool\\\/#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/jdhitsolutions.com\\\/blog\\\/powershell\\\/7559\\\/an-expanded-powershell-scripting-inventory-tool\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/jdhitsolutions.com\\\/blog\\\/wp-content\\\/uploads\\\/2020\\\/06\\\/get-psscriptinventory.png\",\"datePublished\":\"2020-06-19T14:01:05+00:00\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/jdhitsolutions.com\\\/blog\\\/powershell\\\/7559\\\/an-expanded-powershell-scripting-inventory-tool\\\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/jdhitsolutions.com\\\/blog\\\/powershell\\\/7559\\\/an-expanded-powershell-scripting-inventory-tool\\\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/jdhitsolutions.com\\\/blog\\\/powershell\\\/7559\\\/an-expanded-powershell-scripting-inventory-tool\\\/#primaryimage\",\"url\":\"https:\\\/\\\/jdhitsolutions.com\\\/blog\\\/wp-content\\\/uploads\\\/2020\\\/06\\\/get-psscriptinventory.png\",\"contentUrl\":\"https:\\\/\\\/jdhitsolutions.com\\\/blog\\\/wp-content\\\/uploads\\\/2020\\\/06\\\/get-psscriptinventory.png\",\"width\":1669,\"height\":235},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/jdhitsolutions.com\\\/blog\\\/powershell\\\/7559\\\/an-expanded-powershell-scripting-inventory-tool\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"PowerShell\",\"item\":\"https:\\\/\\\/jdhitsolutions.com\\\/blog\\\/category\\\/powershell\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"An Expanded PowerShell Scripting Inventory Tool\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/jdhitsolutions.com\\\/blog\\\/#website\",\"url\":\"https:\\\/\\\/jdhitsolutions.com\\\/blog\\\/\",\"name\":\"The Lonely Administrator\",\"description\":\"Practical Advice for the Automating IT Pro\",\"publisher\":{\"@id\":\"https:\\\/\\\/jdhitsolutions.com\\\/blog\\\/#\\\/schema\\\/person\\\/d0258030b41f07fd745f4078bdf5b6c9\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/jdhitsolutions.com\\\/blog\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":[\"Person\",\"Organization\"],\"@id\":\"https:\\\/\\\/jdhitsolutions.com\\\/blog\\\/#\\\/schema\\\/person\\\/d0258030b41f07fd745f4078bdf5b6c9\",\"name\":\"Jeffery Hicks\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/832ae5d438fdcfc1420d720cd1991307927de8a0b12f2342e81c30f773e21098?s=96&d=wavatar&r=pg\",\"url\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/832ae5d438fdcfc1420d720cd1991307927de8a0b12f2342e81c30f773e21098?s=96&d=wavatar&r=pg\",\"contentUrl\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/832ae5d438fdcfc1420d720cd1991307927de8a0b12f2342e81c30f773e21098?s=96&d=wavatar&r=pg\",\"caption\":\"Jeffery Hicks\"},\"logo\":{\"@id\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/832ae5d438fdcfc1420d720cd1991307927de8a0b12f2342e81c30f773e21098?s=96&d=wavatar&r=pg\"}}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"An Expanded PowerShell Scripting Inventory Tool &#8226; The Lonely Administrator","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/jdhitsolutions.com\/blog\/powershell\/7559\/an-expanded-powershell-scripting-inventory-tool\/","og_locale":"en_US","og_type":"article","og_title":"An Expanded PowerShell Scripting Inventory Tool &#8226; The Lonely Administrator","og_description":"The other day I shared my code that I worked up to solve an Iron Scripter PowerShell challenge. One of the shortcomings was that I didn't address a challenge to include a property that would indicate what file was using a given command. I also felt I could do better with performance in Windows PowerShell...","og_url":"https:\/\/jdhitsolutions.com\/blog\/powershell\/7559\/an-expanded-powershell-scripting-inventory-tool\/","og_site_name":"The Lonely Administrator","article_published_time":"2020-06-19T14:01:05+00:00","og_image":[{"width":1669,"height":235,"url":"https:\/\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2020\/06\/get-psscriptinventory.png","type":"image\/png"}],"author":"Jeffery Hicks","twitter_card":"summary_large_image","twitter_creator":"@JeffHicks","twitter_site":"@JeffHicks","twitter_misc":{"Written by":"Jeffery Hicks","Est. reading time":"4 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/jdhitsolutions.com\/blog\/powershell\/7559\/an-expanded-powershell-scripting-inventory-tool\/#article","isPartOf":{"@id":"https:\/\/jdhitsolutions.com\/blog\/powershell\/7559\/an-expanded-powershell-scripting-inventory-tool\/"},"author":{"name":"Jeffery Hicks","@id":"https:\/\/jdhitsolutions.com\/blog\/#\/schema\/person\/d0258030b41f07fd745f4078bdf5b6c9"},"headline":"An Expanded PowerShell Scripting Inventory Tool","datePublished":"2020-06-19T14:01:05+00:00","mainEntityOfPage":{"@id":"https:\/\/jdhitsolutions.com\/blog\/powershell\/7559\/an-expanded-powershell-scripting-inventory-tool\/"},"wordCount":534,"commentCount":0,"publisher":{"@id":"https:\/\/jdhitsolutions.com\/blog\/#\/schema\/person\/d0258030b41f07fd745f4078bdf5b6c9"},"image":{"@id":"https:\/\/jdhitsolutions.com\/blog\/powershell\/7559\/an-expanded-powershell-scripting-inventory-tool\/#primaryimage"},"thumbnailUrl":"https:\/\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2020\/06\/get-psscriptinventory.png","keywords":["AST","Classes","Iron Scripter","parallel","PowerShell","ThreadJob"],"articleSection":["PowerShell","PowerShell 7","Scripting"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/jdhitsolutions.com\/blog\/powershell\/7559\/an-expanded-powershell-scripting-inventory-tool\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/jdhitsolutions.com\/blog\/powershell\/7559\/an-expanded-powershell-scripting-inventory-tool\/","url":"https:\/\/jdhitsolutions.com\/blog\/powershell\/7559\/an-expanded-powershell-scripting-inventory-tool\/","name":"An Expanded PowerShell Scripting Inventory Tool &#8226; The Lonely Administrator","isPartOf":{"@id":"https:\/\/jdhitsolutions.com\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/jdhitsolutions.com\/blog\/powershell\/7559\/an-expanded-powershell-scripting-inventory-tool\/#primaryimage"},"image":{"@id":"https:\/\/jdhitsolutions.com\/blog\/powershell\/7559\/an-expanded-powershell-scripting-inventory-tool\/#primaryimage"},"thumbnailUrl":"https:\/\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2020\/06\/get-psscriptinventory.png","datePublished":"2020-06-19T14:01:05+00:00","breadcrumb":{"@id":"https:\/\/jdhitsolutions.com\/blog\/powershell\/7559\/an-expanded-powershell-scripting-inventory-tool\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/jdhitsolutions.com\/blog\/powershell\/7559\/an-expanded-powershell-scripting-inventory-tool\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/jdhitsolutions.com\/blog\/powershell\/7559\/an-expanded-powershell-scripting-inventory-tool\/#primaryimage","url":"https:\/\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2020\/06\/get-psscriptinventory.png","contentUrl":"https:\/\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2020\/06\/get-psscriptinventory.png","width":1669,"height":235},{"@type":"BreadcrumbList","@id":"https:\/\/jdhitsolutions.com\/blog\/powershell\/7559\/an-expanded-powershell-scripting-inventory-tool\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"PowerShell","item":"https:\/\/jdhitsolutions.com\/blog\/category\/powershell\/"},{"@type":"ListItem","position":2,"name":"An Expanded PowerShell Scripting Inventory Tool"}]},{"@type":"WebSite","@id":"https:\/\/jdhitsolutions.com\/blog\/#website","url":"https:\/\/jdhitsolutions.com\/blog\/","name":"The Lonely Administrator","description":"Practical Advice for the Automating IT Pro","publisher":{"@id":"https:\/\/jdhitsolutions.com\/blog\/#\/schema\/person\/d0258030b41f07fd745f4078bdf5b6c9"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/jdhitsolutions.com\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":["Person","Organization"],"@id":"https:\/\/jdhitsolutions.com\/blog\/#\/schema\/person\/d0258030b41f07fd745f4078bdf5b6c9","name":"Jeffery Hicks","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/secure.gravatar.com\/avatar\/832ae5d438fdcfc1420d720cd1991307927de8a0b12f2342e81c30f773e21098?s=96&d=wavatar&r=pg","url":"https:\/\/secure.gravatar.com\/avatar\/832ae5d438fdcfc1420d720cd1991307927de8a0b12f2342e81c30f773e21098?s=96&d=wavatar&r=pg","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/832ae5d438fdcfc1420d720cd1991307927de8a0b12f2342e81c30f773e21098?s=96&d=wavatar&r=pg","caption":"Jeffery Hicks"},"logo":{"@id":"https:\/\/secure.gravatar.com\/avatar\/832ae5d438fdcfc1420d720cd1991307927de8a0b12f2342e81c30f773e21098?s=96&d=wavatar&r=pg"}}]}},"jetpack_publicize_connections":[],"jetpack_featured_media_url":"https:\/\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2020\/06\/get-psscriptinventory.png","jetpack_sharing_enabled":true,"jetpack_likes_enabled":true,"jetpack-related-posts":[{"id":8107,"url":"https:\/\/jdhitsolutions.com\/blog\/powershell\/8107\/scripting-challenge-meetup\/","url_meta":{"origin":7559,"position":0},"title":"Scripting Challenge Meetup","author":"Jeffery Hicks","date":"February 1, 2021","format":false,"excerpt":"As you probably know, I am the PowerShell problem master behind the challenges from the Iron Scripter site. Solving a PowerShell scripting challenge is a great way to test your skills and expand your knowledge. The final result is merely a means to an end. How you get there and\u2026","rel":"","context":"In &quot;PowerShell&quot;","block_context":{"text":"PowerShell","link":"https:\/\/jdhitsolutions.com\/blog\/category\/powershell\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2021\/02\/rubik.jpg?resize=350%2C200&ssl=1","width":350,"height":200},"classes":[]},{"id":9018,"url":"https:\/\/jdhitsolutions.com\/blog\/powershell\/9018\/an-iron-scripter-warm-up-solution\/","url_meta":{"origin":7559,"position":1},"title":"An Iron Scripter Warm-Up Solution","author":"Jeffery Hicks","date":"May 6, 2022","format":false,"excerpt":"We just wrapped up the 2022 edition of the PowerShell+DevOps Global Summit. It was terrific to be with passionate PowerShell professionals again. The culmination of the event is the Iron Scripter Challenge. You can learn more about this year's event and winner here. But there is more to the Iron\u2026","rel":"","context":"In &quot;PowerShell&quot;","block_context":{"text":"PowerShell","link":"https:\/\/jdhitsolutions.com\/blog\/category\/powershell\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":7489,"url":"https:\/\/jdhitsolutions.com\/blog\/powershell\/7489\/powershell-word-play\/","url_meta":{"origin":7559,"position":2},"title":"PowerShell Word Play","author":"Jeffery Hicks","date":"May 19, 2020","format":false,"excerpt":"A few weeks ago an Iron Scripter PowerShell challenge was issued that involved playing with words and characters. Remember, the Iron Scripter challenges aren't intended to create meaningful, production worthy code. They are designed to help you learn PowerShell fundamentals and scripting techniques. This particular challenge was aimed at beginner\u2026","rel":"","context":"In &quot;PowerShell&quot;","block_context":{"text":"PowerShell","link":"https:\/\/jdhitsolutions.com\/blog\/category\/powershell\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2020\/05\/doublechar.png?resize=350%2C200&ssl=1","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2020\/05\/doublechar.png?resize=350%2C200&ssl=1 1x, https:\/\/i0.wp.com\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2020\/05\/doublechar.png?resize=525%2C300&ssl=1 1.5x, https:\/\/i0.wp.com\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2020\/05\/doublechar.png?resize=700%2C400&ssl=1 2x, https:\/\/i0.wp.com\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2020\/05\/doublechar.png?resize=1050%2C600&ssl=1 3x, https:\/\/i0.wp.com\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2020\/05\/doublechar.png?resize=1400%2C800&ssl=1 4x"},"classes":[]},{"id":7024,"url":"https:\/\/jdhitsolutions.com\/blog\/powershell\/7024\/managing-the-recycle-bin-with-powershell\/","url_meta":{"origin":7559,"position":3},"title":"Managing the Recycle Bin with PowerShell","author":"Jeffery Hicks","date":"December 3, 2019","format":false,"excerpt":"A while ago, I posted an Iron Scripter challenge asking you to write some PowerShell code for working with items in the recycle bin. You were asked to calculate how much space the recycle bin is using and then restore a file. If you'd prefer, stop reading this post, check\u2026","rel":"","context":"In &quot;PowerShell&quot;","block_context":{"text":"PowerShell","link":"https:\/\/jdhitsolutions.com\/blog\/category\/powershell\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2019\/12\/image_thumb-7.png?resize=350%2C200&ssl=1","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2019\/12\/image_thumb-7.png?resize=350%2C200&ssl=1 1x, https:\/\/i0.wp.com\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2019\/12\/image_thumb-7.png?resize=525%2C300&ssl=1 1.5x, https:\/\/i0.wp.com\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2019\/12\/image_thumb-7.png?resize=700%2C400&ssl=1 2x"},"classes":[]},{"id":7680,"url":"https:\/\/jdhitsolutions.com\/blog\/powershell\/7680\/friday-fun-back-to-school-with-powershell\/","url_meta":{"origin":7559,"position":4},"title":"Friday Fun: Back to School with PowerShell","author":"Jeffery Hicks","date":"September 11, 2020","format":false,"excerpt":"For today's fun with PowerShell, I thought I'd share my solutions for a recent Iron Scripter challenge. If you aren't familiar with these challenges, and you should be, they are designed to test your PowerShell skills and hopefully help you learn something new. There are challenges for all skill levels\u2026","rel":"","context":"In &quot;Friday Fun&quot;","block_context":{"text":"Friday Fun","link":"https:\/\/jdhitsolutions.com\/blog\/category\/friday-fun\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2020\/09\/get-cylindervolume-format.png?resize=350%2C200&ssl=1","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2020\/09\/get-cylindervolume-format.png?resize=350%2C200&ssl=1 1x, https:\/\/i0.wp.com\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2020\/09\/get-cylindervolume-format.png?resize=525%2C300&ssl=1 1.5x, https:\/\/i0.wp.com\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2020\/09\/get-cylindervolume-format.png?resize=700%2C400&ssl=1 2x"},"classes":[]},{"id":7638,"url":"https:\/\/jdhitsolutions.com\/blog\/powershell\/7638\/friday-fun-a-powershell-nonsense-challenge\/","url_meta":{"origin":7559,"position":5},"title":"Friday Fun &#8211; A PowerShell Nonsense Challenge","author":"Jeffery Hicks","date":"August 7, 2020","format":false,"excerpt":"Today I thought I'd share my PowerShell solution to a recent Iron Scripter challenge. The challenge was to create PowerShell code that would create nonsense documents, with a goal of creating 10 sample files filled with gibberish. Yes, other than maybe wanting some test files to work with, on its\u2026","rel":"","context":"In &quot;PowerShell&quot;","block_context":{"text":"PowerShell","link":"https:\/\/jdhitsolutions.com\/blog\/category\/powershell\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2020\/08\/nnp.png?resize=350%2C200&ssl=1","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2020\/08\/nnp.png?resize=350%2C200&ssl=1 1x, https:\/\/i0.wp.com\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2020\/08\/nnp.png?resize=525%2C300&ssl=1 1.5x, https:\/\/i0.wp.com\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2020\/08\/nnp.png?resize=700%2C400&ssl=1 2x, https:\/\/i0.wp.com\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2020\/08\/nnp.png?resize=1050%2C600&ssl=1 3x"},"classes":[]}],"_links":{"self":[{"href":"https:\/\/jdhitsolutions.com\/blog\/wp-json\/wp\/v2\/posts\/7559","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/jdhitsolutions.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/jdhitsolutions.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/jdhitsolutions.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/jdhitsolutions.com\/blog\/wp-json\/wp\/v2\/comments?post=7559"}],"version-history":[{"count":0,"href":"https:\/\/jdhitsolutions.com\/blog\/wp-json\/wp\/v2\/posts\/7559\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/jdhitsolutions.com\/blog\/wp-json\/wp\/v2\/media\/7560"}],"wp:attachment":[{"href":"https:\/\/jdhitsolutions.com\/blog\/wp-json\/wp\/v2\/media?parent=7559"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/jdhitsolutions.com\/blog\/wp-json\/wp\/v2\/categories?post=7559"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/jdhitsolutions.com\/blog\/wp-json\/wp\/v2\/tags?post=7559"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}