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

PowerTip: Find Help about PowerShell Objects

$
0
0

Summary: Learn how to find Help about Windows PowerShell objects.

Hey, Scripting Guy! Question How can I find Help about Windows PowerShell objects?

Hey, Scripting Guy! Answer In Windows PowerShell 4.0 or Windows PowerShell 3.0, open the Windows PowerShell console
          with Admin rights, and run Update-Help.
           (For more information, see Understanding and Using Updatable PowerShell Help.)

Then use the Get-Help cmdlet:

Get-Help about_Objects


A PowerShell Object Lesson: Part 2

$
0
0

Summary: Microsoft Scripting Guy, Ed Wilson, talks about the importance of returning objects to ease administrative work.

Microsoft Scripting Guy, Ed Wilson, is here. This afternoon I am sipping a magnificent cup of Oolong green tea. I added a spoonful of jasmine buds to my teapot, in addition to a half cinnamon stick and a spoonful of lemon grass. The result is perfect. It goes well with the macadamia nuts I am munching on right now.

Note  This is the second part of a three part series that talk about Windows PowerShell objects. In A PowerShell Object Lesson: Part 1, I talked about the advantage of using Windows PowerShell objects, and how they are helpful to the scripter.

I got it through the pipeline

The next really cool thing about objects is that I can send them down the pipeline to other commands. The other commands might do things or format things. For example, a common way that people who come from other scripting languages write scripts is to “echo” everything. When they find the Write-Host cmdlet, they immediately begin to select the properties they want to display. An example of such a script is shown in the following image:

Image of script

In addition to wasting a lot of time selecting properties, it takes a long time to write all that code. I am actually stepping back in time in terms of usefulness and productivity. The default output from Get-Process is actually better, in my opinion—and as will be seen in a bit, it is more useful. Here is the default output from Get-Process:

Image of command output

If I send the output to the Get-Member cmdlet, I can see that there are more properties available than just the default displayed here. In fact, there are a number of methods as shown in the following image:

Image of command output

The cool thing about methods is that methods do things. After I use the Get-Member cmdlet, the first thing that appears at the top of the screen is the TypeName line. It tells me that I have an instance of the System.Diagnostics.Process object. If I do not know what that is, I can look up that exact phrase on MSDN, and it will tell me everything about the object—including how to use the various methods that appear in the output.

The cool thing about Windows PowerShell and objects is that I do not need to dive into the .NET Framework or MSDN to be able to do some things. For example, my Process object has the following cmdlets that automatically work:

PS C:\> Get-Command -Noun process

 

CommandType     Name                                               ModuleName

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

Cmdlet          Debug-Process                                  Microsoft.Powe...

Cmdlet          Get-Process                                       Microsoft.Powe...

Cmdlet          Start-Process                                     Microsoft.Powe...

Cmdlet          Stop-Process                                     Microsoft.Powe...

Cmdlet          Wait-Process                                      Microsoft.Powe...

For example, if I want to stop the Notepad process, I can open MSDN and figure out how to call the Kill()method, or I can simply try the Stop-Process cmdlet. When I take the process object that comes from Get-Process and send it to the Stop-Process object, it automatically stops the processes. The command is shown here:

Get-Process notepad | Stop-Process

The following image illustrates the command, and then shows that Get-Process can no longer find any Notepad processes:

Image of message

Without using the pipeline, I would need to call the Killmethod. It is not difficult, but it is another step. Here is an example:

PS C:\> $process = Get-Process notepad

PS C:\> $process.Kill()

Join me tomorrow when I will talk more about working with objects.

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 the Type of an Object

$
0
0

Summary: Use Windows PowerShell to find the type of an object.

Hey, Scripting Guy! Question How can I use Windows PowerShell to easily find the type of object that is stored in a variable?

Hey, Scripting Guy! Answer Use the GetType method (which all objects have):

PS C:\> $date = get-date

PS C:\> $date.GetType()

 

IsPublic IsSerial Name                     BaseType

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

True     True     DateTime                  System.ValueType 

A PowerShell Object Lesson: Part 3

$
0
0

Summary: Microsoft Scripting Guy, Ed Wilson, talks about using Windows PowerShell to create custom objects.

Microsoft Scripting Guy, Ed Wilson, is here. Tonight is the PowerScripting PodCast. It is always a big deal around the Scripting House, because the Scripting Wife does all their scheduling for the crew and because of our shared interest in the Windows PowerShell community. Tonight will be a brilliant podcast (as they all are).

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

Creating custom object in Windows PowerShell

One of the cool things to do with Windows PowerShell is to create a custom object. In fact, when I create a function, I normally return an object. I consider it a Windows PowerShell best practice that functions should return objects.

So how do I use Windows PowerShell to create objects? Surprisingly, it is easy. Very easy. What may not be so easy, is finding a good example on the Internet. (Of course, if you search for “PowerShell” and “create custom object” on the Hey, Scripting Guy! Blog, you will find over 300 topics).

The Windows PowerShell 1.0 way

It is a bit sad when I see people using Add-Member to create custom objects. This was the way I had to create custom objects in the Windows PowerShell 1.0 days. Now, this is all good in that Add-Member still works for creating objects, but it is a lot of work.

I think people search for “creating a custom object,” and they arrive at old code somewhere. It is also possible that people search for scripts, find an old script, see how the script creates the custom object, and adopt the technique. Like I said, this is fine. It works. Here is an example of using Add-Member to create a custom object:

myhost = $env:COMPUTERNAME

$date = (get-date).ToString()

$object = New-Object psobject

$object = Add-Member -InputObject $object -MemberType `

 ScriptProperty -name host -value {$myhost} -PassThru

$object = Add-Member -InputObject $object -MemberType `

 ScriptProperty -name date -Value {$date} -PassThru 

$object

When I run the script, the output shown in the figure that follows appears in the output pane of the Windows PowerShell ISE.

Image of command output

When I pipe the $object variable to the Get-Member cmdlet, I see the following output:

PS C:\> $object | Get-Member

 

   TypeName: System.Management.Automation.PSCustomObject

 

Name        MemberType     Definition                      

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

Equals      Method         bool Equals(System.Object obj)  

GetHashCode Method         int GetHashCode()               

GetType     Method         type GetType()                  

ToString    Method         string ToString()               

date        ScriptProperty System.Object date {get=$date;} 

host        ScriptProperty System.Object host {get=$myhost;}

The Windows PowerShell 2.0 way

In Windows PowerShell 2.0, it became easier to create custom objects. This is because when I use the New-Object cmdlet, I can specify a hash table for the properties. I still use New-Object to create a new PSObject, but I now can specify the properties via the –Property parameter. I then create a hash table that assigns the property names and the property values.

$myhost = $env:COMPUTERNAME

$date = (get-date).ToString()

$object = New-Object psobject -Property @{

    host = $myhost

    date = $date }

$object 

The script is several lines shorter, and it is a whole lot cleaner (no line continuation marks needed, and no–PassThru switches to confuse people). The command and its output are shown in the following image:

Image of command output

The command still creates a custom object. The members of the object are shown here:

PS C:\> $object | Get-Member

 

   TypeName: System.Management.Automation.PSCustomObject

 

Name        MemberType   Definition                             

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

Equals      Method       bool Equals(System.Object obj)         

GetHashCode Method       int GetHashCode()                      

GetType     Method       type GetType()                          

ToString    Method       string ToString()                      

date        NoteProperty System.String date=10/28/2013 5:53:03 PM

host        NoteProperty System.String host=EDLT       

The Windows PowerShell 3.0 way

