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

PowerTip: Use PowerShell and WSMan to Obtain Service Info

$
0
0

Summary: Use Windows PowerShell and WSMan to obtain service information about WinRM.

Hey, Scripting Guy! Question How can I use Windows PowerShell 3.0 with WSMan to query service information?

Hey, Scripting Guy! Answer Use the Get-WsmanInstance cmdlet and specify a resource URI.

This example obtains information about the WinRM service on a remote computer named DC1:

get-wsmaninstance wmicimv2/win32_service -selectorset @{name="winrm"} -computer dc1


HelpInfoUri, HelpUri, and other Help Mysteries

$
0
0

Summary: Learn about Updatabe Help in Windows PowerShell 3.0.

Scripting Guy, Ed Wilson here. Today June Blender is back to tell us about Updatable Help in Windows PowerShell 3.0. Take it away, June…

One of the great advances in Windows PowerShell 3.0 is Updatable Help. The advantages of updating the Help files on the box between releases are significant, far outweighing the minor hassle of running that first Update-Help or Save-Help command if the module does not include Help files. But there is still a bit of confusion associated with Updatable Help, and I'm here to explain.

Does this module support Updatable Help?

Some modules support Updatable Help and others do not. Fortunately, there's an easy way to tell which modules support Updatable Help.

If the HelpInfoUri property of a command (such as cmdlet, function, CIM command, or workflow) has a value, and the value is valid (that is, it begins with "http" or "https"), the module supports Updatable Help.

            PS C:\> Get-Module -ListAvailable | where HelpInfoUri

The HelpInfoUri property of each module object comes from the HelpInfoUri key in the module manifest. Following is the module manifest of the NetQos module, and you can see the HelpInfoUri key:

PS C:\ps-test> cat (get-module -list netqos).path

@{

    GUID = '743692B7-A227-4389-B082-2B47DE1D0D2D'

    Author = "Microsoft Corporation"

    CompanyName = "Microsoft Corporation"

    Copyright = "© Microsoft Corporation. All rights reserved."

    ModuleVersion = '1.0'

    PowerShellVersion = '3.0'

    NestedModules = @('MSFT_NetQosPolicy.cdxml')

    FormatsToProcess = @('MSFT_NetQosPolicy.format.ps1xml')

    TypesToProcess = @('MSFT_NetQosPolicy.types.ps1xml')

    HelpInfoUri = "http://go.microsoft.com/fwlink/?LinkId=216150"

    FunctionsToExport = @(

        'Get-NetQosPolicy',

        'Set-NetQosPolicy',

        'Remove-NetQosPolicy',

        'New-NetQosPolicy')

}

This command gets the manifest as a hash table, so you can get the keys as properties. The command returns the value of the HelpInfoUri key.

PS C:\ps-test>(Invoke-Expression (Get-Content (Get-Module -List NetQos).Path -Raw)).HelpInfoUri

http://go.microsoft.com/fwlink/?LinkId=216150

Why is it called HelpInfoUri?

You might think that the Windows PowerShell team spends its lunch hours dreaming up weird property names, like HelpInfoUri, but if you know a bit about the innards of Updatable Help, this name actually makes sense.

The URI in HelpInfoUri is the address of the HelpInfo.xml file for the module's Updatable Help files. So the value is really the URI of HelpInfo. (The Updatable Help files can be in the same location, and often are, but they can also be in a different location.)

The HelpInfo.xml file is like an old-fashioned .inf file. Remember Autorun.inf? These INFormation files store options for programs like the registry does. We use XML files for efficiency and easy parsing (see Select-XML) but the idea is the same.

The HelpInfo XML file

Because we're picking on NetQos today (an AWESOME module), let's look at the HelpInfo.xml file for the NetQos module. When you use Update-Help or Save-Help to download Updatable Help files, the cmdlets also download the HelpInfo XML file and save it in the module directory on your hard drive. So, we can examine the file locally:

PS C:\ps-test> Get-Content C:\Windows\System32\WindowsPowerShell\v1.0\Modules\NetQos\NetQos
_743692b7-a227-4389-b082-2b47de1d0d2d_HelpInfo.xml

 

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

<HelpInfo xmlns="http://schemas.microsoft.com/powershell/help/2010/05">

  <HelpContentURI>http://go.microsoft.com/fwlink/?LinkId=216150</HelpContentURI>

  <SupportedUICultures>

    <UICulture>

      <UICultureName>en-US</UICultureName>

      <UICultureVersion>3.0.0.0</UICultureVersion>

    </UICulture>

  </SupportedUICultures>

</HelpInfo>

The HelpInfo XML file has an element for each supported UI culture. Each SupportedUICulture element contains the HelpContentUri, which is the URI of the Updatable Help CAB file. If the HelpInfo XML file and the CAB file are in the same place, the HelpContentUri value is same as the HelpInfoUri value. The SupportedUICulture element also contains information for each UI culture, including the version number of the Help files.

The Update-Help and Save-Help cmdlets use the version number in the HelpInfo XML file to determine whether you have the latest Help files on your machine. Otherwise, the cmdlets download and install the latest version.

Why can't I read Help at the HelpInfoUri location?

I bet everyone who's played with Windows PowerShell Updatable Help has tried a variation of this command, which opens your default Internet browser to the location that is specified by the HelpInfoUri value:

PS C:\> $u = (Get-Module -List NetQos).HelpInfoUri

PS C:\> (new-object -com shell.application).Open($u)

Or you might have pasted the URI in your browser address bar. And you expected to see the Help topics for the module, or some interesting content. But there was nothing there except a file.

Image of file

In the location where Windows stores Updatable Help files, there are no HTML files to display content. There are just the lowly CAB files, which are very valuable, but don't create an UI.

If you try the same trick with the HelpUri property of a cmdlet, the result is much better.

PS C:\> $u = (Get-Command Get-History).HelpUri

PS C:\> (New-Object -Com Shell.Application).Open($u)

Or, better yet:

PS C:\> Get-Help Get-History -Online

What is HelpUri?

Now that we know about the HelpInfoUri property of modules, what is the HelpUri property of commands?

The URI in the value of the HelpInfo property is the online location of the Help topic for the command:

PS C:\ps-test> (Get-Command Get-History).HelpUri

http://go.microsoft.com/fwlink/?LinkID=113317

The Online parameter of Get-Help uses the HelpUri (among other things) to find the online Help topic. It can be overridden, but it's the best guess for the URI at the time that the command is written.

The value of the HelpUri comes from the command script. In cmdlets, HelpUri is a named parameter of the Cmdlet attribute:

[Cmdlet(VerbsCommon.Get, "History", HelpUri = "http://go.microsoft.com/fwlink/?LinkID=001122")]

In functions, HelpUri is a parameter of the CmdletBinding attribute:

