I Stream of PowerShell
Before I dive into today's content, I want to spend a moment to talk about this newsletter. First, although I refer to this as a newsletter, it is a newsletter in format only. I use a mailing service to deliver it to you and provide archive access. I typically think of a newsletter as a collection of related items of interest. The wrap-up I send out at the end of each month is a newsletter in that sense. I approach the other "issues" as a series of articles. I think of them as short book chapters.
Even though I write about topics like creating HTML reports, I'm not necessarily writing about the best way to accomplish that task. My goal with this newsletter is to teach you about PowerShell. I want to show you how to use PowerShell to solve problems, but I also want to show you how to think about using PowerShell. I want you to learn how to approach problems and how to use PowerShell to solve them. This means focusing on language, concepts, syntax, and techniques rather than specific solutions. Take my recent series of articles on creating HTML reports. I wasn't trying to show the best or only way to create HTML reports. Some of you may have no need to crete such reports. You should view all of my content as learning opportunities. You may not need to use here strings, import file content, or create files to build an HTML report. But those concepts could be useful in other contexts. When you read my content, I want you to think about how you can apply the content to your own work. What gaps in your knowledge are getting filled in? What new ideas are you getting about how to use PowerShell?
Focus on the journey I lead you through in each issue, not the destination or end product.
Today's topic, which I'm sure will encompass multiple issues, is a great example. I expect many of you aren't looking for where I end up, but there is value in exploring the process.
Alternate Data Streams
In Windows, we typically think of files and their content. We open a file, read its content, modify it, and save it. The content is saved with the file. However, there is much more to files in Windows than content alone.
Alternate data streams (ADS) are a feature of the NTFS file system that allows you to store additional metadata or content within a file without affecting the primary data stream. This means you can have multiple streams of data associated with a single file, each identified by a unique name. PowerShell provides cmdlets and parameters to work with these streams, allowing you to create, read, and manage them. I'm sure I've mentioned alternate data streams in the past.
In PowerShell, we can use commands with the -Stream
parameter to work with these alternate data streams.
Get-Command -ParameterName Stream | Select Name
Name
----
Add-Content
Clear-Content
Get-Content
Get-Item
Out-String
Remove-Item
Set-Content
However you should still read help for these commands as -Stream
can have other meanings.
Finding Streams
First, let's look at how to find streams associated with a file. Here's how we typically work with a file in PowerShell:
PS C:\temp> Get-tIem .\walrus.txt
Directory: C:\temp
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a--- 7/10/2025 9:58 AM 29 walrus.txt
PS C:\temp> Get-Item .\walrus.txt | Get-Content
I am the PowerShell walrus!
When you read the help for Get-Item
, you will see that it has a -Stream
parameter. This parameter allows you to specify a stream to retrieve. You can use a wildcard character to retrieve all streams associated with the file.
PS C:\temp> Get-Item .\walrus.txt -Stream *
PSPath : Microsoft.PowerShell.Core\FileSystem::C:\temp\walrus.txt::$DATA
PSParentPath : Microsoft.PowerShell.Core\FileSystem::C:\temp
PSChildName : walrus.txt::$DATA
PSDrive : C
PSProvider : Microsoft.PowerShell.Core\FileSystem
PSIsContainer : False
FileName : C:\temp\walrus.txt
Stream : :$DATA
Length : 29
The stream name is :$DATA
, which is the default data stream for files in Windows.
PS C:\temp> Get-Item .\n.txt -Stream :`$DATA
PSPath : Microsoft.PowerShell.Core\FileSystem::C:\temp\n.txt::$DATA
PSParentPath : Microsoft.PowerShell.Core\FileSystem::C:\temp
PSChildName : n.txt::$DATA
PSDrive : C
PSProvider : Microsoft.PowerShell.Core\FileSystem
PSIsContainer : False
FileName : C:\temp\n.txt
Stream : :$DATA
Length : 449
Note that this specific stream name begins with a colon (:
) and is followed by $DATA
. Because $DATA
looks like a PowerShell variable, I am using the backtick to escape the $
so PowerShell treats it as a literal character.