In Windows PowerShell 3.0 (and this continues in Windows PowerShell 4.0), it is even easier to create custom objects. This is because we added a type accelerator, and therefore it is not necessary to use the New-Object command. I use the [PsCustomObject] type accelerator to cast a hash table that contains property names and values into a custom Windows PowerShell object.

This is by far, the cleanest way to create a custom object. The script is a bit shorter, and it is much more readable than the way I had to do things in the Windows PowerShell 2.0 world.

$myhost = $env:COMPUTERNAME

$date = (get-date).ToString()

$object = [pscustomobject]@{

    host = $myhost

    date = $date }

$object 

The command and associated output are shown in the following image:

Image of command output

When I pipe the object to the Get-Member command, I see the following output:

PS C:\> $object | Get-Member

 

   TypeName: System.Management.Automation.PSCustomObject

 

Name        MemberType   Definition                             

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

Equals      Method       bool Equals(System.Object obj)         

GetHashCode Method       int GetHashCode()                      

GetType     Method       type GetType()                         

ToString    Method       string ToString()                      

date        NoteProperty System.String date=10/28/2013 6:04:06 PM

host        NoteProperty System.String host=EDLT                

Join me tomorrow when I have a guest blogger who will talk about Windows PowerShell and security.

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 Create a Date String

$
0
0

Summary: Use Windows PowerShell to create a date string that has a month, day, and year.

Hey, Scripting Guy! Question How can I use Windows PowerShell to create a string that represents the date with the month, day, and year?

Hey, Scripting Guy! Answer Feed a pattern of ‘M/d/y’ to the ToString method from Get-Date:

PS C:\> (get-date).ToString('M/d/y')

10/28/13

Leveraging PowerShell to Help Minimize Risk While Performing Administrative Tasks

$
0
0

Summary: Guest blogger, Marc Carter, talks about learning to use Windows PowerShell and reducing security risks.

Microsoft Scripting Guy, Ed Wilson, is here. Today we have back with us guest blogger, Marc Carter. Take it away Marc…

Despite it becoming more and more of a necessity, I’m always surprised whenever I hear about colleagues who have yet to take the opportunity to really get acquainted with Windows PowerShell. What’s not surprising are the initial concerns revolving around security with anything (new) that if not adequately examined can become a stumbling block for those in my network. Like other products and technologies, Windows PowerShell is not immune to scrutiny and misunderstanding, whether due to lack of communication, assumptions, or insufficient understanding.

One of the responsibilities in my role as a sys admin is to evaluate and minimize change introduced into an environment that could potentially compromise information security. One method we implement to reduce that potential in our environment is to employ the Policy of Least Privilege and the Least-Privileged User Account approaches. The following quote about Applying the Principle of Least Privilege to User Accounts is posted on TechNet:

“A defense-in-depth strategy, with overlapping layers of security, is the best way to counter these threats, and the least-privileged user account (LUA) approach is an important part of that defensive strategy. The LUA approach ensures that users follow the principle of least privilege and always log on with limited user accounts. This strategy also aims to limit the use of administrative credentials to administrators, and then only for administrative tasks.”

Windows PowerShell plays a significant role in my ability to adhere to those best practices while allowing me to accomplish the routine and complex duties that are associated with my job. As asserted by Mr. Mike Krieger, U.S. Army Chief Information Officer, “PowerShell is a significant enabler for sys admins. It enables the automation of a significant number of mundane tasks without comprising security. So it improves operational effectiveness and efficiency and maintains same security levels.

In this blog post, I hope to dispel some misunderstandings that surround concerns over security, and demonstrate how Windows PowerShell lends itself to overcoming those issues. So, let’s take a look at one way we can take advantage of inherit qualities of Windows PowerShell by looking at the Invoke-Command cmdlet.

The Invoke-Command cmdlet runs commands on a local or remote computer and returns all output from the commands, including errors. With a single Invoke-Command command, you can run commands on multiple computers.

Accepted practice for every sys admin in our organization is to log on to their workstation environments with a non-privileged account, lacking elevated permissions on the domain. That account provides credentials sufficient to utilize office automation tools (such as email, document processing, and web surfing), but not much else.

Therefore, to simply view or control services in our server environment, I’m constantly faced with the hurdle of authentication. Although Remote Desktop permits me to abandon my cubical for occasional Starbucks iced coffee refills and subsequent restroom breaks, it inhibits my ability to manage several systems at one time. Neither can I easily extract information from those systems and compile it.

So, my first step is to open a Windows PowerShell console with my non-privileged account. By using the Get-Credential cmdlet, I specify the credentials for an account with sufficient permissions and save them in a $c variable. As long as I keep this Windows PowerShell session open, I can reuse those credentials as many times as needed by simply referring to the variable.

$c = Get-Credential <PSCredential>

Image of command output

This creates a credential object for a specified user name and password. More importantly, notice in the following example, that it stores the Password property as a System.Security.SecureString. The System.Security.SecureString class represents text that should be kept confidential. The text is encrypted for privacy when being used, and deleted from computer memory when it is no longer needed.

A SecureString object is similar to a String object in that it has a text value. However, the value of a SecureString object is automatically encrypted, and it can be deleted from computer memory by your application or the .NET Framework garbage collector. (For more information, see SecureString Class.)

Image of command output

A common scenario that I’m faced with is validating the existence and consistency of files between multiple servers. In the following examples, I’ll demonstrate how I utilize Invoke-Command to compare files on two SharePoint web front end (WFE) servers. Keep in mind that the only time I’m utilizing my elevated credentials is during the execution of my command on the remote systems.

Within our farm, we utilize a Site Map web part, to display the site structure in a hierarchical tree view. It requires me to deploy a .sitemap XML file to both WFEs and maintain strict consistency between both servers.

Note  For my examples I’ll be connecting to systems running Windows Server 2008 R2 and configured with the Windows Remote Management (WinRM) tool which is the Microsoft implementation of the WS-Management Protocol. For more information, see An Introduction to WinRM Basics.

One of the cool things about Invoke-Command is the default output that includes the PSComputerName property, which returns the system name with each result. This makes it easy for me to quickly compare results.

I simply specify the name of each of my WFEs, and the –ComputerName parameter allows me to use the NETBIOS name, IP address, or fully qualified domain name of one or more computers in a comma-separated list. I pass my elevated credentials within –Credential parameter, and the command that I want to execute within the –ScriptBlock parameter:

Invoke-Command -ComputerName wfe1 -Credential $c -ScriptBlock { dir "c:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\12\TEMPLATE\LAYOUTS\*.sitemap" }

Image of command output

In the previous example, I first executed Invoke-Command with my Windows PowerShell session credentials. Notice the Access is denied error message. Next I executed the same command again, this time specifying my stored credentials contained within the $c variable. In the directory that lists the results at the bottom of the console window, I highlighted (in yellow) the differences in the file results. Obviously, a mistake like this could cause our SharePoint site to not render properly and be unavailable.

This is only one way to leverage Windows PowerShell to help minimize risk while performing administrative tasks. By using this basic example, I can execute commands on a number of systems concurrently again without exposing my elevated credentials to all the other desktop office applications running on my workstation. After all, it’s one thing to compromise your workstation by browsing to a website or opening a file with malicious code when logged in as a non-privileged user—it’s a resume-generating event if you do that with your domain or network admin account.

So there we have it, an exercise in secure automation and system administration. Can it get any better? Well actually, on the topic of securing credentials, I recommend an interesting article by Windows PowerShell MVP, Tome Tanasovski. Storing Passwords to Disk in PowerShell with Machine-key Encryption presents some great insight and easy-to-follow steps for how to encrypt and store a password to disk so that it can be used later.

