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

Use PowerShell to Show Update Messages from a Specific App

$
0
0

Summary: Microsoft Scripting Guy, Ed Wilson, shows how to use Windows PowerShell to display Windows Update messages from specific apps in Windows 8.

Hey, Scripting Guy! Question Hey, Scripting Guy! I need to find out how often I am getting Windows Update messages for various apps in Windows 8. It seems like I am having to update things quite often, but I want to make sure. Can you help me?

—JS

Hey, Scripting Guy! Answer Hello JS,

Microsoft Scripting Guy, Ed Wilson, is here. My Scripting Neighbor who works nights is out washing his motor vehicle right now. I know, because the window in my home office looks out over my front yard and into his driveway. The window shades, which I generally keep closed, are open as I continue to watch for my alleged new laptop to arrive. It is actually somewhat cool; it makes me feel connected to the world to see other humans from time to time. I am now wondering if I were to drive the Scripting Wife’s motor vehicle over to the Scripting Neighbor’s driveway if he would notice. Hmm, he might just go ahead and wash it. After the big Charlotte snow storm of 2013, the Scripting Wife’s vehicle could stand a bit of attention.

Anyway, so far my day is going great. I am drinking a nice Darjeeling tea with a half spoon of peppermint herb, lemon grass, and a crushed Cinnamon stick. The flavor is perfect for my Scottish shortbread biscuits (I am out of Anzac biscuits—have been for a while now). I am listening to my cool music channel via a really cool Pandora app I found for my Windows 8 laptop. Yes life is good…would be great if my new laptop would show up. (OK, I will quit whining and get back to work.)

Yes, JS. Using Windows PowerShell it is really easy to find Windows 8 app updates.

First, find the particular app

The first thing I need to do is to use Windows PowerShell to find entries that mention the particular app. This is really easy because each software package appears in the Message property of the event log entry.

Note   This is a continuation of the Use PowerShell to Find Windows 8 Modern App Updates log I wrote yesterday. You should read that blog first.

The event log entry for an Event 17 from the WindowsUpdateClient is shown in the image that follows.

Image of menu

To make sure of exactly where I can find the mention of a specific event log entry, I select a single event log entry and pipe it to the Format-List cmdlet as shown here.

Get-EventLog -LogName system -InstanceId 17 -source *update* -Newest 1 | fl *

The command and the associated output are shown in the image that follows.

Image of command output

