Using AI to write PowerShell scripts
Table of contents
As a seasoned PowerShell scripts enthusiast, I’ve witnessed firsthand the immense potential AI holds in revolutionizing coding practices. With tools like ChatGPT and GitHub Copilot, writing PowerShell scripts has become more intuitive, efficient, and powerful, resulting in better functionality.
In this blog post, I’ll share my journey of integrating these AI tools into my coding workflow. From setting up your development environment to crafting advanced scripts, you’ll discover how AI can elevate your coding skills and streamline your processes. Join me as I delve into the world of AI-assisted PowerShell scripting and unlock new levels of productivity and creativity.
Before we get into the fun AI coding examples, I think it’s important to make sure we all agree on what AI is and how we’re using it. To start with, AI (artificial intelligence) is a huge field with many branches. When you speak to Siri, it uses AI called Natural Language Processing to understand what you’re asking for, for better or worse. When your GPS calculates a route for you, that’s also AI at work.
Machine Learning is another popular branch of AI. One thing all these examples have in common is that they don’t create anything new. They interpret things that exist in the real world, or in the case of GPS, they connect things that are given to them. But they don’t create new things.
Generative AI tools for writing PowerShell
For the rest of this blog post, when I refer to AI, I will be referring to Generative AI, an exciting, relatively new branch of AI that can create new content from information it has been trained on. While AI itself isn’t new, and even Gen AI has been around for a few years, recent advances have resulted in it being the AI everyone is talking about these days. Today’s blog post is all about Generative AI.
There are quite a few Gen AIs out right now. In this blog post I’ll only be using ChatGPT from OpenAI, GitHub Copilot, and GitHub Copilot Chat. If you are a fan of a different Gen AI, it’ll probably work the same way. I’ll also be doing all my demos in Visual Studio Code. If you prefer a different IDE, it probably has similar hooks and functionality.
Assuming you’re using VS Code, you’ll need to install a couple of PowerShell extensions. I recommend Microsoft’s PowerShell extension. It’s good to have whether you’re using AI or not. It gives you some great features like syntax highlighting, Intellisense, Script Analyzer, and a host of other necessary features.
You’ll also need the GitHub Copilot extension and the GitHub Copilot Chat extension. These extensions watch over your shoulder as you work in VS Code and offer help. If you want to follow along, install VS Code and install all those extensions. None of them have much in the way of customizations, so it should go pretty smoothly.
One of the things that is important when dealing with AI is for you and the AI to both know what role you want it to play. It’s very flexible, and it will try to do everything if you don’t give it some constraints. Knowing the AI’s role will also help you choose the correct tool, as well as help you interact with it.
Do you want it to create your code for you? Do you want it to collaborate with you on code as you’re writing it? Do you want it to review the code you’ve already written? All of these and more are legitimate ways to use AI.
I tend to use AI mostly as a creator and collaborator. I find it most useful to me as a tool to get past the tyranny of the blank page, it helps me get started, then we work on the code together. It also has the same benefits that rubber duck debugging does, it forces you to organize your ideas so that you can explain them to someone else.
To explain to the AI what I want, I must know what I wish to do from start to finish. While AI is more like a person than a computer, it does have the same garbage in/garbage out limitation. If you just tell it, “Write the thing,” you aren’t going to get very good results. But thinking through what you want before you explain it to the AI will help it help you.
It’s also essential that you understand AI’s tendency to hallucinate. Hallucinating is when AI just flat-out makes stuff up. In the case of writing PowerShell code, this could come in the form of a cmdlet that doesn’t exist like, Do-TheThing, or a made-up parameter like -domagic. AI will happily write code using made-up cmdlets or parameters and not blink an eye.
It’s imperative that you test all the code you use AI to write or collaborate on. Remember, it’s your copilot; you’re the pilot, and you’re responsible for the code.
ChatGPT PowerShell scripts
Let’s try this out with a real-world example. I was recently reorganizing some files, and I wanted to know how big a certain directory was and what it was mostly made up of. This is a script I have probably written a dozen times and could write again. But I can write it faster and better with AI. When it comes to creating new code, I tend to start with ChatGPT. I opened up ChatGPT and told it what I wanted. Here was my prompt:
write a powershell program that will count all the files in the current directory and its subdirectories. Also give a count of the 10 most common file extensions found.
With a few seconds, ChatGPT had given me the following PowerShell script:
# Get all files in the current directory and subdirectories
$files = Get-ChildItem -Recurse -File
# Count total number of files
$totalFiles = $files.Count
# Group files by extension and count each group
$extensionCounts = $files | Group-Object -Property Extension | Select-Object Name, @{Name="Count";Expression={$_.Count}}
# Sort the extensions by count in descending order and select the top 10
$topExtensions = $extensionCounts | Sort-Object -Property Count -Descending | Select-Object -First 10
# Display the results
Write-Host "Total number of files: $totalFiles"
Write-Host "Top 10 most common file extensions:"
$topExtensions | ForEach-Object {
Write-Host "$($_.Name): $($_.Count)"
}
I mentioned earlier that AI is more like another person than it is a computer, and here’s one place where that’s important. I never, ever run AI-generated code unless I understand 100% of what it’s doing. This code is pretty simple, and it looks safe, so I ran it. I used ChatGPT’s “Copy code” button and copied it into VS Code. Here’s what the conversation with ChatGPT looked like:
It’s easy to think it functions like a search engine and replies with some code someone else wrote, but notice how at the bottom it explains its code to you. It’s not just writing it; it understands it, too.
VS Code PowerShell scripts results using ChatGPT
I pasted it into VS Code and ran it. It looked like this:
I chose the “Run Without Debugging” option from the Run menu. Notice in the lower left corner VS Code recognized this code was PowerShell. You can also see in the editor that it’s formatted correctly as PowerShell.
Since I chose to run it in VS Code, it ran it in the PowerShell terminal window on the bottom. I had previously changed it to the directory I wanted to run it against. The output looked like this:
Without any modification, it did exactly what I asked it to. After I ran it, I thought to myself, “Wouldn’t it be nice to know the total file size too?” My wish was ChatGPT’s command. I went back to it and gave it this prompt:
Great! Now add the total file size too
A couple of seconds later, it was finished. It generated this code:
# Get all files in the current directory and subdirectories
$files = Get-ChildItem -Recurse -File
# Count total number of files
$totalFiles = $files.Count
# Calculate the total file size
$totalFileSize = ($files | Measure-Object -Property Length -Sum).Sum
# Group files by extension and count each group
$extensionCounts = $files | Group-Object -Property Extension | Select-Object Name, @{Name="Count";Expression={$_.Count}}
# Sort the extensions by count in descending order and select the top 10
$topExtensions = $extensionCounts | Sort-Object -Property Count -Descending | Select-Object -First 10
# Display the results
Write-Host "Total number of files: $totalFiles"
Write-Host "Total file size: $([math]::round($totalFileSize / 1MB, 2)) MB"
Write-Host "Top 10 most common file extensions:"
$topExtensions | ForEach-Object {
Write-Host "$($_.Name): $($_.Count)"
}
I copied that code into VS Code and ran it.
Now I can tell how much space is being used, too. Now, that would be impressive enough, but let’s take it up a notch. As we move up through the PowerShell maturity model, one of the things we do is stop writing stand-alone .PS1 files and we start writing functions that are stored in Modules, .PSM1 files. Let’s have AI do that for us. When I did it, I used this prompt:
Turn this code into a function called Get-TKFileList
That’s all it took. A couple of seconds later I got this code back:
function Get-TKFileList {
# Get all files in the current directory and subdirectories
$files = Get-ChildItem -Recurse -File
# Count total number of files
$totalFiles = $files.Count
# Calculate the total file size
$totalFileSize = ($files | Measure-Object -Property Length -Sum).Sum
# Group files by extension and count each group
$extensionCounts = $files | Group-Object -Property Extension | Select-Object Name, @{Name="Count";Expression={$_.Count}}
# Sort the extensions by count in descending order and select the top 10
$topExtensions = $extensionCounts | Sort-Object -Property Count -Descending | Select-Object -First 10
# Display the results
Write-Host "==========================="
Write-Host "Summary of Files and Sizes"
Write-Host "==========================="
Write-Host "Total number of files: $totalFiles"
Write-Host "Total file size: $([math]::round($totalFileSize / 1MB, 2)) MB"
Write-Host "==========================="
Write-Host "Top 10 most common file extensions:"
$topExtensions | ForEach-Object {
Write-Host "$($_.Name): $($_.Count)"
}
Write-Host "==========================="
}
# Call the function to execute it
Get-TKFileList
Again, nothing I couldn’t have done myself, but this was so much faster. Let’s take it another step further. Let’s have it add a parameter for the directory to run this against instead of always running it against the current directory:
Add a parameter, -directory
Things really get crazy here. Notice I didn’t tell it anything about the parameter; I just wanted to add it. Here was its reply before it gave me the new code:
Sure, I’ll add a parameter called -Directory to the function, allowing you to specify the directory to scan. Here’s the updated function:
Then, the new code:
function Get-TKFileList {
param (
[string]$Directory = (Get-Location)
)
# Get all files in the specified directory and subdirectories
$files = Get-ChildItem -Path $Directory -Recurse -File
# Count total number of files
$totalFiles = $files.Count
# Calculate the total file size
$totalFileSize = ($files | Measure-Object -Property Length -Sum).Sum
# Group files by extension and count each group
$extensionCounts = $files | Group-Object -Property Extension | Select-Object Name, @{Name="Count";Expression={$_.Count}}
# Sort the extensions by count in descending order and select the top 10
$topExtensions = $extensionCounts | Sort-Object -Property Count -Descending | Select-Object -First 10
# Display the results
Write-Host "==========================="
Write-Host "Summary of Files and Sizes"
Write-Host "==========================="
Write-Host "Directory: $Directory"
Write-Host "Total number of files: $totalFiles"
Write-Host "Total file size: $([math]::round($totalFileSize / 1MB, 2)) MB"
Write-Host "==========================="
Write-Host "Top 10 most common file extensions:"
$topExtensions | ForEach-Object {
Write-Host "$($_.Name): $($_.Count)"
}
Write-Host "==========================="
}
# Example call to the function
Get-TKFileList -Directory "C:\path\to\your\directory"
It was even kind enough to give us an example. Speaking of examples, let’s have it document the function and provide examples:
Please add a description and 2 examples of how to use this function
Here’s the part it added:
<#
.SYNOPSIS
Gets a summary of files and sizes in a specified directory and its subdirectories.
.DESCRIPTION
The Get-TKFileList function retrieves all files in the specified directory and its subdirectories.
It counts the total number of files, calculates the total file size, and provides a count of the
10 most common file extensions.
.PARAMETER Directory
The directory path to scan for files. If not specified, the current directory is used.
.EXAMPLE
Get-TKFileList -Directory "C:\Users\YourUsername\Documents"
This command retrieves and summarizes files in the "C:\Users\YourUsername\Documents" directory and its subdirectories.
.EXAMPLE
Get-TKFileList -Directory "D:\Projects"
This command retrieves and summarizes files in the "D:\Projects" directory and its subdirectories.
.NOTES
Author: Your Name
Date: Today's Date
#>
In just a couple of minutes, I’ve taken an idea, added some extra functionality, and then turned it into a properly formatted and documented PowerShell Function. Not too shabby.
GitHub Copilot with GitHub Copilot Chat — PowerShell scripts
I mentioned I also use GitHub Copilot to help me write amazing code. Let’s see how to put that to use. You may have noticed this screen when you opened a new document in VS Code:
This is the GitHub Copilot Chat extension giving you a chat interface to GitHub Copilot. You can hit Ctrl+I anywhere to bring it up and start chatting with it about your code. Let’s do that. I swung for the fences and asked it to make the whole function for me in one go. Here’s how it went:
I accepted the code, and it looked good. Then I ran it. Things didn’t go quite as well as they did with ChatGPT:
In its defense, I did ask a lot. This does go to show that the code AI creates does not always work. You do have to check it every step of the way. Let’s dig in and see what went wrong. We can tell the error is on line 12, so let’s look at the code and see what’s going on there:
Line 12 seems innocent enough until you start looking at the variable names. Smartly, GitHub Copilot gave our function a parameter, TopExtensions (upper case “T”), so that we could specify how many we wanted. That’s all good, but then it used topExtensions (lowercase “t”) to collect the extensions.
PowerShell is not case sensitive, so it thinks TopExtensions and topExtensions are the same variable. Since TopExtensions is already an Int32, it can’t be reassigned an array of objects. The fix is easy: rename one of the variables. I renamed topExtensions (lowercase “t”) to Extensions, and everything worked great.
As we saw before, hitting Ctrl+I anywhere in the code window will bring up the GitHub Copilot Chat box. You can also hit Ctrl+Alt+I to bring up a side chat. This is handy if you want to ask GitHub Copilot questions without having it generate code.
If you don’t want to disturb your left pane, you can use Ctrl+Shift+I to bring up a quick chat window at the top. In any of these interfaces, you can hit the / and get a list of the things GitHub Copilot Chat can do for you.
GitHub Copilot without Chat — PowerShell scripts
GitHub Copilot is always on the job, even when you’re not using Chat. It functions slightly differently than the two chat-based examples we’ve examined. Instead of generating whole scripts as the result of a conversation, it watches as we type in VS Code and offers suggestions when it can. Think of it as Intellisense, but after Intellisense reads all the code on the Internet, it has explained it to it.
I generally use it when I’m working on existing code, as opposed to when I’m starting something from scratch. Not only does it make suggestions based on its training data set, it also takes the other files in your workspace into consideration as well as the entirety of the file you’re working on.
Let’s say we want to add a CSV file output option to our Get-FileStats function. Of course, the correct way to do this would be to pipe the output of Get-FileStats to Export-CSV, but we’re going to add native support for this example. Here is a screenshot of what I did.
I added the switch parameter CSV to the top of the file. Then, at line 26, I hit enter. GitHub Copilot suggested the code in gray. It knew there was a parameter named CSV and that nothing had been done with it. It also knew from other code that that was usually a way to trigger CSV output. It put that all together and suggested the code to do it.
To accept the code, all I had to do was hit Tab. This is a pretty simple example, but it works just as well with more complicated examples. One of my favorite ways to use it is to write my code in English, in the form of comments, and let GitHub Copilot write the code for me.
Speaking of comments, it can also comment on your code for you. We can do this in a couple of different ways. First, we can just ask GitHub Copilot Chat to do it for use. Something as simple as “Please comment my code for me” works like a champ. You can also just type a hash character above a line, and GitHub Copilot will know what you want and type the comment for you.
This might seem trivial, but how many times have you opened up someone else’s code and wished there were comments? Now you can have GitHub Copilot read through it and comment it for you, or for the next person that reads your code.
And GitHub Copilot hasn’t just mastered PowerShell. As a matter of fact, even as magical as it is with PowerShell, that’s one of its weakest languages. It’s trained on all languages that appear in public GitHub repositories. Because of that, it’s strongest with more popular languages like JavaScript and Python.
I mentioned before that AI learns like humans do, for better or worse. This is important to keep in mind when letting it write code for you. If the code it was trained on was bad or used out-of-date practices, that’s what it’s going to do. Always keep your eye on the code it writes.
It is also important to mention that GitHub Copilot is not free, though there is a 30 day free trial if you’d like to take it for a spin and kick the tires a bit. After that it’s a mere $10 USD a month for an individual. I find I get my $10 worth of value about 7 minutes into my first work day of the month.
VS Code Speech
If you really want to embrace chatting with AI, you can install the VS Code Speech extension. This lets you talk directly to VS Code and GitHub Copilot. I haven’t used it a lot, but it’s a fun way to talk things out. Different people solve problems differently. Maybe talking them out to GitHub Copilot will work for you.
As you can see, you have several ways to use AI to write better and faster code. You can have a conversation with ChatGPT. It loves helping people write code, and it’s pretty good at it. Microsoft’s GitHub Copilot is a pretty skilled programming partner, too. It can look over your shoulder and gently offer suggestions, or you can use its Chat feature to chat with it. Whichever route you take, with just a little effort, your code can be more functional and fully featured without taking you any additional time.