~Marc

Thank you, Marc, for sharing your time and knowledge. 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 Determine System Stability

$
0
0

Summary: Use Windows PowerShell to determine the stability of your system.

Hey, Scripting Guy! Question How can I use Windows PowerShell 4.0 on my laptop running Windows 7 to find out how stable my system is?

Hey, Scripting Guy! Answer Use the Get-CimInstance cmdlet to query the Win32_ReliabilityStabilityMetrics, and pipe
          the result to Measure-Object to determine the average value of the SystemStabilityIndex:

Get-CimInstance Win32_ReliabilityStabilityMetrics | measure SystemStabilityIndex -Average 

Weekend Scripter: Use PowerShell to Back Up SharePoint Installation

$
0
0

Summary: Microsoft SharePoint MVP and guest blogger, Todd Klindt, talks about using Windows PowerShell to back up a SharePoint environment.

Microsoft Scripting Guy, Ed Wilson, is here. Please welcome back guest blogger, Todd Klindt. Todd also has a blog at http://www.toddklindt.com/blog/default.aspx and you can find him on Twitter @ToddKlindt. Take it away Todd…

When I set up a new SharePoint environment for a customer, whether it’s big or small, I always have the disaster recovery talk with them. There are a few different disaster recovery options, and each is right depending the circumstances. In some cases I suggest big expensive enterprise backup solutions. In other cases, I tell them to perform a SQL Server database backup and be done with it. Each situation is different and requires some discussion to decide which disaster recovery solution is appropriate. In a few cases, I’ve suggested the built-in farm backups in SharePoint. Farm backups don’t get a lot of respect, and some of that disrespect is well earned. One of the common complaints is that they’re not easy to schedule.

There’s got to be a fun, easy way to do this in Windows PowerShell…

With SharePoint 2010, if you wanted to schedule a farm backup by using Backup-SPFarm, you had to do this complicated dance of using the Windows Task Scheduler to execute a .cmd file that fires up Windows PowerShell, which executes a .ps1 file. All it needed was a marble and a mousetrap to be complete.

SharePoint 2013 requires Windows PowerShell 3.0, which introduces a new object, ScheduledJob. To learn all about them, see about_Scheduled_Jobs. In this blog post, I’m going to walk you through using the job functionality in Windows PowerShell to easily schedule a full farm backup in SharePoint. You can then use this technique for smaller farms or non-production farms, where rudimentary backups are okay. Enough ado—let’s take a look at how to do it.

Create your script

The first step is to create your backup script. This is the thing we’re going to schedule to run. It doesn’t need to be anything fancy. When I was testing this, here’s the script I used:

Add-PSSnapin microsoft.sharepoint.powershell

Backup-SPFarm -Directory '\\sp2013server01\Backups' -BackupMethod Full

Image of command output

Nothing fancy at all. It adds the SharePoint snap-in (because we aren’t starting this from the SharePoint Management Shell), and it runs the simplest of Backup-SPFarm scripts. It does a full farm backup and saves it into the share at \\sp2013server01\Backups. All the same rules apply as when using this from Central Admin or interactively in Windows PowerShell. You must specify a UNC path, not a local directory. The backup activities are spread out amongst the servers in your farm, so a local path won’t cut it. The server running SQL Server gets in on the fun too, so your UNC path must be accessible from that server, and the account that the SQL Server service is running as must have Read/Write access to it.

Although this blog post is about backups, you can schedule a script to do anything. It could generate a report about Site Collection owners. It could upload files to a SharePoint document library. It could warm up all your web apps. It could email your friends a link to your favorite new meatloaf recipe…anything. Your imagination is the limit. Whatever your script is, make sure you run it from an interactive prompt to verify that it works. It’s much easier to troubleshoot that way than after you’ve scheduled it.

Create the scheduled job

Now that we’ve got a working script, let’s turn that into a scheduled job. There are some mechanics to this, but it’s pretty easy to get the hang of it. Here’s how it looked when I created the scheduled job to run my backup script:

$password = ConvertTo-SecureString 'pass@word1' -AsPlainText -Force
$account = New-Object system.management.automation.pscredential 'contoso\sp_install', $password
$opt = New-ScheduledJobOption -RunElevated
Register-ScheduledJob -Name BackupSharePoint -FilePath E:\Scripts\BackupSharePoint.ps1 -Credential $account -ScheduledJobOption $opt

Image of command output

Because we’re scheduling this to run, we need to tell Windows who to run it as. The first two lines deal with that. The first line creates an object, $password, that stores an encrypted version of the account’s password. The next line takes that and the account’s name and creates a PSCredential object. The third line creates an object, $opt, that is the options we’re going to set on our scheduled job. There are a bunch of different options we can set and New-ScheduledJobOption does a good job of outlining them. In our case, we need to make sure the script runs as Elevated. There are other options, such as Run on battery, which I hope doesn’t apply to any of your servers.

The next line registers our new scheduled job. You have to give it a name. I named mine BackupSharePoint, so there was no confusion as to what that Job does. I plan on winning the lottery some day; and when I do, I want to leave things as nice as possible for my former coworkers. That way, if I blow all my lottery winnings on Pez dispensers, maybe I can get my old job back.

We specify the FilePath to the script we want to schedule. Point that to the script we wrote earlier. The Register-ScheduledJob cmdlet also supports running only a snippet of Windows PowerShell script, instead of a script file. I prefer the file route because that gives me some flexibility. If I decide I want to do something differently, I can easily change the text in the .ps1 file as opposed to tweaking the ScheduledJob object.

We pass our Credential object so Windows PowerShell knows who to run the job as, and then, we pass it our options. That’s it, the job has been created and it’s ready to do our bidding. Well, all except for one bit, but we’ll cover that in a minute.

Test the scheduled job

When we registered our job, you might have noticed that one particularly important piece of information was missing. We have the “who,” and we have the “what.” We have a “where” and even a “how.” What we don’t have is a “when.” The “when” is the whole point of this! Don’t worry, we’ll get to that...

Before I schedule the job I want to test it. I know what you’re thinking, “We’ve already tested it.” We’ve already tested the script, but we haven’t tested the job. The job has a couple of other attributes that I wanted to make sure were good before I went any farther. We can use Start-Job to run our job the way Windows PowerShell will when it runs it as part of a schedule. Here’s what it looked like when I tested the job:

Start-Job -DefinitionName BackupSharePoint

Image of command output

I used Start-Job, passed it the name we gave the scheduled job, and off it ran. We can run Get-Job to see what jobs are currently running. The state will change to Completed when it has finished. When the job shows Completed, and you’ve verified that the backup actually worked (check the “spbackup.txt” file and search for “error”), we can schedule the job.

Schedule the job

When scheduling jobs, Windows PowerShell uses “triggers.” Jobs can be started by time or by events, so trigger is a pretty good word for them. In my case, I wanted the backups to run at 2:00 AM Sunday. To do that, I used the New-JobTrigger cmdlet. Here’s how it looked:

$t = New-JobTrigger -Weekly -DaysOfWeek Sunday -At "02:00"
Get-ScheduledJob -Name BackupSharePoint | Set-ScheduledJob $job -Trigger $t

Image of command output

New-JobTrigger is a pretty easy going cmdlet. I want my job to run weekly, it has a –Weekly parameter. I want it to run on Sunday, it has a –DaysOfWeek parameter that takes Sunday as a parameter. If I wanted my job to run multiple days of the week, I could put multiple days there. It’s very easy to configure. If it were any easier to use, it would need one of those big “Easy” buttons on it.

