Quantcast
Channel: Hey, Scripting Guy! Blog
Viewing all 3333 articles
Browse latest View live

Weekend Scripter: Exploring Windows PowerShell 5.0

$
0
0

Summary: Teresa Wilson, Powershell MVP, talks about exploring Windows PowerShell 5.0 on Windows 10.

Teresa Wilson, AKA the Scripting Wife, is here. The Microsoft Scripting Guy, Ed Wilson, is still missing in action. He downloaded Windows 10, and I haven’t seen him since. So I thought I would write about getting Windows PowerShell 5.0 running on Windows 10.

The first thing I need to do is find Windows PowerShell 5.0. I type PowerShell in the Web and Windows search box on my task bar. Cortana comes up and locates the Windows PowerShell 5.0 executable. I click the top of the list for the Desktop app, and Windows PowerShell opens. It opens with the working directory in my profile, but with a shortened version of my logon name. It says Users\Scrip as my directory. Cool enough. Here is the menu from Cortana:

Image of menu

I know I need to update Help. I type Update-Help –module * -Force. The Windows PowerShell progress hums along and it appears to work. Suddenly I am looking at the following long list of error messages:

Image of messages

It is a lot of red, but about half way down, it provides the reason why. I need to run Windows PowerShell as an Administrator, and then try the command again. Once again Cortana and I meet.

This time, I right-click Windows PowerShell, select Run as Administrator, and try the command again. This time, there is only an error message about one of the modules that does not have updatable Help. Cool, it worked.

I again close the Windows PowerShell Admin console. This time when I type PowerShell, I right-click PowerShell and select Pin to Start. I again right-click PowerShell and select Pin to Taskbar. This is shown here:

Image of menu

Sweet. Now I don’t have to mess around searching for a program that I know I will use all the time.

I want to see what I have in Windows PowerShell 5.0. I use the Get-Command cmdlet (gcm is an alias), and I count the number of cmdlets that return. I then decide to group the commands by version of the module. This is shown here:

Image of command output

There are 1285 cmdlets, and of these, 20 are in a module with the version of 3.3.5, and 191 of them are in a module version 3.1.0.0. Now I decide to sort them by the number of cmdlets in each module. To do this, I keep building my command. I get all of the commands from all modules and then sort by module. I then group by module and sort the results by the number of commands in each module. I do a descending sort. I then send the format to Format-Table and choose the name and count. Here is my command:

gcm -Module * | sort Module | group module | sort count -Descending | ft name, count

The command and output from the command are shown here:

Image of command output

I continue to look at modules by looking at the cmdlets in each module. Here is an example of such a command:

gcm -Module PKI

I will let you go play. Time to see where that script monkey wandered off to. Hope you have a great weekend.

~Teresa

I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

Ed Wilson, Microsoft Scripting Guy 


PowerTip: Install PowerShell Help on PowerShell 5.0

$
0
0

Summary: Update Help for Windows PowerShell 5.0.

Hey, Scripting Guy! Question I installed Windows 10 and opened Windows PowerShell, but how can I update the Help for Windows PowerShell 5.0?

Hey, Scripting Guy! Answer Open Windows PowerShell as an Administrator, type Update-Help, select all of the modules, and use the –Force 
            
parameter if you run the command more than once a day:

Update-Help –module * -Force

Cool Stuff about PowerShell 5.0 in Windows 10

$
0
0

Summary: Ed Wilson, Microsoft Scripting Guy, talks about cool stuff in Windows PowerShell 5.0 for Windows 10.

Microsoft Scripting Guy, Ed Wilson, is here. Over the weekend, I installed Windows 10. It is way cool. I love what we have done with the charms bar…it is gone. I also like the streamlined user interface, the new Edge browser, and especially Windows PowerShell 5.0. I mean, it is awesome. It rocks.

By the way, the easiest way to upgrade to Windows PowerShell 5.0 right now, is to upgrade to Windows 10—which by the way, is a free upgrade from Windows 7, 8, and 8.1. For now, that is what I am going to be doing—talking about Windows PowerShell 5.0 on Windows 10. And this brings me to Cool Stuff Week...

What is so cool about PowerShell 5.0?

Well, first of all, what’s not cool? Pretty much nothing. I love it. When I install Windows 10, I have Windows PowerShell 5.0. As Teresa explained yesterday, the next step is to pin Windows PowerShell to the Start page and to the Taskbar (see Exploring Windows PowerShell 5.0). I do this for the ISE and for the Windows PowerShell console.

I am not going to enable scripting right now. I will do that later. For now, I am exploring. I also need to open Windows PowerShell as an Administrator and update Help. Teresa also talked about how to do that yesterday.

The Clipboard

I will admit that when I first heard about Get-Clipboard and Set-Clipboard, I figured they would be pretty lame. I mean, I have been piping to Clip.exe for years. In addition to that, there are Clipboard cmdlets in the PowerShell Community Extension Project, so I figured this was just some low hanging fruit that was easy for the Windows PowerShell team to add.

Really, though, it is pretty cool.

First of all, I can pipe strings to Set-Clipboard and then get it back via Get-Clipboard. It works as expected.

But, when I pipe a directory list to Set-Clipboard, and then I use Get-Clipboard, I don’t get anything back—until I specify the format I want as a FileDropList. As shown here, I then get back a collection of objects:

Image of command output

Of course, it should come as no surprise that we have an object-oriented Clipboard. I mean, everything in Windows PowerShell is an object, so why not the Clipboard? This also means that I can index into the Clipboard, and return a FileInfo object. This is shown here:

Image of command output

At this point, I am starting to think, “You have got to be kidding. Really? Objects on the Clipboard?” Dude, dude, dude."

I know how to use objects and how to work with collections of objects. I mean, this is awesome stuff. I can grab only the base names from the collection of objects, or I can create a table with the base name and the last access time—all from the same Clipboard content. This is shown here:

Image of command output

So, yeah. The Clipboard cmdlets are cool. Way cool. And that is just two of the 1,285 Windows PowerShell cmdlets in Windows 10.

That is just scratching the surface of Windows PowerShell 5.0. Cool Stuff Week will continue tomorrow when I will talk about, well, uh, more cool stuff.

I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

Ed Wilson, Microsoft Scripting Guy

PowerTip: Find PowerShell Help Content

$
0
0

Summary: Learn how to find all Windows PowerShell Help content.

Hey, Scripting Guy! Question I updated the Help files on my installation of Windows PowerShell. I can find the cmdlet Help,
           but how can I see the Help text files?

Hey, Scripting Guy! Answer Use the Get-Help cmdlet, search for the HelpFile category, select all of them with a wildcard
           character, and then pipe the output to More:

Get-Help –category helpfile * | more

Use Transcript Tool in PowerShell ISE

$
0
0

Summary: Ed Wilson, Microsoft Scripting Guy, talks about using the Windows PowerShell ISE transcript in PowerShell 5.0 in Windows 10.

Microsoft Scripting Guy, Ed Wilson, is here. It is not a secret that I love Windows PowerShell. It is also not a secret that Windows PowerShell keeps getting better and better. One of the things about Windows PowerShell is that if I do not like the way it does something, or if there is a functionality that is missing, I can write my own commands and make my own modifications. In this way, Windows PowerShell extensible, and it is pretty awesome. Something that is also awesome, is that the Windows PowerShell team listens to customers, and adds the functionality that is wanted in the product.