function New-Calendar {

    [CmdletBinding(SupportsShouldProcess=$true,

    HelpURI="http://go.microsoft.com/fwlink/?LinkID=01122")]

You can also specify a HelpUri property value in CIM commands and workflows. For the details, see Supporting Online Help in the Windows PowerShell SDK.

Now, about the "it can be overridden" part. When a Help topic for the command is installed on the computer, and the first related link in the Help topic is a valid URL, the first related link URL takes precedence over the HelpUri value of the command. That is, the Online parameter of Get-Help uses the first related link if it's a URL. And if it's not, it uses the value of the HelpUri property of the command.

Why? In Windows PowerShell 3.0, the HelpUri property value of a command comes from the HelpUri attribute in the cmdlet code. But, in Windows PowerShell 2.0, the HelpUri property value came from the first related link in the Help topic, if that link included a URL.

When we developed Updatable Help, we wanted online Help to work even when the Help files were not installed on the local machine—so we couldn't rely on a value in a local Help file. That's when we added the HelpUri attribute to the cmdlet (and function, CIM command, and workflow) script.

But, we maintained the first related link source for backward compatibility and to provide a way to change the online Help topic location without changing the cmdlet.

Here's the relevant section from the Get-History Help topic:

Related Links

    Online Version: http://go.microsoft.com/fwlink/?LinkID=113317

    Add-History

    Clear-History

    Invoke-History

    about_History

And here's the XML for the first related link:

<maml:relatedLinks>

  <maml:navigationLink>

   <maml:linkText>Online Version:</maml:linkText>

   <maml:uri>http://go.microsoft.com/fwlink/?LinkID=113317</maml:uri>

  </maml:navigationLink>

 </maml:relatedLinks>

…and other Help mysteries

The Help system in Windows PowerShell is not simple. In fact, it's quite revolutionary. And you can use every feature of the Help system to make the modules and commands that you write really easy for users to use.

If you have question about the Updatable Help system, or any other aspect of Windows PowerShell, please reply in the Feedback section at the bottom of this page or tweet me a question at @juneb_get_help, and I'll answer them in a follow-up blog post.

If you are confused about URIs, URLs, URNs, and other URs, see this great blog post by Windows Azure Active Directory PM, Vittorio Bertocci. It's the best one I've seen: URL, URN, URI: Oh My!

~June

Thank you, June.

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: Supporting Updatable Help

$
0
0

Summary: Find Windows PowerShell modules that support Updatable Help.

Hey, Scripting Guy! Question Which modules support Updatable Help?

Hey, Scripting Guy! Answer Modules that have a HelpInfoUri property value support Updatable Help:

Get-Module -ListAvailable | where HelpInfoUri | Select Name, HelpInfoUri

Hey, Dude! Where Are My Methods?

$
0
0

Summary: Windows PowerShell MVP, Richard Siddaway, talks about WMI's missing methods in Windows PowerShell.

Microsoft Scripting Guy, Ed Wilson, is here. Today we have a guest blog post by Windows PowerShell MVP, Richard Siddaway.

Richard has been working with Microsoft technologies for 25 years and has spent time in most  IT roles, including analyst-programmer, server administrator, support, DBA, and architect. He has been interested in automation techniques (including automating job creation and submission on main frames many years ago). He originally used VBScript and WMI since it became available on Windows NT 4.0. Windows PowerShell caught his interest during the early beta versions in 2005. 

Richard blogs extensively about Windows PowerShell and founded the UK Windows PowerShell User Group in 2007. A Windows PowerShell MVP for the last six years, Richard has given numerous talks on Windows PowerShell at various events in the UK, Europe, and the USA. He is frequent speaker for Windows PowerShell User Groups worldwide.

He has published a number of posts about Windows PowerShell, including expert commentaries on the Microsoft Scripting Games, for which he has been a judge for the last four years. He has written two Windows PowerShell books: Windows PowerShell in Practice (Manning 2010) and Windows PowerShell and WMI (Manning 2012). He then collaborated with Don Jones and Jeff Hicks to write Windows PowerShell in Depth, which was published in 2013. Richard is currently writing an introductory book for Active Directory administrators that features Windows PowerShell.

Contact information: Richard Siddaway's Blog

Take it away Richard…

Thanks, Ed.

WMI has been with us for a long time, and it is one of those technologies that you either love or hate. (Some of us manage both at the same time, but that is another story.) Admins love WMI because it’s so powerful—if I can get a WMI connection to your machine, I can do just about anything to that machine. On the flip side, admins hate WMI because it can be very difficult to use, and finding information can be very difficult.

Windows PowerShell has had great WMI support, which was enhanced in Windows PowerShell 3.0 by the introduction of the CIM cmdlets and other WMI enhancements. The new functionality has caused some confusion because the WMI class methods, which we have come to rely on, seem to have disappeared.         

Before I show you what’s happened to your methods, I need to dive into a little WMI theory.

The starting point is that WMI is Microsoft’s implementation of the Common Information Model (CIM). CIM is part of the Web-Based Enterprise Management (WBEM) industry standards that are maintained by the Distributed Management Task Force (DMTF). WBEM is defined as “a set of management and Internet standard technologies developed to unify the management of distributed computing environments.”  WBEM standards are designed to be independent of the operating system and hardware. 

When you start Windows, the WMI repository is created. The repository consists of a number of things:

  • WMI providers (a DLL that provides access to WMI classes)
  • WMI namespaces
  • WMI class definitions
  • WMI instances

This is where some if the confusion arises. WMI classes can be taken to refer to the class definition (comparable to Active Directory schema definitions) and the class instances (compare to an object in Active Directory, such as a user).

When we only had the WMI cmdlets in Windows PowerShell 2.0, life was fairly simple in that you do things like this:

Get-WmiObject -Class Win32_Volume -Filter "Name = 'C:\\'" |

Get-Member -MemberType method 

And you would see the list of methods:

  • AddMountPoint
  • Chkdsk
  • Defrag
  • DefragAnalysis
  • Dismount
  • Format
  • Mount
  • Reset
  • SetPowerState

At this point, I need to remind you that the WMI is COM based, and the WMI cmdlets work over DCOM to local or remote machines.

You could access the methods by creating a Windows PowerShell object that represents the class, and call the method directly:

$disk = Get-WmiObject -Class Win32_Volume -Filter "Name = 'C:\\'"

$disk.DefragAnalysis()

The methods that you have seen so far are Instance methods. They only make sense and can only be used in the context of an instance. For example, you can’t perform DefragAnalysis against a non-existent volume!

Let’s skip over to Win32_Process for a minute:

Get-WmiObject -Class Win32_Process  | Get-Member -MemberType method

This produces the following list of methods:

  • AttachDebugger
  • GetOwner
  • GetOwnerSid
  • SetPriority
  • Terminate

Notice that there isn’t a Create method—these are all Instance methods. You have to have an instance of a process before you can terminate it.

When you are dealing with instances, you are dealing with the System.Management.ManagementObject .NET class. This is where some of the issues lie because you are working with a .NET wrapper for DCOM-based WMI.

Windows PowerShell is .NET based, and you can create just about any .NET object in Windows PowerShell. The Windows PowerShell team recognized that some .NET objects were of great interest to admins, and they provided shortcuts known as “type accelerators.” WMI has a number of type accelerators—in particular, the [wmiclass] type accelerator.

If you apply the [wmiclass] accelerator to the Win32_Process class, you get this:

$proc = [wmiclass]'\\.\root\cimv2:Win32_Process'

$proc | Get-Member -MemberType method

Name   MemberType

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

Create Method

We now have access to a method that we can use to create instances of the WMI class. The other important point is that we are dealing with a new .NET class: System.Management.ManagementClass. It’s a ManagementClass rather than a ManagementObject.

As a side note, the Create method is one of the intrinsic methods of a WMI class. It will always be there even if you can’t access it. Creating a new instance of the Win32_OperatingSysyem class could do interesting things to your machine!

In .NET terms, you can think of these as Static methods. You don’t need an instance of the class to utilize the methods. The WMI registry provider is a classic example of using WMI Static methods.

Get-WmiObject is a bit of a general work horse. You can use it to work with the instances of a WMI class, and you can use it to investigate WMI classes.

The Windows PowerShell 3.0 CIM cmdlets have separated these functions. Get-CimInstance is for working with instances of a WMI class only!

Get-CimInstance -Class Win32_Volume -Filter "Name = 'C:\\'" |

Get-Member -MemberType method

  • Clone
  • Dispose
  • Equals
  • GetCimSessionComputerName
  • GetCimSessionInstanceId
  • GetHashCode
  • GetObjectData
  • GetType
  • ToString

Hang on! This is totally different.  These aren’t the methods we want.

Remember that the WMI cmdlets work over DCOM to the local or remote machine. DCOM isn’t a firewall-friendly or Internet-friendly protocol. It also relies on an old programming methodology that is being replace by .NET.

The CIM cmdlets use DCOM to access the local machine if the –ComputerName parameter isn’t specified. As soon as you utilize the –ComputerName parameter to access the local or a remote machine, the CIM cmdlets switch to using WSMAN through the WinRM service. You can also use a CIM session with the CIM cmdlets. CIM sessions also work over WSMAN by default. (CIM sessions will be the subject of a future post.) 

WSMAN is the protocol that is used for Windows PowerShell remoting. Windows PowerShell remoting always returns an inert object—one that doesn’t have any methods. This is exactly the situation with the CIM cmdlets—they are designed to work over WSMAN, so they return inert objects.

The methods that you saw belong to the .NET class, which has changed from the WMI cmdlets. It is now Microsoft.Management.Infrastructure.CimInstance.

So far, it’s been established that you can’t access the Instance methods of a WMI class directly through Get-CimInstance. What you can do is pipe the results from your use of the Get-CimInstance cmdlet to Invoke-CimInstance to trigger the method, for example:

Get-CimInstance -ClassName Win32_Process -Filter "Name = 'notepad.exe'" |

Invoke-CimMethod -MethodName Terminate

The Invoke-CimMethod cmdlet has an –Arguments parameter, so you can work with methods that need arguments.

Get-CimInstance enables you to access instances of a WMI class. How do you investigate WMI classes?

This is the function of the Get-CimClass cmdlet. It gives you access to the WMI class metadata (such as properties, methods, and parameters).

PS> Get-CimClass -ClassName Win32_Process | fl *

 

CimClassName        : Win32_Process

CimSuperClassName   : CIM_Process

CimSuperClass       : ROOT/cimv2:CIM_Process

CimClassProperties  : {Caption, Description, InstallDate, Name...}

CimClassQualifiers  : {Locale, UUID, CreateBy, DeleteBy...}

CimClassMethods     : {Create, Terminate, GetOwner, GetOwnerSid...}

CimSystemProperties : Microsoft.Management.Infrastructure.CimSystemProperties

Notice that the Create method is listed. Let’s drill down into the method definition:

PS> $class.CimClassMethods["Create"].Parameters

Name                       CimType Qualifiers

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

CommandLine                 String {ID, In, MappingStrings}

CurrentDirectory            String {ID, In, MappingStrings}

ProcessStartupInformation Instance {EmbeddedInstance, ID, In, MappingStrings}

ProcessId                   UInt32 {ID, MappingStrings, Out}

The fact that I get the parameter name and expected data type makes life so much easier. It’s worth upgrading to Windows PowerShell 3.0 for this alone.

Secondly, Get-CimClass gives you one way to access to the Static methods of a WMI class:

Get-CimClass -ClassName Win32_Process |

Invoke-CimMethod -MethodName Create -Arguments @{CommandLine='notepad.exe'}

The arguments are presented as a hash table with the parameter names as the keys. This removes the issue that arises with Invoke-WmiMethod, where the expected order of the parameters can change from that in the documentation.

One last piece of the puzzle remains: How can we tell if a particular method is a Static (or intrinsic) method as opposed to an Instance method? The information is buried in the qualifiers for the methods. Windows PowerShell MVP, Shay Levy, supplied the following script during the discussions that prompted this post:

$class = Get-CimClass -ClassName Win32_Process

$class.CimClassMethods | select Name, @{N='MethodType';

E={if ($_.Qualifiers['Static']){'Static'}else{'Instance'} }} 

Name           MethodType

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

Create         Static

Terminate      Instance

GetOwner       Instance

GetOwnerSid    Instance

SetPriority    Instance

AttachDebugger Instance         

You can derive the same information by using the WMI cmdlets:

$class = Get-WmiObject -List Win32_Process -Amended

$class.Methods | select Name, @{N='MethodType';

E={if ($_.Qualifiers['Static']){'Static'}else{'Instance'} }}

You need to be aware that using the –Amended parameter drills deep into the WMI repository, and it is an expensive operation in CPU cycles. It will take several seconds to return the data.

To return to the original question…

Dude, your methods are where they have always been. You just need to access them in a different way.

~Richard

Thanks, Richard! You can learn more about using WMI and the new CIM cmdlets with Windows PowerShell in Richard’s book, Windows PowerShell and WMI from Manning Publications. Join me tomorrow when Windows PowerShell MVP, Sean Kearney, will talk about Getting Funky with Windows PowerShell.

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 3.0 to Find WMI Classes

$
0
0

Summary: Use Windows PowerShell 3.0 to find WMI classes that create stuff.

Hey, Scripting Guy! Question How can I use Windows PowerShell 3.0 to find WMI classes that can create stuff?

Hey, Scripting Guy! Answer Use the Get-CimClass cmdlet and specify the Create value to the MethodName parameter:

Get-CimClass -MethodName create

Weekend Scripter: Use Windows PowerShell to Play Recorded Music

$
0
0

Summary: Use a hash table and an array to run prearranged actions.

Honorary Scripting Guy, Sean Kearney here. I’m filling in for our good friend, Ed again today. I think he’s busy playing some tunes with the Windows PowerShell piano we created last week.

So continuing from last weekend’s silliness, I had the song “Popcorn” in my head. I was thinking, “Is there any way we could have some prerecorded music for Windows PowerShell?”

Well, this is definitely something we can do, but we need to figure out a few details to play music. Looking at a typically sheet of music, we have some details to be aware of:

Note  A note (A,B,C,D) could be sharp, flat, or neither (let’s say, neutral).

Octave  Our note would be an octave, and there can be multiple C notes at different frequencies.

Time  Timing could be quarter, half, or whole (even eighth or sixteenth, but we’ll start simple). The note could also be “dotted” (plays 50% longer than its variant).

Beats  Each measure plays a certain number of beats at a time (4/4 is what we’ll work with).

So before Ludwig Von Beethoven comes out to correct my musical errors, we’re going to define a special array. We’re going to change our original music array from last week slightly.

First, instead of a key on the keyboard, we’re going to create a new pattern for musical notes. We’re going to identify octave, note, and sharp or flat with this simple designation:

# First Column Identifies Octave (A-C) (where C is the highest) - Octave 0 = REST

# Second Column Identifies Note

# Third Column Designates Note as Sharp "S" or Not "N"

For a C note in the second octave, it will look like this:

BCN

A D# in the third octave looks like this 

CDS

And of course, a rest note will have a special designation of RRR. So our list of notes will translate to this:

[array]$notes=$NULL

$notes+=@{"RRR"=37}

$notes+=@{"BCN"=290}

$notes+=@{"BCS"=310}

$notes+=@{"BDN"=330}

$notes+=@{"BDS"=345}

$notes+=@{"BEN"=360}

$notes+=@{"BFN"=390}

$notes+=@{"BFS"=415}

$notes+=@{"BGN"=440}

$notes+=@{"BGS"=460}

$notes+=@{"CAN"=480}

$notes+=@{"CAS"=505}

$notes+=@{"CBN"=530}

$notes+=@{"CCN"=580}

$notes+=@{"CCS"=605}

$notes+=@{"CDN"=630}

$notes+=@{"CDS"=670}

$notes+=@{"CEN"=710}

$notes+=@{"CFN"=710}

$notes+=@{"CFN"=710}

$notes+=@{"CGN"=710}

$notes+=@{"CGN"=710} 

After that, we need to define some particular details for notes and duration. For time, we’ll base everything on 4/4 and over two blocks of measures:

Eighth note       8

Quarter note      4

Half note           2

Whole note       1

To plug in a dotted note, we’ll add an additional column, 1 or 0 for dotted or non-dotted. 

So to play an F#, second octave, whole note, non-dotted, we would use this:

BFS10

Or to play a G note, third octave, half note, dotted, we would use this: 

CGN21

So for our data array, the first three entries will contain the note, and the last pair is how long we’ll play it. Let’s plug-in some data directly:

# Whole Note Dotted

$Length=1

$Dotted=1

 

$PlayTime=4*(1/$Length)*(.5*[int]($Dotted="1"))

Cool. So let’s put this together as a small array to play “Popcorn”:

$MusicString=$NULL

$MusicString+="CEN80CDN80CEN80CCN80CAN80CCN80BGN80RRR80"

$MusicString+="CEN80CDN80CEN80CCN80CAN80CCN80BGN80RRR80"

$MusicString+="CEN80CFS80CGN80CFS80CGN80CEN80CFS80CEN80"

$MusicString+="CFS80CDN80CEN80CCN80CAN80CCN80CEN80"

We’ll create a loop to parse the data, and pass the results through to the array to determine the note and duration:

$Start=0

$End=$musicstring.Length

 

do {

$Data=$MusicString.Substring($Start,5)

 

$Note=$data.substring(0,3)

$Length=[int]$data.substring(3,1)

$Dotted=$data.substring(4,1)

 

$Tone=$Notes.$Note

 

$PlayTime=4*(1/$length)*(.5*[int]($Dotted="1"))

With this data in hand, let’s play the music:

If ($Note -ne "RRR")
{[console]::beep($tone,300*($Playtime))}
Else

{START-SLEEP -milliseconds (600*$Playtime)}

$Start=$Start+5

 

} Until ($Start -ge $End) 

TaDa! You’re a musician with Windows PowerShell!

If you’d like to download and listen to this silly little script, it’s sitting in the TechNet Gallery: Windows PowerShell Popcorn Tune.

You can adapt the music array to meet your own needs, including speeding it up and optimizing it (maybe even tune my horridly guessed notes).

Tune in tomorrow when we use Windows PowerShell to cook up a little popcorn for our script.

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

Sean Kearney,
Honorary Scripting Guy and Windows PowerShell MVP
 

PowerTip: Use PowerShell to Send Beep to Console

$
0
0

Summary: Use the Beep command in Windows PowerShell.

Hey, Scripting Guy! Question How can I send a sound in Windows PowerShell to notify a user if an error occurs in the script?

Hey, Scripting Guy! Answer Windows PowerShell can send a Beep to the console:

[console]::beep(500,300)

Change the value of the first number to alter the pitch (anything lower than 190 or higher than 8500 can’t be heard), and change the value of the second number to alter the duration:

[console]::beep(2000,500)

Weekend Scripter: Using PowerShell to Cook Popcorn for Music

$
0
0

Summary: Leverage Windows PowerShell for some simple on-screen animation.

Honorary Scripting Guy, Sean Kearney, here. I’m filling in for our good friend, Ed Wilson. Ed got hungry today and asked me to make him some popcorn. So today we’ll use Windows PowerShell to make some—just some little bits for fun.

In actual reality I’m going to have Windows PowerShell generate some simple animations on the screen. This is for fun. Nothing else. After all, it’s the weekend. That’s my excuse!

So first we’ll define an array of the characters that will participate in our little animated fun:

[Array]$Popcorn=$NULL

$Popcorn+="( 0 )"

$Popcorn+=" ( ) "

$Popcorn+=" O "

$Popcorn+=" o "

$Popcorn+=" * "

$Popcorn+=" | "

$Popcorn+=" \ "

$Popcorn+=" - "

$Popcorn+=" . "

$Popcorn+=" . " 

If you’re feeling particularly creative and have too much time on your hands like I do, feel free to tweak or expand to this list.

We’re going to have a simple animation occur to show our piece of popcorn moving up the screen. If you remember, last week we used the $Host variable to move the position of the cursor on the screen.

Today we’ll have our piece of popcorn rise up the screen in random positions. That should be fairly easy. We’ll pick a random position to place our “kernel.” To determine where we can “pop” on the screen, we’ll use a little simple logic.

Our column can be anywhere from the left to the right side of the console. Well almost. We have to ensure that we don’t go too far to the left or right.

We’re going to pop in an upwards direction, so we’ll have to make sure that we start as low on the screen as we have elements within our array.

This is all fine and dandy, but wouldn’t it make sense to know just how big the Windows PowerShell window is? We can use the $Host variable. Under RawUI, there is a property called WindowSize, which contains the width and height of our Windows PowerShell window.

Image of command output

When we know this information, we can use Get-Random to accurately place the piece of popcorn. We can use the Width to determine our maximum column position and the Height to figure out just how low we can go (I sense a limbo):

$MaximumX=$Host.UI.RawUI.WindowSize.Width

$MaximumY=$Host.UI.RawUI.WindowSize.Height

Now for the row, we’re going to have to take into account the amount of array elements. So we’ll subtract the total values contained within the array:

$StartColumn=([int](($popcorn[0].Length/2)+.1)

$MaximumX=$Host.UI.RawUI.WindowSize.Width-$StartColumn

$TotalPopcorn=$Popcorn.Count

$MaximumY=$Host.UI.RawUI.WindowSize.Height-$TotalPopcorn 

Now we’ll just use a simple Get-Random for our starting position:

$StartX=$StartColumn=GET-RANDOM $MaximumX

$StartY=(GET-RANDOM $MaximumY)+$TotalPopcorn

Before we get going, we’ll have to obtain the location of the cursor so that we can manipulate it:

$Location=$Host.UI.RawUI.CursorPosition

Now to draw our bits (or technically, bytes) on the screen. For this, we’ll execute a simple loop to start from the bottom of the array to the top:

For ($p=$TotalPopcorn-1;$p --; $p –ge 0)

{

            $Location.Y=($StartY+$p)

            $Host.UI.RawUI.CursorPosition=$Location

            WRITE-HOST $Popcorn[$p] 

During our loop we’re going to make sure that whatever might have been below the cursor will be erased. So move the cursor precisely down one position and draw a blank.

            $Erase=$Location

            $Erase.Y=($StartY+$p+1)

            $Host.UI.RawUI.CursorPosition=$Erase

            WRITE-HOST "  "

            start-sleep -milliseconds 10

}

Now if we just randomly looped through all of this, we’d see some basic little animations popping on the screen. But popcorn seems to pop randomly. So we’ll use a simple “flip of the coin” to decide in the parent loop on whether the popcorn will “pop”:

If ((GET-RANDOM 50) –lt 30)

{ ScriptBlock for Popping Corn}

ELSE

{ START-SLEEP –milliseconds 1000 }

 Oh. One more thing for fun (and possibly to annoy our coworkers—a random “POP” sound:

[console]::beep(4000+(GET-RANDOM 4000),100)

So let’s put this all together. I’ll call our loop that draws our $Popcorn on the screen a function called “POP-corn” (Oh come on, I had to…I really just had to):

# PowerShell Popcorn

# Define our array of Kernel pieces

 

[Array]$Popcorn=$NULL

$Popcorn+="( 0 )"

$Popcorn+=" ( ) "

$Popcorn+=" O "

$Popcorn+=" o "

$Popcorn+=" * "

$Popcorn+=" | "

$Popcorn+=" \ "

$Popcorn+=" - "

$Popcorn+=" . "

$Popcorn+=" . "

 

# Grab the cursor position

$Location=$Host.UI.RawUI.CursorPosition

 

# Get our parameters within the Console

$StartColumn=([int](($popcorn[0].Length/2)+.1)

$MaximumX=$Host.UI.RawUI.WindowSize.Width-$StartColumn

 

$TotalPopcorn=$Popcorn.Count

$MaximumY=$Host.UI.RawUI.WindowSize.Height-$TotalPopcorn

 

#Clear the Screen

CLEAR-HOST

 

# Declare our popping cool function

Function pop-corn()

{

 

#Grab a random position

$StartX=GET-RANDOM $MaximumX

$StartY=(GET-RANDOM $MaximumY)+$TotalPopcorn

 

$Location.X=$StartX

 

For ($p=$TotalPopcorn-1;$p --; $p –ge 0)

            {

                        $Location.Y=($StartY+$p)

 

                        # Move the cursor

                        $Host.UI.RawUI.CursorPosition=$Location

 

                        # Drop a Kernel

                        WRITE-HOST $Popcorn[$p]

 

                        # Move the cursor

                        $Erase=$Location

                        $Erase.Y=($StartY+$p+1)

 

                        # Krush an older Kernel

 

                        $Host.UI.RawUI.CursorPosition=$Erase

                        WRITE-HOST "  "

 

                        # Grab a quick nap

start-sleep -milliseconds 10

            }

}

 

# Let’s decorate the console

$Host.Ui.RawUI.WindowTitle="PowerShell Popcorn"

 

do {

            # You can adjust the 30 Higher or Lower

            # to increase or decrease the possibility of

            # popping

            if ((GET-RANDOM 50) -lt 30)

                        {

                        # irritate your coworkers with noise

                        [console]::beep(4000+(GET-RANDOM 4000),100)

 

                        # Pop some Corn

                        Pop-Corn                      }

            ELSE

                        # 1000 milliseconds equals one second.

                        # You could make this larger or smaller or even sleep

                        # RANDOM amount of time using

                        #

                        # START-SLEEP –milliseconds (GET-RANDOM 5000)

{ START-SLEEP -milliseconds 1000 }

            }

until ($FALSE)

 Save this as popcornscreen.ps1, run it, and watch the pieces appear all over the screen.

OK, no much silliness in console land. Back to work and more cool Windows PowerShell fun tomorrow!

Does anyone have some butter? I’m getting hungry.

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

Sean Kearney,
Honorary Scripting Guy and Windows PowerShell MVP


PowerTip: Determine Size of PowerShell Console

$
0
0

Summary: Determine how large your Windows PowerShell console is.

Hey, Scripting Guy! Question How can I see how large my Windows PowerShell console is?

Hey, Scripting Guy! Answer Access the WindowSize property from the $Host object:

$Host.UI.RawUI.WindowSize.Width

$Host.UI.RawUI.WindowSize.Height

Adding Array Index Numbers to PowerShell Custom Objects

$
0
0

Summary: Microsoft Scripting Guy, Ed Wilson, talks about adding array index numbers to Windows PowerShell custom objects.

Hey, Scripting Guy! Question Hey, Scripting Guy! Our software application returns arrays of information. What I need is a way to artificially add the array element number to the object that the cmdlet outputs. For example, I want to add the highlighted information to the following output, and I need the highlighted numbers to be the array indexes:

Image of command output

Can you help me?

—WS

Hey, Scripting Guy! Answer Hello WS,

Microsoft Scripting Guy, Ed Wilson, is here. Well it is just a couple of days until the Scripting Wife’s birthday. Shhhhhh…don’t tell her that I have not forgotten. It is sort of a game we like to play. I pretend to forget it is her birthday, she pretends to be upset. In the end, I wind up getting her great presents that she really likes—such as the complete series of the old Get Smart television show. She is still talking about that present!

So WS, when I first read your email sent to scripter@microsoft.com, I thought, “Well, that will not be too hard. Maybe I will use Select-Object, add a custom column, and pick up the index number.”

Well unfortunately, that did not work out too well.

I do not have your custom software package installed, but that really does not matter because PowerShell is PowerShell is PowerShell. It always behaves the same.

What I ended up doing was creating a custom PSObject and using the IndexOf method to pick up the index number. Then I added in the value of the array element. Here is an example of what I am talking about:

PS C:\> $array = "dog","cat","mongoose","bird"

PS C:\> $array.IndexOf('dog')

0

PS C:\> $array.IndexOf('mongoose')

2

PS C:\>

As you can see in the previous script, the IndexOf method will accept an array value and return the associated index number from it. So here is how I added this into a little demo script:

# -----------------------------------------------------------------------------

# Script: ArrayIndexAndValues.ps1

# Author: ed wilson, msft

# Date: 09/06/2013 11:33:26

# Keywords: Scripting Techniques, Arrays, Custom PSObjects

# comments:

# HSG-9-23-2013

# -----------------------------------------------------------------------------

$array = "dog","cat","mongoose","bird"

Foreach($a in $array)

{

New-Object -TypeName psobject -Property @{

    index= $array.indexof($A)

    Value= $A } }

But I was not really happy with my answer. So I emailed Lee Holmes and asked his opinion. Here is his answer…

First of all, there is no really nice way to do what you are attempting to do. There’s an example from my Windows PowerShell Cookbook, second version, which talks about how to add Add-FormatTableIndexParameter to Format-Table. But other than that, an approach similar to yours is good.

Using Array.IndexOf() will be a little slow. Here is an alternative:

dir c:\ | Foreach-Object { $index = 0 } {

    [PSCustomObject] @{ Index = $index; Object = $_ }; $index++

}

WS, that is all there is to using Windows PowerShell to add array index values to a custom object. Join me tomorrow when we will have a great guest blog post by Microsoft PowerShell MVP, Jeffrey Hicks. He’ll be talking about a script profile tool that he wrote. It is good stuff and you do not want to miss out.

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 Array with Five Numbers

$
0
0

Summary: Easily create an array that contains five numbers with Windows PowerShell.

 Hey, Scripting Guy! Question How can I use Windows PowerShell to easily create an array that contains five numbers?

Hey, Scripting Guy! Answer Use the Range operator and assign the results to a variable:

$a = 1..5

Are You Safe?

$
0
0

Summary: Windows PowerShell MVP, Jeffery Hicks, talks about profiling scripts.

Microsoft Scripting Guy, Ed Wilson, is here. Today we have as our guest blogger, Jeffery Hicks, Windows PowerShell MVP and author. To read more of Jeff’s previous guest posts, see these Hey, Scripting Guy! Blog posts.

Jeffery Hicks is a Microsoft MVP in Windows PowerShell, a Microsoft Certified Trainer, and an IT veteran with over 20 years of experience. He spent much of this time as an IT consultant specializing in Microsoft server technologies with an emphasis on automation and efficiency. He works today as an independent author, trainer, and consultant. Jeff writes  the popular Professor PowerShell column for MPCMag.com, is a regular contributor to the Petri IT Knowledgebase, 4SysOps, and the Altaro Hyper-V blog. He is a frequent speaker at technology conferences and user groups. If he isn't writing, Jeff is most likely recording training videos for companies like TrainSignal or hanging out in the forums at PowerShell.org.

Jeff's latest books are:

Jeff’s contact information:

Blog: The Lonely Administrator
Twitter: twitter.com/jeffhicks
Google Plus: Jeffery Hicks 

In Windows PowerShell 2.0 Best Practices by Ed Wilson, I contributed a side bar about how to test if a script was safe to run. You could blindly run it in a test environment, such as in a virtual machine. But you really should look at it first. However, I realize you may still be learning Windows PowerShell, and perhaps you don’t fully understand everything you might see. So I have something to help.

In Ed’s book, I had a Windows PowerShell script that would “profile” a script. That is, give you an idea of what commands it would run and identify any potentially dangerous commands. Frankly, the previous version was not very well written and never worked the way I imagined it. Fortunately, Windows PowerShell 3.0 introduced a new way of parsing script files by using an Abstract Syntax Tree (or AST).

Don’t worry too much about what that means except that it is now much easier to analyze or parse a Windows PowerShell script. By using AST, I wrote a new Windows PowerShell 3.0 script called Get-ASTScriptProfile.ps1 that finally meets my original goal. 

The script takes the name of a script to “profile.” You can specify a .ps1 or .psm1 file name. By using AST, the script prepares a text report showing you any script requirements, script parameters, commands, and type names. You will see all commands that are used, including those that can’t be resolved and those that I thought might be considered potentially dangerous, such as cmdlets that use the verbs Remove or Stop.

Because some people might invoke methods from .NET classes directly, I’ve also captured all type names such as System.IO.FileInfo. Most type names will probably be related to parameters, but at least you’ll know what to look for. The entire report is turned into a Help topic that is stored in your Documents folder and can be displayed with Get-Help and the awesome –ShowWindow parameter.

For example, I might run a command like this:

PS C:\> C:\scripts\Get-ASTScriptProfile.ps1 C:\scripts\Remove-OldFiles.ps1

In my Documents folder, I’ll get a file called Remove-OldFiles.help.txt, but I’ll also see the report with the Help viewer:

Image of report

The report won’t detail parameters from nested functions, but you’ll still see what commands they will use. The script uses Get-Command to identify commands that might entail loading a module. Most of the time this shouldn’t be an issue, but you still might want to profile the script in a virtualized or test environment.

Of particular interest should be the Warning section, which shows commands that might affect your system. Any unresolved command that you see is either from a module that couldn’t be loaded, or it might be an internally defined command. When you know what to look for, you can open the script in your favorite editor and search for the mystery commands or anything that you don’t understand.

And here is ASTScriptProfile.ps1:

#requires -version 3.0

 

#Get-ASTScriptProfile.ps1

 

<#

.Synopsis

Profile a PowerShell Script

.Description

This script will parse a PowerShell script using the AST to identify elements

and any items that might be dangerous. The output is a text report which by

default is turned into a help topic stored in your Windows PowerShell folder

under Documents.

 

DETAILS

The script takes the name of a script to profile. You can specify a ps1 or

psm1 filename. Using the AST the script will prepare a text report showing

you any script requirements, script parameters, commands and type names. You

will see all commands used including those that can't be resolved as well as

those that I thought might be considered potentially dangerous such as cmdlets

that use the verbs Remove or Stop. Because some people might invoke methods

from .NET classes directly I've also captured all typenames. Most of them will

probably be related to parameters but as least you'll know what to look for.

 

The report won't detail parameters from nested functions but you'll still see

what commands they will use. The script uses Get-Command to identify commands

which might entail loading a module. Most of the time this shouldn't be an

issue but you still might want to profile the script in virtualized or test

environment.

 

Any unresolved command you see is either from a module that couldn't be loaded

or it might be an internally defined command. Once you know what to look for

you can open the script in your favorite editor and search for the mystery

commands.

 

.Example

PS C:\> c:\scripts\Get-ASTScriptProfile c:\download\UnknownScript.ps1

 

This will analyze the script UnknownScript.ps1 and show the results in a

help window. It will also create a text file in your documents folder called

UnknownScript.help.txt.

.Link

Get-Command

Get-Alias

#>

 

[cmdletbinding()]

Param(

[Parameter(Position=0,Mandatory,HelpMessage="Enter the path of a PowerShell script")]

[ValidateScript({Test-Path $_})]

[string]$Path

)

 

Write-Verbose "Starting $($myinvocation.MyCommand)"

#need to resolve full path

$Path = (Resolve-Path -Path $Path).Path

Write-Verbose "Analyzing $Path"

 

Write-Verbose "Parsing File for AST"

New-Variable astTokens -force

New-Variable astErr -force

 

$AST = [System.Management.Automation.Language.Parser]::ParseFile($Path,[ref]$astTokens,[ref]$astErr)

 

$report=@"

Script Profile report for: $Path

 

"@

 

Write-Verbose "Getting requirements and parameters"

$report+=@"

 

******************

*  Requirements  *

******************

$(($ast.ScriptRequirements | out-string).Trim())

 

******************

*  Parameters    *

******************

$(($ast.ParamBlock.Parameters |

 Select Name,DefaultValue,StaticType,Attributes |

 Format-Table -autosize | Out-String).Trim())

 

"@

 

Write-Verbose "Getting all command elements"

 

$commands = @()

$unresolved = @()

 

$genericCommands = $astTokens |

where {$_.tokenflags -eq 'commandname' -AND $_.kind -eq 'generic'}

 

$aliases = $astTokens |

where {$_.tokenflags -eq 'commandname' -AND $_.kind -eq 'identifier'}

 

Write-Verbose "Parsing commands"

foreach ($command in $genericCommands) {

    Try {

       $commands+= Get-Command -Name $command.text -ErrorAction Stop

    }

    Catch {

      $unresolved+= $command.Text

    }

}

 

foreach ($command in $aliases) {

Try {

       $commands+= Get-Command -Name $command.text -erroraction Stop |

       foreach {

         #get the resolved command

         Get-Command -Name $_.Definition 

       }

    }

    Catch {

        $unresolved+= $command.Text

    }

}

 

 

Write-Verbose "All commands"

$report+=@"

 

******************

*  All Commands  *

******************

$(($Commands | Sort -Unique | Format-Table -autosize | Out-String).Trim())

 

"@

 

Write-Verbose "Unresolved commands"

$report+=@"

 

******************

*  Unresolved    *

******************

$($Unresolved | Sort -Unique | Format-Table -autosize | Out-String)

"@

 

Write-Verbose "Potentially dangerous commands"

#identify dangerous commands

$danger="Remove","Stop","Disconnect","Suspend","Block",

"Disable","Deny","Unpublish","Dismount","Reset","Resize",

"Rename","Redo","Lock","Hide","Clear"

 

$danger = $commands | where {$danger -contains $_.verb}

 

#get type names, some of which may come from parameters

Write-Verbose "Typenames"

$report+=@"

 

******************

*  TypeNames     *

******************

$($asttokens | where {$_.tokenflags -eq 'TypeName'} |

Sort @{expression={$_.text.toupper()}} -unique |

Select -ExpandProperty Text | Out-String)

"@

 

$report+=@"

 

******************

*  Warning       *

******************

$($danger | Format-Table -AutoSize | Out-String)

"@

 

Write-Verbose "Display results"

 

#create a help topic file using the script basename

$basename = (Get-Item $Path).basename    

#stored in the Documents folder

$reportFile = Join-Path -Path "$env:userprofile\Documents" -ChildPath "$basename.help.txt"

 

#insert the Topic line so help recognizes it

"TOPIC" | Out-File -FilePath $reportFile -Encoding ascii

#create the report

$report | Out-File -FilePath $reportFile -Encoding ascii -Append

 

#view the report with Get-Help and -ShowWindow

Get-Help (Join-Path -Path "$env:userprofile\Documents" -ChildPath $basename) -ShowWindow

 

Write-Verbose "Profiling complete"

Practicing safe Windows PowerShell is a habit you have to cultivate, not only in the scripts and tools that you write, but also when using code that is written by someone else. A tool like Get-ASTScriptProfile.ps1 might help. I hope you’ll try it out and let me know what you think.

~Jeff

Thank you Jeff, for sharing your knowledge and time.

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 Access Online Help

$
0
0

Summary: Learn how to access online Help easily by using Windows PowerShell.

Hey, Scripting Guy! Question How can I ensure that I am always accessing the latest Help content—even if I suspect my Help file for Windows PowerShell is out-of-date, and I do not have time to download and install the latest?

Hey, Scripting Guy! AnswerUse the –Online switch when using the Get-Help cmdlet.
         This example opens Help content for Set-WSManInstance:

Get-Help Set-WSManInstance -Online 

The Admin’s First Steps: Documenting Servers

$
0
0

Summary: Richard Siddaway talks about using Windows PowerShell to automate the creation of your server documentation.

Hey, Scripting Guy! Question Hey, Scripting Guy! I’ve just starting learning Windows PowerShell, and I understand how to use it as a scripting language and shell. But what can I use it for in my environment to make my job easier? Can you give me some suggestions?

—CA

Hey, Scripting Guy! Answer Hello CA,

Honorary Scripting Guy, Richard Siddaway, here today filling in for my good friend, The Scripting Guy.

Richard Siddaway is based out of the UK, and he spends his time automating anything and everything for Kelway Ltd. A Windows PowerShell MVP since 2007. Richard is a prolific blogger, mainly about Windows PowerShell (see Richard Siddaway's Blog: Of PowerShell and Other Things), and a frequent speaker at user groups and Windows PowerShell conferences. He has written a number of Windows PowerShell books: PowerShell in Practice, PowerShell and WMI, PowerShell in Depth (co-author); and PowerShell Dive (co-editor). He is currently finishing Learn Active Directory Management in a Month of Lunches, which features a lot of Windows PowerShell. All of the books are available from Manning Publications Co.

CA, You’re in luck because today I start a new series that explains how to make the jump from knowing some Windows PowerShell to being able to use it to make your life easier as an administrator. You’ve spent some time learning to use Windows PowerShell. You’ve got some scripts that you copied from books or downloaded. But what you really need is an answer to the question, “How do I get started automating my environment?”

As with so many other things, the only correct answer is that it depends. It depends on what are the most pressing issues you need to solve. It depends on what you have in place in terms of monitoring, configuration management, and change control. Most of all, it depends on the time and resources you have available to develop or tailor automation scripts for your environment.

The Windows PowerShell community has been very good at producing material to help explain Windows PowerShell as you go through the learning process. There is also a mass of “how to do X…” materials in the various code repositories—especially in the Windows PowerShell Script Center Repository.

However, there is one big gap that the community hasn’t addressed properly. How do you get started automating your environment after you’ve learned some Windows PowerShell? That’s the question I’m going to start to answer in this series of posts as I talk you through developing some automation processes. These processes aren’t prescriptive or limited to what is explained here. They are simply suggestions that you can use as is—or better still, to spark an idea of your own.

If you have ideas for topics that could be discussed in future posts please leave a comment at the end of this post. No promises though. If a suggestion is adopted depends on a number of factors, including the limitations of my development environment.

Active Directory administration is a common target for automation, and I’ll certainly discuss that later—but I think a better start is documenting your servers. Server documentation is a fact of life. No one likes doing it, and very few organizations find the time to do it—but boy, are you grateful when it’s there.

Creating a server document has two main steps. First, you need to find the information, and then you need to put it into the document. How you write the information into the document depends on the type of document (Word, Excel, or even a web page). I’m going to assume that we are using a Word document for now.

Let’s look at collecting some data. Typically as a starting point, you would want to document:

  • Operating system version and Service Pack level
  • Hardware (CPU, RAM, disk sizes)
  • Network configuration
  • Running services

Getting this information is a snap with Windows PowerShell because you can use WMI to pull the information directly from the remote machine—all from the comfort of your own Windows PowerShell prompt.

Note   I am assuming throughout this post that you are running Windows PowerShell 3.0 on the local computer and on remote machines.

Starting with the operating system:

Get-CimInstance -ClassName Win32_OperatingSystem |

select Caption, ServicePackMajorVersion, LastBootUpTime

Some of the hardware comes from the Win32_ComputerSystem class:

Get-CimInstance -ClassName Win32_ComputerSystem |

select Manufacturer, Model, NumberOfProcessors, NumberOfLogicalProcessors

The memory could be retrieved from this class, but it may not be accurate because it takes into account memory that is allocated to graphics cards and other peripherals. A better way to get the memory is:

Get-CimInstance -ClassName Win32_PhysicalMemory  |

 Measure-Object -Property Capacity -Sum |

 select -ExpandProperty Sum

We’ll restrict disk sizes to local disks. Life would get very complicated if we included mapped network drives and other options.

Get-CimInstance -ClassName Win32_LogicalDisk -Filter "DriveType=3" |

 select DeviceId, VolumeName, Size, FreeSpace

Network data comes in two flavors: data on the hardware and data on the configuration (IP address). The IP address data is a good starting point because we can filter to ensure that only those adapters with TCP/IP enabled will be returned.

 Get-CimInstance -ClassName Win32_NetworkAdapterConfiguration `

     -Filter "IPEnabled=$true" |

 foreach  

   select -InputObject $psitem -Property Description, IPAddress, IPSubnet,

   DefaultIPGateway, DNSServerSearchOrder, MACAddress,

   @{Name="Name";

     Expression={Get-CimInstance -ClassName  Win32_NetworkAdapter `

     -Filter "DeviceId=$($_.Index)" | select -ExpandProperty NetConnectionID }}

 }

The last part of the information consists of the running services on the server. You could use Get-Service for this, but I’ll stick with WMI. You’ll see why in a minute.

Each time you make a call to a remote machine by using WMI, you have to create, use, and then destroy the connection. One of the coolest parts of Windows PowerShell 3.0 is the ability to create a persistent session to a remote machine for WMI. This is known as a CIM session. You create them like this:

$cimsess = New-CimSession -ComputerName server02

The CIM session is used via the –CIMsession parameter:

Get-CimInstance -ClassName Win32_ComputerSystem -CimSession $CimSession  

select Manufacturer, Model, NumberOfProcessors, NumberOfLogicalProcessors

The last piece of the WMI side of the puzzle is using the WMI calls. I put them into their own little functions and then put the functions into a module. That way, I can use the functions interactively in addition to generating the report. It also means I’m dealing with objects until I create the report. Each function is of this form:

function getos {

param (

[Microsoft.Management.Infrastructure.CimSession]

$CimSession

)

 

Get-CimInstance -ClassName Win32_OperatingSystem -CimSession $CimSession |

select Caption, ServicePackMajorVersion, LastBootUpTime 

}

Pass the CIM session as a parameter and output the required data. I deliberately didn’t hyphenate the function names to separate them from other functions.

How do we get the data into a Word document? First we have to create a new Word document. Unfortunately, you don’t have any cmdlets to work with Word documents, but you can fall back on the COM object:

function Add-Text {

param (

 [string] $style = "Normal",

 [string] $text

)

$paragraph = $doc.Content.Paragraphs.Add()

$range = $paragraph.Range

$paragraph.Range.Text = $Text

$paragraph.Range.Style = $Style 

$paragraph.Range.InsertParagraphAfter()

}

 

$word = New-Object -ComObject "Word.application"

$word.visible = $true

$doc = $word.Documents.Add()

$doc.Activate()

 

add-text -style "Title" -text "Server02"

add-text -style "Heading 1" -text "Operating System"

add-text -style "Normal" -text "Some old text"

 

$file = [ref]"C:\Scripts\HSG\testsave.doc"

$saveFormat = [Enum]::Parse([Microsoft.Office.Interop.Word.WdSaveFormat], "wdFormatDocument97") -as [ref]

$doc.SaveAs($file, $saveformat)

 

$Word.Quit()

Create a COM object for Word:

$word = New-Object -ComObject "Word.application"

Make Word visible, add a document, and then activate the document for use. The Add-Text function is used to write the data into the Word document. Pass a Word style and the text to be written into the document to the function. Add-Text creates a paragraph, sets the style and text, and adds it to the document.

Saving Word documents in Windows PowerShell 3.0 is a little more complicated because of issues handling [ref], and we need to define the file path, the format in which the document will be saved (in this case, a good old .doc format), and then call the SaveAs() method. The final action is to quit Word.

Putting it all together, we get this:

param (

[string]$computer=$env:COMPUTERNAME

)

function getos {

param (

[Microsoft.Management.Infrastructure.CimSession]

$CimSession

)

 

Get-CimInstance -ClassName Win32_OperatingSystem -CimSession $CimSession |

select Caption, ServicePackMajorVersion, LastBootUpTime

}

 

function Add-Text {

param (

 [string] $style = "Normal",

 [string] $text

)

$paragraph = $doc.Content.Paragraphs.Add()

$range = $paragraph.Range

$paragraph.Range.Text = $Text

$paragraph.Range.Style = $Style

$paragraph.Range.InsertParagraphAfter()

}

 

$word = New-Object -ComObject "Word.application"

$word.visible = $true

$doc = $word.Documents.Add()

$doc.Activate()

 

$cimsess = New-CimSession -ComputerName $computer

add-text -style "Title" -text $computer

add-text -style "Heading 1" -text "Operating System"

add-text -style "Normal" -text (getos  -CimSession $cimsess | format-list | out-string )

 

$file = [ref]"C:\Scripts\HSG\testreport.doc"

$saveFormat = [Enum]::Parse([Microsoft.Office.Interop.Word.WdSaveFormat], "wdFormatDocument97") -as [ref]

$doc.SaveAs($file, $saveformat)

 

$Word.Quit()

$cimsess | Remove-CimSession

The data from our function is formatted into a list (this makes it easier to make sure that it fits on the page), converted to a string, and passed into the Add-Text function. The previous example shows only a single function. The full script is available in the Script Center Repository: Server Report.

This technique works. I’ve used it to prepare the documentation for 150 servers, overnight, in preparation for an audit. Needless to say, we passed the audit!

Ways you could extend this include:

  • Test for a version of WSMAN on the remote machine, and build a DCOM link if it is WSMAN 2.0.
  • Modify code to deal with a Windows Server 2003 error in reporting processors correctly (or apply the hot fix available in article 932370 in the Microsoft Knowledge Base to your computers running Windows Server 2003).
  • Collect more data.
  • Create a document template to use for the report.
  • Use a fixed width font such as Courier New to improve the formatting.
  • Use Word tables to present the data.
  • Convert the Word document to a .pdf.
  • Use Windows PowerShell workflows to run in parallel on multiple servers.

CA, that is all there is to using Windows PowerShell to document your servers. Next time, I’ll have another idea for you to try as you bring automation into your environment.

Bye for now.

~Richard

Thanks, Richard.

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: Make Multiple WMI Queries to Remote Machine

$
0
0

Summary: Learn how to make multiple WMI queries to a remote machine more efficient.

Hey, Scripting Guy! Question How can I make running multiple WMI queries against a remote machine more efficient?

Hey, Scripting Guy! Answer Create a CIM session with New-CimSession, and use that in your calls instead of a computer name:

$sess = New-CimSession –Computername server02

Get-CimInstance –ClassName Win32_OperatingSystem –CimSession $sess


Scripting Wife Talks About the PowerShell Summit

$
0
0

Summary: Guest blogger, Teresa Wilson, talks about the Windows PowerShell Summit.

Microsoft Scripting Guy, Ed Wilson, is here. Today the Scripting Wife wants to talk a bit about what the Windows PowerShell Summit means to her. Take it away Scripting Wife…

Anyone who follows social media will know by now that today is my birthday. I see no sense in hiding the fact, so I am just going to wallow in my day. I don’t need gifts, but I do love to see the messages and hugs that are sent my way. They are far more valuable than anything that could be wrapped and presented to me. Speaking of hugs,,,

One of the great joys in my life as the Scripting Wife is the luxury of attending conferences with Ed and making friends. I anxiously await any opportunity to attend user group meetings, PowerShell Saturdays, and the bigger conferences like TechEd and the Windows PowerShell Summit. I will confess that last year at the Windows PowerShell Summit, not only did I get to see existing friends (read hug existing friends) and make a bunch of new friends, I also gained something tangible for me.

I saw an attendee with a laptop that immediately caught my eye. It was an XXXX brand with colored lights on the keyboard. Oh boy, did my eyes light up! I dragged Ed over to where Michael (owner of the laptop) sat, and I just said, “Look Ed.” He laughed because he knew where this was going. He shook his head and said, “No, that is a gaming laptop, and you do not need a gaming laptop.”

Long story short…

I am now the proud owner of a laptop that is huge, so it has become my workstation that does not get moved. It has a beautiful display of colored lights. I can tell you this was certainly one Windows PowerShell Summit that I will not soon forget. One of the attendees even wrote a Windows PowerShell script that makes the lights rotate! (Thanks, Chris.)   

Now for the best part, I made so many new friends, and I have helped some of these people get user groups started in their hometowns. I have been able to help people be connected within the Windows PowerShell family. Those are some of the great things that happened in the social time. The following photo illustrates one such room.

Photo of classroom

Next comes the wealth of knowledge the attendees gain from attending the Summit. The call for session submissions closed yesterday, and I cannot wait to see the lineup for the sessions for 2014. The really nice thing about the Windows PowerShell Summit is the time you get to spend with other professionals. There is so much knowledge to learn and share. Members from the Windows PowerShell team are able to get away from their desks to make presentations and answer questions. Here is a picture of me and Lee Holmes.

Photo of Lee Holmes and Scripting Wife

MVPs are in attendance. They have so much experience that it is mind boggling. Plus they would not be MVPs if they have not shown a giving spirit and truly want to help others learn. My advice is: Do yourself and your company a favor and get signed up for the Windows PowerShell Summit in April 2014. You will be amazed!

Here is a link for Here is a link for PowerShell.org, where you can find a wealth of information. There is not only information about the Windows PowerShell Summit—you can also find a list of Windows PowerShell books, information about the Forums and user groups, and much more.

~Scripting Wife

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 Example Code

$
0
0

Summary: Use Windows PowerShell to find easy-to-use example code.

Hey, Scripting Guy! Question How can I find examples of using Windows PowerShell cmdlets and functions—in particular examples that begin with the verb Get?

Hey, Scripting Guy! Answer Use the Get-Command cmdlet to find commands beginning with the verb Get, pipe the results to the Foreach-Object cmdlet, and call Get-Help on each item.

For Windows PowerShell 4.0 and Windows PowerShell 3.0:

(Get-Command -Verb get).name | foreach {get-help $psitem -example}

For Windows PowerShell 2.0 and on Windows PowerShell 1.0:

Get-Command -Verb get | foreach {get-help $_.name -example} 

The PowerShell Summit: Why You Should Care

$
0
0

Summary: Microsoft Scripting Guy, Ed Wilson, talks about the Windows PowerShell summit in Seattle, and why you should care.

Hey, Scripting Guy! Question Hey, Scripting Guy! Can you tell me why I should attend the Windows PowerShell summit in Seattle? I am wondering if it would be worth my while. I have been using Windows PowerShell for a while, and I feel like I know almost everything there is to know about Windows PowerShell. So how useful can this Summit really be?

—SH

Hey, Scripting Guy! Answer Hello SH,

Microsoft Scripting Guy, Ed Wilson, is here. This afternoon is shaping up to be a lovely day. I spent the morning teaching a Windows PowerShell class to a group of writers via LYNC. It was fun, and there were lots of questions. The Scripting Wife and I have all the windows in our house open, and we are enjoying the fresh breezes that are blowing across Charlotte, North Carolina now. The leaves are starting to change colors, and it is shaping up to be a wonderful fall down here. As someone who loves taking pictures, I am currently ecstatic.

We are also chomping at the bit for another reason: the Windows PowerShell Summit. The Summit will be in Bellevue, Washington (close to our Microsoft campus) for three days in April: April 28, 29, and 30. First of all, Bellevue is a really cool town, and there are excellent restaurants and hotels in the area. Not only this, but it is a great place to take pictures. But those are completely secondary reasons.

One thing is rather interesting. I am not certain I have ever met anyone who knows everything there is to know about Windows PowerShell. In fact, I am not certain I have ever met anyone who knows nearly everything there is to know about Windows PowerShell. I certainly do not know everything about Windows PowerShell—not by a long shot. In fact, I learn new stuff about Windows PowerShell every single day.

I am lucky because my job has me working with Windows PowerShell nearly eight hours a day, every day. Therefore I discover new things about Windows PowerShell on a daily basis. But it is more than just me. There are some really smart people who write about Windows PowerShell, and I pick up stuff via Twitter, via Facebook, via comments on my blog, and other places. On my Windows laptop running Windows 8.1, there are 1,321 cmdlets and functions—and I have not even installed the RSAT tools yet. That is a very wide coverage.

Of course, it is fairly easy to master the basic syntax, learn the operators, and get very good at using the core cmdlets—but to know everything about everything? That is a real stretch. Keep in mind, that the modules are produced by many different teams at Microsoft; and therefore, there are a lot of different technologies represented.

What I like about the Windows PowerShell Summit is that there are so many Windows PowerShell experts all gathered in a single place. Just about any question you could think of, there will be someone there who can answer it. Couple the fact that there are so many Windows PowerShell heavy weights (not literally…well, at least not in all cases are they heavy weights) in one place. In addition, it is a small crowd.

Microsoft TechEd is an awesome conference, and there are tons of experts gathered together from all over the world. It is cool. It is fun. It is also very crowded with audience numbers in the tens of thousands present. The Scripting Wife and I talk to several thousand scripters in a week when at the Scripting Guys booth. Obviously, most of the conversations are rather brief.

Now, at the Windows PowerShell Summit, there are only a couple hundred attendees. Therefore, the one-on-one time is much greater. In addition, the venue facilitates after-hours interaction. Last year, two of the nights, we had dinner parties after the sessions. It was absolutely awesome.

For me, there is absolutely no other place to be. Last year we had attendees from nearly a dozen different countries. It became like a great big family reunion when we got to meet with the various people. The following photo shows a session in progress in the IT Pro track.

Photo of class

SH, that is my opinion about the Windows PowerShell Summit. Join me tomorrow for 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: Use PowerShell to Multiply Strings

$
0
0

Summary: Use Windows PowerShell to multiply strings.

Hey, Scripting Guy! Question How can I use Windows PowerShell to display a string a number of times, without doing a lot of typing?

Hey, Scripting Guy! Answer Multiply the string:

PS C:\> "cat " *5

cat cat cat cat cat

A PowerShell Object Lesson: Part One

$
0
0

Summary: Microsoft Scripting Guy, Ed Wilson, talks about the importance of objects when working with Windows PowerShell.

Microsoft Scripting Guy, Ed Wilson, is here. It is starting to get rather cold and rainy outside here in Charlotte, North Carolina in the United States. On a day like this, I like to have a cup of Lapsang Souchong tea. Lapsang Souchong tea is one of the teas that I routinely add milk to before drinking. The smoky flavor and smell remind me of standing in front of the fireplace when I was a kid to warm up after a day of playing in the snow. The nice hot tea allows me to relive that moment, and it warms me nicely. A hot cup of tea, and a cool laptop keyboard with a warm blue Windows PowerShell console is all I need to spend a productive day inside—away from the ravages of cold, pelting rain and bone-chilling wind.

Note  This is the first of a three-part series of posts that talk about Windows PowerShell objects.

A fundamental concept of Windows PowerShell

One of the most fundamental concepts of Windows PowerShell is the concept of objects. It is a truism that all good Windows PowerShell functions return objects. This is a design goal to which all good scripters should aspire. All Windows PowerShell cmdlets return objects. But what is an object? Why is an object desirable? Why should we care about objects?

The great thing about an object is that it makes it easy to do things, and it makes it easy to obtain information. With an object, doing things is called a method and obtaining information is called a property. If I do not have an object, it is much more difficult to do things or to obtain information.

One way information can be obtained without an object is to use command-line utilities. These are the same utilities that have been around for years—going back to the earliest days of DOS (and in some cases, before DOS). These old utilities all return text. They do not return rich objects. (To be fair, in Windows PowerShell, even text is an object. In fact, everything in Windows PowerShell is an object.)

When I use a utility that returns only text, I am pretty much stuck with the information that it returns. This means that if I do not like the output, well, that’s tough. An example of a utility that returns only text is the IPConfig command. The output comes back and fills the console with lots of text, but if I want to find only the IP address of the DNS server, I am stuck reading through lots of output as shown here:

Image of command output

When I finally find it, then what? I have a textual representation of the IP address of the DNS server. But what if I am troubleshooting my computer? If I want to ping that DNS server, how do I do it? I have to type a bunch of numbers and hope that I get it in the right order. What if I want to ping the IPv6 address? Dude, I am out of luck. Lots of typing. Hopefully I can cut and paste the address to another utility to do the ping.

So, what happens if I have an object that comes back with IP address types of information? Initially, it does not look like much difference. I type the command, and it returns output to the Windows PowerShell console, just like the IPConfig command. So what is the difference?

Well, the difference is under the covers. With an object, I can gain direct access to the property that contains the information. For example, if I want the IP address of the DNS server, I access the DNS server property from the object. This is easier that it sounds:

Get-NetIPConfiguration

The output is shown here:

Image of command output

What is not an object

…well at least, not really an object.

When you use the Write-Host cmdlet, it displays output to the Windows PowerShell console (or even to the output pane of the Windows PowerShell ISE). Write-Host does not generate an object—it simply displays to the console, as shown in the following image:

Image of command output

If I have a script that uses a series of Write-Host cmdlets, that script is not returning any objects; and therefore, the script does not go anywhere else. It cannot feed the output to a text file or to another Windows PowerShell cmdlet. This is a common mistake that is made by beginning script writers. There is nothing wrong with using Write-Host to provide status notification the Windows PowerShell console when you are running a script. But Write-Host does not return any objects, and it should not be used for returning the data from your Windows PowerShell script or function because once you use Write-Host, you are done.

Note  One of the things I like to tell my classes is that Write-Host equals “write-forever.” The data has reached the end of the line, and it is forever trapped on the console. You cannot do anything else with it.

Join me tomorrow when I will talk about using the objects that Windows PowerShell generates.

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 

Viewing all 3333 articles
Browse latest View live


<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>