After I created my new JobTrigger object, I assigned it to the scheduled job I already created. I did that with a double-teaming of Get-ScheduledJob and Set-ScheduledJob. If I wanted to be fancy, I could have created my trigger and assigned it to my scheduled job when I first registered it. I’m new to scheduled jobs though, so I decided baby steps were the way to go. When I create my next scheduled job, I may throw caution to the wind and register it with the trigger in one step. I’ll make sure I eat a healthy breakfast beforehand.

Now our job is defined and scheduled to run every Sunday morning at 2:00. If you’re curious and poke around, you’ll see this is leveraging the Windows Task Scheduler in the background. I can use the Get-ScheduledTask cmdlet to see all the Task Scheduler tasks. In the following screenshot, I filtered on all the Scheduled Tasks that are associated with Windows PowerShell:

Get-ScheduledTask | Where-Object -Property TaskPath -Value *powershell* -Like

Image of command output

If you want to dig into the task more, you can get the object and look at its properties by using Get-Member to discover them and Select-Object to display them.

Triumphant conclusion

I’m happy to report that my scheduled job performed admirably. Sunday morning after I got up and got my wits about me with some coffee, I checked. Sure enough. There was a SharePoint backup in my Backup folder.

Image of menu

Remember that there’s a lot you can do with these scheduled jobs. Backups are just scratching the surface. I hope this blog post inspires you to automate more of your Windows PowerShell tasks.

~Todd

Thank you, Todd, for sharing your time and knowledge.

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 Display Syntax for a Cmdlet

$
0
0

Summary: Use Windows PowerShell to display the syntax of a Windows PowerShell cmdlet.

Hey, Scripting Guy! Question How can I use Windows PowerShell to look at the syntax of a cmdlet without the distraction of a lot of additional information?

Hey, Scripting Guy! AnswerUse the Get-Help cmdlet, and choose only the Syntax property. 
          (The following example uses the Get-Process cmdlet):

PS C:\> (get-help get-process).syntax

 

Get-Process [[-Name] <String[]>] [-ComputerName <String[]>] [-FileVersionInfo]

[-Module] [<CommonParameters>]

Get-Process [-ComputerName <String[]>] [-FileVersionInfo] [-Module] -InputObject

<Process[]> [<CommonParameters>]

Get-Process [-ComputerName <String[]>] [-FileVersionInfo] [-Module] -Id <Int32[]>

[<CommonParameters>]

Get-Process -Id <Int32[]> -IncludeUserName [<CommonParameters>]

Get-Process -IncludeUserName -InputObject <Process[]> [<CommonParameters>]

Get-Process [[-Name] <String[]>] -IncludeUserName [<CommonParameters>] 

Weekend Scripter: PowerShell Does Scientific Notation

$
0
0

Summary: Microsoft PFE and guest blogger, Clint Huffman, talks about scientific notation and Windows PowerShell.

Microsoft Scripting Guy, Ed Wilson, is here. Today we have another guest blogger from the past—Clint Huffman. The keyboard is yours Clint…

In Performance Monitor, if a number in a field is too large to display, scientific notation is used to represent the number. A value such as 30,064,771,072 (28 GB) is too large for a field to display, so it is represented as 3.0064e+010. This is not an error—it is scientific notation. It indicates that the decimal point must be moved to the right the number of times indicated after the plus (+) symbol. There are 9 digits behind a gigabyte, so the number 2.8637e+010 in the following image appears as a “ball park” figure of 28 GB. But after dividing each thousands group by 1024, the true answer is 26.7 GB.

Image of menu

Math is hard for me, and I avoid it when I can, so I use Windows PowerShell to help me with it. Windows PowerShell can convert values in gigabytes and return the number in bytes or vice-versa. For example, Windows PowerShell can convert 35GB into the full numeric value of 37580963840 in bytes.

Image of command output

It can convert scientific notation into a full numeric value. For example, in the previous image, I typed the value 3.7580e+010 from Perfmon into Windows PowerShell and it returned 37580000000. Knowing what this number is in gigabytes would be nice, so I divided it by 1GB (Windows PowerShell understands KB as kilobytes, MB as megabytes, and so on) to give me the number in gigabytes. In this case, it didn’t quite return an even 35 GB because the actual number was truncated by Perfmon, but it is close enough.

Image of command output

The editor in me has to add this…

Proper technical documentation is to use a space between the number and the unit, such as 10 GB. The reason I specified 35GB earlier is because in order for Windows PowerShell to interpret the number as 35 gigabytes, the space must be removed between number and the unit—35<space>GB will throw an error.

Enjoy!

~Clint

Cool. Thank you, Clint. 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: Back-to-Basics Math Class

$
0
0

Summary: Learn how to find Windows PowerShell math methods.

Hey, Scripting Guy! Question How can I use Windows PowerShell to find math methods so I can perform some mathematical calculations?

Hey, Scripting Guy! Answer Use the [math] type accelerator, pipe it to the Get-Member cmdlet, and use the –Static operator:

[math] | get-member -Static

The Admin’s First Steps: Capacity Planning Part 2

$
0
0

Summary: Richard Siddaway talks about using Windows PowerShell to store data for capacity planning.

Hey, Scripting Guy! Question Hey, Scripting Guy! I’ve just starting learning PowerShell and I understand how to use it as a scripting language and shell. I’ve been told to start performing capacity planning for my servers, and I am wondering how I can use Windows PowerShell to help with this task.

—PH

Hey, Scripting Guy! Answer Hello PH,

Honorary Scripting Guy, Richard Siddaway, here today filling in for my good friend, The Scripting Guy. As part of The Admin’s First Steps series, this is the second of three posts about capacity planning. Hopefully, this will answer your question.

In the last post, you saw how to use Windows PowerShell to collect data for capacity planning purposes. Techniques to collect data on disk space and network usage were shown. Capacity planning is all about observing trends, which means that you need to store the data somewhere.

Windows PowerShell doesn’t supply a storage mechanism, but it does supply the means to interact with your chosen storage to enable you to add data—and maybe more importantly, get data back out of storage for creating your reports. Your boss isn’t going to want a table of figures; he’ll want nicely formatted pretty graphs…but that’s a problem for next time.

You have a number of options for storing data:

  • Flat files, such as .csvfiles
  • XML
  • Excel
  • Database

Which one you choose comes down to your personal choice and the tools that you have available. For the purposes of this post, I’m going to use SQL Server. The techniques presented here could be easily adapted to another database such as Access.

Windows PowerShell is famous for providing multiple ways of achieving a task, and working with SQL Server is no different. You have two main options:

  • Use the Invoke-SQLCommand cmdlet from the SQL Server tools
  • Use ADO.NET in a script

Using the cmdlet is the simplest method, and it reduces the amount of work you have to perform. ADO.NET isn’t dependent on anything else, so it’s available on any computer. I’m going to show you both methods, starting with the cmdlet.

I’m going to assume that you have a database available with a table created. I’m going to use CAPREP as my database. Following on from last week’s post, I’m going to concentrate on the disk capacity data. You need a table in the database to store this data. I’m going to use DiskCAP as my table name and create it with the structure shown in the following image:

Image of table

Note  If you don’t know how to create the database and table, ask your nearest friendly database administrator.

I know that this isn’t the best table structure that could be created, but its designed to illustrate how to use Windows PowerShell against a SQL Server table, not be a treatise on database design. I’m going to use SQL Server 2008 for my scripts, but that is only because I already have a machine built with that configuration. Any of the later versions of SQL Server would work just as well for your purposes, as would the free SQL Server Express.