One of the way cool features of Windows PowerShell 1.0 was the transcript tool in the Windows PowerShell console. It provides a log of commands and the output from the commands in addition to other valuable information. When Windows PowerShell 2.0 shipped and introduced the Windows PowerShell ISE, I was asked by hundreds of customers why we did not have a transcript tool in the ISE. So in 2010, I wrote a transcript tool for the Windows PowerShell ISE, and added it to my ISE profile. Here is the article where I talked about the tool: Create a Transcript of Commands from the Windows PowerShell ISE.

In Windows PowerShell 5.0 in Windows 10, I have a transcript tool that works in the Windows PowerShell ISE. All I need to do is to use the Start-Transcript cmdlet to start the transcript. I can run a few commands, and then use the Stop-Transcript cmdlet to stop the transcript. The commands are shown here:

Image of command output

I can then view my transcript in Notepad or some other text reader:

Image of text

As you can see here, a cool thing about the Windows PowerShell ISE transcript tool is that I can have a transcript run in my Windows PowerShell console and in my ISE at the same time:

Image of console and ISE

I can even have multiple Windows PowerShell consoles and ISEs open at the same time, with each having its own transcript running at the same time:

Image of console and ISE

One thing that may be helpful in working with transcripts is to create your own names. This is because the file names all begin to look alike, and it can be very difficult to find a specific transcript among a directory of similar looking files. Here is an example:

Image of folder

That is all there is to using Windows PowerShell ISE transcript. Join me tomorrow when I will talk about more cool stuff.

I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

Ed Wilson, Microsoft Scripting Guy 

PowerTip: Use PowerShell 5.0 to Clear Recycle Bin

$
0
0

Summary: Learn how to clear the recycle bin by using Windows PowerShell 5.0 in Windows 10.

Hey, Scripting Guy! Question How can I use Windows PowerShell to clear the recycle bin in my laptop running Windows 10?

Hey, Scripting Guy! Answer Use the Clear-RecycleBin cmdlet.

Create Temporary Files with PowerShell 5.0

$
0
0

Summary: Ed Wilson, Microsoft Scripting Guy, talks about creating temporary files with Windows PowerShell 5.0 in Windows 10.

Microsoft Scripting Guy, Ed Wilson, is here. This morning I am sipping a nice Gunpowder Green tea with hibiscus leaves and rose hips in it. I also added a bit of lemon grass for taste. I made the tea last night and chilled it. The taste is surprisingly clean and refreshing. Along with some Belgium crisp chocolate sticks, it makes a delightful mid-morning snack.

I am stilling playing around with Windows 10 and Windows PowerShell 5.0. I suspect this will probably be a task that will occupy me until the next versions ship. One thing I know I to have to do is clean up my profiles. The reason? Well, many of the things I had written (like the ISE Transcript function) are now included in Windows PowerShell 5.0, for example, the ISE Transcript function (see Use Transcript Tool in PowerShell ISE).

In July 2010, I wrote a function I called Out-TempFile (see How Can I Create, Display, and Then Delete a Temporary Text File?) I could use it to create temporary files. One thing that function did that the New-TemporaryFile cmdlet doesn’t do is allow me to pipe directly to a temporary file. In addition, when I was done, it would automatically delete the temporary file. So I may keep it around and update it to use New-TemporaryFile.

In Windows PowerShell 5.0, there is a new cmdlet called New-TemporaryFile. It will create a new temporary file in the temporary file location. It also returns a FileInfo object, so I can easily pick up the complete path to the file. All I need to do is to capture the return from the cmdlet. This technique is shown here:

$tmp = New-TemporaryFile

To get the complete path to the temporary file, I use the FullName property:

$tmp.FullName

If I want to output to the temporary file, I can pipe to the Out-File cmdlet, and pass the FullName parameter, like this:

Get-Process | Out-File $tmp.FullName

These commands are shown in the following image:

Image of command output

It is really important to capture the output from New-TemporaryFile cmdlet because it is basically a random file name, and therefore, it can be a major pain to find—even supposing I can remember where the temporary file storage is located. Also, this makes it easy for me to view the temp file because I can use Notepad and pass the $tmp.FullName command to it. As shown here, when I do, the output appears in Notepad:

Image of command output

When I am done, I like to clean up. Therefore, I do not want to leave the temporary file lying around. Once again, I use the FullName property from the $tmp variable:

Remove-Item $tmp.FullName -Force

That is all there is to using Windows PowerShell 5.0 to create and to delete temporary files. Join me tomorrow when I'll continue talking about way cool stuff.

I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

Ed Wilson, Microsoft Scripting Guy 

PowerTip: Create a GUID with PowerShell 5.0

$
0
0

Summary: Learn how to create a new GUID with Windows PowerShell 5.0.

Hey, Scripting Guy! Question How can I use Windows PowerShell 5.0 in Windows 10 to create a new GUID?

Hey, Scripting Guy! Answer Use the New-Guid cmdlet:


Playing Around with Format-Hex

$
0
0

Summary: Ed Wilson, Microsoft Scripting Guy, talks about the Format-Hex cmdlet in Windows PowerShell 5.0.

Microsoft Scripting Guy, Ed Wilson, is here. One of the cool things about Windows PowerShell is that it usually just works. Well of course, after you learn how, it just works. For example, it is largely self-describing, and it has standard parameters. Other cmdlets that use parameters with similar names generally take pains to ensure that those parameters work the same way.

For example, the Pathparameter is not a standard parameter. Yet, it is usually always called Path, and it generally points to the path to something. In addition, I can generally supply relative and literal paths, PSPaths, and often even UNC paths to a parameter with a name of Path. Many times, Patheven accepts an array of paths—although sometimes it will not.

So how do I know?

I like to use a combination of Get-Help and Get-Command. If a cmdlet does not have any formal Help associated with it, it will still provide a basic sort of help that provides a nicely formatted syntax. Often, that is all that is required. Here is an example:

Get-Help Format-Hex

One of the cool things about Windows PowerShell 5.0 Help is that it displays aliases for the cmdlets. In addition, the Help displays three parameter sets (or ways of using the cmdlet). I can provide a path, a literal path, or an input object. If I provide an input object, I can also specify encoding.

This default Help for the Format-Hex cmdlet is shown here:

Image of command output

If I want to pipe a string to the Format-Hex cmdlet (that would be the input object), I retrieve a hex representation of the asci data that is in the string:

Image of command output

I can also read the contents of a file and display the hex value of the content of that file by supplying a path:

Image of command output

If I look at the content of the text file, I can compare it to my previous string. The string that I piped to Format-Hex is the same as the string that is contained in the text file. If I look at the hex data from the string and the text file, I can see that it is very similar—only the last five hex values from the text file are different. This is shown here:

Image of command output

So, what kind of an object am I returning from the Format-Hex cmdlet? Well, it is a byte collection. Here are the members:

PS C:\> 'A file with a `in the name.' | format-hex | Get-Member

   TypeName: Microsoft.PowerShell.Commands.ByteCollection

Name        MemberType Definition

----        ---------- ----------

Equals      Method     bool Equals(System.Object obj)

GetHashCode Method     int GetHashCode()

GetType     Method     type GetType()

ToString    Method     string ToString()

Bytes       Property   byte[] Bytes {get;}

Offset      Property   uint32 Offset {get;}

Path        Property   string Path {get;}

This means, that I can compare the bytes. I store the objects to variables, and then use Compare-Object to do a comparison:

Image of command output

I can even search for stuff in my hex output. For example, I can look for "file":