OK, it looks good, but I still need to make sure. This is because sometimes cmdlets rename the display name from the actual property name—not too often, but it does happen. This is true of this particular cmdlet (the property Time does not actually exist. The property names are TimeWritten and TimeGenerated. To make sure I know what I am working with, I pipe the results to the Get-Member cmdlet (gm is an alias), and I select the Message property. This is shown here.

PS C:\> Get-EventLog -LogName system -InstanceId 17 -source *update* -Newest 1 | gm -

Name message

 

   TypeName:

System.Diagnostics.EventLogEntry#system/Microsoft-Windows-WindowsUpdateClient/17

 

Name    MemberType Definition

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

Message Property   string Message {get;}

This tells me that there is no special formatting for the Message property. It is a plain old everyday string. This means I can use the –match parameter with the Where-Object cmdlet to parse the results contained in the Message property. Therefore, I can find all messages that mention, for example, the Reader app. In fact, it gets better than that because I do not have to know the exact app name. For example, the BingMaps app name is Microsoft.BingMaps. The following command returns all event log entries that mention the Reader app.

Get-EventLog -LogName system -InstanceId 17 -source *update*| where message -match 'reader'

The command and its associated output are shown in the image that follows.

Image of command output

Group by month

One thing that I am interested in is how often these updates occur. To do this, I need to group the entries. Unfortunately, if I attempt to directly group by date, it all goes pear shaped. This is shown here.

PS C:\> Get-EventLog -LogName system -InstanceId 17 -source *update*| where message -

match 'reader' | group timewritten

 

Count Name                      Group

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

    1 2/21/2013 7:43:52 AM      {System.Diagnostics.EventLogEntry}

    1 2/20/2013 10:59:12 AM     {System.Diagnostics.EventLogEntry}

This is because each event log entry stores the time it was written as a DateTime object. Hmmm, seems like I have had this problem before. Hmmmm, how did I solve it?

Note   See my Troubleshoot Outlook Problems with PowerShellblog for more information about parsing event log entries. I had to refer to it for this section. It is quite good!

The trick is to use the Select-Object cmdlet and use the –ExpandProperty parameter to expand the time-written DateTime object. This permits easy access to any of the properites of a DateTime object. Properties of a DateTime object are shown here:

PS C:\> get-date | gm -MemberType property | select name, definition

 

Name                                       Definition

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

Date                                       datetime Date {get;}

Day                                        int Day {get;}

DayOfWeek                                  System.DayOfWeek DayOfWeek {get;}

DayOfYear                                  int DayOfYear {get;}

Hour                                       int Hour {get;}

Kind                                       System.DateTimeKind Kind {get;}

Millisecond                                int Millisecond {get;}

Minute                                     int Minute {get;}

Month                                      int Month {get;}

Second                                     int Second {get;}

Ticks                                      long Ticks {get;}

TimeOfDay                                  timespan TimeOfDay {get;}

Year                                       int Year {get;}

This means that after I expand the DateTime object, I can sort or group any of these properties as I see fit. Cool ...therefore I can filter defender updates by month. This command, and its associated output are shown here.

PS C:\> Get-EventLog -LogName system -InstanceId 17 -source *update*| where message -

match 'defender'  | select -ExpandProperty timewritten | group month

 

Count Name                      Group

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

   44 2                         {2/21/2013 7:43:20 AM, 2/20/2013 10:59:12 AM, 2/1...

   22 1                         {1/31/2013 12:31:11 PM, 1/30/2013 6:30:56 PM, 1/3...

   13 12                        {12/31/2012 4:20:47 PM, 12/30/2012 5:37:25 PM, 12...

   13 11                        {11/25/2012 7:23:21 AM, 11/22/2012 10:14:12 AM, 1...

   23 10                        {10/31/2012 8:58:26 PM, 10/31/2012 8:58:26 PM, 10...

    2 9                         {9/29/2012 6:26:45 PM, 9/22/2012 9:41:32 PM}

JS, that is all there is to parsing the event log for app updates. 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: Find the Number of Entries in Event Logs

$
0
0

PowerTip: Find the Number of Entries in Event Logs

Summary: Use Windows PowerShell to find the number of entries in all event logs and ETL logs?

 Hey, Scripting Guy! Question How can you use Windows PowerShell to show how many entries exist in all traditional event logs as well as ETL logs?

Hey, Scripting Guy! Answer Use the Get-WinEvent cmdlet and pass a wild card to the –ListLog parameter. Sort the recordcount in a descending manner, and select both the recordcount and logname. To tighten up the output, pipe the results to the Format-Table cmdlet and use –autosize.

Get-WinEvent -ListLog * -ea 0 | sort recordcount -Descending |

select recordcount, logname | Format-Table -auto

Get Exchange Online Mailbox Size in GB

$
0
0

Summary: Microsoft PFE, Brian Jackett, talks about using Windows PowerShell to get Exchange Online Mailbox size in GB.

Microsoft Scripting Guy, Ed Wilson, is here. Today I want to welcome back guest blogger, Brian T. Jackett.

Brian T. Jackett is a premier field engineer at Microsoft who has specialized in SharePoint development, Project Server, and Windows PowerShell for over four years. Brian is a steering committee member of the Buckeye SharePoint User Group, and he enjoys giving back to the community through giving presentations, planning and volunteering at conferences, maintaining a SharePoint/.NET-centric blog, and contributing to the SharePoint Twitterverse. He also holds several Microsoft Certified Technology Specialist (MCTS) certifications for SharePoint-related technologies.
Brian’s blog: The Frog Pond of Technology

Now, here’s Brian…

Office 365 is picking up in deployments and user adoption. Windows PowerShell is one tool for the administration of Office 365 that is similar to its on-premises counterparts, but it can have a few differences. In this post, I will discuss one way to get the size of an Exchange Online mailbox in bytes and consequently GBs.

Problem

Getting the size of an on-premises Exchange 2010 mailbox is fairly easy. In his blog The Get-MailboxStatistics Cmdlet, the TotalitemSize Property, and that pesky little “b”,  fellow PFE Gary Siepser, explains how to accomplish this task by using the mailbox TotalItemSize property with methods such as ToMB() and ToGB(). Note that Gary’s script will not work when remoting from a local machine that doesn’t have the Exchange object model installed or loaded.

A similar type of scenario exists if you are executing Windows PowerShell against Exchange Online. The data type for TotalItemSize being returned (ByteQuantifiedSize) exists in the Exchange namespace. If the WindowsPowerShell session doesn’t have access to that namespace (or hasn’t loaded it), Windows PowerShell works with an approximation of that data type.

The following script is a sample on the TechNet article View Mailbox Sizes and Mailbox Quotas Using Windows PowerShell, which a customer of mine tried, but it was failing. (I made minor edits to fit it on page and remove references to deleted item size.)

Get-Mailbox -ResultSize Unlimited |

  Get-MailboxStatistics |

  Select DisplayName,StorageLimitStatus, `

  @{name="TotalItemSize (MB)"; expression={[math]::Round( `

  ($_.TotalItemSize.Split("(")[1].Split(" ")[0].Replace(",","")/1MB),2)}}, `

  ItemCount |

  Sort "TotalItemSize (MB)" -Descending

Image of command output

The script is targeted to Exchange 2010, but it fails for Exchange Online. In Exchange Online, when referencing the TotalItemSize property, there is not a Split method, which ultimately causes the script to return no data for “Total Item Size (MB)” column.

Solution

A simple solution would be to add a call to the ToString method off of the TotalItemSize property (shown in bold on line 5 in the following script).

Get-Mailbox -ResultSize Unlimited |

  Get-MailboxStatistics |

  Select DisplayName,StorageLimitStatus, `

  @{name="TotalItemSize (MB)"; expression={[math]::Round( `

  ($_.TotalItemSize.ToString().Split("(")[1].Split(" ")[0].Replace(",","")/1MB),2)}}, `

  ItemCount |

  Sort "TotalItemSize (MB)" -Descending |

  Export-CSV "C:\My Documents\All Mailboxes.csv" -NoTypeInformation

Image of command output

This fixes the script to run but the numerous string replacements and splits are an eyesore to me. I attempted to simplify the string manipulation with a regular expression. (For more information about regular expressions in Windows PowerShell, see the Regular-Expressions.info website.)

The result is a working script that adds two pieces of functionality. First, the script allows you to input a specific user to search for (or all users if no input is supplied). A technique called splatting is used to allow “@Params” to send additional optional parameters to the Get-Mailbox cmdlet. Second, the script adds a new member to the mailbox statistics called TotalItemSizeInBytes. With this member you can then convert to any byte level that suits your needs (for example, KB, MB, or GB).

Note   You can download the full version of this script from the Script Center Repository (it includes commands to connect to Exchange Online session):  
Get Exchange Online Mailbox Size in GBs

$userToFind = Read-Host -Prompt "Enter user to find (leave blank for all)"

 

$params = @{}

if([string]::IsNullOrEmpty($userToFind) -eq $false)

{$params = @{Identity = $userToFind}

}

 

$UserMailboxStats = Get-Mailbox -RecipientTypeDetails UserMailbox `

  -ResultSize Unlimited @Params |

  Get-MailboxStatistics

$UserMailboxStats |

  Add-Member -MemberType ScriptProperty -Name TotalItemSizeInBytes `

  -Value {$this.TotalItemSize -replace "(.*\()|,| [a-z]*\)", ""}

$UserMailboxStats |

  Select-Object DisplayName, TotalItemSizeInBytes,@{Name="TotalItemSize (GB)"; `

  Expression={[math]::Round($_.TotalItemSizeInBytes/1GB,2)}}

Image of command output

The regular expression‘$this.TotalItemSize –replace "(.*\()|,| [a-z]*\)", ""’ is searching for the following three patterns and replacing them with empty strings. The result is the total item size in bytes with no other extra characters.

  1. .*\( = Any number of characters followed by an opening parentheses
  2. , = Commas
  3. [a-z]*\) = Any number of alphabetic characters followed by a closing parentheses (i.e. “bytes)”)

Moving from on-premises to the cloud with Windows PowerShell (and Windows PowerShell remoting in general) can sometimes present new challenges due to what you have access to. This means that you must always test your code and scripts.

Photo

I still believe that not having to physically RDP to a server is a huge gain over some of the small hurdles that you may encounter during the transition. Scripting is the future of administration, and it makes you more valuable. Hopefully this script and the concepts presented help you be a better admin and developer.

For more information, watch my video series: The One Thing: Brian Jackett and SharePoint 2010.

—Frog Out

Links

Awesome job, Brian. Thanks for an informative and helpful guest blog post. Join me tomorrow when I will talk about using Windows PowerShell to explore process threads.

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 Easily Find How Long a Process Runs

$
0
0

Summary: Use Windows PowerShell to show easily how long a process runs.

Hey, Scripting Guy! Question You need to find how long a particular process has been running. How can you easily do this with Windows PowerShell?

Hey, Scripting Guy! Answer Use the New-TimeSpan cmdlet and specify the –Start parameter as the results from Get-Process on a process and the StartTime property. This is shown here using Notepad as an example.

PS C:\> New-TimeSpan -Start (get-process notepad).StartTime

 

Days       : 0

Hours       : 6

Minutes      : 53

Seconds      : 54

Milliseconds   : 55

Ticks       : 248340555164

TotalDays     : 0.287431198106481

TotalHours    : 6.89834875455556

TotalMinutes   : 413.900925273333

TotalSeconds   : 24834.0555164

TotalMilliseconds : 24834055.5164

 

Use PowerShell to Explore Process Threads in Windows

$
0
0

Summary: Microsoft Scripting Guy talks about using Windows PowerShell to explore process threads in Windows.

Hey, Scripting Guy! Question Hey, Scripting Guy! I have a problem. On our system, every once in a while, we have this application where the threads go crazy. I need an easy way to check threads. Can you help?

—BC

Hey, Scripting Guy! Answer Hello BC,

Microsoft Scripting Guy, Ed Wilson, is here. Well it is official; there will be a Microsoft Scripting Guy booth at TechEd 2013 in New Orleans. The Scripting Wife will also be at the booth. We are planning to share our booth with the Windows PowerShell community from PowerShell.org as well. It will be a lot of fun, and we are already looking forward to it. The dates for TechEd 2013 in New Orleans, by the way, are June 3 – June 6.

Use WMI to find info about threads

To find information about threads, I use the Win32_Thread WMI class. I found this by using the Get-CimClass cmdlet as shown here.

Get-CimClass *thread*

The command and its associated output are shown in the following image.

Image of command output

I can also do the same thing by using the Get-WmiObject cmdlet. This technique is shown here.

Get-wmiobject -list *thread*

So, I decide to query the WMI class. Here is the Windows PowerShell 2.0 version of the command.

Get-WmiObject win32_thread

I can do the same thing with the CIM cmdlets in Windows PowerShell 3.0. This command is shown here.

Get-CimInstance win32_thread

The command and the output from the command are shown here.

 Image of command output

Find a specific thread

The easiest way to find a specific thread is to first get the process handle, and then use that handle in a WMI filter. The following command obtains the handle for a running instance of Notepad, and then obtains the thread information.

$handle = (Get-Process notepad).handle

Get-WmiObject win32_thread -filter "handle = $handle"

By using the Get-CimInstance Windows PowerShell 3.0 CIM cmdlet, I arrive at the following syntax.

$handle = (Get-Process notepad).handle

Get-CimInstance win32_thread -filter "handle = $handle"

There is very little difference between the two commands. There is a bit of a difference between the output from the two commands. The output from the Get-CimInstance cmdlet is cleaner. The command and output from Get-CimInstance is shown here.

Image of command output

To understand the thread state, it is necessary to look up the ThreadState property. I can do this in the MSDN article, Win32_Thread WMI class. The ThreadState values are shown here.

Value

Meaning

0

  Initialized. It is recognized by the microkernel.

1

  Ready. It is prepared to run on the next available processor.

2

  Running. It is executing.

3

  Standby. It is about to run. Only one thread may be in this state at a time.

4

  Terminated. It is finished executing.

5

  Waiting. It is not ready for the processor. When ready, it will be rescheduled.

6

  Transition. The thread is waiting for resources other than the processor.

7

  Unknown. The thread state is unknown.

The ThreadWaitReason value codes are shown in the table that follows. 

Value

Meaning

0

Executive

1

FreePage

2

PageIn

3

PoolAllocation

4

ExecutionDelay

5

FreePage

6

PageIn

7

Executive

8

FreePage

9

PageIn

10

PoolAllocation

11

ExecutionDelay

12

FreePage

13

PageIn

14

EventPairHigh

15

EventPairLow

16

LPCReceive

17

LPCReply

18

VirtualMemory

19

PageOut

20

Unknown


BC, that is all there is to using Windows PowerShell and WMI to find information about threads. Join me tomorrow when I will talk about more cool stuff.Therefore, the Notepad process is waiting and not ready for the processor. The reason it is waiting is EventPairLow.

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: Determine How Long a PowerShell Command Takes

$
0
0

Summary: Figure out how long a Windows PowerShell command takes to run.

Hey, Scripting Guy! Question You want to know how long a particular Windows PowerShell command takes to run so you can optimize your code. How can you do this?

Hey, Scripting Guy! AnswerUse the Measure-Command cmdlet and specify the code in the –expression script block.

Measure-Command -Expression {Get-Process}

 

Use PowerShell to Explore Windows Audio Drivers

$
0
0

Summary: Microsoft Scripting Guy, Ed Wilson, talks about using Windows PowerShell to explore Windows audio drivers.

Hey, Scripting Guy! Question Hey, Scripting Guy! I have a problem with one of my computers—the audio driver is causing me fits. I would like to know if I can use Windows PowerShell to explore this issue. I think it would be really valuable, because I might have this issue come up again. Can you help me?

—DB

Hey, Scripting Guy! Answer Hello DB,

Microsoft Scripting Guy, Ed Wilson, is here. Hey, it is almost the weekend. In celebration of almost the weekend, I decided to get up early, fix a pot of Irish steel-cut oats, and a nice pot of English Breakfast tea. While the oats cooked, I used my Windows Surface to check my email. DB, this is when I ran across your email. Yes, Windows PowerShell can help in many different ways in looking at audio drivers.

First, find the audio device

The first thing to do is to find the audio device. To do this, use the WMI class Win32_SoundDevice WMI class. The Win32_SoundDevice WMI class tells me the device ID and the name of the audio device. The command is shown here.

 Get-CimInstance win32_sounddevice | fl *

 The command and its associated output are shown here.

 Image of command output

Next, find the driver 

Now that I know the name of the audio device, I can look for system drivers. To do this, I use the Win32_SystemDriver WMI class. I “cheap out” and pipe the results to the Where-Object. My resulting command is shown here. (gwmi is an alias for Get-WmiObject, ? is an alias for Where-Object, and fl is an alias for Format-List). 

gwmi win32_systemdriver | ? caption -match 'conexant' | fl * 

The command and its associated output are shown here.

Image of command output

Now get driver file info

Now that I have the path to the driver file, I can use the Get-Item cmdlet to retrieve version information. The first thing I need to do is to obtain the path to the driver. I can get this from the PathName property. I store it in a variable named $path. This is shown here. 

PS C:\> $path = (gwmi win32_systemdriver | ? caption -match 'conexant').pathname 

PS C:\> $path 

C:\WINDOWS\system32\drivers\CHDRT64.sys

Now I want to get only the VersionInfo property. To do this, I use the Get-Item cmdlet and return only VersionInfo as shown here.

PS C:\> (Get-Item $path).versioninfo 

 

ProductVersion   FileVersion      FileName 

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

8.32.43.0        8.32.43.0 bui... C:\WINDOWS\system32\drivers\CHDRT64.sys 

Hmm…

I know there is more information in this property, so I pipe it to the Format-List cmdlet as shown here.

(Get-Item $path).versioninfo | fl *

The commands and the associated output are shown here.

Image of command output

DB, that is all there is to using Windows PowerShell to look at audio driver information.  Join me tomorrow for the Weekend Scripter.

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 Easily Compare Running Processes

$
0
0

Summary: Use Windows PowerShell to easily compare running processes on local or remote computers.

Hey, Scripting Guy! Question How can I compare running processes on my local computer or on two remote computers? 

Hey, Scripting Guy! Answer Use the Get-Process cmdlet to retrieve running process information (this can be used remotely). Store the results in a variable.
Do the same thing again against other computers or against the local computer after things have possibly changed.
Then use the Compare-Object cmdlet to compare the two objects. 

PS C:\> $a = Get-Process

PS C:\> notepad

PS C:\> $b = Get-Process

PS C:\> Compare-Object -ReferenceObject $a -DifferenceObject $b -Property name

name                                       SideIndicator

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

notepad                                    =>

SearchProtocolHost                         <=

 


Weekend Scripter: Use PowerShell to Display Process Name and Uptime

$
0
0

Summary: Microsoft Scripting Guy, Ed Wilson, talks about using Windows PowerShell to create a custom table that displays the process name and uptime.

Microsoft Scripting Guy, Ed Wilson, is here. Sometimes it seems as if the Scripting Neighbors are crazy. Yep, it is early on a Saturday morning, and at least one of my neighbors is outside mowing the grass. Dude, it is not even June yet—what’s up with that? Oh well, their lawn mower makes a cheap alarm clock. I guess that is what neighbors are for—even Scripting Neighbors. So, while I am making a pot of tea, and wondering how long the neighbors have been up, I also begin to wonder how long certain applications on the computer have been up. So I slide over to the Scripting Wife’s computer and begin to play.

Find all processes that return a process start time

I use the Get-Process cmdlet to return all processes, and I pipe the results to the Where-Object. Then I look for the existence of StartTime. I select only the name and StartTime because that is what I am interested in seeing. The command is shown here.

gps | ? starttime | select name, starttime

Create a time span

Now I need to create a TimeSpan object. I can do this by using the New-TimeSpan cmdlet. In the following command, I practice with the Notepad process, while I figure out how to best display the newly created TimeSpan.

PS C:\> (New-TimeSpan -start (gps notepad).starttime).tostring("g")

7:24:02.2344579

I finally decide that rather than creating a custom TimeSpan format, I will use a standard TimeSpan format. The standard TimeSpan format strings are documented on MSDN: Standard TimeSpan Format Strings. Using a standard TimeSpan format makes the process a lot simpler.

Create a custom table

Now that I know how to create the formatting I need for my time span, it is time to create a custom table. The trick here is to use a hash table to specify the custom property for the Format-Table cmdlet. Here is the hash table I use:

@{LABEL='uptime';EXPRESSION={(New-TimeSpan -start ($_.StartTime).tostring("g"))}} -AutoSize

The entire command is shown here. This is a single-line command that wraps over three lines in my Windows PowerShell console.

gps |

? starttime |

 select name, starttime |

sort starttime -descending|

Format-Table name,

@{LABEL='uptime';EXPRESSION={(New-TimeSpan -start ($_.StartTime).tostring("g"))}} -AutoSize

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

Image of command output

That is it for today. Join me tomorrow for a guest blog about the Windows PowerShell community by the 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 Return Only Processes that Display a Start Time

$
0
0

Summary: Use Windows PowerShell to display processes that return a StartTime property value.

Hey, Scripting Guy! Question How can I filter process information so that only processes that return a StartTime display?

Hey, Scripting Guy! AnswerUse Where-Object with the Get-Process cmdlet. Then filter on StartTime.

Get-Process | ? starttime | select name, starttime

 The following command uses Windows PowerShell 2.0 syntax:

Get-Process | ? { $_.starttime} | select name, starttime

 

Weekend Scripter: The PowerShell Community State of the Union

$
0
0

Summary: The Scripting Wife provides a status update on the Windows PowerShell Community and insights on upcoming activities.

Microsoft Scripting Guy, Ed Wilson, is here. Today I was busy working on a Windows PowerShell script when the Scripting Wife came floating in and asked me if she could write a guest blog. When that happens (for that matter, whenever the Scripting Wife asks for anything), the answer is always, “Yes, of course.” So without further delay, here is Teresa Wilson, aka The Scripting Wife.

PowerShell User Group update

Hello Scripters. Hope you are having a wonderful day. I asked Ed if I could write today about some new information for user groups and about a couple of upcoming events. Although he is a good husband and he usually agrees with things I ask him to do, he also is a great advocate for Windows PowerShell and the PowerShell community, which meant he said, “Of course.”

In June, I offered some information about starting a Windows PowerShell User Group in my blog, Scripting Wife Discusses Setting Up a PowerShell User Group.

Here is the list of items that I suggested:

And now the new kids in town... 

And finally, one change that I am aware of:

This is now Microsoft Tech Affiliate Program.

I will continue to update you as I know new or different ideas to help get your group rolling.

If you go to the Scripting Community tab on the Script Center, there is a list of Ed’s upcoming engagements. Typically if Ed is there, I will be also. I cannot answer your questions about how to troubleshoot your script, but I know where to find answers. I am also happy to talk with people and remember what city they live near, then connect people as time goes by. So be sure to stop by and say, “Hello!” if you attend any of the events that Ed and I are attending.

Two big events coming up are the PowerShell Summit North America 2013 in Redmond, Washington in April and TechEd North America 2013 in New Orleans in June. I will be at the Scripting Guy booth the majority of the time. I typically only sneak off to grab a drink or a bite to eat. I love being at the booth and talking to people as they come through.

As always, contact me on twitter @ScriptingWife or email me at ScriptingWife@hotmail.com if I can be of assistance. Have a scriptastic day.

Thanks for a great blog post, Teresa, and for sharing your user group experience with the community.

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 Out-GridView with NetStat

$
0
0

Summary: Learn to use Windows PowerShell Out-GridView to filter NetStat output.

Hey, Scripting Guy! Question How can I use NetStat to check TCP port connections, and additionally have a graphical tool to filter the output?

Hey, Scripting Guy! Answer Use the NetStat command inside Windows PowerShell, and pipe the results to the Out-GridView cmdlet. Then use the Filter parameter to filter the output:

netstat -a | Out-GridView

The Out-GridView control is shown here.

Image of menu

 

Use PowerShell to Find Detailed Windows Profile Information

$
0
0

Summary: Microsoft Scripting Guy, Ed Wilson, talks about using Windows PowerShell to find detailed Windows profile information.

Hey, Scripting Guy! Question Hey, Scripting Guy! I am in a bind. I need to find out who is using what profile on a desktop computer. I also need to find out when the profile was last used. This is because many of the computers in our lab have rather small hard disk drives, and these drives are nearly filled up with user profiles. After I know who has been using a profile and when it was last used, I can decide if I want to remove the profile.

—KG

Hey, Scripting Guy! Answer Hello KG,

Microsoft Scripting Guy, Ed Wilson, is here. It is a new week for the Hey, Scripting Guy! Blog. It is also a new month. This means that this Thursday, March 7, 2013 we will have our Charlotte Windows PowerShell User Group meeting at the Microsoft office. If you are in the area, check it out. You will need to register first so we know how much food to purchase. It is always a lot of fun, and we will be playing a mini Scripting Game.

KG, your request is a reasonable one. To find user profile information, I use WMI.

Finding user profile information

To find user profile information, I use the Win32_UserProfile WMI class. To query the Win32_UserProfile WMI class, I can use the Get-WmiObject cmdlet. This technique is shown here (gwmi is an alias for Get-WmiObject).

gwmi win32_userprofile

The command and its associated output are shown here.

Image of command output

There is a lot of information that I am not interested in examining. In fact, what I really need is LastUseTime, LocalPath, and SID. The revised query is shown here.

gwmi win32_userprofile | select lastusetime, localpath, sid

The revised command and its output are shown here.

Image of command output

But I need something a bit easier to read. Also, I am not interested in profiles that are not used. In Windows PowerShell 3.0, the Get-CimInstance cmdlet translates the time. This brings me a step closer to what I need. Here is the command.

get-ciminstance win32_userprofile | ? lastusetime | select lastusetime, localpath, sid

The command and its output are shown here.

Image of command output

Translating SID to a user

Groovy. Now I need to translate the SID to a readable user account, and I am home free. There are several ways to translate a SID to a user account. One way to do this is to use the Win32_UserAccount WMI class. It is easy to do—and hey, I am already messing around with WMI.

So how does it work? Well, the Win32_UserAccount WMI class has a SID property in addition to a Name property. But I like to use Caption because it has both the domain and the name in it. So I filter on the SID, and boom it works. Here are a couple of examples:

PS C:\> (gwmi win32_useraccount -Filter "sid = 'S-1-5-21-1457956834-3844189528-354135

0385-1613'").name

Tim O'Brian

PS C:\> (gwmi win32_useraccount -Filter "sid = 'S-1-5-21-1457956834-3844189528-354135

0385-1613'").Caption

IAMMRED\Tim O'Brian

PS C:\>

Sweet, so now all I need to do is to create a custom property on my custom object, and I am good to go.

Create custom object with required info

I need to create a custom object with the LastUseTime property, the LocalPath, and the user name. To do this, I use the Select-Object cmdlet. I choose the LastUseTime property and the LocalPath property. I then use a hash table to resolve the SID to a user account name. Here is the hash table I use:

@{LABEL='user';EXPRESSION={(gwmi win32_useraccount -filter "SID = '$($_.sid)'").caption}}

Now, what I am doing in the expression is using WMI to query the Win32_UserAccount WMI class. I specify the filter as Sid = ‘$($_.sid)’. I need the subexpression here to keep the WMI SID property from unraveling. Because the query returns a user account object, and I only want the caption, I group the expression and select only the Caption property. The entire command is shown here (it is a single line command):

get-ciminstance win32_userprofile |

? lastusetime |

select lastusetime, localpath,

@{LABEL='user';EXPRESSION={(gwmi win32_useraccount -filter "SID = '$($_.sid)'").caption}}

The complete command and the output from the command are shown in the image that follows.

Image of command output

KG, that is all there is to using Windows PowerShell and WMI to return user profile stuff. 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 to Find Application Hangs

$
0
0

Summary: Learn how to use Windows PowerShell to find applications that are hanging.

Hey, Scripting Guy! Question How can I find which applications are hanging on my desktop computer that is running Windows 8?

Hey, Scripting Guy! AnswerUse the Get-EventLog cmdlet to query the application log for InstanceID 1002 and a source of *hang*.
         Select TimeWritten and Message to see what applications are hanging and when these hangs occur.

Get-EventLog -LogName application -Newest 20 -InstanceId 1002 -source *hang*|
select timewritten, message 

Use PowerShell to Find Certificates that are About to Expire

$
0
0

Summary: Microsoft Scripting Guy, Ed Wilson, talks about using Windows PowerShell to find certificates that are about to expire.

Hey, Scripting Guy! Question Hey, Scripting Guy! We recently implemented an internal certification authority that we use for various scenarios, such as issuing code-signing certificates for our developers and certain admins as well as for user authentication scenarios. Now, of course, we have a problem. My pointy headed boss is worried that people with certificates will not renew them properly, so he wants me to write a script that can find out when scripts are about to expire. Is this something that I can do easily?

—AR

Hey, Scripting Guy! Answer Hello AR,

Microsoft Scripting Guy, Ed Wilson, is here. Today is Tuesday, and the Scripting Wife and I are on the road for a bit. Luckily, Windows 8 phone easily sets up as a modem, and I can connect to the Internet with my laptop and check my email at scripter@microsoft.com. It is cool. The bad thing about a road trip is that it is nearly impossible to get a decent cup of tea. I made a pot before we left, so I have some decent tea—at least for a little while.

AR, dude, this is so easy…

The reason it is so easy to find certificates that are about to expire in Windows PowerShell 3.0 is because we add a dynamic parameter to the Get-ChildItem cmdlet when the cmdlet targets the Cert: PSDrive. The dynamic parameter is called –ExpiringInDaysand it does exactly what you might think it would do— it reports certificates that are going to expire within a certain time frame. To find certificates that will expire within 75 days, use the command shown here.

Get-ChildItem -Path cert: -Recurse -ExpiringInDays 75

The command and the output associated with the command to find certificates that expire in 75 days are shown here.

Image of command output

I do not have to set my working location to the Cert: PSDrive, because I can specify it as the path of the Get-ChildItem cmdlet. If I need to perform more than one or two operations, I will change my working location to the Cert: PSDrive to simplify some of the typing requirements. To change to the Cert: PSDrive, I use the Set-Location cmdlet (SL is an alias, as is CS). This technique is shown here.

PS C:\> sl cert:

After I have changed my working location to the Cert: PSDrive, the Windows PowerShell prompt (by default) changes to include the Cert: drive location as shown here.

PS Cert:\>                

Finding about to expire certificates the PowerShell 2.0 way

If you are using Windows PowerShell 2.0 (or if you just like to type), you can still find certificates that are about to expire by using the Get-ChildItem cmdlet on your Cert: PSDrive, and then piping the results to the Where-Object. You need to filter on the NotAfter property of the returned certificate object. The great thing is that Windows PowerShell makes it easy to work with dates. I use the AddDays method from the DateTime object that is returned by the Get-Date cmdlet.

To gain access to the AddDays method, I group the Get-Date cmdlet first. Each certificate object crosses the pipeline to the Where-Object cmdlet. Inside the script block for the Where-Object, I look at the NotAfter property, and I check to see if it is less than a date that is 75 days in the future. Upon finding the certificates that have an expiration date of less than 75 days in the future, I send the results to the Select-Object cmdlet, where I choose the thumbprint and the subject. The following command returns certificates that have an expiration date that is before 75 days in the future.

Get-ChildItem -Recurse | where { $_.notafter -le (get-date).AddDays(75) } | select thumbprint, subject

When I run the command, the results do not compare very well with those from the previous command. The command and its resulting output are shown here.

Image of command output

The reason the output is different is because the new ExpiringInDaysparameter for Windows PowerShell 3.0 does not include already expired certificates. Windows ships with expired certificates because certain executables that have been signed with a certificate, but have not been resigned with a new certificate, need the old certificate to ensure the validity of the certificate.

By modifying the command so it also filters out expired certificates, the results on my computer become the same. Here is the revised command.

Get-ChildItem -Recurse | where { $_.notafter -le (get-date).AddDays(75) -AND $_.notafter -gt (get-date)} | select thumbprint, subject

The command and the output associated with the command are shown here.

Image of command output

AR, that is all there is to using the certificate provider in Windows PowerShell to find certificates that will expire in a certain time frame. Join me tomorrow when I will talk about more cool stuff.

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

Ed Wilson, Microsoft Scripting Guy


PowerTip: Use PowerShell to Find Code-Signing Certificates

$
0
0

Summary: Learn how to use Windows PowerShell to find code-signing certificates on the local computer.

Hey, Scripting Guy! Question How can I find if a computer contains a code-signing certificate in its local certificate store?

Hey, Scripting Guy! Answer Use the Certificate PS Drive and perform a recursive search by using the Get-ChildItem cmdlet while specifying the –CodeSigningCert parameter.

Get-ChildItem -Path cert: -Recurse -CodeSigningCert 

 

Learn How to Use the PowerShell Env: PSDrive

$
0
0

Summary: Microsoft Scripting Guy, Ed Wilson, talks about using the Windows PowerShell Env: PSDrive.

Hey, Scripting Guy! Question Hey, Scripting Guy! I was attending a Windows PowerShell user group meeting the other day, and I overheard someone talking about using an Environment PowerShell Drive. Huh? I did not want to ask him about this, because I would feel foolish. I have been using Windows PowerShell for a while now, but I do not have an Environmental PowerShell drive that I know of. Can you explain what this is, how I would get one, and why I would even want to use such a thing?

—BB

Hey, Scripting Guy! Answer Hello BB,

Microsoft Scripting Guy, Ed Wilson, is here. My endless days of searching have finally come to an end—my new laptop finally arrived. I have taken it out of the box, and plugged it in to charge the battery, but that is about it. I know I will be spending the next several weeks setting up the thing.

Where is the Environment PowerShell drive?

To an extent, there really is no such thing as an Environment PowerShell drive. If I use the Get-PSDrive cmdlet, I see that there is a drive named Env, and a provider named Environment. This command and associated output are shown here.

PS C:\> Get-PSDrive

 

Name           Used (GB)     Free (GB) Provider      Root

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

Alias                                  Alias

C                  40.70        108.00 FileSystem    C:\

Cert                                   Certificate   \

D                                      FileSystem    D:\

Env                                    Environment

Function                               Function

HKCR                                   Registry      HKey_Classes_Root

HKCU                                   Registry      HKEY_CURRENT_USER

HKLM                                   Registry      HKEY_LOCAL_MACHINE

Variable                               Variable

WSMan                                  WSMan

If I use the Get-PSProvider cmdlet, I see the names of the PS Providers and the capabilities and drives associated with them as shown here.

PS C:\> Get-PSProvider

 

Name                 Capabilities                    Drives

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

Alias                ShouldProcess                   {Alias}

Environment          ShouldProcess                   {Env}

FileSystem           Filter, ShouldProcess, Crede... {C, D}

Function             ShouldProcess                   {Function}

Registry             ShouldProcess, Transactions     {HKLM, HKCU, HKCR}

Variable             ShouldProcess                   {Variable}

Certificate          ShouldProcess                   {Cert}

WSMan                Credentials                     {WSMan}

Exploring the Env PowerShell drive

The Env drive created by the Environment PS provider provides access to the environmental variables. These are the same variables you would see if you opened a traditional CMD prompt and typed the command set. This output is shown here.

Image of command output

When I try to set my working location to the Env drive, an error arises. This is shown in the following image.

Image of error message

The error message is a bit misleading. It says it cannot find c:\env because it does not exist. So, I need to use drive notation and include a colon at the end of the drive name. After I set my working location to the Env drive, I can use the same type of commands I use on drive C or any other drive. This is shown here.

Image of command output

I can use the standard Windows PowerShell cmdlets such as Get-Item, Get-Content, Get-ChildItem, Set-Item, Set-Content to work with the Env drive. For example, I can use the Get-Item cmdlet to return the path for the tempEnvironment variable. This technique is shown here.

PS Env:\> Get-Item temp

 

Name                           Value

----                           -----

TEMP                           C:\Users\ED6C0B~1.IAM\AppData\Local\Temp

I can also use the Get-Content cmdlet to retrieve the value. This technique is shown here.

PS Env:\> Get-Content temp

C:\Users\ED6C0B~1.IAM\AppData\Local\Temp

The members of the Environment variables appear here.

PS Env:\> Get-Item temp | Get-Member -MemberType *property

 

   TypeName: System.Collections.DictionaryEntry

 

Name          MemberType    Definition

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

Name          AliasProperty Name = Key

PSDrive       NoteProperty  System.Management.Automation.PSDriveInfo PSDrive=Env

PSIsContainer NoteProperty  System.Boolean PSIsContainer=False

PSPath        NoteProperty  System.String PSPath=Microsoft.PowerShell.Core\Enviro...

PSProvider    NoteProperty  System.Management.Automation.ProviderInfo PSProvider=...

Key           Property      System.Object Key {get;set;}

Value         Property      System.Object Value {get;set;}

This means that I can directly access the valueproperty as shown here.

PS Env:\> (Get-Item temp).value

C:\Users\ED6C0B~1.IAM\AppData\Local\Temp

I can also use a shortcut method to access this. This technique is shown here.

PS Env:\> $env:temp

C:\Users\ED6C0B~1.IAM\AppData\Local\Temp

This shortcut technique has the advantage of being usable from any PSDrive location as shown here.

PS Env:\> sl c:

PS C:\> $env:temp

C:\Users\ED6C0B~1.IAM\AppData\Local\Temp

BB, that is all there is to using the Env PowerShell drive. Windows PowerShell Provider Week will continue tomorrow when I will talk about working with the Alias PSDrive.

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 Commands from Imported Modules

$
0
0

Summary: Learn how to use Windows PowerShell to find commands from imported modules.

Hey, Scripting Guy! Question How can I use the Get-Command cmdlet to list commands that support a specific verb, but suppress the auto module import feature of Windows PowerShell 3.0?

Hey, Scripting Guy! Answer Use the –ListImported parameter with the Get-Command cmdlet (gcm is an alias for Get-Command):

gcm -Verb get -ListImported 

 

Working with the PowerShell Alias: Drive

$
0
0

Summary: Microsoft Scripting Guy, Ed Wilson, talks about how to work with the Windows PowerShell Alias: drive.

Hey, Scripting Guy! Question Hey, Scripting Guy! Can you tell me what an Alias: drive is? Is it anything like a virtual drive?

—EC

Hey, Scripting Guy! Answer Hello EC,

Microsoft Scripting Guy, Ed Wilson, is here. Today we have the Windows PowerShell User Group meeting in Charlotte, North Carolina at the Microsoft office. The Scripting Wife has been talking about the meeting all week, and there are several new people signed up to attend. It will be fun. We are planning to do another miniscripting games event. Those are always fun. It is not too late to sign up. You can find details on the Charlotte PowerShell Users Group site.

EC, the Alias: drive is a virtual drive—just not in the way you are probably thinking.

What is the Alias: drive?

The Alias: drive is a drive that is created by the Windows PowerShell provider subsystem to expose information about aliases in a way that allows you to interact with aliases in the same way you would work with the file system. This sounds more confusing than it really is. For example, to get to the Alias: drive, I use the Set-Location cmdlet (or I can use the alias cdor sl). This is shown here.

PS C:\> cd alias:

PS Alias:\>

When I am on the Alias: drive, I can use the Get-ChildItem cmdlet (or the alias diror ls) to obtain a list of all the aliases that are defined on my computer as shown here.

PS Alias:\> dir

The command and its associated output are shown here.

Image of command output

To examine all of the properties of a specific alias, use the Get-Item cmdlet to return information about the alias. Then pipe the results to the Format-List cmdlet and choose the * wildcard for the properties. This command is shown here.

PS Alias:\> Get-Item r | fl *

The command and the output associated with the command are shown in the image that follows.

Image of command output

When attempting to understand more about aliases, the crucial things to pay attention to are the Definition and the Options properties.

Note   The command that the alias resolves to is actually available in a number of properties. These properties are ResolvedCommandName, ReferencedCommand, ResolvedCommand, and Definition.

To find all of the aliases that are defined for a particular cmdlet (one that I use on a regular basis so that it would pay great dividends on reducing typing workload), I pipe the results of the Get-ChildItem cmdlet on the Alias: drive and filter on the Definition property for the cmdlet. This technique is shown here (dir is an alias for Get-ChildItem).

PS Alias:\> dir | ? definition -eq 'get-childitem'

 

CommandType     Name                                               ModuleName

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

Alias           dir -> Get-ChildItem

Alias           gci -> Get-ChildItem

Alias           ls -> Get-ChildItem

Looking at alias options

Several cmdlets have ReadOnly aliases. These appear when I look at the Options property. All of the ReadOnly aliases are also defined as available to all scopes within Windows PowerShell. Some aliases are not ReadOnly, but most of the not ReadOnly aliases are defined for all scopes. On my computer, three aliases have no options. Because working with the Alias: drive returns objects, this type of information is really easy to obtain. I use the Get-Childitem cmdlet to return all aliases from the Alias: drive. I pipe the resulting AliasInfo objects to the Sort object cmdlet, and I sort on the Options property. I then group the objects by piping to the Group-Object cmdlet. The command and output associated with the command are shown here.

PS Alias:\> dir | sort options | group options

 

Count Name                      Group

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

    3 None                      {ep, sls, gmh}

   48 AllScope                  {exsn, set, popd, ipsn...}

   99 ReadOnly, AllScope        {rdr, ni, nv, npssc...}

Note  When working with the Alias: drive, it does not automatically import modules. Therefore, the results are limited to only the modules that I have imported.

If I want to know about all of my modules and cmdlets, I need to first import all of the modules. This command and output are shown here.

PS Alias:\> gmo -ListAvailable | ipmo

PS Alias:\> dir | sort options | group options

 

Count Name                      Group

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

   72 None                      {Get-ProvisionedAppxPackage, gfsd, Get-GPPermissi...

   48 AllScope                  {lp, rd, ren, rjb...}

  111 ReadOnly, AllScope        {rcms, rcsn, tee, swmi...}

To delete an alias that is not marked as ReadOnly, all I need to do is use the Remove-Item cmdlet. When I do this, the Get-Item cmdlet informs me that I was successful. These commands are shown here.

PS Alias:\> Remove-Item lp

PS Alias:\> Get-Item lp

Get-Item : Cannot find path 'Alias:\lp' because it does not exist.

At line:1 char:1

+ Get-Item lp

+ ~~~~~~~~~~~

    + CategoryInfo          : ObjectNotFound: (Alias:\lp:String) [Get-Item], ItemNo

   tFoundException

    + FullyQualifiedErrorId : PathNotFound,Microsoft.PowerShell.Commands.GetItemCom

   Mand

If I attempt to delete a ReadOnly alias, an error arises as shown here.

PS Alias:\> Remove-Item rcms

Remove-Item : Alias was not removed because alias rcms is constant or read-only.

At line:1 char:1

+ Remove-Item rcms

+ ~~~~~~~~~~~~~~~~

    + CategoryInfo          : WriteError: (rcms:String) [Remove-Item], SessionState

   UnauthorizedAccessException

    + FullyQualifiedErrorId : AliasNotRemovable,Microsoft.PowerShell.Commands.Remov

   eItemCommand

However, if I use the –forceswitched parameter when I attempt to remove the ReadOnly alias, the command succeeds. This is shown here.

PS Alias:\> Remove-Item rcms -Force

PS Alias:\> Get-Alias rcms

Get-Alias : This command cannot find a matching alias because an alias with the

name 'rcms' does not exist.

At line:1 char:1

+ Get-Alias rcms

+ ~~~~~~~~~~~~~~

    + CategoryInfo          : ObjectNotFound: (rcms:String) [Get-Alias], ItemNotFou

   ndException

    + FullyQualifiedErrorId : ItemNotFoundException,Microsoft.PowerShell.Commands.G

   etAliasCommand

EC, that is all there is to using the Alias: drive. Windows PowerShell Provider Week will continue tomorrow when I will talk about the Variable: drive.

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 All Defined One-Letter PowerShell Aliases

$
0
0

Summary: Learn how to find all the one-letter Windows PowerShell aliases.

Hey, Scripting Guy! Question How can I find all the defined Windows PowerShell aliases that are exactly one letter in length?

Hey, Scripting Guy! Answer Use the Get-Alias cmdlet and choose a one-letter wildcard pattern:

Get-Alias ?

 

Viewing all 3333 articles
Browse latest View live




Latest Images

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