The way you normally learn how to use SQL Server is that you are shown how to select data out of the database first, and then you are shown how to add data. You don’t learn how to create your capacity planning reports until the next week. So I’m going to reverse that order and start with inserting data.

A data insertion query can take a number of forms, but this is the one I prefer:

$query = "INSERT DiskCAP

(Rundate, ComputerName, Drive, Capacity, Freespace, PercentUsed, PercentFree)

VALUES ('$date', '$($_.PSComputerName)', '$($_.Caption)', $($_.Capacity_GB), $($_.FreeSpace_GB), $($_.PercentUsed), $($_.PercentFree) )"

Create a string starting with the INSERT command and the name of table (in this case, DiskCap). The table’s column names are listed next in a comma-separated list surrounded by parentheses. There are ways to avoid listing the columns, but I prefer to do it this way so that I have an explicit record of what I’m doing. Never assume—it will bite you one day.

The VALUES command is next, and a comma-separated set of values to insert into the columns is provide in parentheses. String values have to be surrounded by single quotation marks.

Now, how do you use that query? I included the Get-DiskCapacity function from the last post for completeness—although, notice that I’ve added the parameter attribute to enable the function to take pipeline input.

function get-diskcapacity {

[CmdletBinding()]

param (

 [parameter(ValueFromPipeline=$true,

   ValueFromPipelineByPropertyName=$true)]

 [string[]]$computername = $env:COMPUTERNAME

)

PROCESS {

foreach ($computer in $computername) {

Get-CimInstance -ClassName Win32_LogicalDisk -Filter "DriveType = 3" -ComputerName $computer |

select PSComputerName, Caption,

@{N='Capacity_GB'; E={[math]::Round(($_.Size / 1GB), 2)}},

@{N='FreeSpace_GB'; E={[math]::Round(($_.FreeSpace / 1GB), 2)}},

@{N='PercentUsed'; E={[math]::Round(((($_.Size - $_.FreeSpace) / $_.Size) * 100), 2) }},

@{N='PercentFree'; E={[math]::Round((($_.FreeSpace / $_.Size) * 100), 2) }}

} # end foreach

} # end PROCESS

}

 

$sess = New-PSSession -ComputerName w08r2sql08

Invoke-Command -Session $sess -Command {Add-PSSnapin SqlServerCmdletSnapin100}

Import-PSSession -Session $sess -CommandName 'Invoke-Sqlcmd'

 

$date = (Get-Date -Format 'yyyyMMdd HH:mm:ss').ToString()

$servers = 'server02','w12sus', 'win12r2'

$servers | get-diskcapacity |

foreach {

 $query = "INSERT DiskCAP

(Rundate, ComputerName, Drive, Capacity, Freespace, PercentUsed, PercentFree)

VALUES ('$date', '$($_.PSComputerName)', '$($_.Caption)', $($_.Capacity_GB), $($_.FreeSpace_GB), $($_.PercentUsed), $($_.PercentFree) )"

Invoke-Sqlcmd -Query $query -ServerInstance w08r2sql08 -Database CAPRep

}

 

Remove-PSSession $sess

A Windows PowerShell remote session is created to the machine running SQL Server after the function definition. The SQL Server snap-in, which contains the cmdlets I want, is loaded into the session. In later versions of SQL Server, this line should become:

Invoke-Command –Session $sess –Command {Import-Module sqlps}

The session is imported into my current Windows PowerShell session, but I limit the commands that are imported to Invoke-Sqlcmd. This avoids all of the warnings you’d normally get about commands not being imported because they already exist.

The date is formatted so that SQL Server will accept it. I’ve used a culture-neutral format of year, month, day and then time. The list of servers from which I want to retrieve data is defined; however, they could just as easily be pulled from Active Directory, a .csv file, or even your capacity reporting database.

The list of servers is piped into the function, a query is created for each object, and Invoke-SQLcmd is used to run it. The database and server instance need to be supplied to Invoke-SQLcmd so that it can find your database table.

The final action of the script removes the remote session to the machine running SQL Server and destroys the module that is holding the imported commands.

You might not be able to use Invoke-SQLcmd for a number of reasons. The most common reason is that you aren’t allowed to enable remoting on machines running SQL Server. This is still a common occurrence. In that case, you need to drop back to ADO.NET, and the code changes to this:

function get-diskcapacity {

[CmdletBinding()]

param (

 [parameter(ValueFromPipeline=$true,

   ValueFromPipelineByPropertyName=$true)]

 [string[]]$computername = $env:COMPUTERNAME

)

PROCESS {

foreach ($computer in $computername) {

Get-CimInstance -ClassName Win32_LogicalDisk -Filter "DriveType = 3" -ComputerName $computer |

select PSComputerName, Caption,

@{N='Capacity_GB'; E={[math]::Round(($_.Size / 1GB), 2)}},

@{N='FreeSpace_GB'; E={[math]::Round(($_.FreeSpace / 1GB), 2)}},

@{N='PercentUsed'; E={[math]::Round(((($_.Size - $_.FreeSpace) / $_.Size) * 100), 2) }},

@{N='PercentFree'; E={[math]::Round((($_.FreeSpace / $_.Size) * 100), 2) }}

} # end foreach

} # end PROCESS

}

 

$conn = New-Object System.Data.SqlClient.SqlConnection("Data Source=W08R2SQL08; Initial Catalog=CAPRep; Integrated Security=SSPI")

$date = (Get-Date -Format 'yyyyMMdd HH:mm:ss').ToString()

$servers = 'server02','w12sus', 'win12r2'

 

$servers | get-diskcapacity |

foreach {

$conn.Open()

$cmd = $conn.CreateCommand()

$cmd.CommandText = "INSERT DiskCAP

(Rundate, ComputerName, Drive, Capacity, Freespace, PercentUsed, PercentFree)

VALUES ('$date', '$($_.PSComputerName)', '$($_.Caption)', $($_.Capacity_GB), $($_.FreeSpace_GB), $($_.PercentUsed), $($_.PercentFree) )"

$cmd.ExecuteNonQuery()

$conn.Close()

}

The Get-DiskCapacity function is defined as previously. This time New-Object is used to create a connection string to the database as follows:

  • Data source = the SQL Server instance
  • Initial Catalog = the database
  • Integrated Security=SSPI means use your Windows account

The date and the servers are defined as previously, and the server names are piped into Get-DiskCapacity. This function’s output is piped into Foreach-Object where the connection to SQL Server is opened and a command object IS created from the connection. The INSERT query you saw in the earlier example is used as the CommandText for the command, which is executed by using the ExecuteNonQuery() method. This method doesn’t expect any return data. The connection is then closed.

There are a number of ways to work with ADO.NET through Windows PowerShell. I prefer this method because I find it simple to use, and I know it works—definitely a result of finding something that works and sticking with it.

Ways you could extend this include:

  • Create a module for gathering data, and add a function to manage the queries in SQL Server
  • Test that data has been added to SQL Server and it looks correct

PH, that is how you store data for your capacity planning. Next time we’ll look at using the data to create reports.

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: Use PowerShell to Format Dates

$
0
0

Summary: Learn how to use Windows PowerShell to format dates.

Hey, Scripting Guy! Question How can I discover the formats that are available to use in Windows PowerShell when I format date and time information?

Hey, Scripting Guy! Answer One way is to keep this script handy and run it when you have a date formatting issue to output common formats:

"d ShortDatePattern                                                :  {0} " -f (get-date -Format d )

"D LongDatePattern                                                 :  {0} " -f (get-date -Format D )

