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

Use PowerShell to Create Windows To Go Keys—Part 4

$
0
0

Summary: Use Windows PowerShell to inject drivers and populate the data for Unattend.xml.

Hey, Scripting Guy! Question Hey, Scripting Guy! I have a Windows To Go key. Is there a script to automatically give the key a proper workstation name or possibly even add some additional drivers?

—TG

Hey, Scripting Guy! Answer Hello TG,

Honorary Scripting Guy, Sean Kearney, is here, and the magical answer to your question, which is, "YES!" 

   Note  This is the fourth post in a five-part series. Before you read this, you should read:

Windows To Go isn’t really all that special. The ability to add drivers though DISM, automatically name a workstation, or join a workstation to a domain through Unattend.xml is still there.

The question is, "What drivers need to be injected into a Windows To Go key?"

First consider what are your target hardware platforms? If you have a defined set of machines, you could prepopulate the set after downloading them from the vendor.

Your other option is to consider that there is really only a small set of network card vendors and video drivers. If you were to prepopulate at least the main drivers from Intel, NVIDIA, and ATI for Video, and then possibly a few key network card drivers, the Windows To Go environment should be “mostly there” for a number of platforms.

To add drivers to the Windows To Go key, I use the Add-WindowsDriver cmdlet. It’s very much like using DISM. I need to provide the folder that contains the drivers I am adding and the drive letter of the operating system on the Windows To Go key.

For this example, I am continuing with the presumption that you are using the variables from my previous post and that the drivers are contained within the same folder as our PowerShell script in a folder called Drivers.

$Drivers=’.\Drivers’

Add-WindowsDriver –Path “$DriveOS`:” –driver $Drivers –recurse

These lines will inject all drivers found within the Drivers folder into the Windows To Go key. If you remove the –recurse parameter, it will only target the root of the $Drivers variable.

To populate the settings needed for the workstation, I need to create an Unattend.xml file and copy it directly into the Sysprep folder within the Windows To Go key. At bootup, the Windows operating system will process this file and its commands for configuration.

This will be similar to working with the SAN-Policy.xml file with the exception that the data will be forever changing. Some values, such as the workstation name, will need to be populated.

I can also use a here string for this case because it allows me to dynamically create the file.

A simple Unattend.xml file to assign the time zone, workstation name, and various details in Windows could look the following example. It will create a workstation named WTG- followed by a random number sequence, and then set the time zone as Eastern Standard Time.

$Computername=”WTG-$(Get-Random)”

$Organization=’Contoso Inc.’

$Owner=’Contoso Inc. IT Dept.’

$Timezone=’Eastern Standard Time’

$AdminPassword=’P@ssw0rd’

$Unattend=@"

<?xml version="1.0" encoding="utf-8"?>

<unattend xmlns="urn:schemas-microsoft-com:unattend">

 <settings pass="specialize">

  <component name="Microsoft-Windows-Shell-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

   <ComputerName>$Computername</ComputerName>

   <RegisteredOrganization>$Organization</RegisteredOrganization>

   <RegisteredOwner>$Owner</RegisteredOwner>

   <TimeZone>$Timezone</TimeZone>

  </component>

 </settings>

 <settings pass="oobeSystem">

  <component name="Microsoft-Windows-Shell-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

   <UserAccounts>

    <AdministratorPassword>

     <Value>$Adminpassword</Value>

     <PlainText>true</PlainText>

    </AdministratorPassword>

   </UserAccounts>

              <AutoLogon>

               <Password>

                <Value>$Adminpassword</Value>

                <PlainText>true</PlainText>

               </Password>

              <Username>administrator</Username>

              <LogonCount>1</Log\onCount>

              <Enabled>true</Enabled>

              </AutoLogon>

   <RegisteredOrganization>$Organization</RegisteredOrganization>

   <RegisteredOwner>$Owner</RegisteredOwner>

   <OOBE>

    <HideEULAPage>true</HideEULAPage>

    <SkipMachineOOBE>true</SkipMachineOOBE>

   </OOBE>

  </component>

 </settings>

 <cpi:offlineImage cpi:source="" xmlns:cpi="urn:schemas-microsoft-com:cpi" />

</unattend>

"@

I can use the same process as I did with the SAN-Policy.xml file to re-create this file dynamically, but at the end, I will copy the file directly to the Sysprep folder on the Windows To Go key.

$UnattendFile=’.\unattend.xml’

Remove-item $UnattendFile -erroraction SilentlyContinue

Add-content -path $Unattendfile -Value $Unattend

Copy-Item -path $Unattendfile -destination "$DriveOS`:\Windows\System32\Sysprep"

With this is place, I can customize the individual Windows To Go keys with the data needed for the operating system. Of course, this is only an example for the Unattend.xml file, and there are better ways to obfuscate the Administrator password or to have the computer join a domain afterwards (whether online or offline).

What more can we do? Here’s a fun thought. Maybe we can wrap this whole script together as a workflow to image multiple keys at once!

     ...and that is a task I shall explore tomorrow.

I invite you to follow The Scripting Guys on Twitter and Facebook. If you have any questions, send an email to The Scripting Guys at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, remember to eat your cmdlets every day with a dash of creativity.

Sean Kearney, Windows PowerShell MVP and Honorary Scripting Guy


PowerTip: Back up Drivers with PowerShell

$
0
0

Summary: Use the Export-WindowsDriver cmdlet to back up all of your Windows drivers elsewhere.

Hey, Scripting Guy! Question How can I use Windows PowerShell in Windows 10 to get a copy of the current drivers in my Windows
           operating system when my computer is a clone system?

