{"id":6996,"date":"2019-11-26T10:12:56","date_gmt":"2019-11-26T15:12:56","guid":{"rendered":"https:\/\/jdhitsolutions.com\/blog\/?p=6996"},"modified":"2019-11-26T10:13:04","modified_gmt":"2019-11-26T15:13:04","slug":"powershell-controller-scripts","status":"publish","type":"post","link":"https:\/\/jdhitsolutions.com\/blog\/powershell\/6996\/powershell-controller-scripts\/","title":{"rendered":"PowerShell Controller Scripts"},"content":{"rendered":"<p>When it comes to PowerShell scripting we tend to focus a lot on functions and modules. We place an emphasis on building re-usable tools. The idea is that we can then use these tools at a PowerShell prompt to achieve a given task. More than likely, these tasks are repetitive. In these situations, it makes sense to take the commands that you would normally type interactively at a PowerShell prompt and put them into a PowerShell controller script. This script in turn uses the functions and modules that you've built. I've been building a set of PowerShell tools for backing up important folders. I've also created a few management controller scripts I want to share with you.<\/p>\n<h2>Designing PowerShell Scripts<\/h2>\n<p>The basic idea with a PowerShell script is that it is a \"canned\" PowerShell session. You should be able to take the contents of a ps1 file, paste it into a PowerShell session, and it should run. Instead of typing the commands interactively, the script does it for you. However, PowerShell scripts can also take parameters, use cmdlet binding, and take advantage of scripting constructs like If\/Else statements. PowerShell scripts don't have to follow the same rules we stress when building functions. The script makes it easy to run a series of commands that <em>controls<\/em> the functions, or cmdlets. Here's an example.<\/p>\n<pre class=\"lang:ps mark:0 decode:true\">#requires -version 5.1\n\n[cmdletbinding()]\n\nParam(\n    [parameter(position = 0)]\n    [ValidateNotNullOrEmpty()]\n    [string]$Backup = \"*\",\n    [Parameter(HelpMessage = \"Show raw objects, not a formatted report.\")]\n    [switch]$Raw\n)\n\n$f = Get-ChildItem -path \"D:\\backup\\$backup-log.csv\" | ForEach-Object {\n $p = $_.fullname\n Import-Csv $_.fullname | Add-Member -MemberType NoteProperty -Name Log -value $p -PassThru\n } | Where-Object { ([int32]$_.Size -gt 0) -AND ($_.IsFolder -eq 'False') }\n\n$files = ($f.name | Select-Object -Unique).Foreach({ $n = $_; $f.where({ $_.name -eq $n }) |\nSort-Object -Property { $_.date -as [datetime] } | Select-Object -last 1 })\n\nif ($raw) {\n    $files\n}\nelse {\n    $grouped = $files | Group-Object Log\n    #display formatted results\n    $files | Sort-Object -Property Log, Directory, Name | Format-Table -GroupBy log -Property Date, Name, Size, Directory\n    $summary = foreach ($item in $grouped) {\n        [PSCustomObject]@{\n            Backup = (Get-Item $item.Name).basename.split(\"-\")[0]\n            Files  = $item.Count\n            Size   = ($item.group | Measure-Object -Property size -sum).sum\n        }\n    } #foreach item\n\n    $total = [PSCustomObject]@{\n        TotalFiles = ($grouped | Measure-Object -property count -sum).sum\n        TotalSizeMB = [math]::round(($summary.size | Measure-Object -sum).sum\/1MB,4)\n    }\n    Write-Host \"Incremental Pending Backup Summary\" -ForegroundColor cyan\n    ($summary | Format-Table | Out-String).TrimEnd() | Write-Host -ForegroundColor cyan\n\n    $total | Format-Table  | Out-String | Write-Host -ForegroundColor cyan\n}\n<\/pre>\n<p>This script, MyBackupPending.ps1, is designed to show me what files will be backed up as part of my daily incremental backup job. The script file goes through my list of backup csv files and by default displays a formatted report.<\/p>\n<p><a href=\"https:\/\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2019\/11\/image-15.png\"><img loading=\"lazy\" decoding=\"async\" style=\"display: inline; background-image: none;\" title=\"formatted report\" src=\"https:\/\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2019\/11\/image_thumb-15.png\" alt=\"formatted report\" width=\"1028\" height=\"650\" border=\"0\" \/><\/a><\/p>\n<p>Normally, you wouldn't use <a title=\"Read online help for this command\" href=\"http:\/\/go.microsoft.com\/fwlink\/?LinkID=113303\" target=\"_blank\" rel=\"noopener noreferrer\">Format-Table<\/a> or <a title=\"Read online help for this command\" href=\"http:\/\/go.microsoft.com\/fwlink\/?LinkID=113426\" target=\"_blank\" rel=\"noopener noreferrer\">Write-Host<\/a> in a PowerShell function. But this control script isn't designed to write objects to the pipeline. It is designed to give me a formatted report. However, that doesn't mean I can't have things both ways.<\/p>\n<p>Scripts can have parameters. Mine includes parameters to indicate the backup set or an option to display raw, unformatted data.<\/p>\n<p><a href=\"https:\/\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2019\/11\/image-16.png\"><img loading=\"lazy\" decoding=\"async\" style=\"display: inline; background-image: none;\" title=\"Raw output\" src=\"https:\/\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2019\/11\/image_thumb-16.png\" alt=\"Raw output\" width=\"971\" height=\"540\" border=\"0\" \/><\/a><\/p>\n<p>You can use the same scripting techniques in your scripts that you do in your functions. Here is my control script that gives me a report on the backup archives.<\/p>\n<pre class=\"lang:ps mark:0 decode:true\"># requires -version 5.1\n\n[cmdletbinding()]\nParam(\n    [Parameter(Position = 0, HelpMessage = \"Enter the path with extension of the backup files.\")]\n    [ValidateNotNullOrEmpty()]\n    [ValidateScript( { Test-Path $_ })]\n    [string]$Path = \"\\\\ds416\\backup\",\n    [Parameter(HelpMessage = \"Get backup files only with no formatted output.\")]\n    [Switch]$Raw\n)\n\n&lt;#\n A regular expression pattern to match on backup file name with named captures\n to be used in adding some custom properties. My backup names are like:\n\n  20191101_Scripts-FULL.rar\n  20191107_Scripts-INCREMENTAL.rar\n\n  #&gt;\n[regex]$rx = \"^20\\d{6}_(?&lt;set&gt;\\w+)-(?&lt;type&gt;\\w+)\\.rar$\"\n\n&lt;#\nI am doing so 'pre-filtering' on the file extension and then using the regular\nexpression filter to fine tune the results\n#&gt;\n$files = Get-ChildItem -path $Path -filter *.rar | Where-Object { $rx.IsMatch($_.name) }\n\n#add some custom properties to be used with formatted results based on named captures\nforeach ($item in $files) {\n    $setpath = $rx.matches($item.name).groups[1].value\n    $settype = $rx.matches($item.name).groups[2].value\n\n    $item | Add-Member -MemberType NoteProperty -Name SetPath -Value $setpath\n    $item | Add-Member -MemberType NoteProperty -Name SetType -Value $setType\n}\n\nif ($raw) {\n    $Files\n}\nelse {\n    $files | Sort-Object SetPath, SetType, LastWriteTime | Format-Table -GroupBy SetPath -Property LastWriteTime, Length, Name\n    $grouped = $files | Group-Object SetPath\n    $summary = foreach ($item in $grouped) {\n        [pscustomobject]@{\n            BackupSet = $item.name\n            Files  = $item.Count\n            Size   = ($item.group | Measure-Object -Property size -sum).sum\n        }\n    }\n\n    $total = [PSCustomObject]@{\n        TotalFiles = ($grouped | Measure-Object -property count -sum).sum\n        TotalSizeMB = [math]::round(($summary.size | Measure-Object -sum).sum\/1MB,4)\n    }\n    Write-Host \"Backup Summary\" -ForegroundColor cyan\n    ($summary | sort-object Size -Descending| Format-Table | Out-String).TrimEnd() | Write-Host -ForegroundColor cyan\n\n    $total | Format-Table  | Out-String | Write-Host -ForegroundColor cyan\n}\n<\/pre>\n<p>You'll notice that the parameters have attributes and validation. But now I have a very easy command to run that displays my backup files formatted and ordered, including a summary.<\/p>\n<p><a href=\"https:\/\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2019\/11\/image-17.png\"><img loading=\"lazy\" decoding=\"async\" style=\"display: inline; background-image: none;\" title=\"My backup report\" src=\"https:\/\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2019\/11\/image_thumb-17.png\" alt=\"My backup report\" width=\"826\" height=\"772\" border=\"0\" \/><\/a><\/p>\n<h2>Run PowerShell Scripts from Anywhere<\/h2>\n<p>One drawback to PowerShell control scripts is that you have to specify the full path to the ps1 file. If your scripts are in a simple path like C:\\Scripts, perhaps that isn't that much more to type. But I'm always looking for ways to be \"creatively lazy\". Here's one approach you might like.<\/p>\n<p>PowerShellGet, which makes it easy to find and install modules from the PowerShell Gallery, also has a set of related commands to find and install PowerShell <em>scripts<\/em>. You can install a PowerShell script file from the PowerShell gallery and run the .ps1 file from anywhere. You don't need to specify the path. i want that!<\/p>\n<p>I'm going to take the easy route and use the location that PowerShell already knows about: $env:ProgramFiles\\WindowsPowerShell\\Scripts. If\u00a0 you copy a ps1 file to this folder, you should be able to run it by simply typing the script name. This is perfect. Almost.<\/p>\n<p>I like to keep all my scripting work in C:\\Scripts and I knew I'd be tweaking my backup control scripts for awhile. I didn't want to have to deal with editing the file and keeping the version in Installed Scripts in synch. So I linked them.\u00a0 I took advantage of symbolic links to create a linked copy in Installed Scripts. Now, I can run the controller script by name alone, and all I need to do is edit the file under C:\\Scripts.<\/p>\n<p>To simplify the process, naturally I created a controller script.<\/p>\n<pre class=\"lang:ps mark:0 decode:true\">#requires -version 5.1\n\n#create linked script copies in Installed Scripts\n\n[cmdletbinding(SupportsShouldProcess)]\nParam(\n\n    [Parameter(Position = 0, Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)]\n    [ValidateScript({Test-Path $_})]\n    [String[]]$Path\n)\n\nBegin {\n    $installPath = \"$env:ProgramFiles\\WindowsPowerShell\\Scripts\"\n    Write-Verbose \"Creating links to $installpath\"\n}\nProcess {\n    if (Test-Path -Path $installPath) {\n\n        foreach ($file in $Path) {\n\n            Try {\n                $cfile = Convert-Path $File -ErrorAction Stop\n            }\n            Catch {\n                Throw \"Failed to find or convert $file\"\n            }\n            if ($cfile) {\n                $name = Split-Path -Path $cfile -Leaf\n                $target = Join-Path -path $installPath -ChildPath $name\n                Write-Verbose \"Creating a link from $cfile to $target. A 0 byte file is normal.\"\n\n                #overwrite the target if it already exists\n                New-Item -Path $target -value $cfile -ItemType SymbolicLink -Force\n            }\n        } #foreach file\n    }\n    else {\n        Write-Warning \"Can't find $installPath. This script might require a Windows platform.\"\n    }\n}\nEnd {\n    Write-Verbose \"Finished linking script files.\"\n}\n\n# Get-command -CommandType ExternalScript\n<\/pre>\n<p>I have a script file I run to show me a custom view of running Hyper-V virtual machines, C:\\Scripts\\HyperVStatus.ps1. I'll create a link to Installed Scripts.<\/p>\n<p><a href=\"https:\/\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2019\/11\/image-18.png\"><img loading=\"lazy\" decoding=\"async\" style=\"display: inline; background-image: none;\" title=\"Creating a PowerShell script link\" src=\"https:\/\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2019\/11\/image_thumb-18.png\" alt=\"Creating a PowerShell script link\" width=\"1028\" height=\"370\" border=\"0\" \/><\/a><\/p>\n<p>As you can see this creates a 0 byte link back to the target script. Now I can run this script file from anywhere.<\/p>\n<p><a href=\"https:\/\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2019\/11\/image-19.png\"><img loading=\"lazy\" decoding=\"async\" style=\"display: inline; background-image: none;\" title=\"Running the script from anywhere\" src=\"https:\/\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2019\/11\/image_thumb-19.png\" alt=\"Running the script from anywhere\" width=\"997\" height=\"772\" border=\"0\" \/><\/a><\/p>\n<p>If I update the script, I don't have to do anything to the link. In fact, I can go even further and define an alias.<\/p>\n<p><a href=\"https:\/\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2019\/11\/image-20.png\"><img loading=\"lazy\" decoding=\"async\" style=\"display: inline; background-image: none;\" title=\"Using a script alias\" src=\"https:\/\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2019\/11\/image_thumb-20.png\" alt=\"Using a script alias\" width=\"1028\" height=\"430\" border=\"0\" \/><\/a><\/p>\n<p>I created links to my backup control scripts. Use <a title=\"Read online help for this command\" href=\"http:\/\/go.microsoft.com\/fwlink\/?LinkID=113309\" target=\"_blank\" rel=\"noopener noreferrer\">Get-Command<\/a> to discover external scripts.<\/p>\n<p><a href=\"https:\/\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2019\/11\/image-21.png\"><img loading=\"lazy\" decoding=\"async\" style=\"display: inline; background-image: none;\" title=\"External PowerShell Scripts\" src=\"https:\/\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2019\/11\/image_thumb-21.png\" alt=\"External PowerShell Scripts\" width=\"1028\" height=\"389\" border=\"0\" \/><\/a><\/p>\n<p>Don't be afraid to create an take advantage of PowerShell controller scripts. These are tools for <em>you<\/em> to use to make your job easier. As long as you understand their purpose.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>When it comes to PowerShell scripting we tend to focus a lot on functions and modules. We place an emphasis on building re-usable tools. The idea is that we can then use these tools at a PowerShell prompt to achieve a given task. More than likely, these tasks are repetitive. In these situations, it makes&#8230;<\/p>\n","protected":false},"author":1,"featured_media":0,"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 on the blog: #PowerShell Controller Scripts","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],"tags":[534,540],"class_list":["post-6996","post","type-post","status-publish","format-standard","hentry","category-powershell","tag-powershell","tag-scripting"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v27.5 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>PowerShell Controller Scripts &#8226; The Lonely Administrator<\/title>\n<meta name=\"description\" content=\"Make your PowerShell work easier and faster by building PowerShell controller scripts that use functions and cmdlets. I&#039;ll even show you a trick or two.\" \/>\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\/6996\/powershell-controller-scripts\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"PowerShell Controller Scripts &#8226; The Lonely Administrator\" \/>\n<meta property=\"og:description\" content=\"Make your PowerShell work easier and faster by building PowerShell controller scripts that use functions and cmdlets. I&#039;ll even show you a trick or two.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/jdhitsolutions.com\/blog\/powershell\/6996\/powershell-controller-scripts\/\" \/>\n<meta property=\"og:site_name\" content=\"The Lonely Administrator\" \/>\n<meta property=\"article:published_time\" content=\"2019-11-26T15:12:56+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2019-11-26T15:13:04+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2019\/11\/image_thumb-15.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=\"6 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/jdhitsolutions.com\\\/blog\\\/powershell\\\/6996\\\/powershell-controller-scripts\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/jdhitsolutions.com\\\/blog\\\/powershell\\\/6996\\\/powershell-controller-scripts\\\/\"},\"author\":{\"name\":\"Jeffery Hicks\",\"@id\":\"https:\\\/\\\/jdhitsolutions.com\\\/blog\\\/#\\\/schema\\\/person\\\/d0258030b41f07fd745f4078bdf5b6c9\"},\"headline\":\"PowerShell Controller Scripts\",\"datePublished\":\"2019-11-26T15:12:56+00:00\",\"dateModified\":\"2019-11-26T15:13:04+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/jdhitsolutions.com\\\/blog\\\/powershell\\\/6996\\\/powershell-controller-scripts\\\/\"},\"wordCount\":785,\"commentCount\":2,\"publisher\":{\"@id\":\"https:\\\/\\\/jdhitsolutions.com\\\/blog\\\/#\\\/schema\\\/person\\\/d0258030b41f07fd745f4078bdf5b6c9\"},\"image\":{\"@id\":\"https:\\\/\\\/jdhitsolutions.com\\\/blog\\\/powershell\\\/6996\\\/powershell-controller-scripts\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/jdhitsolutions.com\\\/blog\\\/wp-content\\\/uploads\\\/2019\\\/11\\\/image_thumb-15.png\",\"keywords\":[\"PowerShell\",\"Scripting\"],\"articleSection\":[\"PowerShell\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\\\/\\\/jdhitsolutions.com\\\/blog\\\/powershell\\\/6996\\\/powershell-controller-scripts\\\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/jdhitsolutions.com\\\/blog\\\/powershell\\\/6996\\\/powershell-controller-scripts\\\/\",\"url\":\"https:\\\/\\\/jdhitsolutions.com\\\/blog\\\/powershell\\\/6996\\\/powershell-controller-scripts\\\/\",\"name\":\"PowerShell Controller Scripts &#8226; The Lonely Administrator\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/jdhitsolutions.com\\\/blog\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/jdhitsolutions.com\\\/blog\\\/powershell\\\/6996\\\/powershell-controller-scripts\\\/#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/jdhitsolutions.com\\\/blog\\\/powershell\\\/6996\\\/powershell-controller-scripts\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/jdhitsolutions.com\\\/blog\\\/wp-content\\\/uploads\\\/2019\\\/11\\\/image_thumb-15.png\",\"datePublished\":\"2019-11-26T15:12:56+00:00\",\"dateModified\":\"2019-11-26T15:13:04+00:00\",\"description\":\"Make your PowerShell work easier and faster by building PowerShell controller scripts that use functions and cmdlets. I'll even show you a trick or two.\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/jdhitsolutions.com\\\/blog\\\/powershell\\\/6996\\\/powershell-controller-scripts\\\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/jdhitsolutions.com\\\/blog\\\/powershell\\\/6996\\\/powershell-controller-scripts\\\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/jdhitsolutions.com\\\/blog\\\/powershell\\\/6996\\\/powershell-controller-scripts\\\/#primaryimage\",\"url\":\"https:\\\/\\\/jdhitsolutions.com\\\/blog\\\/wp-content\\\/uploads\\\/2019\\\/11\\\/image_thumb-15.png\",\"contentUrl\":\"https:\\\/\\\/jdhitsolutions.com\\\/blog\\\/wp-content\\\/uploads\\\/2019\\\/11\\\/image_thumb-15.png\",\"width\":1028,\"height\":650},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/jdhitsolutions.com\\\/blog\\\/powershell\\\/6996\\\/powershell-controller-scripts\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"PowerShell\",\"item\":\"https:\\\/\\\/jdhitsolutions.com\\\/blog\\\/category\\\/powershell\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"PowerShell Controller Scripts\"}]},{\"@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":"PowerShell Controller Scripts &#8226; The Lonely Administrator","description":"Make your PowerShell work easier and faster by building PowerShell controller scripts that use functions and cmdlets. I'll even show you a trick or two.","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\/6996\/powershell-controller-scripts\/","og_locale":"en_US","og_type":"article","og_title":"PowerShell Controller Scripts &#8226; The Lonely Administrator","og_description":"Make your PowerShell work easier and faster by building PowerShell controller scripts that use functions and cmdlets. I'll even show you a trick or two.","og_url":"https:\/\/jdhitsolutions.com\/blog\/powershell\/6996\/powershell-controller-scripts\/","og_site_name":"The Lonely Administrator","article_published_time":"2019-11-26T15:12:56+00:00","article_modified_time":"2019-11-26T15:13:04+00:00","og_image":[{"url":"https:\/\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2019\/11\/image_thumb-15.png","type":"","width":"","height":""}],"author":"Jeffery Hicks","twitter_card":"summary_large_image","twitter_creator":"@JeffHicks","twitter_site":"@JeffHicks","twitter_misc":{"Written by":"Jeffery Hicks","Est. reading time":"6 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/jdhitsolutions.com\/blog\/powershell\/6996\/powershell-controller-scripts\/#article","isPartOf":{"@id":"https:\/\/jdhitsolutions.com\/blog\/powershell\/6996\/powershell-controller-scripts\/"},"author":{"name":"Jeffery Hicks","@id":"https:\/\/jdhitsolutions.com\/blog\/#\/schema\/person\/d0258030b41f07fd745f4078bdf5b6c9"},"headline":"PowerShell Controller Scripts","datePublished":"2019-11-26T15:12:56+00:00","dateModified":"2019-11-26T15:13:04+00:00","mainEntityOfPage":{"@id":"https:\/\/jdhitsolutions.com\/blog\/powershell\/6996\/powershell-controller-scripts\/"},"wordCount":785,"commentCount":2,"publisher":{"@id":"https:\/\/jdhitsolutions.com\/blog\/#\/schema\/person\/d0258030b41f07fd745f4078bdf5b6c9"},"image":{"@id":"https:\/\/jdhitsolutions.com\/blog\/powershell\/6996\/powershell-controller-scripts\/#primaryimage"},"thumbnailUrl":"https:\/\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2019\/11\/image_thumb-15.png","keywords":["PowerShell","Scripting"],"articleSection":["PowerShell"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/jdhitsolutions.com\/blog\/powershell\/6996\/powershell-controller-scripts\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/jdhitsolutions.com\/blog\/powershell\/6996\/powershell-controller-scripts\/","url":"https:\/\/jdhitsolutions.com\/blog\/powershell\/6996\/powershell-controller-scripts\/","name":"PowerShell Controller Scripts &#8226; The Lonely Administrator","isPartOf":{"@id":"https:\/\/jdhitsolutions.com\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/jdhitsolutions.com\/blog\/powershell\/6996\/powershell-controller-scripts\/#primaryimage"},"image":{"@id":"https:\/\/jdhitsolutions.com\/blog\/powershell\/6996\/powershell-controller-scripts\/#primaryimage"},"thumbnailUrl":"https:\/\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2019\/11\/image_thumb-15.png","datePublished":"2019-11-26T15:12:56+00:00","dateModified":"2019-11-26T15:13:04+00:00","description":"Make your PowerShell work easier and faster by building PowerShell controller scripts that use functions and cmdlets. I'll even show you a trick or two.","breadcrumb":{"@id":"https:\/\/jdhitsolutions.com\/blog\/powershell\/6996\/powershell-controller-scripts\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/jdhitsolutions.com\/blog\/powershell\/6996\/powershell-controller-scripts\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/jdhitsolutions.com\/blog\/powershell\/6996\/powershell-controller-scripts\/#primaryimage","url":"https:\/\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2019\/11\/image_thumb-15.png","contentUrl":"https:\/\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2019\/11\/image_thumb-15.png","width":1028,"height":650},{"@type":"BreadcrumbList","@id":"https:\/\/jdhitsolutions.com\/blog\/powershell\/6996\/powershell-controller-scripts\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"PowerShell","item":"https:\/\/jdhitsolutions.com\/blog\/category\/powershell\/"},{"@type":"ListItem","position":2,"name":"PowerShell Controller Scripts"}]},{"@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":"","jetpack_sharing_enabled":true,"jetpack_likes_enabled":true,"jetpack-related-posts":[{"id":3722,"url":"https:\/\/jdhitsolutions.com\/blog\/powershell\/3722\/reflections-on-the-powershell-scripting-games\/","url_meta":{"origin":6996,"position":0},"title":"Reflections on the PowerShell Scripting Games","author":"Jeffery Hicks","date":"February 26, 2014","format":false,"excerpt":"During the most recent PowerShell Scripting Games, I was fortunate enough to be one of the judges. Now that the games have concluded I thought I'd share my reflections on the entries. Naturally these are merely my opinions but they are drawn from years of experience with PowerShell and almost\u2026","rel":"","context":"In &quot;Best Practices&quot;","block_context":{"text":"Best Practices","link":"https:\/\/jdhitsolutions.com\/blog\/category\/best-practices\/"},"img":{"alt_text":"talkbubble","src":"https:\/\/i0.wp.com\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2011\/10\/talkbubble.png?resize=350%2C200","width":350,"height":200},"classes":[]},{"id":1729,"url":"https:\/\/jdhitsolutions.com\/blog\/scripting\/1729\/ise-scripting-geek-module\/","url_meta":{"origin":6996,"position":1},"title":"ISE Scripting Geek Module","author":"Jeffery Hicks","date":"October 27, 2011","format":false,"excerpt":"Over the last year I've posted functions I've written to extend the Windows PowerShell ISE. I have finally pulled everything together into a module I'm calling the ISE Scripting Geek. Download and extract the zip file (below) to your modules folder. You should end up with a path like 'C:\\Users\\Jeff\\Documents\\WindowsPowerShell\\Modules\\ISEScriptingGeek'.\u2026","rel":"","context":"In &quot;PowerShell ISE&quot;","block_context":{"text":"PowerShell ISE","link":"https:\/\/jdhitsolutions.com\/blog\/category\/powershell-ise\/"},"img":{"alt_text":"ISE Scripting Geek add-on menu","src":"https:\/\/i0.wp.com\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2011\/10\/isescriptinggeek-300x200.png?resize=350%2C200","width":350,"height":200},"classes":[]},{"id":1036,"url":"https:\/\/jdhitsolutions.com\/blog\/powershell\/1036\/join-me-in-orlando\/","url_meta":{"origin":6996,"position":2},"title":"Join Me in Orlando","author":"Jeffery Hicks","date":"December 30, 2010","format":false,"excerpt":"I will be presenting 3 sessions at Techmentor Orlando 2011. The conference runs March 14-18, 2011 at the Disney Yacht Club. My sessions are all on Wednesday March 16. In addition to all the other fabulous material at the conference I will be presenting the following: PowerShell Scripting Best Practices\u2026","rel":"","context":"In &quot;Active Directory&quot;","block_context":{"text":"Active Directory","link":"https:\/\/jdhitsolutions.com\/blog\/category\/active-directory\/"},"img":{"alt_text":"Disney Yacht Club","src":"https:\/\/i0.wp.com\/techmentorevents.com\/design\/ecg\/techmentorevents\/home\/img\/portal_2011spring.gif?resize=350%2C200","width":350,"height":200},"classes":[]},{"id":8741,"url":"https:\/\/jdhitsolutions.com\/blog\/powershell\/8741\/building-a-powershell-module-inception-style\/","url_meta":{"origin":6996,"position":3},"title":"Building a PowerShell Module Inception-Style","author":"Jeffery Hicks","date":"December 17, 2021","format":false,"excerpt":"Over the course of the last week or so, I've been sharing PowerShell functions and scripts for working with PowerShell functions and scripts. I showed PowerShell functions to export functions to a script file and code to convert scripts to functions It has all been very Inception-like. To wrap this\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\/12\/psinception.jpg?resize=350%2C200&ssl=1","width":350,"height":200},"classes":[]},{"id":5626,"url":"https:\/\/jdhitsolutions.com\/blog\/powershell\/5626\/throwing-the-kitchen-sink-at-powershell\/","url_meta":{"origin":6996,"position":4},"title":"Throwing the Kitchen Sink at PowerShell","author":"Jeffery Hicks","date":"August 22, 2017","format":false,"excerpt":"The other day I was watching a good intro video from Shane Young on getting started with PowerShell profiles. I use profile scripts extensively, and they can be extremely useful in configuring your PowerShell experience. One element you could add to your profile is a customized PowerShell prompt. Microsoft provides\u2026","rel":"","context":"In &quot;PowerShell&quot;","block_context":{"text":"PowerShell","link":"https:\/\/jdhitsolutions.com\/blog\/category\/powershell\/"},"img":{"alt_text":"everythingprompt1","src":"https:\/\/i0.wp.com\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2017\/08\/everythingprompt1_thumb.png?resize=350%2C200","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2017\/08\/everythingprompt1_thumb.png?resize=350%2C200 1x, https:\/\/i0.wp.com\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2017\/08\/everythingprompt1_thumb.png?resize=525%2C300 1.5x, https:\/\/i0.wp.com\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2017\/08\/everythingprompt1_thumb.png?resize=700%2C400 2x"},"classes":[]},{"id":8787,"url":"https:\/\/jdhitsolutions.com\/blog\/powershell\/8787\/prerelease-of-psfunctiontools-for-powershell\/","url_meta":{"origin":6996,"position":5},"title":"Prerelease of PSFunctionTools for PowerShell","author":"Jeffery Hicks","date":"January 13, 2022","format":false,"excerpt":"At the end of last year wrote a series of blog posts describing tools and techniques for working with PowerShell scripts and functions. My goal was to build a framework of tools that I could use to automate PowerShell scripting work, such as creating a new module from a group\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\/2022\/01\/psfunctiontools-commands.png?resize=350%2C200&ssl=1","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2022\/01\/psfunctiontools-commands.png?resize=350%2C200&ssl=1 1x, https:\/\/i0.wp.com\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2022\/01\/psfunctiontools-commands.png?resize=525%2C300&ssl=1 1.5x, https:\/\/i0.wp.com\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2022\/01\/psfunctiontools-commands.png?resize=700%2C400&ssl=1 2x, https:\/\/i0.wp.com\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2022\/01\/psfunctiontools-commands.png?resize=1050%2C600&ssl=1 3x, https:\/\/i0.wp.com\/jdhitsolutions.com\/blog\/wp-content\/uploads\/2022\/01\/psfunctiontools-commands.png?resize=1400%2C800&ssl=1 4x"},"classes":[]}],"_links":{"self":[{"href":"https:\/\/jdhitsolutions.com\/blog\/wp-json\/wp\/v2\/posts\/6996","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=6996"}],"version-history":[{"count":0,"href":"https:\/\/jdhitsolutions.com\/blog\/wp-json\/wp\/v2\/posts\/6996\/revisions"}],"wp:attachment":[{"href":"https:\/\/jdhitsolutions.com\/blog\/wp-json\/wp\/v2\/media?parent=6996"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/jdhitsolutions.com\/blog\/wp-json\/wp\/v2\/categories?post=6996"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/jdhitsolutions.com\/blog\/wp-json\/wp\/v2\/tags?post=6996"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}