"f Full date and time (long date and short time)                   :  {0} " -f (get-date -Format f )

"F FullDateTimePattern (long date and long time)                   :  {0} " -f (get-date -Format F )

"g General (short date and short time)                             :  {0} " -f (get-date -Format g )

"G General (short date and long time)                              :  {0} " -f (get-date -Format G )

"m MonthDayPattern                                                 :  {0} " -f (get-date -Format m )

"M MonthDayPattern                                                 :  {0} " -f (get-date -Format M )

"o Round-trip date/time pattern always uses the invariant culture  :  {0} " -f (get-date -Format o )

"O Round-trip date/time pattern always uses the invariant culture  :  {0} " -f (get-date -Format O )

"r RFC1123Pattern always uses the invariant culture                :  {0} " -f (get-date -Format r )

"R RFC1123Pattern always uses the invariant culture                :  {0} " -f (get-date -Format R )

"s SortableDateTimePattern always uses the invariant culture       :  {0} " -f (get-date -Format s )

"t ShortTimePattern                                                :  {0} " -f (get-date -Format t )

"T LongTimePattern                                                 :  {0} " -f (get-date -Format T )

"u UniversalSortableDateTimePattern                                :  {0} " -f (get-date -Format u )

"U Full date and time - universal time                             :  {0} " -f (get-date -Format u )

"y YearMonthPattern                                                :  {0} " -f (get-date -Format y )

"Y YearMonthPattern                                                :  {0} " -f (get-date -Format Y )

 

"`nCustom Formats"

"d/M/y                                  :  {0} " -f (get-date -Format d/M/y )

"%d/%M/yy                               :  {0} " -f (get-date -Format d/M/yy )

"dd/MM/yyyy                             :  {0} " -f (get-date -Format dd/MM/yyyyy )

"dd/MM/yyyy %g                          :  {0} " -f (get-date -Format 'dd/MM/yyyyy %g')

"dd/MM/yyyy gg                          :  {0} " -f (get-date -Format 'dd/MM/yyyyy gg')

"dddd dd/MM/yyyy gg                     :  {0} " -f (get-date -Format 'dddd dd/MM/yyyyy gg')

"dddd dd/MM/yyyy %h:m:s tt gg           :  {0} " -f (get-date -Format 'dddd dd/MM/yyyyy %h:m:s tt gg')

"dddd dd/MM/yyyy hh:mm:s tt gg          :  {0} " -f (get-date -Format 'dddd dd/MM/yyyyy hh:mm:s tt gg')

"dddd dd/MM/yyyy HH:mm:s gg             :  {0} " -f (get-date -Format 'dddd dd/MM/yyyyy HH:mm:s gg')

"dddd dd/MM/yyyy HH:mm:s.ffff gg        :  {0} " -f (get-date -Format 'dddd dd/MM/yyyyy HH:mm:s.ffff gg')

"dddd dd MMM yyyy HH:mm:s.ffff gg       :  {0} " -f (get-date -Format 'dddd dd MMM yyyyy HH:mm:s.ffff gg')

"dddd dd MMMM yyyy HH:mm:s.ffff gg      :  {0} " -f (get-date -Format 'dddd dd MMMM yyyyy HH:mm:s.ffff gg')

"dddd dd MMMM yyyy HH:mm:s.ffff zz gg   :  {0} " -f (get-date -Format 'dddd dd MMMM yyyyy HH:mm:s.ffff zz gg')

"dddd dd MMMM yyyy HH:mm:s.ffff zzz gg  :  {0} " -f (get-date -Format 'dddd dd MMMM yyyyy HH:mm:s.ffff zzz gg') 

Combine Automation Tasks by Using PowerShell Objects

$
0
0

Summary: Microsoft Scripting Guy, Ed Wilson, talks about using Windows PowerShell objects to combine output from multiple WMI classes.

Microsoft Scripting Guy, Ed Wilson, is here. I registered for Windows PowerShell Saturday #007, which will be held in Charlotte, NC. I am going to be presenting one or two talks (now I am scheduled for two talks, but we may end up with a special guest, and if we do, I will give him one of my slots). The lineup of speakers is absolutely stellar. I know that the Scripting Wife is busy working on a blog post that will describe the speakers and their sessions. This event is going to be the most awesome event outside of the Windows PowerShell Summit.

Note  Last week I wrote a three-part series that talked about Windows PowerShell objects:

Use objects to facilitate access to WMI data

There are thousands of WMI classes available in modern versions of Windows. The classes seem to vary in terms of usefulness, but they do represent a broad spectrum of classes. For some good articles about working with Windows PowerShell and WMI, see this collection of Hey, Scipting Guy! Blog posts.

One of the problems with WMI is that the classes are narrowly defined. I mean, one class covers the computer hardware, and another class covers the computer operating system. I generally want to know about both. For example, if I have a version of Windows that is running in 32-bit mode, I generally want to know if the hardware will support 64-bit, because I may want to reinstall the operating system to take advantage of greater amounts of memory.

An easy way to do this is to return a single object that contains the information I need.

Note  In this script, I am using the Get-CimInstance cmdlet, which is available in Windows PowerShell 4.0 and Windows PowerShell 3.0. If you are still using Windows PowerShell 2.0, substitute Get-WmiObject in the two places where I use Get-CimInstance.

The complete CombineWMIClasses.ps1 script is shown here:

# CombineWMIClasses.ps1

# ed wilson, msft

# HSG-11-12-13

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

$comp = Get-CimInstance -ClassName Win32_ComputerSystem

$os = Get-CimInstance -ClassName win32_OperatingSystem

$object = [pscustomobject]@{

    Make = $comp.Manufacturer

    Model = $comp.Model

    DNSName = '{0}.{1}' -f $comp.DNSHostName, $comp.Domain

    SystemType = $comp.SystemType

    Caption = $os.Caption

    InstallDate = $os.InstallDate

    LastBootUpTime = $os.LastBootUpTime

    OsArchitecture = $os.OSArchitecture

    BuildNumber = $os.BuildNumber

    ServicePack = '{0}.{1}' -f

      $os.ServicePackMajorVersion, $os.ServicePackMinorVersion}

 

$object

Gather the data

The first step is to gather the data. I decided to query two WMI classes and combine the output into a custom object that I need. The first command queries the Win32_ComputerSystem WMI class. I opened the Windows PowerShell console, and piped the results to the Format-List cmdlet to examine the information that is available from the class. The command is shown here:

Get-CimInstance Win32_ComputerSystem | Format-List *

Note  One of the advantages of using the Get-CimInstance cmdlet is that tab expansion works for the WMI classes, so I do not have to type the entire class name. On my laptop, I type Win32_Computer<tab> and it picks up the class on the second Tab press.

The output from the previous command is shown here:

Image of command output

I have the Windows PowerShell console pushed off of my main screen to my secondary monitor, so it is easy to refer to the output while I decide which properties I need to access.

I then do the same thing with the Win32_OperatingSystem WMI class as shown here:

Get-CimInstance Win32_OperatingSystem | Format-List *

The command and output appear in the following image:

Image of command output

Now, I decide to create the custom object. To do this, I use the [pscustomobject] type accelerator, and I pass a hash table to it with the properties I need. Here are the first three lines, where I gather the WMI data and I create the custom object:

$comp = Get-CimInstance -ClassName Win32_ComputerSystem

$os = Get-CimInstance -ClassName win32_OperatingSystem