PS C:\> format-hex 'C:\fso\A File with a `in the name.txt' | Select-String "file"

00000000   41 20 66 69 6C 65 20 77 69 74 68 20 61 20 60 69  A file with a `i

That is all there is to using the Format-Hex cmdlet in Windows PowerShell. Join me tomorrow when I will talk about more cool stuff.

I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

Ed Wilson, Microsoft Scripting Guy 

PowerTip: Find Alias for PowerShell Cmdlet

$
0
0

Summary: Learn how to find an alias for a specific Windows PowerShell cmdlet.

Hey, Scripting Guy! Question How can I find is there is an alias for a specific Windows PowerShell cmdlet that I use a lot?

Hey, Scripting Guy! Answer Use the Get-Alias cmdlet, specify the –Definition parameter, and supply the name of the cmdlet, for example:

Get-Alias -Definition Get-CimClass

The PowerShell 5 NoNewLine Parameter

$
0
0

Summary: Ed Wilson, Microsoft Scripting Guy, talks about the NoNewLine parameter in Windows PowerShell 5.0 in Windows 10.

Microsoft Scripting Guy, Ed Wilson, is here. In addition to new cmdlets in Windows PowerShell 5.0, there are also improvements to existing cmdlets. These improvements are often really cool because it means I can use the old cmdlets in new ways, which is just about like having a new cmdlet.

These add-ons (if you will) are not as easy to find as a whole brand new cmdlet. I mean, a brand new cmdlet is pretty easy to find with Get-Command because all I need to do is type Get-Command, and search for a verb or a noun, or page through all of the cmdlets and look for things that are not familiar. Or, if I want to be more scientific, I can use Get-Command in Windows 8.1, then in Windows 10, and use Compare-Object to compare the two lists of cmdlet names.

Finding new parameters

But finding new parameters is more difficult. It is not impossible, but it is more difficult. In the beta of Windows PowerShell 2.0, for example, one of the Windows PowerShell MVPs wrote a really cool script that would compare drops for new cmdlets and for new parameters. It was a very useful script for keeping up with all the changes that were appearing like rabbits in an unmown field of clover.

In the case of the NoNewLine parameter, it is listed in the What's new in Windows PowerShell 5.0 article, and so that makes it easy to find. At other times, it means paying attention or actually searching by comparing commands from previous versions with commands from the latest version. That can be a rather slow and tedious process.

The problem with new lines

The issue with new lines is not that we now have a solution in search of a problem. It was a problem, and it has been ever since Windows PowerShell 1.0. The reason is that there was inconsistent behavior. For example, if I create a new file and I have text in the file, I will not have a new line. If I add content to a file, I get new lines, and there was no good way to suppress this.

So what is the big deal?

Well, the big deal is that sometimes I want to control the new lines myself—maybe I am trying to build a CSV file or some other file, and I do not want invisible stuff entering my file (such as a carriage return or line feed).

What are the commands that have the new NoNewLine parameter? I can use the Get-Command cmdlet (gcm is an alias) and search for the parameter by using the –ParameterName parameter. Of course, I am searching for the NoNewLine parameter. The command and the associated output are shown in the following image:

Image of command output

The four cmdlets with the new NoNewLine parameter are:

  • Add-Content
  • Out-File
  • Set-Content
  • Write-Host.

I create a new file and set an initial value. This command and its output are shown here:

PS C:\> New-Item -Path c:\fso\newfile.txt -ItemType file -Value "initial value"

    Directory: C:\fso

Mode                LastWriteTime         Length Name

----                -------------         ------ ----

-a----         8/5/2015   1:33 PM             13 newfile.txt

The file appears in Notepad:

Image of text

Now I add content to the file, and I do not add a new line. I do this again, and I add two additional lines, but do not use the NoNewLine switched parameter:

PS C:\> Add-Content C:\fso\newfile.txt -Value "more content" -NoNewline

PS C:\> Add-Content C:\fso\newfile.txt -Value "even more content" -NoNewline

PS C:\> Add-Content C:\fso\newfile.txt -Value "additional content"

PS C:\> Add-Content C:\fso\newfile.txt -Value "more additional content"

The file in Notepad looks like this:

Image of text

Notice that the first time I added “additional content” without adding the NoNewLine parameter, I was adding content to the prior line that did not have a new line, so the “additional content” text appears without a new line. It was only when I added the text the second time without the parameter that “more additional content” appears on its new line.

As shown here, when I use Write-Host with NoNewLine, the output is a bit weird:

Image of command output

Out-File and Set-Content work the same way as adding content to a file because they add directly to a file without adding new lines. So clearly, I have some pretty cool new tools I can use. Sweet.

That is all there is to using the NoNewLine parameter on Windows PowerShell 5.0. Join me tomorrow when I will talk about more cool stuff.

I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

Ed Wilson, Microsoft Scripting Guy 

PowerTip: Find Cmdlets with NoNewLine Parameter

$
0
0

Summary: Easily find Windows PowerShell 5.0 cmdlets with the NoNewLine parameter.

Hey, Scripting Guy! Question I heard that Windows PowerShell 5.0 introduces a NoNewLine parameter for some cmdlets, but how can I
           find the cmdlets?

Hey, Scripting Guy! Answer Use the Get-Command cmdlet (gcm is an alias), and search for the ParameterName of NoNewLine:

gcm -ParameterName nonewline

Weekend Scripter: A Week's Worth of Windows 10

$
0
0

Summary: Ed Wilson, Microsoft Scripting Guy, talks more about his first impressions of Windows 10.

Microsoft Scripting Guy, Ed Wilson, is here. I will admit that I am not a typical user—of anything, as far as that goes, but certainly not of desktop operating systems. So my thoughts about Windows 10 may or may not, be relevant.

First of all, I can say that I have been "using" Windows 10 for about a year because I get the daily builds. But this is not all that true because installing Windows 10 and immediately launching Windows PowerShell hardly counts as using Windows 10. So in reality, my only real experience has been this past week.

I have two experiences to report:

  1. I did a complete fresh installation on my laptop. The reason for this is, well, I am old school, and always believe that a fresh installation of Windows is a good thing. In fact, in the Windows 95 days, I did a complete reinstall every three months. Your mileage may vary.
  2. I let Windows Update do the upgrade on my beloved Surface Pro 3. This device is my favorite device, and I was halfway intending to keep it running Windows 8.1 for a while. But I have Windows 8.1 running on my Surface Pro 2, which is also a great device, so I figured I could risk it.

Fresh install versus Windows Update

Because I have been installing Windows 10 nearly every day for a year, I did not think about it too much. I logged on to MSDN, downloaded the ISE, burned it to a DVD, and fought with my laptop to get it to boot off of the DVD. I mean, Windows 8.1 boots so quickly that there is little time for pressing F1, or F2, F10, or F12, holding down ESC, or whatever other secret combination a laptop maker tends to dream up for whatever current model they are shipping.

My Surface Pro 3 was easy. I clicked the pop-up balloon, and it started.

I finally got the laptop to boot off of the DVD, typed in the key, and it was off to the races. Oh yeah, at some point, I told it to wipe out my existing partitions and go from there.

Note  Prior to doing any upgrade, it is always an excellent idea to perform a data backup. This is especially important if you intend to delete your existing partitions, reformat the drives, and do a fresh installation.

When the installation began on both devices, the Scripting Wife and I headed to the library and out for dinner. When we returned, there was a simple prompt on both devices. I answered a question or two and it was done.

After installing/upgrading, now what?

After the fresh installation on my laptop, I had to search around for a few minutes to find where we had hidden Windows Update because there are always security updates and bug fixes. I found it at:

Windows flag > Settings > Update and Security > Windows Update

I forced it to check for updates, and then did this a couple more times. I installed Office 2013 and checked for more updates. This time, it did not seem to find anything, but I know there are lots of Office updates that need to be applied. I then remembered that Windows Update on Windows 10 likes to run in the background and do everything itself. So I left my laptop on and checked back the next morning.

Upgrade results

On my Windows Surface Pro 3, things were streamlined. I already had Office 2013 installed in addition to a bunch of apps. So following the upgrade, everything was seamless. I did not have to do any new installing. I clicked my Facebook app, and I was able to immediately make posts. I clicked the new Windows 10 Mail app, and my online email account immediately worked. Unfortunately, it lost the settings for a different legacy email server. That was rather annoying, and it required some time to fix.

My biggest shock on my Surface Pro 3 was that it went immediately into “Desktop mode,” and the Start screen (which I love on a mobile surface device) was basically gone. I did a few searches on the Internet trying to figure this out (I remembered that Windows 10 was supposed to detect the type of device and switch “modes” on the fly.

I guess, this means that with the keyboard attached (95 percent of the time, I keep my keyboard installed because it folds back and makes a nice stable platform for using the device in “Tablet mode.” Unfortunately, I never did find the correct answer for how to switch modes. I finally stumbled across it in the settings. It is really easy to switch modes:

Windows flag > Settings > System > Tablet mode

Then I clicked Make Windows more touch-friendly when using your device as a tablet and When I sign In. Then I selected Automatically Switch to Tablet Mode.

That is it. I guess all of the “bad advice” I found on the Internet was applicable to earlier beta versions of Windows 10.

By the way, the “touch mode” is disabled if your device (such as an old laptop) is not touch-enabled.

So I can say that the upgrade on my Windows Surface Pro 3 has been flawless. I mean, really, it went perfectly. It kept my installed apps, remembered 99% of my settings, and has worked perfectly. On my laptop, I have had to reinstall everything—including existing apps that I had previously owned. This is actually a little surprising, because when I installed Windows 8.1 and signed in with my online Microsoft account, it remembered which apps I had and reconstituted my Start screen. I miss that.

And of course, Windows PowerShell 5.0 in Windows 10 is awesome. All in all, this has been a great week. It is like a birthday week for a geek with new toys coming every day as I discover more reasons to love Windows 10. It is like all new devices.

I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

Ed Wilson, Microsoft Scripting Guy 

PowerTip: Find Commands Exported by PowerShell Module

$
0
0

Summary: Find commands that are exported by a Windows PowerShell module.

Hey, Scripting Guy! Question How can I see what commands a Windows PowerShell module provides?

Hey, Scripting Guy! Answer There are two quick ways to find commands exported by a Windows PowerShell module.

The first expands the ExportedCommands property from a module (gmo is an alias for the Get-Module cmdlet):

gmo PSReadline | select -ExpandProperty exportedcommands

The second uses the Get-Command cmdlet (gcm is an alias for Get-Command):

gcm -Module PSreadline

Weekend Scripter: PowerShell 5 Overview Video

$
0
0

Summary: Ed Wilson, Microsoft Scripting Guy, presents a video he made as an overview of Windows PowerShell 5.0.

Microsoft Scripting Guy, Ed Wilson, is here. It has been a great week. It is a nice sunny day here in central Florida, and I am sipping a cup of Earl Grey tea. I decided to make a video to summarize the things I have been talking about with Windows PowerShell 5.0. Sometimes it is just easier to say things in a video. 

(Please visit the site to view this video)

Here is a link to the video from YouTube if you would like to save it to watch at a later time:

Windows PowerShell 5.0 Overview video by Ed Wilson

I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

Ed Wilson, Microsoft Scripting Guy


PowerTip: Find Commands and Descriptions from Module

$
0
0

Summary: Find commands and their descriptions exported from a Windows PowerShell module.

Hey, Scripting Guy! Question How can I see the commands with their descriptions that are available in a new Windows PowerShell module?

Hey, Scripting Guy! Answer Use the Get-Command (gcm is an alias) cmdlet and specify the module. Then select the name and create
           a custom property to retrieve the description from Get-Help, for example:

gcm -Module psreadline |

select name, @{L='description'; E={(get-help $_).description.text}}

Connect to USN Change Journal

$
0
0

Summary: Honorary Scripting Guy, Boe Prox, shows us how to connect to the USN change journal by using Windows PowerShell.

Honorary Scripting Guy and Windows PowerShell MVP, Boe Prox, here today, filling in for my good friend, the Scripting Guy. Over the course of the next few days, I am going to be talking about a topic that many of you might not have heard about, which deals with the update sequence number (USN) change journal. We will use Windows PowerShell to take a walk-through, with help from PInvoke, to connect to the change journal and view the entries within the journal.

First, a quick overview of the USN change journal...

This is journal contains entries of changes made to the volume that you are looking at. This could be a local system drive or another drive that is connected to a system. Depending on the size of the journal, you could go back several days and view all of the changes that have been made, ranging from created files to deleted files, including their time stamps and locations.

You might think that something like this would be off limits for us to view. It can actually be viewed by using fsutil, but that only returns text, which as you know, doesn’t really provide much use to us who live and breathe Windows PowerShell and demand that objects be returned.

So with that, let’s get started by building our code so we can make that connection to the change journal!

I mentioned earlier that PInvoke would be playing a part in all of this and that begins now! Between pinvoke.net and looking up information on MSDN, I can find exactly what I need. In fact, this site has some great information about the various functions that are needed: Change Journals.

I can see from my research that I need the CreateFile function to get a handle to the change journal, which will get a lot of use in future posts. It also retrieves an object that will list some useful information about the change journal.

Everything I do with PInvoke is done using reflection, so if you were expecting some C# code that is compiled by using Add-Type...well maybe next time! You can thank fellow Windows PowerShell MVP, Matt Graeber, for getting me hooked on this approach.

I am going to have a type called PoshChJournal when this is all done, so I can perform a Try/Catch against the type to see if it actually exists. If not, I'll perform the necessary steps to build it.

Try {

    [void][PoshChJournal]

}

Assuming it is not already available, I'll start building the pieces needed, such as the Enums, Structs, and Methods.

    #region Module Builder

    $Domain = [AppDomain]::CurrentDomain

    $DynAssembly = New-Object System.Reflection.AssemblyName('ChJournalAssembly')

    $AssemblyBuilder = $Domain.DefineDynamicAssembly($DynAssembly, [System.Reflection.Emit.AssemblyBuilderAccess]::Run) # Only run in memory

    $ModuleBuilder = $AssemblyBuilder.DefineDynamicModule('ChJournal', $False)

    #endregion Module Builder

 

    #region Enum

    #region EIOControlCode Enum

    $EnumBuilder = $ModuleBuilder.DefineEnum('EIOControlCode', 'Public', [uint32])

    [void]$EnumBuilder.DefineLiteral('FSCTL_QUERY_USN_JOURNAL', [uint32] 0x900f4)

    [void]$EnumBuilder.DefineLiteral('FSCTL_READ_USN_JOURNAL', [uint32] 0x900bb)

    [void]$EnumBuilder.CreateType()

    #endregion EIOControlCode Enum

    #endregion Enum

 

    #region STRUCT

    #region USN_JOURNAL_DATA

    $Attributes = 'AutoLayout, AnsiClass, Class, Public, SequentialLayout, Sealed, BeforeFieldInit'

    $STRUCT_TypeBuilder = $ModuleBuilder.DefineType('USN_JOURNAL_DATA', $Attributes, [System.ValueType], 8)

    [void]$STRUCT_TypeBuilder.DefineField('UsnJournalID', [long], 'Public')

    [void]$STRUCT_TypeBuilder.DefineField('FirstUsn', [long], 'Public')

    [void]$STRUCT_TypeBuilder.DefineField('NextUsn', [long], 'Public')

    [void]$STRUCT_TypeBuilder.DefineField('LowestValidUsn', [long], 'Public')

    [void]$STRUCT_TypeBuilder.DefineField('MaxUsn', [long], 'Public')

    [void]$STRUCT_TypeBuilder.DefineField('MaximumSize', [long], 'Public')

    [void]$STRUCT_TypeBuilder.DefineField('AllocationDelta', [long], 'Public')

    [void]$STRUCT_TypeBuilder.CreateType()

    #endregion USN_JOURNAL_DATA

    #endregion STRUCT

 

    #region Initialize Type Builder

    $TypeBuilder = $ModuleBuilder.DefineType('PoshChJournal', 'Public, Class')

    #endregion Initialize Type Builder

 

    #region Methods

    #region CreateFile

    $PInvokeMethod = $TypeBuilder.DefineMethod(

        'CreateFile', #Method Name

        [Reflection.MethodAttributes] 'PrivateScope, Public, Static, HideBySig, PinvokeImpl', #Method Attributes

        [IntPtr ], #Method Return Type

        [Type[]] @(

            [string],                   # Filename

            [System.IO.FileAccess],     # Access

            [System.IO.FileShare],      # Share

            [intptr],                   # Security attributes

            [System.IO.FileMode],       # Creation Disposition

            [System.IO.FileAttributes], # Flags and Attributes

            [intptr]                    # Template File

        ) #Method Parameters

    )

    $DllImportConstructor = [Runtime.InteropServices.DllImportAttribute].GetConstructor(@([String]))

    $FieldArray = [Reflection.FieldInfo[]] @(

        [Runtime.InteropServices.DllImportAttribute].GetField('EntryPoint'),

        [Runtime.InteropServices.DllImportAttribute].GetField('SetLastError')

        [Runtime.InteropServices.DllImportAttribute].GetField('ExactSpelling')

    )

 

    $FieldValueArray = [Object[]] @(

        'CreateFile', #CASE SENSITIVE!!

        $True,

        $False

    )

 

    $SetLastErrorCustomAttribute = New-Object Reflection.Emit.CustomAttributeBuilder(

        $DllImportConstructor,

        @('kernel32.dll'),

        $FieldArray,

        $FieldValueArray

    )

 

    $PInvokeMethod.SetCustomAttribute($SetLastErrorCustomAttribute)

    #endregion CreateFile

    #region DeviceIoControl

    $PInvokeMethod = $TypeBuilder.DefineMethod(

        'DeviceIoControl', #Method Name

        [Reflection.MethodAttributes] 'PrivateScope, Public, Static, HideBySig, PinvokeImpl', #Method Attributes

        [bool ], #Method Return Type

        [Type[]] @(

            [intptr],                           # hDevice

            [uint32],                           # IOControlCode

            [long].MakeByRefType(),             # InBuffer

            [int],                              # InBufferSize

            [USN_JOURNAL_DATA].MakeByRefType(),             # OutBuffer

            [int],                              # OutBufferSize

            [int].MakeByRefType(),              # lpBytesReturned

            [intptr] # lpOverlapped

        ) #Method Parameters

    )

    $DllImportConstructor = [Runtime.InteropServices.DllImportAttribute].GetConstructor(@([String]))

    $FieldArray = [Reflection.FieldInfo[]] @(

        [Runtime.InteropServices.DllImportAttribute].GetField('EntryPoint'),

        [Runtime.InteropServices.DllImportAttribute].GetField('SetLastError')

        [Runtime.InteropServices.DllImportAttribute].GetField('ExactSpelling')

    )

 

    $FieldValueArray = [Object[]] @(

        'DeviceIoControl', #CASE SENSITIVE!!

        $True,

        $False

    )

 

    $SetLastErrorCustomAttribute = New-Object Reflection.Emit.CustomAttributeBuilder(

        $DllImportConstructor,

        @('kernel32.dll'),

        $FieldArray,

        $FieldValueArray

    )

 

    $PInvokeMethod.SetCustomAttribute($SetLastErrorCustomAttribute)

 

    $PInvokeMethod = $TypeBuilder.DefineMethod(

        'DeviceIoControl', #Method Name

        [Reflection.MethodAttributes] 'PrivateScope, Public, Static, HideBySig, PinvokeImpl', #Method Attributes

        [bool ], #Method Return Type

        [Type[]] @(

            [intptr],                           # hDevice

            [uint32],                           # IOControlCode

            [intptr],                             # InBuffer

            [int],                              # InBufferSize

            [intptr],                           # OutBuffer

            [int],                              # OutBufferSize

            [int].MakeByRefType(),              # lpBytesReturned

            [intptr] # lpOverlapped

        ) #Method Parameters

    )

    $DllImportConstructor = [Runtime.InteropServices.DllImportAttribute].GetConstructor(@([String]))

    $FieldArray = [Reflection.FieldInfo[]] @(

        [Runtime.InteropServices.DllImportAttribute].GetField('EntryPoint'),

        [Runtime.InteropServices.DllImportAttribute].GetField('SetLastError')

        [Runtime.InteropServices.DllImportAttribute].GetField('ExactSpelling')

    )

 

    $FieldValueArray = [Object[]] @(

        'DeviceIoControl', #CASE SENSITIVE!!

        $True,

        $False

    )

 

    $SetLastErrorCustomAttribute = New-Object Reflection.Emit.CustomAttributeBuilder(

        $DllImportConstructor,

        @('kernel32.dll'),

        $FieldArray,

        $FieldValueArray

    )

 

    $PInvokeMethod.SetCustomAttribute($SetLastErrorCustomAttribute)

    #endregion DeviceIoControl

    #region CloseHandle

    $PInvokeMethod = $TypeBuilder.DefineMethod(

        'CloseHandle', #Method Name

        [Reflection.MethodAttributes] 'PrivateScope, Public, Static, HideBySig, PinvokeImpl', #Method Attributes

        [bool ], #Method Return Type

        [Type[]] @(

            [intptr] # Handle

        ) #Method Parameters

    )

    $DllImportConstructor = [Runtime.InteropServices.DllImportAttribute].GetConstructor(@([String]))

    $FieldArray = [Reflection.FieldInfo[]] @(

        [Runtime.InteropServices.DllImportAttribute].GetField('EntryPoint'),

        [Runtime.InteropServices.DllImportAttribute].GetField('SetLastError')

        [Runtime.InteropServices.DllImportAttribute].GetField('ExactSpelling')

    )

 

    $FieldValueArray = [Object[]] @(

        'CloseHandle', #CASE SENSITIVE!!

        $True,

        $False

    )

 

    $SetLastErrorCustomAttribute = New-Object Reflection.Emit.CustomAttributeBuilder(

        $DllImportConstructor,

        @('kernel32.dll'),

        $FieldArray,

        $FieldValueArray

    )

 

    $PInvokeMethod.SetCustomAttribute($SetLastErrorCustomAttribute)

    #endregion CloseHandle

    #endregion Methods

 

    #region Create Type

    [void]$TypeBuilder.CreateType()

    #endregion Create Type

Now we can verify that all of this worked by running through some of the methods in the type and looking at the Enum and Structs.

Image of command output

Now that we are done with that, it is time to make our connection to the change journal. First off, I want to define some variables prior to opening the file handle on local drive C, and I'll specify what type of access I want to drive C when using CreateFile().

$FileName = "\\.\c:"

$Access = [System.IO.FileAccess]::Read -BOR [System.IO.FileAccess]::Write

$ShareAccess = [System.IO.FileShare]::Read -BOR [System.IO.FileShare]::Write

$FileMode = [System.IO.FileMode]::Open

Now I can get the file handle for drive C. This handle will be used in all operations involving the change journal, so ensuring that we get it is crucial for the rest of the things that we will be doing.

$hVol = [PoshChJournal]::CreateFile(

    $FileName,

    $Access,

    $ShareAccess,

    [intptr]::Zero,

    $FileMode,

    0,

    [intptr]::Zero

)

Image of command output

We have a handle returned from the CreateFile() method, and that means that we can move on to the last operation of the day, which is using DeviceIOControl() to perform the necessary action to pull the change journal data to our structure. This will tell us more information about it.

If you are wondering...

If the result happened to be a -1, this means an error occurred, and we would have to see what happened by using the following code:

If ($hVol -eq -1) {

    Write-Warning ("CreateFile failed: 0x{0:x}" -f [System.Runtime.InteropServices.Marshal]::GetHRForLastWin32Error())

}

As before, I need to specify a couple of variables prior to running my method:

$JournalData = New-Object USN_JOURNAL_DATA

[long]$dwBytes=0

Now we can call DeviceIOControll and take note of [EIOControlCode]::FSCTL_QUERY_USN_JOURNAL, which tells the system to look at the change journal. I'll be using DeviceIOControl in other examples, but using a different EIOControlCode, based on what I need to do.

$return = [PoshChJournal]::DeviceIoControl(

    $hVol,

    [EIOControlCode]::FSCTL_QUERY_USN_JOURNAL,

    [ref]$Null,

    0,

    [ref]$JournalData,

    [System.Runtime.InteropServices.Marshal]::SizeOf([type][USN_JOURNAL_DATA]),

    [ref]$dwBytes,

    [intptr]::Zero

)

Assuming that $Return is $True, we know that this was successful, and we can now look at the referenced structure, $JournalData, to see what it has:

 Image of command output

Perfect! You might be wondering what each of these properties mean. Let’s run through each:

  • UsnJournalID: The current journal identifier. A journal is assigned a new identifier when it is created, and it can be stamped with a new identifier during the course of its existence.
  • FirstUsn: The number of the first record that can be read from the journal
  • NextUsn: The number of the next record to be written to the journal
  • LowestValidUsn: The first record that was written into the journal for this journal instance
  • MaxUsn: The largest USN that the change journal supports
  • MaximumSize: The target maximum size for the change journal, in bytes
  • AllocationDelta: The number of bytes of disk memory added to the end and removed from the beginning of the change journal each time memory is allocated or deallocated

The last thing that we need to do is to close the handle that we opened:

[void][PoshChJournal]::CloseHandle($hVol)

With that, we are finished making a connection to the USN change journal. In tomorrow’s post, I'll dive into viewing the change journal entries.

We invite you to follow the Scripting Guys on Twitter and Facebook. If you have any questions, send email to the Scripting Guy at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. Until then, see ya!

Boe Prox, Windows PowerShell MVP and Honorary Scripting Guy 

PowerTip: Use PowerShell to List Exported Types

$
0
0

Summary: Boe Prox shows how to use Windows PowerShell to list exported types.

Hey, Scripting Guy! Question How can I use Windows PowerShell to list all of the exported types?

Hey, Scripting Guy! Answer Use this command:

 [appdomain]::CurrentDomain.GetAssemblies()|ForEach {

    Try {

        $_.GetExportedTypes()

    } Catch {}

}

View Entries in USN Change Journal

$
0
0

Summary: Boe Prox shows us how to view the entries in the USN change journal.

Honorary Scripting Guy and Windows PowerShell MVP, Boe Prox, here today, filling in for my good friend, the Scripting Guy. Today I'm continuing on from yesterday’s post, Connect to USN Change Journal. In today’s post, I'll look at the actual entries of the journal, which will show us information about the files and folders that are being created, modified, and deleted.

Much like yesterday, I need to create some methods through reflection in addition to some Structs and Enums to handle some of the work that I will be doing. Some of this will look familiar because I created these when I set up for the change journal connection.

I won’t be showing all of the code here because it is literally 18 pages of building up everything. Instead, you can find the script in the Script Center Repository: Demo Script to View USN Change Journal Entries. I will show the rest of the code to make the connection and then to pull the entries.

First off, let’s make the connection to the change journal so we can have the handle available to us. Unlike what I used yesterday, I'll make some of my code into functions because these have the potential to be used more than once. Here are the functions I use to make the journal connection:

Function OpenUSNJournal {

    Param ($DriveLetter = 'c:')

 

    $FileName = "\\.\$DriveLetter"

    $Access = [System.IO.FileAccess]::Read -BOR [System.IO.FileAccess]::Write

    $ShareAccess = [System.IO.FileShare]::Read -BOR [System.IO.FileShare]::Write

    $FileMode = [System.IO.FileMode]::Open

    $VolumeHandle = [PoshChJournal]::CreateFile(

        $FileName,

        $Access,

        $ShareAccess,

        [intptr]::Zero,

        $FileMode,

        0,

        [intptr]::Zero

    )

    If ($VolumeHandle -eq -1) {

        Write-Warning ("CreateFile failed: 0x{0:x}" -f [System.Runtime.InteropServices.Marshal]::GetHRForLastWin32Error())

    } Else {

        $VolumeHandle

    }

}

Function GetUsnJournal {

    [OutputType('System.Journal.UsnJournal')]

    Param ($DriveLetter = 'C:')

    $JournalData = New-Object USN_JOURNAL_DATA

    [long]$dwBytes=0

    $VolumeHandle = OpenUSNJournal -DriveLetter $DriveLetter

    If ($VolumeHandle -AND $VolumeHandle -ne -1) {

        $return = [PoshChJournal]::DeviceIoControl(

            $VolumeHandle,

            [EIOControlCode]::FSCTL_QUERY_USN_JOURNAL,

            [ref]$Null,

            0,

            [ref]$JournalData,

            [System.Runtime.InteropServices.Marshal]::SizeOf([type][USN_JOURNAL_DATA]),

            [ref]$dwBytes,

            [intptr]::Zero

        )

        If ($Return) {

            $JournalData.pstypenames.insert(0,'System.Journal.UsnJournal')

            $JournalData

        }

    } Else {

        Write-Warning "Unable to get handle of volume <$($DriveLetter)>!"

    }

}

Now we can move forward with connecting to the journal:

$VolumeHandle = OpenUSNJournal -DriveLetter $DriveLetter #Using ‘C:’ as $DriveLetter

If ($VolumeHandle) {

    $JournalData = GetUsnJournal -VolumeHandle $VolumeHandle

}

Image of command output

Now we have our handle and the journal information, which will prove useful in a short bit.

I want to ensure that I pull all of the possible journal entries, so I set up a variable that covers all of the Reason Masks:

$ReasonMask = [USN_REASON]([USN_REASON].GetEnumNames())

Image of command output

Now we can start the fun of working through the entries of the change journal. I need to create a buffer that will be used to track where we are with the entries. I am also using the journal data that I collected earlier to ensure that I am starting at the first USN entry. This ensures that I am not needlessly looking for data that isn’t there.

    Write-Verbose 'Creating buffer'

    $DataSize = [System.Runtime.InteropServices.Marshal]::SizeOf([type][uint64]) * 0x4000

    $DataBuffer = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($DataSize)

    [void][PoshChJournal]::ZeroMemory($DataBuffer, $DataSize)

    $AvailableBytes = 0

 

    $ReadData = New-Object READ_USN_JOURNAL_DATA

    $ReadData.StartUsn = $JournalData.FirstUsn

 

    Write-Debug "Starting USN: $($ReadData.StartUsn)"

    $ReadData.ReasonMask = $ReasonMask

    $ReadData.UsnJournalID = $JournalData.UsnJournalID

    $ReadDataSize = [System.Runtime.InteropServices.Marshal]::SizeOf($ReadData)

    $ReadBuffer = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($ReadDataSize)

    [void][PoshChJournal]::ZeroMemory($ReadBuffer, $ReadDataSize)

 

    [System.Runtime.InteropServices.Marshal]::StructureToPtr($ReadData, $ReadBuffer, $True)

    $ReadMore = $True

    $Page = 0

I am looking to query the journal for entries based on what I have already defined with the buffer and starting USN entry. Note that I am using the same DeviceIOControl method, but instead, I am using EIOControlCode::FSCTL_READ_USN_JOURNAL, which says that I want to read the entries instead of only viewing the journal information.

While ($ReadMore) {      

    $Page++

    $return = [PoshChJournal]::DeviceIoControl(

        $VolumeHandle,

        [EIOControlCode]::FSCTL_READ_USN_JOURNAL,

        $ReadBuffer,

        $ReadDataSize,

        $DataBuffer,

        $DataSize,

        [ref]$AvailableBytes,

        [intptr]::Zero

    )

Assuming that I was successful in my query and there is data available to read, I will start processing the data. Basically, I will take the bytes returned and convert that into an object by using a couple of custom functions:

  • NewUSNEntry (which uses a function called GetFilePath)
  • ConvertToUSNReason.

These functions are used to pull the data into a Struct to make the results humanly readable. Here is the source code for these functions:

Function ConvertToUSNReason {

    [cmdletbinding()]

    Param(

        $ReasonCode

    )

    $List = New-Object System.Collections.ArrayList

    Switch ($ReasonCode) {

        ($ReasonCode -BOR 0x00000001) {[void]$List.Add('USN_REASON_DATA_OVERWRITE')}

        ($ReasonCode -BOR 0x00000002) {[void]$List.Add('USN_REASON_DATA_EXTEND')}

        ($ReasonCode -BOR 0x00000004) {[void]$List.Add('USN_REASON_DATA_TRUNCATION')}

        ($ReasonCode -BOR 0x00000010) {[void]$List.Add('USN_REASON_NAMED_DATA_OVERWRITE')}

        ($ReasonCode -BOR 0x00000020) {[void]$List.Add('USN_REASON_NAMED_DATA_EXTEND')}

        ($ReasonCode -BOR 0x00000040) {[void]$List.Add('USN_REASON_NAMED_DATA_TRUNCATION')}

        ($ReasonCode -BOR 0x00000100) {[void]$List.Add('USN_REASON_FILE_CREATE')}

        ($ReasonCode -BOR 0x00000200) {[void]$List.Add('USN_REASON_FILE_DELETE')}

        ($ReasonCode -BOR 0x00000400) {[void]$List.Add('USN_REASON_EA_CHANGE')}

        ($ReasonCode -BOR 0x00000800) {[void]$List.Add('USN_REASON_SECURITY_CHANGE')}

        ($ReasonCode -BOR 0x00001000) {[void]$List.Add('USN_REASON_RENAME_OLD_NAME')}

        ($ReasonCode -BOR 0x00002000) {[void]$List.Add('USN_REASON_RENAME_NEW_NAME')}

        ($ReasonCode -BOR 0x00004000) {[void]$List.Add('USN_REASON_INDEXABLE_CHANGE')}

        ($ReasonCode -BOR 0x00008000) {[void]$List.Add('USN_REASON_BASIC_INFO_CHANGE')}

        ($ReasonCode -BOR 0x00010000) {[void]$List.Add('USN_REASON_HARD_LINK_CHANGE')}

        ($ReasonCode -BOR 0x00020000) {[void]$List.Add('USN_REASON_COMPRESSION_CHANGE')}

        ($ReasonCode -BOR 0x00040000) {[void]$List.Add('USN_REASON_ENCRYPTION_CHANGE')}

        ($ReasonCode -BOR 0x00080000) {[void]$List.Add('USN_REASON_OBJECT_ID_CHANGE')}

        ($ReasonCode -BOR 0x00100000) {[void]$List.Add('USN_REASON_REPARSE_POINT_CHANGE')}

        ($ReasonCode -BOR 0x00200000) {[void]$List.Add('USN_REASON_STREAM_CHANGE')}

        ($ReasonCode -BOR 0x80000000) {[void]$List.Add('USN_REASON_CLOSE')}

    }

    $List -join ', '

}

Function NewUsnEntry {

    [cmdletbinding()]

    Param (

        [intptr]$UsnRecord,

        [string]$DriveLetter,

        [IntPtr]$VolumeHandle

    )

    #region Constants

    $FILE_ATTRIBUTE_DIRECTORY = 0x00000010

    #endregion Constants

 

    #region Marshal to Struct

    $USN_RECORD = [System.Runtime.InteropServices.Marshal]::PtrToStructure($UsnRecord, [type][USN_RECORD])

    #endregion Marshal to Struct

 

    #region Data Conversions

    $Name = [System.Runtime.InteropServices.Marshal]::PtrToStringUni([intptr](

        $UsnRecord.ToInt64()+$USN_RECORD.FileNameOffset),

        ($USN_RECORD.FileNameLength/2)

    )

    #endregion Data Conversions

 

    #region Object Creation

    If (($USN_RECORD.FileAttributes -BAND $FILE_ATTRIBUTE_DIRECTORY) -eq 0) {

        $IsFile = $True

        $IsFolder = $False

    } Else {

        $IsFile = $False

        $IsFolder = $True   

    }

    [pscustomobject]@{

        Name = $Name

        FullName = GetFilePath -PFileRefNumber $USN_RECORD.ParentFileReferenceNumber -VolumeHandle $VolumeHandle -DriveLetter $DriveLetter -File $Name

        TimeStamp = [DateTime]::FromFileTime($USN_RECORD.TimeStamp)

        Reason = ConvertToUsnReason $USN_RECORD.Reason

        RecordLength = $USN_RECORD.RecordLength

        FileReferenceNumber = $USN_RECORD.FileReferenceNumber

        ParentFileReferenceNumber = $USN_RECORD.ParentFileReferenceNumber

        USN = $USN_RECORD.USN

        FileAttributes = [System.IO.FileAttributes]($USN_RECORD.FileAttributes)

        IsFile = $IsFile

        IsDirectory = $IsFolder

    }

    #endregion Object Creation

}

Function GetFilePath {

    Param (

        [int64]$PFileRefNumber,

        [IntPtr]$VolumeHandle,

        [string]$DriveLetter,

        [string]$File

    )

 

    $OBJ_CASE_INSENSITIVE = 0x40

    $FILE_OPEN = 0x1

    $FILE_OPEN_FOR_BACKUP_INTENT = 0x4000

    $FILE_OPEN_BY_FILE_ID = 0x2000

 

    [long]$AllocationSize = 0

    $UnicodeString = New-Object UNICODE_STRING

    $ObjectAttributes = New-Object OBJECT_ATTRIBUTES

    $IoStatusBlock = New-Object IO_STATUS_BLOCK

    $FileHandle = [intptr]::Zero

 

    $Buffer = [System.Runtime.InteropServices.Marshal]::AllocHGlobal(4096)

    $RefPtr = [System.Runtime.InteropServices.Marshal]::AllocHGlobal(8)

    $ObjectAttributeSize = [System.Runtime.InteropServices.Marshal]::SizeOf($ObjectAttributes)

    $ObjAttIntPtr = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($ObjectAttributeSize)

 

    [void][System.Runtime.InteropServices.Marshal]::WriteInt64($RefPtr, $PFileRefNumber)

    $UnicodeString.Length = 8

    $UnicodeString.MaximumLength = 8

    $UnicodeString.Buffer = $RefPtr

 

    [void][System.Runtime.InteropServices.Marshal]::StructureToPtr($UnicodeString, $ObjAttIntPtr, $True)

 

    $ObjectAttributes.Length = [System.Runtime.InteropServices.Marshal]::SizeOf($ObjectAttributes)

    $ObjectAttributes.ObjectName = $ObjAttIntPtr

    $ObjectAttributes.RootDirectory = $VolumeHandle

    $ObjectAttributes.Attributes = $OBJ_CASE_INSENSITIVE

 

    $Access = [System.IO.FileAccess]::Read

    $ShareAccess = [System.IO.FileShare]::Read -BOR [System.IO.FileShare]::Write

    $FileMode = [System.IO.FileMode]::Open

    $Return = [PoshChJournal]::NtCreateFile(

        [ref]$FileHandle,

        $Access,

        [ref]$ObjectAttributes,

        [ref]$IoStatusBlock,

        [ref]$AllocationSize,

        0,

        $ShareAccess,

        $FILE_OPEN,

        ($FILE_OPEN_FOR_BACKUP_INTENT -BOR $FILE_OPEN_BY_FILE_ID),

        [intptr]::Zero,

        0

    )

    If ($Return -eq 0) {

        $Return = [PoshChJournal]::NTQueryInformationFile(

            $FileHandle,

            [ref]$IoStatusBlock,

            $Buffer,

            4096,

            [FILE_INFORMATION_CLASS]::FileNameInformation

        )

        If ($Return -eq 0) {

            $NameLength = [System.Runtime.InteropServices.Marshal]::ReadInt32($Buffer,0)

            $BufferPtr = New-Object IntPtr -ArgumentList ($Buffer.ToInt64()+4)

            $Path = [System.Runtime.InteropServices.Marshal]::PtrToStringUni($BufferPtr,($NameLength/2))

            "{0}{1}\{2}" -f $DriveLetter,$Path,$File

        }

    }

    [void][PoshChJournal]::CloseHandle($FileHandle)

    [void][System.Runtime.InteropServices.Marshal]::FreeHGlobal($Buffer)

    [void][System.Runtime.InteropServices.Marshal]::FreeHGlobal($ObjectAttributeSize)

    [void][System.Runtime.InteropServices.Marshal]::FreeHGlobal($RefPtr)

}

Now let’s process all of this data!

If ($return) {

    Write-Verbose "Processing USN entries" -Verbose

    $Uint64Size = [System.Runtime.InteropServices.Marshal]::SizeOf([type][uint64])

    $UsnRecord =  New-Object intptr -ArgumentList ($DataBuffer.ToInt64() + $Uint64Size)

    Write-Verbose "Initial Bytes: $($AvailableBytes)" -Verbose

    While ($AvailableBytes -gt 60) {

        $UsnEntry = NewUsnEntry -UsnRecord $UsnRecord -DriveLetter $DriveLetter -VolumeHandle $VolumeHandle

        $UsnEntry.pstypenames.insert(0,'System.ChangeJournal.UsnEntry')

        $UsnEntry

        $UsnRecord =  New-Object IntPtr -ArgumentList ($UsnRecord.ToInt64() + $UsnEntry.RecordLength)

        $AvailableBytes = $AvailableBytes - $UsnEntry.RecordLength

        Write-Debug "Available Bytes: $($AvailableBytes)"

    }          

} Else {

    Write-Warning 'Issue occurred reading Usn entries!'

    Break

}

We can finally see the results of our data collection!

Image of command output

Pretty wild to see some of the stuff that is available to view here, right? Depending on the size of your change journal, it could take a while to cover all of the changes. Additionally, the starting point could be a few days ago. You could adjust the starting USN number based on some other entries, but keep in mind that if the number isn’t exact with this script, it will most likely throw an error.

We aren’t finished with the code yet. I also have the capability to page through the results if needed. I am also checking to see if any more data is available. And lastly, I'll ensure that I clean up after myself to avoid any issues with memory and not closing open connections.

        $NextUSN = [System.Runtime.InteropServices.Marshal]::ReadInt64($DataBuffer,0)

        Write-Debug "Next USN: $($NextUSN) - Journal Next USN: $($JournalData.NextUsn)"

        $NoMoreData = $NextUsn -ge $JournalData.NextUsn

        If ($NoMoreData) {

            Write-Verbose 'Checking for more data'

            While ($NextUsn -ge $JournalData.NextUsn) {

                Start-Sleep -Milliseconds 500

                $JournalData = GetUsnJournal -VolumeHandle $VolumeHandle                   

            }

        }

        While ($Choice -notmatch 'c|q') {

            $Choice = Read-Host "Page: $($Page) - Press C to display next page or Q to Quit"

        }

        If ($Choice -eq 'c') {

            Write-Verbose "Using next Starting USN: $($NextUSN)"

            [System.Runtime.InteropServices.Marshal]::WriteInt64($ReadBuffer, $NextUSN)

            Remove-Variable Choice -ErrorAction SilentlyContinue

        } Else {

            Write-Verbose "Halting operation"

            $ReadMore = $False

            [System.Runtime.InteropServices.Marshal]::FreeHGlobal($ReadBuffer)

            [System.Runtime.InteropServices.Marshal]::FreeHGlobal($DataBuffer) 

            [void][PoshChJournal]::CloseHandle($VolumeHandle)             

        }

        $Choice = $Null

    }

}

I won’t lie. There is a lot of stuff happening here and definitely not a lot of time to cover everything in great detail. Fortunately, the script that I have available to download in the Script Center Repository can show present the data to you. If you want an even easier approach (because working with PInvoke and reflection isn’t for everyone), you are in luck. I have a module available to use, and I will explain that tomorrow!

We invite you to follow the Scripting Guys on Twitter and Facebook. If you have any questions, send email to the Scripting Guy at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. Until then, see ya!

Boe Prox, Windows PowerShell MVP and Honorary Scripting Guy 

PowerTip: Determine if PowerShell is Running x86 or x64

$
0
0

Boe Prox shows how to determine if Windows PowerShell is running x86 or x64.

...(read more)
Viewing all 3333 articles
Browse latest View live


Latest Images