Hey, Scripting Guy! Answer Use the Export-WindowsDriver cmdlet to back up all of your drivers to a folder or a USB device, for example:

Export-WindowsDriver -Online -Destination I:\

Use PowerShell to Create Windows To Go Keys—Part 5

$
0
0

Summary: Use a basic Windows PowerShell workflow to create multiple devices.

Hey, Scripting Guy! Question Hey, Scripting Guy! I was wondering if there is an efficient way to use Windows PowerShell to create multiple Windows To Go keys at the same time.

—TM

Hey, Scripting Guy! Answer Hello TM,

Honorary Scripting Guy, Sean Kearney, is here today. I am going to wind up my week of posts by playing with that very idea.

   Note  This is the final post in a five-part series. Before you read this, you should read:

Today I'm going to implement a basic Windows PowerShell workflow to image multiple Windows To Go keys from the same image. The process is actually very close to creating one key, but I need to trap for a few situations:

  • Obtain a complete list of all potential Windows To Go keys
  • Create code for multiple and independent Unattend.xml files
  • Designate a unique pair of drive letters for the operating system and system drive for each workflow

First off, I'll give that workflow a name. I’ll name this one simply CreateWTG:

workflow CreateWTG

{

Now I get all available Windows To Go devices attached to the computer:

$WTG= Get-Disk | Where-Object { ('Imation IronKey Wkspace','Kingston DT Ultimate') -match $_.Friendlyname }

I'll begin to process all of the keys attached by using Foreach –parallel. This keyword operates in a similar manner to the standard Foreach-Object, but it launches the processes in parallel.

Foreach -parallel ($WTGDisk in $WTG)

 {

I'm going to add Start-Sleep with a random sequence to try to ensure that the individually spawned processes don’t try to grab the same drive Letter. I’ll pick a three-minute random window:

Start-Sleep (Get-Random 180)

My next task is to go through the list of keys to use to identify all available drive letters. I will do this each time to try to ensure that I do not clash with drive letters that are in use by any other process. For this, I am going to use two separate tricks in PowerShell.

I first use the CIM class, cim_LogicalDisk, which will provide all letters assigned to physical devices and active network drive letters. I can target the DeviceID property, which contains the drive letter:

[string[]]$InUse=$NULL

$InUse+=Get-CimInstance cim_logicaldisk | select-object -ExpandProperty DeviceID

Now I have a second issue: drive letters that are assigned to a network drive but are offline. I have not been able to find a CIM class that has this information. But fortunately, it’s all stored in the Registry under HKEY_CURRENT_USER\Network. I can run Get-ChildItem against this key to get the list. This will show all network drive letters whether or not they are offline.

$InUse+=Get-ChildItem Registry::HKey_Current_User\Network –Name

I now have all the drive letters that are in use by Windows. However, if you examine the list, you will see that the results are “Dirty.” The data from CIM_LogicalDisk and the data from Get-ChildItem don’t align. Some have colons, and some are lowercase.

I only need to use this list for a simple match comparison. So I am going to build a list of drive letters from A – Z, then put together an array that contains only those that are not in use.

First I define the array and start the loop:

[string[]]$Available=$NULL

# Step through all letters from A to Z

# Yes weI could have just said 65 to 90 but I thought

# you might find it neat to see how to get the ASCII number

# for a Character

#

for ($x = ([byte][char]'A'); $x -le ([byte][char]'Z'); $x++)

{

Here I have a simple comparison. If the character in question does not match anything in the array of drive letters that are in use, I will add it to the array.

   Note  For those of you (like myself) who are IT pros, the explanation point character ( ! ) indicates a Boolean NOT.

 If (![boolean]($InUse -match [char][byte]$x)) { $Available+=[char][byte]$x }

}

I could have easily written it like as follows to perform the same thing. (Yes, sometimes Boolean can make your head spin if you’re not a developer.)

 If ([boolean]($InUse -match [char][byte]$x) –eq $False) { $Available+=[char][byte]$x }

Now that I have an available list, I'll grab two drive letters. I’ll use a little Get-Random to avoid having things clash.

$Position=[int](Get-Random $Available.count)

 $DriveSystem=$Available[$Position]

 $DriveOS=$Available[$Position+1]

I can now clear the disk, and partition and format the key in question. Note how I have updated the variable from $WTG to $WTGDisk. (Remember that I am now in a Foreach process.)

$DiskNumber=$WTGDisk.DiskNumber

Clear-Disk –Number $DiskNumber –RemoveData –RemoveOEM –Confirm:$False

Get-Disk –number $DiskNumber | Get-Partition | Remove-Partition –confirm:$False

Initialize-Disk –Number $DiskNumber –PartitionStyle MBR

$System=New-Partition -DiskNumber $DiskNumber -size (350MB) –IsActive

$OS= New-Partition –DiskNumber $DiskNumber –UseMaximumSize

Format-Volume -NewFileSystemLabel "System" -FileSystem FAT32 -Partition $System -confirm:$False

Format-Volume -NewFileSystemLabel "Windows" -FileSystem NTFS -Partition $OS -confirm:$False

Set-Partition -InputObject $System -NewDriveLetter $DriveSystem

Set-Partition -InputObject $OS -NewDriveLetter $DriveOS

Set-Partition -InputObject $OS –NoDefaultDriveLetter

After the disk is partitioned, I apply the image. However, I need to make sure the log file has a unique name because by default, it’s simply called DISM.LOG. So I’ll add the disk number as part of its temporary log name.

$Wimfile=’.\install.wim’

Expand-WindowsImage –imagepath "$wimfile" –index 1 –ApplyPath "$DriveOS`:\" -LogPath ".\Dism$($DiskNumber).log"

Now here is where I hit a snag. The BCDBoot command I need to execute is not a recognized command in the PowerShell workflow engine. But I can alleviate this issue by wrapping it as an inline script, which launches a separate PowerShell process for it to execute out of the workflow.

Because this is a new PowerShell process I need tell it what variables it should use from the existing workflow and then assign it a name. This can be a little confusing for the IT pro at first because the new name can be exactly the same as the old name.

inlinescript

  {

  $OSDrive=Using:OSDrive

  $SystemDrive=UsingSystemDrive 

  & "$($env:windir)\system32\bcdboot" "$OSDrive`:\Windows" /f ALL /s "$Systemdrive`:"

  }

I prepare the SAN-Policy.xml as in my previous post, but with one minor change. I will make the file name unique for this process so that I don’t have multiple PowerShell processes accessing the same file with the same cmdlet and getting some type of File in Use error message. I will use $OSDrive as the unique characteristic to modify the file name.

$Policy=@"

<?xml version='1.0' encoding='utf-8' standalone='yes'?>

<unattend xmlns="urn:schemas-microsoft-com:unattend">

 <settings pass="offlineServicing">

 <component

  xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State"

  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

  language="neutral"

  name="Microsoft-Windows-PartitionManager"

  processorArchitecture="x86"

  publicKeyToken="31bf3856ad364e35"

  versionScope="nonSxS"

  >

  <SanPolicy>4</SanPolicy>

 </component>

 <component

  xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State"

  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

  language="neutral"

  name="Microsoft-Windows-PartitionManager"

  processorArchitecture="amd64"

  publicKeyToken="31bf3856ad364e35"

  versionScope="nonSxS"

  >

  <SanPolicy>4</SanPolicy>

 </component>

 </settings>

</unattend>

"@

$SanPolicyFile=".\$OSDrive-san-policy.xml"

Remove-item $SanPolicyFile -erroraction SilentlyContinue

Add-content -path $SanPolicyFile -Value $Policy

Use-WindowsUnattend –unattendpath $SanPolicyFile –path "$OSdrive`:\"

I now inject the drivers as previously from our source folder:

$Drivers=’.\Drivers’

Add-WindowsDriver –Path “$DriveOS`:” –driver $Drivers –recurse

For the final touch, I will add the Unattend.xml files. I use the same technique with the SAN-Policy.xml file to make the source file unique. What I must do, however, is ensure the file name is still called unattend.xml when it transfers to the destination Windows To Go key.

$Computername=”WTG-$(Get-Random)”

$Organization=’Contoso Inc.’

$Owner=’Contoso Inc. IT Dept.’

$Timezone=’Eastern Standard Time’

$AdminPassword=’P@ssw0rd’

$Unattend=@"

<?xml version="1.0" encoding="utf-8"?>

<unattend xmlns="urn:schemas-microsoft-com:unattend">

 <settings pass="specialize">

  <component name="Microsoft-Windows-Shell-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

   <ComputerName>$Computername</ComputerName>

   <RegisteredOrganization>$Organization</RegisteredOrganization>

   <RegisteredOwner>$Owner</RegisteredOwner>

   <TimeZone>$Timezone</TimeZone>

  </component>

 </settings>

 <settings pass="oobeSystem">

  <component name="Microsoft-Windows-Shell-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

   <UserAccounts>

    <AdministratorPassword>

     <Value>$Adminpassword</Value>

     <PlainText>true</PlainText>

    </AdministratorPassword>

   </UserAccounts>

              <AutoLogon>

               <Password>

                <Value>$Adminpassword</Value>

                <PlainText>true</PlainText>

               </Password>

              <Username>administrator</Username>

              <LogonCount>1</Log\onCount>

              <Enabled>true</Enabled>

              </AutoLogon>

   <RegisteredOrganization>$Organization</RegisteredOrganization>

   <RegisteredOwner>$Owner</RegisteredOwner>

   <OOBE>

    <HideEULAPage>true</HideEULAPage>

    <SkipMachineOOBE>true</SkipMachineOOBE>

   </OOBE>

  </component>

 </settings>

 <cpi:offlineImage cpi:source="" xmlns:cpi="urn:schemas-microsoft-com:cpi" />

</unattend>

"@

$UnattendFile=”.\$OSDrive-unattend.xml”

Remove-item $UnattendFile -erroraction SilentlyContinue

Add-content -path $Unattendfile -Value $Unattend

Copy-Item -path $Unattendfile -destination "$DriveOS`:\Windows\System32\Sysprep\unattend.xml"

Finally, I need to remove the drive letters from the partitions to place them back into the available pool:

 Get-Volume -DriveLetter $OSDrive | Get-Partition | Remove-PartitionAccessPath -accesspath "$OSDrive`:\"

 Get-Volume -DriveLetter $SystemDrive | Get-Partition | Remove-PartitionAccessPath -accesspath "$SystemDrive`:\"

At this point, we should have a complete workflow for creating Windows To Go keys. There are, of course, many ways to improve on this example—such as adding some logging, bringing in online or offline domain joining, or adding some error trapping.

My hope is that you can use this as a small example for how you could leverage a workflow to make your job easier in the Windows To Go world.

If you would like a copy of this workflow, you can download it from the Script Center Repository. Play with it directly and see what you can do to improve on its design: Sample Workflow to Deploy Multiple Windows To Go Keys.

I invite you to follow The Scripting Guys on Twitter and Facebook. If you have any questions, send an email to The Scripting Guys at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, remember eat your cmdlets every day with a dash of creativity.

Sean Kearney, Windows PowerShell MVP and Honorary Scripting Guy

PowerTip: Use Non-Approved Cmdlet or App within PowerShell Workflow

$
0
0

Summary: Learn how to use a basic inline script in Windows PowerShell workflows.

Hey, Scripting Guy! Question I’m trying to convert a Windows PowerShell script to a workflow, but I keep running into an error that suggests
           the cmdlet cannot be used in a workflow. How can I get around this?

Hey, Scripting Guy! Answer Wrap that section of the workflow as an inline script, and recapture your variables with the Using keyword,
           for example:

Inlinescript

            {

            $Somevariable=Using:SomeVariable

            Start-ProblematicFunction $Somevariable

            }

 

Weekend Scripter: Search and Filter Directory Lists with PowerShell

$
0
0

Summary: Ed Wilson, Microsoft Scripting Guy, talks about using Windows PowerShell to sort, search, and to filter through a directory list.

Microsoft Scripting Guy, Ed Wilson, is here. One of my friends on Facebook posted a picture of a bunch of hardwood trees all decked out in fall colors. It was rather pretty. That is the down side to living in central Florida—I mean, I have never seen a palm tree in fall colors. Of course, it is 82 degrees Fahrenheit (28 degrees Celsius), so I guess we don’t really have fall. But the humidity is dropping (down to 55% instead of the usual 75%), so it feels cool outside.

Anyway, I am sitting outside checking email sent to scripter@microsoft.com on my Surface Pro 3 under a huge ZZ Top oak tree (the trees have long gray beards). I am sipping a cup of Earl Grey tea and munching on a piece of homemade carrot cake.

I like to spend a bit of time exploring the disk usage on my Surface Pro 3. It is not infinite, and even though I bought the largest internal storage and I have an additional little chip for storage, it still fills fast—especially with my Kindle reader and all the whitepapers I like to store. Windows PowerShell scripts take up nothing, but a big fluffy PDF file can easily eat up 25 or 30 Mbytes. With a hundred or so, there went 10% of my storage. Not to mention the ease of downloading apps. So I like to keep an eye on things.

I don’t know what happened to File Manager on my Surface Pro 3, but I really don’t need it because I have Windows PowerShell. I open the Windows PowerShell console, and I use Get-ChildItem to obtain a directory list. I then pipe the output to Out-GridView. Because I am sitting under a ZZ Top tree, balancing my Surface on my knee, and using a virtual keyboard, l am loving the short terse cmdlet aliases. Here is the command:

dir c:\fso | ogv

I can now look at the output in the grid. The command I typed appears in the title of the output grid:

 Image of command output 

When did all of these files appear? I want to see the files that were last written to since September 1 of this year. I can use my finger to press the Add Criteria button and to check the box beside the LastWriteTime property.

But my fingers seem a bit too wide to make that check very easily, so I use my stylus instead. It is very precise and is designed for making nice little checks in check boxes. So I add that, and then use the handwriting input box to add my date criteria. As you can see here, there is only one file that has been written to in the last month:

Image of command output

Sweet. So now I know that I am not really using the stuff in this folder, and I can easily move it to the cloud and free up some storage on my local device. Luckily, One Drive is quite nicely integrated into Windows 10.

WooHoo! And now I'm off to enjoy our lovely cool central Florida fall.

See you tomorrow...assuming the gators don’t get me.

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 Sortable GUI List of Files with PowerShell

$
0
0

Summary: Use Windows PowerShell to display a filterable GUI list of files.

Hey, Scripting Guy! Question How can I use Windows PowerShell to filter a list of files using a graphical user interface?

Hey, Scripting Guy! Answer Use the Get-ChildItem cmdlet to obtain a directory list, select all of the properties and pipe the output
           to the Out-GridView cmdlet, for example:

dir c:\fso | select * | ogv

Note  Dir is an alias for Get-ChildItem and ogv is an alias for Out-GridView.

Weekend Scripter: That’s Not Very PowerShell-Like

$
0
0

Summary: Ed Wilson, Microsoft Scripting Guy, talks about what is Windows PowerShell-like scripting.

Microsoft Scripting Guy, Ed Wilson, is here. Before Internet streaming, before Nabster, before CD-ROMs, before cassette tapes, and before 8-track tapes, there were things called records. Now it seems that all technology related to music (unless you are talking about live acoustic performances) are subject to some kind of something that will cause them to fail.

With records, it was a scratches. Not the invisible sorts of scratches that cause DVDs to fail—no, I mean serious scratches that came from someone bumping the record player while the record was playing, which created a new groove in the record. When that happened, the same portion of the record would play over and over and over in an infinite loop that required manual intervention.

It is the same thing with Windows PowerShell. Specifically, conversations about Windows PowerShell and scripting. I hear it over and over and over—the criticism “that is not very PowerShell-like."

What is PowerShell-like?

The conversation goes something like this...

You can do the same thing in Windows PowerShell more than a dozen different ways, and that is bad for noobies because they learn bad habits. And although their code might work now, later when they need to do xyz, it will not be very effective, and they will have to learn a different technique.

And your point is?

The thing is that the reason Windows PowerShell is so flexible, is so that you can work the way that you want to work. You can quickly come up to speed writing Windows PowerShell code in the manner that you like.

For example, I have seen Windows PowerShell scripts that were basically .NET C# code, but it was written using Windows PowerShell syntax—so instead of calling Get-Date, the writer used [System.DateTime]::Now.

Is there anything wrong with that? Not really. If the Windows PowerShell script runs without errors, guess what? It is a Windows PowerShell script. You may not be taking advantage of all the neat things that Windows PowerShell has to offer, but it is a Windows PowerShell script.

I have also seen Windows PowerShell scripts that were basically VBScript script, but it was written in Windows PowerShell. And once again, the script worked. It ran without errors—but yeah, they could have leveraged the Windows PowerShell advantage.

I have also seen Windows PowerShell scripts that were basically C++ code, and they called the Win32 APIs. Sometimes they were doing things that could not be done any other way. Other times, I think they were just experimenting—or maybe that is the way they like to write code. Who knows…

There are people who seem to insist on using WMI to access the registry, instead of using the Windows PowerShell registry provider, and others who insist on using .NET registry classes to read the registry. I have even seen some people use the VBScript RegRead and RegWrite to do the same. You know what? If it works for you, that is cool with me.

Can’t learn everything at once

We learn by doing. So if you come to a complete halt and start over, it will take you several weeks before you can begin to do anything in Windows PowerShell. Or, if you take a class, you will be out for at least a week. Some places run so lean, that they can’t afford that level of disruption. So Windows PowerShell makes it easy to transition.

You learn a few minor syntax things, learn a few concepts, and immediately you are becoming productive. You will, hopefully, begin to learn new things each week, and add to your level of Windows PowerShell knowledge.

Now, what about the standard argument that a noobie will run into problems trying to do new things in the old way? Well, that is what I call a teachable moment. Maybe before learning “the Windows PowerShell way of doing things,” the noobie thought it was simply syntax changes. But when the noobie finds out there is a reason or a real advantage to learning a more PowerShell-centric method of doing things, he will move forward.

So many PowerShell advantages

Windows PowerShell offers many powerful features, and it continues to add to these features with every new release of the operating system and every iteration of Windows PowerShell. It is certainly possible to get away without these new features, but they do offer substantial advantages, and they can shorten your learning curve and increase your productivity. DSC is a major new example, but so are workflows, CIM, WINRM, and so on.

Writing as if Windows PowerShell is VBScript, C#, or C++misses a whole raft of new features that can make easier-to-read code and easier-to-write code.

So fire up the Windows PowerShell ISE and start writing. Whatever your style, whatever your technique, it is your code in the end, after all. You can learn all of the nuances of Windows PowerShell later.

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 to Find System Uptime

$
0
0

Summary: Use Windows PowerShell to find your system uptime.

Hey, Scripting Guy! Question How can I use Windows PowerShell to easily find how long my computer has been running?

Hey, Scripting Guy! Answer Use the Get-Date cmdlet to return the current date and time, and then subtract the value
           of the LastBootUpTime property that comes from the Win32_OperatingSystem, for example:

(get-date) - (gcim Win32_OperatingSystem).LastBootUpTime

Note  GCIM is an alias for Get-CimInstance.


Troubleshoot WinRM with PowerShell—Part 1

$
0
0

Summary: Ed Wilson, Microsoft Scripting Guy, talks about using Windows PowerShell to look at WinRM logs.

Hey, Scripting Guy! Question Hey, Scripting Guy! I am having problems with WinRM. When I use the Get-CimInstance cmdlet, it fails. When I specify the –DCOM protocol for Get-CimInstance, it works. I suspect I have a WinRM problem. How can I go about troubleshooting it?

—TB

Hey, Scripting Guy! Answer Hello TB,

Microsoft Scripting Guy, Ed Wilson, is here. One of the great things about Windows, regardless of the version, is that there are some awesome diagnostics and operational logs. I am not talking about the traditional event logs that have been around since the earliest versions of WinNT (System, Application, and Security logs)—rather, I am talking about the hundreds of other logs that exist.

Of course, I can open the Event Viewer and go to Microsoft  > Windows > Windows Remote Management, then find the Operational log:

Image of menu

But that is a lot of mousing around. So I can use the Get-WinEvent cmdlet, the –ListLog parameter, and a wildcard character to see all of the logs that exist:

Get-WinEvent -ListLog *

When I get through that list, I can look for the log. There is a major issue here, however. First of all, Microsoft-Windows-Windows kind of looks like an error, but, it is really there. Then the Remote Management/Operational part does not appear to exist. The good thing is that I can use wildcard characters and avoid a lot of typing. Here is the command I use:

Get-WinEvent -ListLog *windows-windows*

The command and its associated output are shown here:

Image of command output

Nope. There is no Windows-Remote-Management anywhere to be found. Dude!

So, I am reduced to piping my log output to More, and paging through, one page at a time. Can you say time consuming, frustrating, and total waste of time? Try it. I bet you can.

Get-WinEvent -ListLog * | more

Finally, I find that (for whatever reason), they decided to rename the log name from the display name. I bet that was done to save time.

PS C:\> Get-WinEvent -ListLog *winrm*

LogMode   MaximumSizeInBytes RecordCount LogName

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

Circular             1052672          16 Microsoft-Windows-WinRM/Operational

Clear the operational log

Windows PowerShell has a Clear-EventLog cmdlet, but that only works with traditional logs. To work with the hundreds of other event logs, I need to use the Wevtutil.exe program. Luckily, I can call this command-line tool inside Windows PowerShell, and even pipe stuff to it.

The first thing I like to do when I am troubleshooting is dump the log, and then take steps to re-create the problem. Here is the command to dump the WinRM log:

Get-WinEvent -ListLog *winrm* | % {wevtutil.exe cl $_.LogName}

Now I go back and check to ensure that the log is in fact dumped. These commands and the associated output are shown here:

Image of command output

I want to enable the WinRM log. To do this, again I use the Wevtutil.exe command. But because the command prompts, I need to feed it a Y (for yes). I can feed a Y to the command by using the echo command. The command to enable the log is shown here:

Get-WinEvent -ListLog *winrm* | % {echo y | wevtutil.exe sl $_.LogName /e:true}

To double-check that the log is enabled, I pipe the output from Get-WinEvent to Format-List:

PS C:\> Get-WinEvent -ListLog *winrm*  | fl *

FileSize                       : 69632

IsLogFull                      : False

LastAccessTime                 : 8/6/2015 4:23:05 PM

LastWriteTime                  : 9/30/2015 4:46:32 PM

OldestRecordNumber             : 0

RecordCount                    : 0

LogName                        : Microsoft-Windows-WinRM/Operational

LogType                        : Operational

LogIsolation                   : Application

IsEnabled                      : True

IsClassicLog                   : False

SecurityDescriptor             : O:BAG:SYD:(A;;0x2;;;S-1-15-2-1)(A;;0xf0007;;;SY)(A;

                                 ;0x7;;;BA)(A;;0x7;;;SO)(A;;0x3;;;IU)(A;;0x3;;;SU)(A

                                 ;;0x3;;;S-1-5-3)(A;;0x3;;;S-1-5-33)(A;;0x1;;;S-1-5-

                                 32-573)

LogFilePath                    : %SystemRoot%\System32\Winevt\Logs\Microsoft-Windows

                                 -WinRM%4Operational.evtx

MaximumSizeInBytes             : 1052672

LogMode                        : Circular

OwningProviderName             : Microsoft-Windows-WinRM

ProviderNames                  : {Microsoft-Windows-WinRM}

ProviderLevel                  :

ProviderKeywords               :

ProviderBufferSize             : 64

ProviderMinimumNumberOfBuffers : 0

ProviderMaximumNumberOfBuffers : 64

ProviderLatency                : 1000

ProviderControlGuid            :

To disable the log, I set it to false. This command appears here:

Get-WinEvent -ListLog *winrm* | % {echo y | wevtutil.exe sl $_.LogName /e:false}

And, I can check the output once again with this command:

PS C:\> (Get-WinEvent -ListLog *winrm*).isenabled

False

I obviously don’t want to do all this typing, so I can put these commands in a script easily, or I can create a function and add them to a module. The cool thing is that I can also add other logs. I can even run the commands remotely by using Windows PowerShell remoting (assuming that you get it working).

TB, that is all there is to using Windows PowerShell to help troubleshoot your WinRM connection.  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: Produce List of Disabled Windows Features

$
0
0

Summary: Use Windows PowerShell to produce a list of disabled Windows features.

Hey, Scripting Guy! Question How can I use Windows PowerShell to see what Windows features are disabled in my installation?

Hey, Scripting Guy! AnswerOpen an elevated Windows PowerShell console, use the Get-WindowsOptionalFeatures cmdlet, and specify
           that you want to run the command online. You may also want to filter the list by disabled state and sort the list,
           for example:

Get-WindowsOptionalFeature -Online | ? state -eq 'disabled' | select featurename | sort -Descending

Note  The ? is an alias for the Where-Object cmdlet.

Troubleshooting WinRM with PowerShell—Part 2

$
0
0

Summary: Ed Wilson, Microsoft Scripting Guy, talks about troubleshooting WinRM.

Hey, Scripting Guy! Question Hey, Scripting Guy! So I thought that Windows PowerShell remoting was supposed to just work. Well, it doesn't. I am confused, and don’t even know where to begin. I am pretty sure that it should be enabled, but it is not working. Can you help?

—EG

Hey, Scripting Guy! Answer Hello EG,

Microsoft Scripting Guy, Ed Wilson, is here. This morning it is rather foggy outside. To be honest, I am not really certain if it is fog or low lying clouds. The weather stations are talking about a hurricane, but we are in central Florida, so we will not see a hurricane. Oh, they say we may get rain. Dude, we get thunder storms every afternoon around here in the summer, so why should today be any different? Basically, no one around here is too worried about a hurricane. They are more worried about getting tickets to the game on Saturday. At least they have their priorities straight.

I also have my priorities straight. I am outside enjoying the cool, if somewhat humid, morning, and sipping a nice cup of English Breakfast tea with a cinnamon stick, lemon grass, and a half teaspoon of local honey. It is the perfect combination of sweet, mellow, and tangy that goes well with Windows PowerShell.

So EG, you are having problems with remoting.

Note This is the second post in a series. You might enjoy also reading Troubleshooting WinRM with PowerShell—Part 1.

I am assuming that you are working with the client and not on the server side. In Windows Server (I believe beginning with Windows Server 2012), we enabled WinRM by default, and so there should not need to be any additional configuration.  On the client, it is still necessary to enable Windows PowerShell remoting.

A few things to check

When troubleshooting WinRM, there are basically three things that should be tested first. These steps are:

  1. Does WinRM respond?
  2. Is the WinRM service running?
  3. Is the WinRM service listening on the appropriate TCP ports?

The easy way to see if WinRM is responding is to use the WinRM command and specify id:

Winrm id

If you receive back error messages, the WinRM service is not responding. You should receive information about the protocol version, the schema, and the profile. Here is the command with the proper response:

Image of command output

And here is an example of the command with error messages. Note that in each case (the message and the error number), it says to run WinRM quickconfig.

Image of command output

The next thing to do is to see if the service is running. To do this, you can use either SC.exe or Get-Service.

Note  If you use Windows PowerShell as your default command prompt, keep in mind that SC is an alias for
          Set-Content. Therefore, if you want to use the SC.exe program, you need to specify SC.exe.

The following image shows the use of SC.exe. Note that SC.exe displays exit codes in addition to the service state. If you only want to see if the service is running, use the Get-Service cmdlet.

Image of command output

When the WinRM service is running, the output from SC.exe appears as shown here:

Image of command output

The last thing to check is whether WinRM is listening. To do this, I am looking for two ports: 5985 and 4701. I can use the following NetStat command:

Netstat –anop TCP

Note that in the following output, those ports are absent.

Image of command output

But in a properly configured system, they appear as shown here:

Image of command output

It is obvious that I need to configure my system. To do this, I use the Enable-PSRemoting cmdlet. This makes several changes, each of which are detailed in the output from the command. The first time I run the command I receive the following error message:

Image of command output

The error message says that the network connection is set to Public. I need to change the connection to either Domain or Private. Luckily, I can make this change with Windows PowerShell. I can also tell Enable-PSRemoting to skip the network check. This command is shown here:

Image of command output

By default, there are like four prompts—one for each change it will make. If I have read all of that and I don’t want to be prompted, I use the –Force parameter. It will simply make the changes.

Now it seems that WinRM is working. But if it were not, the next thing I would do is reboot. Sometimes it seems to want a reboot for everything to work (like the firewall changes and the listener ports). I could probably get away with disabling and enabling the network adapter, but on some systems, I don’t have to do that. I guess it is partly driver and hardware dependent.

EG, now you know more about troubleshooting WinRM. Join me tomorrow when I will talk about more cool Windows PowerShell 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: Enable PowerShell Remoting

$
0
0

Summary: Learn how to enable Windows PowerShell remoting.

Hey, Scripting Guy! Question How can I turn on Windows PowerShell remoting on my workstation so it will run remote commands?

Hey, Scripting Guy! Answer Use the Enable-PSRemoting cmdlet.

Dude, a String Is a String in PowerShell

$
0
0

Summary: Ed Wilson, Microsoft Scripting Guy, talks about using string methods to determine null or empty in Windows PowerShell.

Hey, Scripting Guy! Question Hey, Scripting Guy! I have a problem with a script. It is used to write data to another application, but sometimes the variables in it are empty. I have tried to detect this by checking to see if the variable is equal to $null, but this does not work very well. I need a different way to do this. Can you help?

—JB

Hey, Scripting Guy! Answer Hello JB,

Microsoft Scripting Guy, Ed Wilson, is here. When I was looking out the window this morning, I saw a squirrel drop from a tree, look up, and then bunny hop across the yard until he jumped onto another tree and disappeared. I know it was a squirrel because I saw the big bushy tail. But if the Scripting Wife had told me she saw a small furry animal hopping across the yard, I would have assumed it was a rabbit.

Put another way, I can check the weather app on my Windows 10 laptop to see if it is hot or cold outside, or I can put on my shoes and walk out to see for myself. Sometimes, it is best to check on things, rather than to base opinions on preconceived notions—such as animals that hop are bunnies, or that the weather app really knows whether I will feel hot or cold outside.

I can use regular expressions, but dude…

When it comes to determining if a variable is null, empty, or something else, I often think about using regular expressions. In reality, I do not need to do that—especially not in this particular case. This is because System.String has a couple of static methods that are perfect for the occasion.

A static method is one that is always available, and it does not rely on a specific instance of the class before it comes into existence. For example, if I create a string, I have an instance of the String class. But if I don’t create a string, I can still access static string methods and properties directly from the class.

To see what static methods and properties are available, I can pipe [string] to Get-Member, for example:

PS C:\> [string] | Get-Member -Static

   TypeName: System.String

Name               MemberType Definition

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

Compare            Method     static int Compare(string strA, string strB), stati...

CompareOrdinal     Method     static int CompareOrdinal(string strA, string strB)...

Concat             Method     static string Concat(System.Object arg0), static st...

Copy               Method     static string Copy(string str)

Equals             Method     static bool Equals(string a, string b), static bool...

Format             Method     static string Format(string format, System.Object a...

Intern             Method     static string Intern(string str)

IsInterned         Method     static string IsInterned(string str)

IsNullOrEmpty      Method     static bool IsNullOrEmpty(string value)

IsNullOrWhiteSpace Method     static bool IsNullOrWhiteSpace(string value)

Join               Method     static string Join(string separator, Params string[...

new                Method     string new(System.Char*, mscorlib, Version=4.0.0.0,...

ReferenceEquals    Method     static bool ReferenceEquals(System.Object objA, Sys...

Empty              Property   static string Empty {get;}

PS C:\>

One thing that is cool is that there is an IsNullOrEmpty static method. Here is an example of using that static property:

PS C:\> $a = "string a"

PS C:\> [string]::IsNullOrEmpty($a)

False

As I would expect, the string contained in the $a variable is not null, nor is it empty. If I use a variable that is not yet created, I obtain a different result:

PS C:\> [string]::IsNullOrEmpty($z)

True

What if I initialize the $b variable, but I do so with an empty string?

PS C:\> $b = " "

PS C:\> [string]::IsNullOrEmpty($b)

False

This time, my empty string comes back and says that it is not null, nor is it empty. It contains a blank space. What if I remove that space?

PS C:\> $b = ""

PS C:\> [string]::IsNullOrEmpty($b)

True

It seems that removing the space caused it to be null or empty.

Well, what about white space like the one I had earlier with the $b? As shown here, there is a method called IsNullOrWhiteSpace:

PS C:\> $c = " "

PS C:\> [string]::IsNullOrWhiteSpace($c)

True

These commands and their output are shown here:

Image of command output

JB, that is all there is to determining if a string is null or empty.  Join me tomorrow when I will talk about more cool Windows PowerShell 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: Concatenate String Array with PowerShell

$
0
0

Summary: Use Windows PowerShell to concatenate elements of a array.

Hey, Scripting Guy! Question How can I use Windows PowerShell to concatenate the elements of a string array?

Hey, Scripting Guy! Answer Use the static Concat method from the String class, for example:

$a = "string a"

$b = "string b"

$arr = @($a,$b)

[string]::Concat($arr)

Playing with JSON and PowerShell

$
0
0

Summary: Ed Wilson, Microsoft Scripting Guy, talks about playing with JSON and Windows PowerShell 5.0.

Microsoft Scripting Guy, Ed Wilson, is here. The Scripting Wife has an updated shopping list. It includes a Microsoft Band 2 and a Surface Pro 4. The launch was a mouthwatering event and really well done.

One of the cool cmdlets in Windows PowerShell 5.0 on Windows 10 is the ConvertFrom-JSON cmdlet. One reason it is cool is that it will convert a Java Script Object Notation (JSON) string into a custom Windows PowerShell object. This is a cool way to interact with web services, and it can save a bit of time from parsing XML. In fact, it is remarkably easy to do.

The band, man, the band!

As an example, I am going to use a beta JSON interface to the MusicBrainz web service. This permits me to use Invoke-WebRequest and return a JSON formatted string.

The following request performs an artist lookup and returns a description, name, and country of origin (in addition to other stuff) about the artist. The query contains the website, the API, the artist information, and the format requested. Here is the query string:

$request = 'http://musicbrainz.org/ws/2/artist/5b11f4ce-a62d-471e-81fc-a69a8278c7da?inc=aliases&fmt=json'

To execute this query is simple, I use Invoke-WebRequest. This appears here:

Invoke-WebRequest $request

Because I requested the data to return as JSON, I need to convert it from JSON. I use the new ConvertFrom-JSON cmdlet and select the three fields I am interested in looking at:

ConvertFrom-Json |

Select name, disambiguation, country

The script, and the output from the script are shown here:

Image of command output

If I did not convert from JSON, the output would look like this:

Image of command output

The returned and the converted from JSON output are shown here:

Image of command output

I can also find specific information about recordings by querying the Recording interface. The procedure is exactly the same. I store my query string, use Invoke-WebRequest, convert the output to JSON, and select the properties I am interested in.

Because the properties about the recording I am interested in are stored in the Releases property as embedded objects, I need to expand that property, and then choose what I want from the embedded object. Here is that code:

$request = 'http://musicbrainz.org/ws/2/recording/fcbcdc39-8851-4efc-a02a-ab0e13be224f?inc=artist-credits+isrcs+releases&fmt=json'

Invoke-WebRequest $request |

ConvertFrom-Json  |

select -expand releases |

Select title, date, country

The script and the output are shown here:

Image of command output

There are other sample queries on the MusicBrainz.Org JSON site that you can play around with to get familiar with parsing JSON. It is fun, interesting, and pretty easy to do—great combination, if you ask me.

That is all there is to using a web request and returning JSON. 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 Win32 Type Data in PowerShell

$
0
0

Summary: Learn how to find Win32 WMI classes in Windows PowerShell that have type data.

Hey, Scripting Guy! Question How can I easily find which WMI classes in Windows PowerShell have their own type data?

Hey, Scripting Guy! Answer Use the Get-TypeData cmdlet and filter for types that contain Win32, for example:

Get-TypeData -TypeName *win32*

Troubleshoot WinRM—The Video Part 1

$
0
0

SummaryEd Wilson, Microsoft Scripting Guy, presents a video about troubleshooting WinRM (part 1).

Microsoft Scripting Guy, Ed Wilson, is here. Today I present a video where I talk a bit about troubleshooting WinRM. First I talk about looking at the WinRM logs, and then I talk about checking specific things such as:

  • Is the WinRM service responding?
  • Is the WinRM service running?
  • Is WinRM listening to the appropriate TCP ports?

   Note  For more information about this technique, refer to:

Here is the video...

(Please visit the site to view this video)

Here is a link to the video from YouTube if you would like to download it or play it offline in a different video player (like stream it on your Xbox One like I do): Troubleshooting WinRM Part 1 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: Use PowerShell to Get Windows Home Location

$
0
0

Summary: Use Windows PowerShell to get the Windows home location setting.

Hey, Scripting Guy! Question How can I get the Windows GeoID home location setting for the current user account with
           Windows PowerShell 5.0 on Windows 10?

Hey, Scripting Guy! Answer Use the Get-WinHomeLocation cmdlet.

Troubleshooting WinRM—The Video Part 2

$
0
0

Summary: Ed Wilson, Microsoft Scripting Guy, presents a video about troubleshooting WinRM (part 2).

Microsoft Scripting Guy, Ed Wilson, is here. Today I present part two of my troubleshooting WinRM video. In this video, I talk about five basic commands that are used in troubleshooting WinRM.

Note  For more information about these commands see this Hey, Scripting Guy Blog post: Troubleshooting WinRM with PowerShell—Part 2.

I hope you enjoy this video.

(Please visit the site to view this video)

Here is a link to the video from YouTube if you would like to download it or play it offline in a different video player (like stream it on your Xbox One like I do):  Troubleshooting WinRM Part 2 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 Latest Events in Windows Event Log

$
0
0

Summary: Learn how to quickly find the latest events in a Windows event log.

Hey, Scripting Guy! Question How can I use Windows PowerShell to look in the Application log to see the latest events?

Hey, Scripting Guy! Answer Use the Get-Eventlog cmdlet, specify the log name, and select newest events, for example:

Get-EventLog application -Newest 5

Viewing all 3333 articles
Browse latest View live




Latest Images