$object = [pscustomobject]@{

From the Win32_ComputerSystem WMI class, I need the make, model, system type, and DNS name of the computer. I decide to combine the host name and the domain name to create the fully qualified domain name of my system. Here is that portion of the script:

Make = $comp.Manufacturer

Model = $comp.Model

DNSName = '{0}.{1}' -f $comp.DNSHostName, $comp.Domain

SystemType = $comp.SystemType

From the Win32_OperatingSystem WMI class, I want the caption of the Windows system, the install date, the last boot up, operating system architecture, build number, and service pack version. I decide to combine the major and minor service pack version information. Here is that portion of the script:

Caption = $os.Caption

InstallDate = $os.InstallDate

LastBootUpTime = $os.LastBootUpTime

OsArchitecture = $os.OSArchitecture

BuildNumber = $os.BuildNumber

ServicePack = '{0}.{1}' -f

 $os.ServicePackMajorVersion, $os.ServicePackMinorVersion}

When I run the script, it returns a nice object. I could then do anything I want to do with this new object.

Image of command output

That is all there is to using Windows PowerShell to create objects from multiple WMI classes. 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: Use PowerShell Cmdlet to Report CredSSP

$
0
0

Summary: Use a Windows PowerShell cmdlet to find the status of CredSSP.

Hey, Scripting Guy! Question How can I use Windows PowerShell 4.0 on my laptop running Windows 8.1 to find if WinRM is configured for CredSSP?

Hey, Scripting Guy! Answer Use the Get-WSManCredSSP cmdlet:

Get-WSManCredSSP


Use PowerShell to Simplify Collecting Computer Information

$
0
0

Summary: Microsoft Scripting Guy, Ed Wilson, talks about using Windows PowerShell to simplify collecting information from multiple Windows PowerShell cmdlets.

Microsoft Scripting Guy, Ed Wilson, is here. This morning, it is cool and crisp outside. I decided to make a pot of English breakfast, but I also put a spoonful of Darjeeling tea in it. Together with some lemon pith, a cinnamon stick, and a half spoon of Lavender, I have a very refreshing cup of tea to accompany me to the lanai.

Note  Last week I wrote a three-part series that talked about Windows PowerShell objects, which have information related to today’s post:

Collect information from multiple PowerShell cmdlets

One of the really cool things about Windows PowerShell is how easy it makes it to collect lots of useful information. For instance, when I begin to troubleshoot a computer, I look at the system and application event logs. Generally, in that order. I also look at processes and services. For this initial look, however, I do not want to look at all the events, I usually only look at the first few events. Also, for the initial look, I only look at running services. Later, I look for automatic starting services that are not running, but I do not do that initially.

I decided to put these four commands into a simple script, and to return a custom object with the collection of information. This makes it easy to get all the information, but it does not clutter stuff when I am going through the output. It is actually a pretty cool technique. Here is the script:

# CombineDotNetObjects.ps1

# ed wilson, msft

# hsg-11-13-13

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

$app = Get-EventLog -LogName application -Newest 5

$sys = Get-EventLog -LogName System -Newest 5

$proc = Get-Process

$serv = Get-Service | where status -eq 'running'

$object = [pscustomobject]@{

    Applog = $app

    SysLog = $sys

    Process = $proc

    Service = $serv}

$object

Now for the shocker

So I have this really cool script, but the output from the script is probably not what one might expect. In fact, when I talk to newbies about Windows PowerShell, generally one of the first things that comes up when dealing with objects is what to do when a property of an object contains other objects. This is, in fact the beauty of this technique. My custom object is simply a repository that holds the objects that returned from the earlier commands. So it is an object that is itself a collection of other objects. When I run the script, the output is less than impressive:

Image of command output

Less than impressive? In fact, the output is downright useless. But remember, that each property contains other objects. So I can use the $object object to access the objects stored in each property. For instance, here are the five application logs:

PS C:\> $object.Applog

 

   Index Time          EntryType   Source                 InstanceID Message                                                  

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

    6002 Oct 29 14:49  Information Microsoft-Windows...         2003 The Windows Location Provider has successfully shutdown  

    6001 Oct 29 14:48  Information Microsoft-Windows...         2001 The Windows Location Provider has successfully started   

    6000 Oct 29 14:48  Information LocationNotificat...            1 A program accessed information from a location sensor o...

    5999 Oct 29 14:44  Information Microsoft-Windows...         2003 The Windows Location Provider has successfully shutdown  

    5998 Oct 29 14:41  Information Microsoft-Windows...         2001 The Windows Location Provider has successfully started   

Here are the five system logs:

PS C:\> $object.SysLog

 

   Index Time          EntryType   Source                 InstanceID Message                                                   

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

   10114 Oct 29 15:03  Warning     Microsoft-Windows...         8020 The system failed to register host (A or AAAA) resource...

   10113 Oct 29 15:03  Warning     Microsoft-Windows...         1014 Name resolution for the name edlt.iammred.net timed out...

   10112 Oct 29 15:02  Error       NETLOGON                     5719 This computer was not able to set up a secure session w...

   10111 Oct 29 14:33  Warning     Microsoft-Windows...         8020 The system failed to register host (A or AAAA) resource...

   10110 Oct 29 14:33  Warning     Microsoft-Windows...         1014 Name resolution for the name edlt.iammred.net timed out...

When I access the Process property from the object, the output is exactly the same as would have been produced by the Get-Process cmdlet. Here is the command and the output:

Image of command output

I get the same sort of output from the Service property, as shown here:

Image of command output

Now, I could add a bit of additional script, and unravel all of the objects to the Windows PowerShell console when the script runs. But at that point, it is the same as running the four commands and not storing them in variables, but storing them in a custom object.

The big advantage comes from the fact that all the commands run at essentially the same time, so my object becomes a snapshot in time. I then have the opportunity to peruse the data in the stored objects and compare results from one to the other. This technique promotes interactive analysis and discovery.

That is all there is to using Windows PowerShell objects. 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 with Public Network

$
0
0

Summary: Learn how to use Windows PowerShell 3.0 to enable Windows PowerShell remoting on a network adapter that has a public network profile.

Hey, Scripting Guy! Question How can I enable Windows PowerShell remoting on my system if the network adapter has a public firewall profile?

Hey, Scripting Guy! Answer Beginning with Windows PowerShell 3.0, you can use the Enable-PSRemoting cmdlet
          with the –SkipNetworkProfileCheck parameter:

Enable-PSRemoting -SkipNetworkProfileCheck 

Use PowerShell Objects to Simplify Script Output

$
0
0

Summary: Microsoft Scripting Guy, Ed Wilson, talks about using Windows PowerShell to simplify creating script output.

Microsoft Scripting Guy, Ed Wilson, is here. All of the sessions for Windows PowerShell Saturday in Charlotte are pretty much set. The big question is whether we will have a really cool special guest, or a really, really cool special guest. Of course, you will never know the outcome. The tickets for the event have been going pretty quickly also. All of the previous Windows PowerShell Saturday events have sold out, and this one will be no exception. Last year, all the tickets sold out a couple of weeks before the event. This one may sell out even sooner. If you anticipate attending, sign in and get your ticket now. It is a pretty big deal. Last year, we had people from more than a dozen different states. It was a great time to get together, and talk about our favorite scripting language.

Now let’s talk about objects…

The easiest way to create an object is to pipe the object to the Select-Object cmdlet. This permits me to create a new object on the fly. In the following command, by using the Wheresyntax in Windows PowerShell 4.0, I find all the processes that are reporting CPU consumption and virtual memory consumption:

(Get-Process | select name, cpu, vm).where({$_.cpu -AND $_.vm})

Note The cool thing about the Wheresyntax in Windows PowerShell 4.0 is that Wherebecomes a method on an object. I can therefore use same syntax I used when I piped to Where-Object, but the syntax is a bit more compact. The syntax for Windows PowerShell 3.0 and earlier is:

Get-Process | select name, cpu, vm | where {$_.cpu -AND $_.vm}

So I am returning a series of custom objects to the Windows PowerShell console. The command and the associated output are shown here:

Image of command output

Now I am pretty much done. I can add this command to a script, and my job as a scripter is complete. I have created a Windows PowerShell command that returns all processes that return CPU and virtual memory consumption information.

The formatting of that output is now left as an exercise to the person who needs to use the information. For example, do they want a list or a table? Do they want to sort by memory or by CPU utilization?

So, I put the command in a function, and I can call the function interactively via the Windows PowerShell ISE console. Here is the function (and while I am at it, I create an alias to make it easier to use):

# DemoObjectOutput.ps1

# ed wilson, msft

# hsg-11-14-13

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

#Requires -version 4.0

 

Function Get-ProcMemCPU

{

 (Get-Process | select name, cpu, vm).where({$_.cpu -AND $_.vm})

}

New-Alias -Name gpmc -Value Get-ProcMemCPU -Scope global

When I run the function, it is loaded into memory. So, I first sort by CPU utilization:

Get-ProcMemCPU | sort cpu

But what if the output needs to descend from largest to smallest? Well, that is a different command:

Get-ProcMemCPU | sort cpu -Descending

What if the output needs to be tightened up a bit? No problem, send the output to Format-Table and use –AutoSize:

Get-ProcMemCPU | sort vm -Descending | Format-Table -AutoSize

This command and its output are shown here:

Image of command output

Maybe the output should show memory consumed in gigabytes instead of bytes. I use the following command to format the columns in the Format-Table output:

Get-ProcMemCPU | sort vm -Descending | Format-Table Name, cpu, @{L='Memory in Gig';E={$_.vm /1GB}}-AutoSize

The command and the output are shown here:

Image of command output

I can even pipe the output to the Out-GridView cmdlet. If I use the –PassThru parameter, the process object I select returns to the Windows PowerShell ISE console line. If I continued the pipeline, I just added a GUI tool to my Windows PowerShell script. The following command permits me to select the process via Out-GridView and terminate the process via Stop-Process:

Get-ProcMemCPU | Out-GridView -PassThru | Stop-Process

The following image illustrates Out-GridView:

Image of command output

So by returning an object from my script, I permit a nearly infinite choice of additional actions—everything from formatting the output a special way to using the command as a basis of a GUI tool that permits me to kill the process with the greatest memory utilization.

That is all there is to using Windows PowerShell objects. Join me tomorrow when guest blogger, Marc Adam Carter, will share his reprised use of Windows PowerShell to find an installed software blog post. It will be really cool, and he has made some great improvements.

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 Details about Setup Log

$
0
0

Summary: Use Windows PowerShell to get detailed information about the setup log.

Hey, Scripting Guy! Question How can I use Windows PowerShell to find information about the status of the setup log on my computer?

Hey, Scripting Guy! Answer Use the Get-WinEvent cmdlet, select the setup log, and then pipe the output to the Format-List cmdlet:

Get-WinEvent -ListLog setup | Format-List *

Use PowerShell to Find Installed Software

$
0
0

Summary: Guest blogger, Marc Carter, reprises his popular blog post about locating installed software.

Microsoft Scripting Guy, Ed Wilson, is here. Marc Carter is joining us again today with another guest blog post...

Looking back a couple years ago to my previous post, Use PowerShell to Quickly Find Installed Software, I find it interesting to reflect on common issues shared amongst the IT pro community. In our underlying goal to control our environment, whether that environment consists of a desktop computer, a development server, or production data center, we must first discover and understand before we can effectively attempt to control. Such is the case for sys admins when determining what software is currently configuring a server.

But first, let’s have a quick refresher on what initially prompted this discussion…

Win32_Product: The Good, the Bad, and the Ugly

 [Good] The Win32_Product WMI class represents products as they are installed by Windows Installer.

If you choose to query Win32_Product class by using Get-WmiObject, you’ll find yourself [Bad] waiting for your query (or application) to return [Ugly] a consistency check of packages that are installed as it attempts to verify and repair installs. (For more information, see Event log message indicates that the Windows Installer reconfigured all installed applications).

Problem #1: Um, is there a problem, officer?

Querying the Win32_Product class to determine installed software is more than likely not your “best” option. Unfortunately, not everyone knows this.

Solution: (Understanding) Do your part and help spread the word.

Problem #2: Identify better alternatives

I really like some of the refinements and suggestions within comments that were mentioned by others on my previous post. One of the things I take a lot of pride in is my association with the men and women of US Army and their core values (The Army Values).

I see that similar mindset and participation reflected in the esprit de corps (or cohesion) of the Windows PowerShell community. As others have pointed out, there are a lot better and easier ways to gather information without invoking the Win32_Product class. One of my favorite alternatives involved suggestions from Knut Johansen and Mike Crowley: use the PS Registry Provider.

The Windows PowerShell Registry provider lets you get, add, change, clear, and delete registry keys, entries, and values in Windows PowerShell. The Registry provider lets you access a hierarchical namespace that consists of registry keys and subkeys. Registry entries and values are not components of that hierarchy. Instead, they are properties of each of the keys. The Registry provider supports all the cmdlets that contain the “item” noun—that is, the Item cmdlets (except Invoke-Item) such as Get-Item, Copy-Item, and Rename-Item. Use the Item cmdlets when you work with registry keys and subkeys.
For more information, see Registry Provider.

In the following example, I use the Get-ItemProperty cmdlet to return values from the Uninstall Registry Key within the HKEY LOCAL MACHINE (HKLM) Registry Provider, selecting specific properties and then formatting output.

Get-ItemProperty HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\* |  Select-Object DisplayName, DisplayVersion, Publisher, InstallDate |
Format-Table –AutoSize

The Get-ItemProperty cmdlet is a great tool because it’s designed to work with data that is exposed by any provider. To get a better idea of the various providers that are available in your session, simply execute the Get-PSProvider cmdlet.  

And of course, depending on my needs, I could have also used alternative output methods like Out-GridView or Export-Csv. Either way, we’ve now reduced the process to a one-liner that can be used in 64-bit and 32-bit environments:

Get-ItemProperty HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\* | Select-Object DisplayName, DisplayVersion, Publisher, InstallDate |
Format-Table –AutoSize

Problem #3: Can we make it even more useful?

Absolutely! We are talking Windows PowerShell after all…

One way that comes to mind (and again, visible within the comments from the previous post), is addressing the issue of how to query multiple remote devices. My solution (or a number of reasons) is to rely on using the Invoke-Command cmdlet. In the following example, I query both of my SharePoint Web Front End (WFE) servers by using Invoke-Command to execute the same Get-ItemProperty on the remote system’s HKLM PS Registry Provider:

Invoke-Command -cn wfe0, wfe1 -ScriptBlock {Get-ItemProperty HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\* | select DisplayName, Publisher, InstallDate }

The output now includes the PSComputerName column, which will help when I want to sort results down the road. And there we have it…an easy method to report installed software!

I look forward to reading comments from the Windows PowerShell community on other refinements and ways to improve this task. In many ways, I relate our efforts to that of a symphony or band. Each of us plays a different note in that we all hear and see things differently. Put us all together on the same sheet of music, and we have the potential for some awesome melodies.

~Marc

Thank you, Marc, for another awesome blog.

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>