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

PowerTip: Use PowerShell to Display the Desktop

$
0
0

Summary: Use Windows PowerShell to change the display to the Desktop.

Hey, Scripting Guy! Question How can I use Windows PowerShell to switch to displaying the Desktop on my computer?

Hey, Scripting Guy! Answer Use the ToggleDesktop method from the Shell.Application COM object:

(New-Object -ComObject shell.application).toggleDesktop()


Weekend Scripter: Improve Performance When Combining PowerShell Arrays

$
0
0

Summary: Microsoft premier field engineer, Chris Wu, talks about combining Windows PowerShell arrays.

Microsoft Scripting Guy, Ed Wilson, is here. Chris Wu, a Microsoft PFE, is back to share his knowledge. See previous Hey, Scripting Guy! Blog guest posts from Chris.

Here is contact information for Chris:

Twitter: https://twitter.com/chwu_ms
Facebook: https://www.facebook.com/mschwu
LinkedIn: http://ca.linkedin.com/in/mschwu

Take it away Chris…

While teaching a Windows PowerShell workshop, I was asked about how to combine two arrays of different objects (which share one key property) into one that contains objects with properties from both source objects. One real world scenario is to merge information retrieved from Active Directory (a list of Active Directory users and their properties) and Exchange Server (mailboxes).

The current approach by the student is to use two-level loops, which has seen performance issues when source arrays become huge in size (the following code snippet uses dummy data for demonstration purposes).

$ADList = @"

chwu,chwu@microsoft.com,Chris Wu

tst1,,Test User1

tst2,tst2@contoso.com,Test User2

"@ | ConvertFrom-Csv -Header Name,mail,CN

$EXList = @"

chwu,ex1.contoso.com

tst2,ex2.contoso.com

"@ | ConvertFrom-Csv -Header Name,MailServer

 

$Result = @()

foreach($ad in $ADList) {

  $Match = $false

  foreach($ex in $EXList) {

    if ($ad.Name -eq $ex.Name) {

      $Result += New-Object PSObject -Property @{Name=$ad.Name; mail=$ad.mail; CN=$ad.CN; MailServer=$ex.MailServer}

      $Match = $true

    }

  }

  if(-not $Match) {

    $Result += New-Object PSObject -Property @{Name=$ad.Name; mail=$ad.mail; CN=$ad.CN}

  }

}

 Image of command output

In this post, I will explore several options to improve the performance and cleanness of this code snippet.

The first thing we can do is remove the use of the $Result array, which has two drawbacks:

  • Every assignment operation will create a new array in memory with data copied from the source, which is inefficient.
  • It defeats the streaming benefit of the WindowsPowerShell pipeline because it returns all objects as a whole at the end of the processing. A best practice in Windows PowerShell is to emit an individual object whenever it’s ready.

Another performance issue stems from the use of an inner loop to search for a matching record in the second array, which basically multiplies the total number of iterations. We can utilize a hash table for faster lookup. The Group-Object cmdlet offers a convenient AsHashTable parameter that can be used here.

Image of command output

Please be warned that the value portion of each entry is an array of matching records. If we are grouping records by using a property with unique values (such as SamAccountName), those arrays will apparently contains one single element each.

So an enhanced version of the code snippet is like this (with the constructing source arrays removed):

$h = $EXList | Group-Object -Property Name -AsHashTable

$ADList | %{

  if($h[$_.Name]) {

    New-Object PSObject -Property @{Name=$_.Name; mail=$_.mail; CN=$_.CN; MailServer=$h[$_.Name][0].MailServer}

  } else {

    New-Object PSObject -Property @{Name=$_.Name; mail=$_.mail; CN=$_.CN}

  }

}

The last (but not the least) idea is to sort both arrays based on the key property (user name) beforehand, then we can pair records in a single iteration. Note that in this particular example, users found in Active Directory is a superset of users in Exchange, so we need a pointer variable to deal with this little quirk.

$ADList = @"

chwu,chwu@microsoft.com,Chris Wu

tst1,,Test User1

tst2,tst2@contoso.com,Test User2

"@ | ConvertFrom-Csv -Header Name,mail,CN | Sort-Object -Property Name

 

$EXList = @"

chwu,ex1.contoso.com

tst2,ex2.contoso.com

"@ | ConvertFrom-Csv -Header Name,MailServer | Sort-Object -Property Name

 

$ADList | % {$p = 0} {

  if($_.Name -eq $EXList[$p].Name) {

    New-Object PSObject -Property @{Name=$_.Name; mail=$_.mail; CN=$_.CN; MailServer=$EXList[$p].MailServer}

    $p++

  } else {

    New-Object PSObject -Property @{Name=$_.Name; mail=$_.mail; CN=$_.CN}

  }

}

The last two snippets perform much better than the original one, but which one is faster remains a question (I haven’t tested them against large arrays just yet). I would like to hear about your results, and I welcome your thoughts and ideas.

~Chris

Thanks Chris excellent blog post. Join me tomorrow for more cool Windows PowerShell stuff.

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

Ed Wilson, Microsoft Scripting Guy

PowerTip: Add Two PowerShell Arrays Together

$
0
0

Summary: Easily add two Windows PowerShell arrays together.

Hey, Scripting Guy! Question If I have an array stored in one variable, and another array stored in another variable, how can I add them together?

Hey, Scripting Guy! Answer Use the + operator to add to arrays together:

PS C:\> $a = 2,3,4

PS C:\> $b = 5,6,7

PS C:\> $c = $a + $b

PS C:\> $c

2

3

4

5

6

7

PS C:\>

Weekend Scripter: Use PowerShell to Upload a New File Version to SharePoint

$
0
0

Summary: Microsoft PowerShell MVP, Niklas Goude, talks about using Windows PowerShell to upload a new version of a file to SharePoint.

Microsoft Scripting Guy, Ed Wilson, is here. Today Niklas Goude is our guest blogger. You can read more from Niklas in his past Hey, Scripting Guy! Blog posts.

Take it away Niklas…

In a previous post, Use PowerShell Cmdlets to Manage SharePoint Document Libraries, we talked about uploading files to a share in SharePoint 2010. Now we'll take this a step further and update a minor or a major version of an existing document.

Here’s a quick recap of the code used to upload the file:

# Add the Snapin
Add-PSSnapin Microsoft.SharePoint.PowerShell

# Retrieve specific Site
$spWeb = Get-SPWeb http://SP01

# Create instance of Folder
$spFolder = $spWeb.GetFolder("Shared Documents")

# Get the file on Disk that we want to upload
$file = Get-Item C:\Documents\MyDoc.docx

# upload the file.
$spFolder.Files.Add("Shared Documents/MyDoc.docx",$file.OpenRead(),$false)

What we’ve done so far is to upload a single document to a document library in SharePoint 2010. The file used in this example is stored on drive C: 

Image of menu

  

The document contains a single line:

Image of menu

 

After we run the Windows PowerShell code, the document gets uploaded to a document library in SharePoint 2010.

Image of menu

 

We can check the versioning for the document by clicking Version History in SharePoint:

Image of menu

 

We’ll see that the version number is set to 0.1:

Image of menu

 

Now, let’s open the file that is stored on drive C and modify it:

Image of menu

Back in Windows PowerShell, we use SPFileCollection to pick up the document we just uploaded. In this example, we are going to filter out the document where the name is equal to MyDoc.docx by using the Where-Object cmdlet.

# Retrieve specific Site
$spWeb = Get-SPWeb http://SP01

# Create instance of Folder
$spFolder = $spWeb.GetFolder("Shared Documents")

# Retrieve a Specific File.
$spFile = $spFolder.Files | Where-Object { $_.Name -eq "MyDoc.docx" }

We pick up the file because we want to use some of its properties for the new version that we want to upload. When we upload a new version of the document, we still use the Add method that is provided by the Microsoft.SharePoint.SPFileCollection, but with a different overload definition. Here’s the definition on MSDN: SPFileCollection.Add method (String, Stream, SPUser, SPUser, DateTime, DateTime).

The parameters we want to use are: urlOfFile, File, CreatedBy, ModifiedBy, TimeCreated, and TimeLastModified.

We can get the following values from the existing document in SharePoint 2010: UrlOfFile, CreatedBy, ModifiedBy, and TimeCreated. We can get File by using Get-Item, and we can get TimeLastModified by simply using Get-Date.

First, let’s get the modified MyDoc.docx from drive C:

$file = Get-Item C:\Documents\MyDoc.docx

Next we run the method:

$newVersion = $spFolder.Files.Add($spFile.Name, $file.OpenRead(), $spFile.Author, $spFile.ModifiedBy, $spFile.TimeCreated, (Get-Date))

If we open the SharePoint document library again, we’ll see that we have a new minor version of our document, .02.

Image of menu

And finally, if we want to add the modified document as a major version, we use the Publish method:

$newVersion.Publish("")

Now we have a new major version, 1.0, instead.

Image of menu

~Niklas

Thank you, Niklas, for taking your time to share your 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 Find Total CPU Time

$
0
0

Summary: Use Windows PowerShell to find the total CPU time of a process.

Hey, Scripting Guy! Question How can I return a timespan that represents the total CPU time of a process?

Hey, Scripting Guy! Answer Use the Get-Process cmdlet and select the TotalProcessorTime property:

PS C:\> (gps excel).totalprocessortime

 

Days              : 0

Hours             : 0

Minutes           : 0

Seconds           : 4

Milliseconds      : 524

Ticks             : 45240290

TotalDays         : 5.23614467592593E-05

TotalHours        : 0.00125667472222222

TotalMinutes      : 0.0754004833333333

TotalSeconds      : 4.524029

TotalMilliseconds : 4524.029 

Set Up an AlwaysOn Availability Group with PowerShell

$
0
0

Summary: Microsoft SQL Server PFE, Thomas Stringer, talks about using Windows PowerShell to set up an AlwaysOn availability group.

Microsoft Scripting Guy, Ed Wilson, is here. Today we kick off SQL Server Windows PowerShell Week. Our first blog of the series is a guest blog written by SQL Server PFE, Thomas Stringer. Here is a little bit about Thomas.

Thomas Stringer is a SQL Server premier field engineer at Microsoft. He specializes in scripting (Windows PowerShell), high availability, disaster recovery, performance tuning, internals, querying, and database administration. He also has a software development background in languages and platforms from C all the way through .NET (including C# for WinForm and ASP.NET WebForms/MVC applications).

Photo of Thomas Stringer

Email: sqlsalt@outlook.com
Blog: sql salt...ride the SQL Server wave of data
Twitter: @SQLife
LinkedIn: Thomas Stringer

Take it away, Thomas…

If you have any connection to the SQL Server world, by now you have most likely heard of the cutting-edge, high-availability technology in SQL Server 2012 marketed as AlwaysOn. This includes failover cluster instances and stand-alone instances hosting a replica for an availability group. But the possibilities are endless with implementing this new technology. As administrators of data, it is our responsibility to ensure the highest amount of uptime and the quickest and safest way to facilitate disaster recovery. The AlwaysOn Availability Groups feature also gives us cool features, such as active secondaries, which allow us to run reports and backups against secondary replicas.

I will not be going into the nitty gritty about what AlwaysOn availability groups are capable of, but for some great reading, please feel free to check out this great reference about the topic: Overview of AlwaysOn Availability Groups (SQL Server). In today’s blog, I will show you how to set up and configure an AlwaysOn availability group through Windows PowerShell.

Now on to the fun part. I will illustrate this task through Windows PowerShell by creating a single availability group with two replicas. We will review the finer details with each operation, but this is a common scenario for a no-data-loss implementation. An availability group can contain multiple databases; but for the sake of brevity, I will have only one database (our trusty AdventureWorks2012 sample database). The following diagram shows what we will set up in this example:

Image of flow chart

The first step to getting to the diagram’s operational state, is to take the necessary backups on the primary replica and restore them on the secondary replica. We need the database sitting on the secondary replica for the database to be joined on this instance. We can accomplish this by using the Backup and Restore SMO classes. Before I jump into this, I am going to define a handful of parameters that will be consumed throughout this post:

$SqlServerPrimName = "ALWAYSON1"

$SqlServerSecName = "ALWAYSON2"

$SqlAgName = "MyTestAg"

$SqlAgDatabase = "AdventureWorks2012"

$AgListenerName = "AgListener1"

$AgListenerPort = 1433

$AgListenerIpAddress = "192.168.1.100"

$AgListenerSubnetMask = "255.255.255.0"

$HadrEndpointName = "HadrEndpoint"

$HadrEndpointPort = 5022

$BackupDirectory = "\\Your\Backup\Directory"

$SqlServerPrim = New-Object Microsoft.SqlServer.Management.Smo.Server($SqlServerPrimName)

$SqlServerSec = New-Object Microsoft.SqlServer.Management.Smo.Server($SqlServerSecName)

Most of these variables are self-explanatory, but they will be referenced in the following multiple code snippets. So, shifting back to our backups and restores to get the database on the secondary replica:

# backup the database on the primary replica/server (full database backup)

$DbBackup = New-Object Microsoft.SqlServer.Management.Smo.Backup

$DbBackup.Database = $SqlAgDatabase

$DbBackup.Action = [Microsoft.SqlServer.Management.Smo.BackupActionType]::Database

$DbBackup.Initialize = $true

$DbBackup.Devices.AddDevice("$BackupDirectory\$($SqlAgDatabase)_AgSetup_full.bak",

  [Microsoft.SqlServer.Management.Smo.DeviceType]::File)

$DbBackup.SqlBackup($SqlServerPrim)

 

# backup the database on the primary replica/server (transaction log backup)

$DbBackup = New-Object Microsoft.SqlServer.Management.Smo.Backup

$DbBackup.Database = $SqlAgDatabase

$DbBackup.Action = [Microsoft.SqlServer.Management.Smo.BackupActionType]::Log

$DbBackup.Initialize = $true

$DbBackup.Devices.AddDevice("$BackupDirectory\$($SqlAgDatabase)_AgSetup_log.trn",

  [Microsoft.SqlServer.Management.Smo.DeviceType]::File)

$DbBackup.SqlBackup($SqlServerPrim)

 

# restore the database on the secondary replica/server (full database restore)

$DbRestore = New-Object Microsoft.SqlServer.Management.Smo.Restore

$DbRestore.Database = $SqlAgDatabase

$DbRestore.Action = [Microsoft.SqlServer.Management.Smo.RestoreActionType]::Database

$DbRestore.Devices.AddDevice("$BackupDirectory\$($SqlAgDatabase)_AgSetup_full.bak",

  [Microsoft.SqlServer.Management.Smo.DeviceType]::File)

$DbRestore.NoRecovery = $true

$DbRestore.SqlRestore($SqlServerSec)

 

# restore the database on the secondary replica/server (transaction log restore)

$DbRestore = New-Object Microsoft.SqlServer.Management.Smo.Restore

$DbRestore.Database = $SqlAgDatabase

$DbRestore.Action = [Microsoft.SqlServer.Management.Smo.RestoreActionType]::Log

$DbRestore.Devices.AddDevice("$BackupDirectory\$($SqlAgDatabase)_AgSetup_log.trn",

  [Microsoft.SqlServer.Management.Smo.DeviceType]::File)

$DbRestore.NoRecovery = $true

$DbRestore.SqlRestore($SqlServerSec)

The previous code simply creates a full database backup and a transaction log backup on the primary server, and it restores those backups on the secondary server (with NoRecovery).

Availability groups move data between endpoints on the replicas, and this is done through a database mirroring endpoint. What is worth noting is that each instance can only have one database mirroring endpoint (which all availability groups can use). It doesn’t exist out-of-the-box. Therefore, we will perform a quick test for the existence of it on each replica, and if one isn’t already created, we will create it.

# create the endpoint if it doesn't exist on the primary replica

$EndpointPrim = $SqlServerPrim.Endpoints |

  Where-Object {$_.EndpointType -eq [Microsoft.SqlServer.Management.Smo.EndpointType]::DatabaseMirroring}

if(!$EndpointPrim) {

  $EndpointPrim = New-Object Microsoft.SqlServer.Management.Smo.Endpoint($SqlServerPrim, $HadrEndpointName)

  $EndpointPrim.EndpointType = [Microsoft.SqlServer.Management.Smo.EndpointType]::DatabaseMirroring

  $EndpointPrim.ProtocolType = [Microsoft.SqlServer.Management.Smo.ProtocolType]::Tcp

  $EndpointPrim.Protocol.Tcp.ListenerPort = $HadrEndpointPort

  $EndpointPrim.Payload.DatabaseMirroring.ServerMirroringRole = [Microsoft.SqlServer.Management.Smo.ServerMirroringRole]::All

  $EndpointPrim.Payload.DatabaseMirroring.EndpointEncryption = [Microsoft.SqlServer.Management.Smo.EndpointEncryption]::Required

  $EndpointPrim.Payload.DatabaseMirroring.EndpointEncryptionAlgorithm = [Microsoft.SqlServer.Management.Smo.EndpointEncryptionAlgorithm]::Aes

 

  $EndpointPrim.Create()

  $EndpointPrim.Start()

}

 

# create the endpoint if it doesn't exist on the secondary replica

$EndpointSec = $SqlServerSec.Endpoints |

  Where-Object {$_.EndpointType -eq [Microsoft.SqlServer.Management.Smo.EndpointType]::DatabaseMirroring}

if(!$EndpointSec) {

  $EndpointSec = New-Object Microsoft.SqlServer.Management.Smo.Endpoint($SqlServerSec, $HadrEndpointName)

  $EndpointSec.EndpointType = [Microsoft.SqlServer.Management.Smo.EndpointType]::DatabaseMirroring

  $EndpointSec.ProtocolType = [Microsoft.SqlServer.Management.Smo.ProtocolType]::Tcp

  $EndpointSec.Protocol.Tcp.ListenerPort = $HadrEndpointPort

  $EndpointSec.Payload.DatabaseMirroring.ServerMirroringRole = [Microsoft.SqlServer.Management.Smo.ServerMirroringRole]::All

  $EndpointSec.Payload.DatabaseMirroring.EndpointEncryption = [Microsoft.SqlServer.Management.Smo.EndpointEncryption]::Required

  $EndpointSec.Payload.DatabaseMirroring.EndpointEncryptionAlgorithm = [Microsoft.SqlServer.Management.Smo.EndpointEncryptionAlgorithm]::Aes

 

  $EndpointSec.Create()

  $EndpointSec.Start()

}

We can reference these variables against our declarations earlier in this post. Feel free to change the variables. For instance, if you don’t want your database mirroring endpoints to be on port 5022, alter the $HadrEndpointPort variable value. At this point, we are ready to start defining replicas, availability group databases, and listeners. Then we can create the availability group. We will go through this step-by-step.

$AvailabilityGroup = New-Object Microsoft.SqlServer.Management.Smo.AvailabilityGroup($SqlServerPrim, $SqlAgName)

In this script, we defined our base availability group object. Its parent (the constructor’s first parameter) will be the primary Server Class object (defined in our initial variable code block), which we would create the availability group on (that is, the server you want to initially be the primary replica for the availability group). The name (the constructor’s second parameter) is set to the content of our $SqlAgName variable. (Again, feel free to rework any and all code here, including variable definitions and configuration specifications.)

It is time to define our replica (primary and secondary) objects. There are many variations, and your implementation and SLAs will dictate the specifics of commit modes, access allowances, and so on.

# create the primary replica object

$PrimaryReplica = New-Object Microsoft.SqlServer.Management.Smo.AvailabilityReplica($AvailabilityGroup, $SqlServerPrimName)

$PrimaryReplica.EndpointUrl = "TCP://$($SqlServerPrim.NetName):$($EndpointPrim.Protocol.Tcp.ListenerPort)"

$PrimaryReplica.FailoverMode = [Microsoft.SqlServer.Management.Smo.AvailabilityReplicaFailoverMode]::Automatic

$PrimaryReplica.AvailabilityMode = [Microsoft.SqlServer.Management.Smo.AvailabilityReplicaAvailabilityMode]::SynchronousCommit

$AvailabilityGroup.AvailabilityReplicas.Add($PrimaryReplica)

Notice that our primary replica’s object is set with the endpoint URL of our database mirroring endpoint on this replica. This is one of the benefits of using variables: If you need to change the port on which you want the database mirroring endpoint to be created, and you forget to alter a hard-coded value for the URL (if you chose that route), you would not be able to successfully send and receive data due to an endpoint port mismatch. Both of these replicas are going to be automatic failover partners with synchronous commit (a prerequisite for automatic failover capability). Now to create the secondary replica:

# create the secondary replica object

$SecondaryReplica = New-Object Microsoft.SqlServer.Management.Smo.AvailabilityReplica($AvailabilityGroup, $SqlServerSecName)

$SecondaryReplica.EndpointUrl = "TCP://$($SqlServerSec.NetName):$($EndpointSec.Protocol.Tcp.ListenerPort)"

$SecondaryReplica.FailoverMode = [Microsoft.SqlServer.Management.Smo.AvailabilityReplicaFailoverMode]::Automatic

$SecondaryReplica.AvailabilityMode = [Microsoft.SqlServer.Management.Smo.AvailabilityReplicaAvailabilityMode]::SynchronousCommit

$AvailabilityGroup.AvailabilityReplicas.Add($SecondaryReplica)

This is almost identical to the primary replica object definition, except the EndpointUrl property uses the secondary replica’s NetName property. (The end result is to have “TCP://MyPrimaryServer:5022” and “TCP://MySecondaryServer:5022” for the respective replicas.)

Now that the replica objects are created, we will create the availability group database object by defining the constructor’s first parameter as the AvailabilityGroup object that it will belong to, and the database name. (In my case, this is stored in the $SqlAgDatabase variable and set to AdventureWorks2012.)

# create the availability group database object

$AvailabilityDb = New-Object Microsoft.SqlServer.Management.Smo.AvailabilityDatabase($AvailabilityGroup, $SqlAgDatabase)

$AvailabilityGroup.AvailabilityDatabases.Add($AvailabilityDb)

The next step is optional. You do not have to create an availability group listener, but I highly advise it. The reason is because the listener is the virtual network name that always points to the current primary replica. It is the layer of abstraction for clients, and the actual replica server names are not necessary when connecting to the primary replica. For more information about availability group listeners and client connectivity, please see Availability Group Listeners on MSDN.

# create the listener object

$AgListener = New-Object Microsoft.SqlServer.Management.Smo.AvailabilityGroupListener($AvailabilityGroup, $AgListenerName)

$AgListenerIp = New-Object Microsoft.SqlServer.Management.Smo.AvailabilityGroupListenerIPAddress($AgListener)

$AgListener.PortNumber = $AgListenerPort

$AgListenerIp.IsDHCP = $false

$AgListenerIp.IPAddress = $AgListenerIpAddress

$AgListenerIp.SubnetMask = $AgListenerSubnetMask

$AgListener.AvailabilityGroupListenerIPAddresses.Add($AgListenerIp)

$AvailabilityGroup.AvailabilityGroupListeners.Add($AgListener)

There are a handful of parameters set here, and many of these are predefined variables from the earlier script. This will define the specifications for our listener (for instance, IP address and port number).

The last step to the actual availability group creation is simple: We add the availability group object we’ve been working with to the collection of availability groups on the primary server, and we call the Create() method.

# create the availability group

$SqlServerPrim.AvailabilityGroups.Add($AvailabilityGroup)

$AvailabilityGroup.Create()

You might be thinking that we’re done here, but there are two more quick actions to complete. This availability group was created on the primary replica, but now we need to go to the secondary replica and join that replica and its appropriate database to the availability group.

# on the secondary replica, join the replica to the AG, and join the database to the AG

$SqlServerSec.JoinAvailabilityGroup($SqlAgName)

$SqlServerSec.AvailabilityGroups[$SqlAgName].AvailabilityDatabases[$SqlAgDatabase].JoinAvailablityGroup()

And there you have it! We’ve created a functioning availability group for high-availability—all without touching SQL Server Management Studio or writing a line of T-SQL! Not to mention…wisely putting these calls into a script or cmdlets will provide us with reusable code to set up and configure a future availability group. The full script can be viewed and downloaded from the Script Center Repository: Create an AlwaysOn Availability Group with PowerShell.

~Tom

Thank you, Tom, for a great blog post. Join me tomorrow for our second blog in SQL Server Windows PowerShell Week.

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 Printer Configuration in Windows 8

$
0
0

Summary: Use Windows PowerShell 3.0 in Windows to easily find printer configuration settings.

Hey, Scripting Guy! Question How can I use Windows PowerShell to easily find printer configuration settings on my Windows 8 computer?

Hey, Scripting Guy! AnswerUse the Get-Printer function to retrieve all printer objects, and pipe the results to the Get-PrintConfiguration function:

Get-Printer | Get-PrintConfiguration

Monitor an AlwaysOn Availability Group with PowerShell

$
0
0

Summary: Guest blogger, Tom Stringer, illustrates how to monitor an AlwaysOn Availability Group by using Windows PowerShell to ensure high availability functionality in an environment.

Microsoft Scripting Guy, Ed Wilson, is here. Today we have another blog in our SQL Server Week series from Microsoft PFE, Thomas Stringer.

Thomas Stringer is a SQL Server premier field engineer at Microsoft. He specializes in scripting (Windows PowerShell), high availability, disaster recovery, performance tuning, internals, querying, and database administration. He also has a software development background in languages and platforms from C all the way through .NET (including C# for WinForm and ASP.NET WebForms/MVC applications).

Photo of Thomas Stringer

Email: sqlsalt@outlook.com
Blog: http://sqlsalt.com
Twitter: @SQLife
LinkedIn: Thomas Stringer

Here’s Part 2…

In Part 1 of this series, Set Up an AlwaysOn Availability Group with PowerShell, I talked about and illustrated how to set up an availability group. But as tenders of data (and the administrators of high availability), we know far too well that these solutions can sometimes get hiccups or not do exactly what we think they are doing. And that’s what this blog post is going to be about: Monitoring our availability groups to check if they are in a state that we expect and desire.

The SMO namespace lends a large number of classes, and this includes the necessary objects and data access to actively monitor our high availability implementation.

Database synchronization

One of the key points of monitoring availability groups is to make sure that the data on secondary replicas is at the correct synchronization state with the primary replica (for instance, with synchronous commit, the secondary replicas should be in a synchronized state). We can monitor this through the DatabaseReplicaState class.

As always, when we expect to have the SMO namespace available to us, we’ll need to load it:

[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SqlServer.Smo") |

  Out-Null

First, we will look at a relatively manual way to traverse the availability group structure for this desired information (later in this post, I will explain how to automate the availability group monitoring). What I mean by this is that we’ll explicitly create a Server object for each replica. In this example, the primary replica with a server name of “ALWAYSON1” is $SqlServer and the secondary replica with a server name of “ALWAYSON2” is $SqlServerSec:

$SqlServer = New-Object Microsoft.SqlServer.Management.Smo.Server("ALWAYSON1")

$SqlServerSec = New-Object Microsoft.SqlServer.Management.Smo.Server("ALWAYSON2")

Now that we have these objects initialized, we can start diving into the monitoring. To get the synchronization state of the availability group databases, we can run the following code:

$SqlServer.AvailabilityGroups["AgTest100"].DatabaseReplicaStates |

  Select-Object AvailabilityReplicaServerName,

    AvailabilityDatabaseName,

    SynchronizationState

My availability group is named “AgTest100”, and by using the AvailabilityGroups property of the Server object, I am able to specify which availability group I would like to retrieve by specifying the availability group’s name. We then reference the DatabaseReplicaStates property of the AvailabilityGroup object to get the monitoring information we want. The value of this can be any of the following:

  • Synchronized
  • Synchronizing
  • NotSynchronizing
  • Reverting
  • Initializing

For synchronous commit mode, during normal operation, we should be seeing Synchronized. And for asynchronous commit, we should have Synchronizing. The output from my environment looks like the following screenshot:

Image of command output

This tells me that my availability group database, AdventureWorks2012, on my replicas is in the Synchronized state. If I had more than one database in my availability group, its state would also reflect here.

Replica AvailabilityMode

In the previous section, I talked about how to determine the synchronization status of the availability group databases. What I alluded to is that the SynchronizationState will largely be dependent on the availability mode that the replica is set with (either synchronous commit or asynchronous commit). To find that information, we can run the following statement:

$SqlServer.AvailabilityGroups["AgTest100"].AvailabilityReplicas |

  Select-Object Name,

    Role,

    AvailabilityMode

In my test environment, I get the following result set:

Image of command output

This simple statement gives us all of the replica names, their current role (Primary or Secondary), and most importantly, the AvailabilityMode. This is just more information to get the current picture for what we expect, especially pertaining to SynchronizationState.

Log send and redo

Performance and impact is almost always on our mind as DBAs. We absolutely care about synchronization and operational state, but we also need to be mindful about the impact that our workload and the data transmission is having on our availability group. After all, if you have a synchronous commit replica and that replica is unable to redo transactions quickly enough, you could potentially be impacting performance on the primary replica and having poor user experience. Or if you have an asynchronous commit replica that is designed to handle your reporting requirement, you need to ensure that the data is current enough to comply with the SLA.

Luckily, SQL Server gives us these metrics to determine those few key items. Let’s get Windows PowerShell to retrieve this information:

$SqlServer.AvailabilityGroups["AgTest100"].DatabaseReplicaStates |

  Where-Object {$_.IsLocal -eq $false} |

  Select-Object AvailabilityReplicaServerName,

    AvailabilityDatabaseName,

    LogSendRate,

    LogSendQueueSize,

    RedoRate,

    RedoQueueSize

In my test environment, I get the following output:

Image of command output

We see four important results here. They are going to be rates in KB/s or queue sizes in KB.

  • LogSendRate gives us how quickly the log is able to be sent to secondary databases.
  • LogSendQueueSize shows us the amount of log that is waiting to be sent to the secondary.
  • RedoRate is just that—the rate at which the secondary database is redoing the transactions.
  • RedoQueueSize is the amount of log on the secondary database that is waiting to be redone.

What’s worth noting here that is with consistent and sustained queue sizes, you could potentially be seeing issues with the network (not able to transfer log records quickly enough) or the secondary replica’s inability to redo log records quickly enough. Another consideration is the type of workload that is on the primary replica’s database.

Operational and connection states

There are other factors that we need to take into consideration at the replica level. Mainly, these are the operational and connection states. We can use the following Windows PowerShell to gather this information (this is run on each replica in the availability group):

$SqlServer.AvailabilityGroups["AgTest100"].AvailabilityReplicas |

  Where-Object {$_.Name -eq $SqlServer.Name} |

  Select-Object Name,

    Role,

    OperationalState,

    ConnectionState

$SqlServerSec.AvailabilityGroups["AgTest100"].AvailabilityReplicas |

  Where-Object {$_.Name -eq $SqlServerSec.Name} |

  Select-Object Name,

    Role,

    OperationalState,

    ConnectionState

In my environment, I get the following output:

Image of command output

The OperationalState can be any of the following possibilities:

  • Online
  • Offline
  • Pending
  • PendingFailover
  • Failed
  • FailedNoQuorum
  • Unknown

For normal operations, we expect this to be Online. Other states can reflect failover (potentially a normal operation depending on the surrounding situation) or other issues that will require manual intervention to resolve (for instance, Failed indicates that the replica can no longer talk to the cluster).

The ConnectionState can be one of the following:

  • Connected
  • Disconnected
  • Unknown

During normal operations, we should be seeing a Connected state.

Miscellaneous tips and tricks

We are monitoring our availability group with Windows PowerShell, so we have already taken the first step to ensure that this is a streamlined process. If you noticed earlier, a lot of these queries are run against the primary replica. In my case, currently the primary replica is a server named “ALWAYSON1”. But if we have a listener created, we can also utilize our diagnostic scripts against the listener. Most of the data collection commands are run solely against the primary replica, and we know that the availability group listener (if it exists) points to the primary, so we can take advantage of this connection abstraction like any other client application does.

Another point to consider is that this blog post illustrates how to monitor one availability group. There’s probably a good chance that you have more than one availability group in your environment. My suggestion would be to store a list of servers (in a text file or in a table in a centrally managed database server), and simply loop through them. You can retrieve all of this diagnostic data in a ForEach loop by accessing the AvailabilityGroups property of each server.

~Tom

Thanks for showing us how to monitor AlwaysOn availability groups with Windows PowerShell, Tom!

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 Check DHCP Status

$
0
0

Summary: Use Windows PowerShell to check on DHCP status.

Hey, Scripting Guy! Question How can I use Windows PowerShell to check on my DHCP status?

Hey, Scripting Guy! Answer Use the Get-Service cmdlet and search for services that have DHCP in the name. You can do this remotely if the firewall has exceptions and if you have rights.

PS C:\> gsv -Name *dhcp* -ComputerName wds1

 

Status  Name        DisplayName

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

Running Dhcp        DHCP Client

Running DHCPServer     DHCP Server

 

Troubleshooting an AlwaysOn Availability Group with PowerShell

$
0
0

Summary: Guest blogger, Tom Stringer, illustrates how to troubleshoot an AlwaysOn availability group by using Windows PowerShell.

Microsoft Scripting Guy, Ed Wilson, is here. We welcome back guest blogger, Thomas Stringer, for Part 3 of this three-part series. To catch up, read:

Part 1: Set Up an AlwaysOn Availability Group with PowerShell

Part 2: Monitor an AlwaysOn Availability Group with PowerShell

Thomas Stringer is a SQL Server premier field engineer at Microsoft. He specializes in scripting (Windows PowerShell), high availability, disaster recovery, performance tuning, internals, querying, and database administration. He also has a software development background in languages and platforms from C all the way through .NET (including C# for WinForm and ASP.NET WebForms/MVC applications).

Photo of Thomas Stringer

Email: sqlsalt@outlook.com
Blog: http://sqlsalt.com
Twitter: @SQLife
LinkedIn: Thomas Stringer

Here’s Tom…

In the first part of this series on AlwaysOn availability groups and Windows PowerShell, I talked about how to set up and configure an availability group. In the second part, I showed you a few ways you can use to monitor availability groups. By now, you are most likely seeing the progression of my publications, and today we’re going to be talking about how to troubleshoot an AlwaysOn availability group through the use of Windows PowerShell.

Say your availability group is all set up and you’re actively monitoring it. All of a sudden, you start seeing errors or your data synchronization seizes. Where do you look now? How can you troubleshoot this issue?

Depending on the nature of the issue and the symptoms, I almost always default directly to the SQL Server error log.

Note   I also investigate the AlwaysOn_Health Extended Events session—but that’s not quite as accessible through Windows PowerShell. It is worth mentioning though. For thorough troubleshooting, looking through the collected events in this session is extremely helpful.

AlwaysOn availability groups is a relatively chatty technology, whether the situation is good, bad, or indifferent. The SQL Server error log reads like a story for availability groups, especially with role transitioning and connectivity. There are a few situations that can cause the database to go into a suspect state and stop data synchronization. But investigation needs to take place to find exact proof and the root cause of the issue.

What do we do first? Well, let’s first verify the state of the database. The first step is to load SMO and create our Server objects to point to our primary and secondary replicas. I mentioned in the last post that we can also loop through a list of replica names or use the listener to connect to the primary. For simplicity’s sake, I will be explicit here:

$SqlServerPrimaryName = "ALWAYSON1"

$SqlServerSecondaryName = "ALWAYSON2"

 

[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SqlServer.Smo") |

  Out-Null

 

$SqlServer = New-Object Microsoft.SqlServer.Management.Smo.Server($SqlServerPrimaryName)

$SqlServerSec = New-Object Microsoft.SqlServer.Management.Smo.Server($SqlServerSecondaryName)

Note In this blog post, my test availability group is named “AgTest100”.

Now we will run a quick command to see and verify what is happening:

$SqlServer.AvailabilityGroups["AgTest100"].DatabaseReplicaStates |

  Select-Object AvailabilityReplicaServerName,

    AvailabilityDatabaseName,

    IsLocal,

    IsSuspended,

    SuspendReason

Here is my output when I’m in this state:

Image of command output

Notice the second item. It’s the remote secondary replica’s database (AdventureWorks2012). What’s important here is that it is suspended from redoing transactions. Let’s get a little more information as to what the problem is right from the secondary replica:

$SqlServerSec.Databases["AdventureWorks2012"] |

  Select-Object Name, Status

I get the following result, showing me that the database is suspect:

Image of command output

So we have the surrounding issue: we have suspended data movement and a suspect availability group database on the secondary replica. Now we need to find out why this is happening. I head to the SQL Server error log immediately to see what’s happening. On the secondary replica I want to look at the SQL Server error log with any error messages that contain my database that is now suspect:

$SqlServerSec.ReadErrorLog() |

  Where-Object {$_.Text -like "*AdventureWorks2012*"} |

  Select-Object LogDate, Text |

  Format-List

The most recent errors tell me that an error has occurred:

Image of command output

This really only tells me what I already know. So let’s refine our search a little bit. We know that the issue occured on or around 4/17/2013 at 11:55 AM, so let’s get all the log records from that time span to get the full picture (I choose a two minute window):

$SqlServerSec.ReadErrorLog() |

  Where-Object {

    $_.LogDate -ge "4/17/2013 11:54:00 AM" -and

    $_.LogDate -le "4/17/2013 11:56:00 AM"} |

  Select-Object LogDate, Text |

  Format-List

Here’s what that output looks like on my computer:

Image of command output

Bingo! So now we see exactly what the issue is. There was CREATE FILE command that came from the primary replica, and when that transaction went through the redo phase on the secondary replica, it failed because of a mismatch in the directory structure between the two replicas. The fix for this issue is out of the scope of this blog post, but if you happen to run into this issue, the resolution can be found in my blog post, Adding Database Files to an AlwaysOn Availability Group Database (Part 2 – The Reactive Way).

Another potential issue that can be seen with availability groups is connectivity issues. Behind the scene, SQL Server utilizes the database mirroring endpoint for each of the instances that are participating in an availability group to send transactions to the secondary replicas. Like any other network traffic and communication, you could be experiencing issues. To grab information about any connectivity issues that could be happening, you can pull three items from SQL Server through SMO: LastConnectErrorNumber, LastConnectErrorDescription, and LastConnectErrorTimestamp.

$SqlServer.AvailabilityGroups["AgTest100"].AvailabilityReplicas |

  Select-Object Name,

    LastConnectErrorNumber,

    LastConnectErrorDescription,

    LastConnectErrorTimestamp

 

$SqlServerSec.AvailabilityGroups["AgTest100"].AvailabilityReplicas |

  Select-Object Name,

    LastConnectErrorNumber,

    LastConnectErrorDescription,

    LastConnectErrorTimestamp

This is just more diagnostic data that will paint the picture of what the issue is, and lead you to what the solution may be.

AlwaysOn availability groups (and failover cluster instances) are interesting technologies. AlwaysOn availability groups are SQL Server, but they also sit right on top of Windows Server Failover Clustering. As DBAs, there is a good chance that our daily work may not utilize this Windows Server technology. But as we will see now, it will become rather important to be familiar with the basics of Windows Server Failover Clustering and where to look when troubleshooting cluster issues.

Much like SQL Server, there is valuable information that we can gather from the logs that contain clustering information for troubleshooting. These cluster events are stored in the System Event Log on the nodes in the cluster. If you are troubleshooting an issue and trying to look from a time span of the past day, you could use the following Windows PowerShell script to gather the System Event Log events for the cluster on both of the nodes:

$ClusterEvents = foreach ($NodeName in @("ALWAYSON1", "ALWAYSON2")) {

  Get-EventLog -ComputerName $NodeName -LogName System |

    Where-Object {

      $_.Source -eq "Microsoft-Windows-FailoverClustering" -and

      $_.TimeGenerated -ge "4/15/2013 1:00:00 PM" -and

      $_.TimeGenerated -le "4/16/2013 1:00:00 PM"} |

    Select-Object MachineName, TimeGenerated,

      EntryType, InstanceID, Message

}

$ClusterEvents |

  Sort-Object TimeGenerated

All this script does is loop through each of the nodes and gets the event log contents with a source of failover clustering, and a time span of a day.

Image of command output

There are a few other “gotchas” on the clustering side that we simply can’t see or control within SQL Server. One of those is the RegisterAllProvidersIP cluster parameter for the listener. If you are utilizing multiple subnets, and you’re having connection timeouts intermittently, or just high latency on the connection, it could be because you’re not setting (or your driver doesn’t have the capability for) MultiSubnetFailover.

Out-of-the-box, the listener has a parameter called RegisterAllProvidersIP that is set to 1. This dictates whether the cluster will publish all IP addresses (even if they are offline) so that clients with MultiSubnetFailover can attempt connection to all of them simultaneously. For more information about the impact this has, please see Create or Configure an Availability Group Listener (SQL Server).  

To retrieve this cluster setting, you can utilize the FailoverClusters Windows PowerShell module.

Note  You have to run this Windows PowerShell script on a computer that has the aforementioned module readily available.

Get-Cluster -Name "ClusterName" |

  Get-ClusterGroup -Name "AgTest100" |

  Get-ClusterResource |

  Get-ClusterParameter |

  Where-Object {$_.Name -eq "RegisterAllProvidersIP"} |

  Select-Object ClusterObject, Name, Value

This will give you an output similar to the following, which shows whether this parameter is set to 1 (enabled) or 0 (disabled):

Image of command output

Here you can see that I have it set to 1 (the default), and this could potentially be causing my connection issues.

Another possible issue could be that you are having too many failovers within a certain period of time. There is a driving threshold that can prevent a potential automatic failover when you expect it. By default, that threshold is set to a maximum failures of NodeCount – 1 over a period of six hours. So for a two-node cluster, that’ll be one failover for a six-hour period. To get this setting for the availability group, we can use the following script:

Get-Cluster -Name "ClusterName" |

  Get-ClusterGroup -Name "AgTest100" |

  Select-Object Name, FailoverPeriod, FailoverThreshold

I get the following output in my environment:

Image of command output

This basically means that when I reach my threshold of one failover in a six-hour period, automatic failover will not take place.

We’ve looked at a few settings that could be giving us problems on the cluster side of the availability group. A great way to see what’s happening if we have a failed availability group is to see the state of all the availability group’s cluster resources:

Get-Cluster -Name "ClusterName" |

  Get-ClusterGroup -Name "AgTest100" |

  Get-ClusterResource |

  Select-Object Name, State, ResourceType

My cluster has the following output:

Image of command output

I’ve shown you today a few methods that can be used when you are trying to troubleshoot AlwaysOn availability groups. Through the flexibility and efficiency of Windows PowerShell, you can effortlessly comb through many avenues of diagnostics to determine what the problem area may be with your currently high availability solution. Enjoy!

~Thomas

Thank you, Thomas, for an awesome series. Join us tomorrow as SQL Server Week continues with a guest blog by SQL Server MVP, Sean McCowan. 

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 Show Remaining Battery Time

$
0
0

Summary: Use Windows PowerShell to show the percentage of remaining battery time.

Hey, Scripting Guy! Question How can I use Windows PowerShell to show the percentage of remaining battery time on my laptop or on my Windows Surface?

Hey, Scripting Guy! Answer Use the Get-WmiObject cmdlet and query the Win32_Battery WMI class:

(Get-WmiObject win32_battery).estimatedChargeRemaining

2013 Scripting Games: Beginner Event 2

$
0
0

Summary: Dr. Scripto needs to find basic information from his servers.

Microsoft Scripting Guy, Ed Wilson, is here. Welcome to Week 2 of the 2013 Scripting Games. Just in case you are not up to speed, this year The Scripting Guys are not hosting the Scripting Games. They are being hosted at www.powershell.org. I am in full support of the Games, and I am posting the events here for your perusal. However, if you would like to submit an entry to the event, you must go to www.powershell.org.

An Inventory Intervention

Dr. Scripto finally has the budget to buy a few new virtualization host servers, but he needs to make some room in the data center to accommodate them. He thinks it makes sense to get rid of his lowest-powered old servers first…but he needs to figure out which ones those are.

All of the virtualization hosts run Windows Server, but some of them don’t have Windows PowerShell installed, and they’re all running different operating system versions. The oldest operating system version is Windows 2000 Server (he knows, and he’s embarrassed, but he’s just been so darned busy). The good news is that they all belong to the same domain, and that you can rely on having a Domain Admin account to work with.

The good Doctor has asked you to write a Windows PowerShell command or script that can show him each server’s name, the installed version of Windows, amount of installed physical memory, and number of installed processors. For processors, he’ll be happy to get a count of cores, or sockets, or even both—whatever you can reliably provide across all these different versions of Windows. To help you out, he’s given you a text file, C:\IPList.txt, which contains one server IP address per line.

If you can write this as a one-liner, awesome! If not, try to keep your answer as concise and compact as possible (although it’s perfectly okay to use full command and parameter names).

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

2013 Scripting Games: Advanced Event 2

$
0
0

Summary: Dr. Scripto needs to create a tool to find basic information from his servers.

Microsoft Scripting Guy, Ed Wilson, is here. Welcome to Week 2 of the 2013 Scripting Games. Just in case you are not up to speed, this year The Scripting Guys are not hosting the Scripting Games. They are being hosted at www.powershell.org. I am in full support of the Games, and I am posting the events here for your perusal. However, if you would like to submit an entry to the event, you must go to www.powershell.org.

An Inventory Intervention

Dr. Scripto finally has the budget to buy a few new virtualization host servers, but he needs to make some room in the data center to accommodate them. He thinks it makes sense to get rid of his lowest-powered old servers first…but he needs to figure out which ones those are.

This is just the first wave, too. There’s more budget on the horizon, so it’s possible that he’ll need to run this little report a few times. Better make a reusable tool.

All of the virtualization hosts run Windows Server, but some of them don’t have Windows PowerShell installed, and they’re all running different operating system versions. The oldest operating system version is Windows 2000 Server (he knows, and he’s embarrassed, but he’s just been so darned busy). The good news is that they all belong to the same domain, and that you can rely on having a Domain Admin account to work with.

The good Doctor has asked you to write a Windows PowerShell tool that can show him each server’s name, the installed version of Windows, amount of installed physical memory, and number of installed processors. For processors, he’ll be happy to get a count of cores, or sockets, or even both—whatever you can reliably provide across all these versions of Windows. He has a few text files with computer names, and he’d like to pipe the computer names, as strings, to your tool, and have your tool query those computers.

Oh, and in case he forgets how to use it—make sure this tool of yours has a full Help display available.

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

Scripting Wife Comments on Beginner Event 2

$
0
0

Summary: The Scripting Wife reveals her impressions of 2013 Scripting Games Beginner Event 2.

Well I am back from Seattle and from the Windows PowerShell Summit—and boy, did I have a lot of fun. I got to see friends from all over the world, and the amazing thing is that I knew nearly everyone there—at least virtually. Well, nearly everyone. The good thing is that I got to know the ones that I did not previously know. I really want to thank Don, Jeffery, Richard, Chris, and Jason for all their hard work in putting this summit together. Here is a picture of Lee Holmes and me outside the conference room prior to him delivering an awesome presentation.

Photo of Lee Holmes with Scripting Wife

OK…this event is not too bad. Whenever I hear things like inventory, I think of WMI. To help you get moving in the right direction, read my post, Weekend Scripter: Use PowerShell to Find the Version of Windows, where I work with the Scripting Guy to find WMI information. So all I need to do is get the operating system version, the number of processors, and the amount of memory. I also need to be able to read a text file that contains the IP addresses. No problem…this really is not too hard at all.

~Scripting Wife

Use PowerShell to Work with SQL Server Instance-Level Properties

$
0
0

Summary: SQL Server MVP, Sean McCown, talks about using Windows PowerShell to work with SQL Server instance-level properties.

Microsoft Scripting Guy, Ed Wilson, is here. Today is the first of three blogs written by guest blogger, Sean McCown. You can read more about Sean and his previous guest blogs on the Hey, Scripting Guy! Blog site.

Here’s Sean…

Many DBAs don’t realize how much power they get from the Windows PowerShell provider and how much information is at their fingertips. Here I’m going to walk you through some of the better server instance-level properties, and we’ll even save them to a table so you have a nice inventory of your entire shop. Of course, feel free to add your own properties. Also, I explain a lot of basic things here and that’s because I really don’t like to assume too much knowledge. I think you should be familiar with a little bit of Windows PowerShell for this post, but I try to explain things at a low enough level that you should be OK if you only know the most basic of basics.

Let’s start by getting to the server properties. The easiest way to do it depends on where you are when you start. If you’re in SSMS, right-click the server name in Object Explorer, and then click Start PowerShell.

Image of menu

When you’re there, you’ll be at the instance level. You need to be at the server level though. So to get to the server level, you simply type cd..

Image of command output

Or if you’re already in Windows PowerShell, you can navigate to the server path like this:

>cd sqlserver:\sql\sdlseanm

That will put you in the same location. It’s worth noting that if you don’t see SQLSERVER when you run the command psdrive, you need to load the SQL Server provider. To learn how to do this you can watch my video, Watching Powershell Profiles. The code is different for SQL Server 2012. Type Import-Module SQLPS, but the rest of the video applies.

Now that we’re where we need to be, we can start looking at properties. As with anything else, there are more than one way to accomplish the same task in Windows PowerShell, and I’m going to show you a couple of them. Let’s start by pulling up a listing of the items at this level:

>dir

This gives us a list of the instances on this server. And that makes sense because we were at a specific instance and went a level up. In my case, I’ve got two instances because Visual Studio insists on installing SQL Express no matter what I say.

Image of command output

Now that we see the instances, let’s find out the information we can get for them. We do that by typing:

>dir | gm

This gives us a huge list of properties and methods. Here’s a screenshot of the partial list:

Image of command output

Right off the bat you’ll recognize many of these from the T-SQL function SERVERPROPERTY(). So if you’ve already got this information, why bother messing with it in Windows PowerShell? Well, for starters, you can more easily perform large inventories in Windows PowerShell (as we’ll see later), and you can set the writeable properties for all your boxes, which is something you can’t easily do in T-SQL.

First, let’s set a little bit more of the stage. At the end of each of those lines, you’ll see something that looks like this: {get;} or {get;set;}.

The get means you can read the value, and the set means you can change, or set the value. So you can read all of them, but you can only set some of them. And that makes sense because you can’t set the BuildNumber for an install. Only the install process can do that.

But what values can you set? You can look through this list, but they’re not all lined up nicely on the right so it’s hard to make sure that you don’t miss anything. What we need is an easier way to view if these values are writeable.

Before we can do that, we need to learn how to work with instances. Because although we were able to run Get-Member, we won’t be able to work with any of them until we run it against a specific instance. You can get a specific instance like this:

>$a = dir | ?{$_.name –eq “SDLSEANM”}

And now if you run Get-Member against $a, you’ll come up with the same list, but now you’re set to actually work with these values. Now let’s take a look at the {get;set;} info in an easier way.

>$a.properties | FT Name, Writable, Dirty, Value –auto

And again, here’s a partial list of the results.

Image of command output

See how much easier it is to see the properties that you can change? And with this method we can very easily filter down the list to exactly what we want to see, or even change the sorting. I’ll leave those details to you though. You’ll also notice that I included the Dirty column. We’ll be using that in the next post. And if you want to see all the properties that you have available to you here, just type:

>$a.properties | gm

I think we’ve got enough background to actually start working with these items now. Let’s start by pulling some basic instance info.

>$a | FT Name,Edition, VersionMajor, VersionMinor, ProductLevel, Processors, PhysicalMemory –auto

Here’s what I got back:

Image of command output

This is actually pretty good info without hardly trying. But again, that’s all info you can get from SERVERPROPERTY(). So let’s make this more useful right out of the gate so you can see the real power of what I’m talking about.

First we have to know which servers we want to look at. We can’t just say, “Go get us a list of all the SQL boxes from the ether,” and expect results. We have to actually get it from somewhere. And that “somewhere” is where you’ll have a lot of flexibility. For simplicity I’m going to use a simple array with a couple servers in it, but you can use the results from a query, an Excel file, XML, or even a plain text file. You can really use anything that you can query and get a list of server names back from. I’m going to use an array, and here’s how you could set it by using a query:

>$a = invoke-sqlcmd –ServerInstance MyServer –Database “MyDB” –query “Select ServerName from dbo.Servers”

>$a | ?{$b = $_.ServerName;}

Now we’ll use the array instead, but it’ll be almost exactly the same. First we need a table to hold our info because we’re going take all this info from several boxes and put it into a table in a database. This is why this method is so powerful.

I’m going to create a generic table for this example, but you should make your data types a little tighter.

CREATE TABLE ServerProperties

(

ServerName varchar(255),

Edition varchar(255),

Vmajor tinyint,

Vminor varchar(255),

ProductLevel varchar(255),

Processors tinyint,

PhysicalMemory int

)

That should do what we want.

Now we need to cycle through our servers, collect the info, and save it to the table. Let’s get started.

Import-Module SQLPS -DisableNameChecking

$a = "SDLSEANM", "crp01msddb01"

$a | %{ # Begin $a

$ServerName = $_;

Cd SQLSERVER:\SQL\$ServerName;

$InstanceList = dir;

$InstanceList | %{ # Begin $InstanceList

                        $Name = $_.Name;

                        $Edition = $_.Edition;

                        $VersionMajor = $_.VersionMajor;

                        $VersionMinor = $_.VersionMinor;

                        $ProductLevel = $_.ProductLevel;

                        $Processors = $_.Processors;

                        $PhysicalMemory = $_.PhysicalMemory;

                       

$ServerName; ## So we can watch it cycle through the boxes.

"Insert ServerProperties Select '$Name', '$Edition', $VersionMajor, '$VersionMinor', '$ProductLevel', $Processors, $PhysicalMemory"                     

Invoke-sqlcmd –ServerInstance "crp01msddb01" –database "tempdb" –query "Insert ServerProperties Select '$Name', '$Edition', $VersionMajor, '$VersionMinor', '$ProductLevel', $Processors, $PhysicalMemory"

                        } # End $InstanceList

} # End $a

That’s it. We’re done.

Now, I realize that looks like it might be getting to be a long script, but it’s really not that bad. Most of it is control-type stuff. In fact, the Invoke-SqlCmd line is the only line that does any actual work. Everything else is setting things up and making them look pretty so we can manage the script. I could have easily cut all the variable assignment, but it makes the script a lot easier to read and extend, so I prefer to leave it in. Also, if the Query command is going to be long you might consider putting it into a variable too, and then putting the variable in the database call. Here’s what that would look like:

$InsertQuery = "Insert ServerProperties Select '$Name', '$Edition', $VersionMajor, '$VersionMinor', '$ProductLevel', $Processors, $PhysicalMemory"         

Invoke-sqlcmd –ServerInstance "crp01msddb01" –database "tempdb" –query "$InsertQuery"

This makes it easier to manage if you’re going to have a lot of values.

So there you go. I only demonstrated a handful of properties for you, but you can see from Get-Member that there are so many more to be had. Explore them on your own and write your own table to put them in. This is such a powerful technique for documenting your SQL Server environment, and it clearly can’t be done this easily in T-SQL.

Additionally, you don’t have to have the process do the insert. You could save the T-SQL statements to a file and run them manually, if you prefer. That’s easy enough too:

$InsertQuery = "Insert ServerProperties Select '$Name', '$Edition', $VersionMajor, '$VersionMinor', '$ProductLevel', $Processors, $PhysicalMemory"

$InsertQuery | Out-File 'c:\Inserts.sql' -append   

That’s it for now. Next time, we’re going to talk about the Dirty column we saw earlier and explore when it can be useful.

~Sean

Awesome job, Sean. Thank you. SQL Server Week will continue tomorrow when we will have another guest blog by Sean.

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 Reserved System Volumes

$
0
0

Summary: Use Windows PowerShell 3.0 in Windows 8 to find information about the system reserved volume.

Hey, Scripting Guy! Question How can I use Windows PowerShell 3.0 in Windows 8 to return information about my system reserved volume?

Hey, Scripting Guy! Answer Use the Get-Volume function and specify the FileSystemLabel of “System Reserved”:

Get-Volume -FileSystemLabel "System Reserved"

 

Use PowerShell to Examine Dirty and Expensive SQL Server Properties

$
0
0

Summary: SQL MVP, Sean McCown, talks about using Windows PowerShell to examine Dirty and Expensive SQL Server properties.

Microsoft Scripting Guy, Ed Wilson, is here. Here is blog two of three in a series from guest blogger, Sean McCown. Yesterday, we published Use PowerShell to Work with SQL Server Instance-Level Properties. You can read more about Sean and his previous guest blogs on the Hey, Scripting Guy! Blog site.

Here’s Sean…

In the last post, we talked about getting server properties and saving them to a table so you can get a good inventory of your environment. This time we’re going to talk about a couple of the columns in the property list that we didn’t get to before: Dirty and Expensive. I’ll quickly give you the line of code, but you can refer to the last post for a full explanation:

$a = dir | ?{$_.name –eq “SDLSEANM”} 

$a.properties | FT Name, Value, Expensive –auto

Let’s explore the Dirty column first. As it turns out, Windows PowerShell works with the same concept of caching that SQL Server does. So whenever you have a Windows PowerShell session open, and you make changes to objects, you’re quite often only making changes to the in-session copy of the objects, and not to the objects on the server.

Those of you who follow my videos have heard me say many times that if an object has an Alter() method, it’s usually meant to be used. A simple example is when you change the recovery model of your databases. If you go to the database level in Windows PowerShell, you can run the following code to change your recovery model from Full to Simple.

Dir | %{$_.RecoveryModel = 3}

There are three recovery models in SQL, and in SMO you work with the numbers—even though the numbers are translated when you query them. A good way to remember it is that they go from the most protective to the least. So Full is 1 and Simple is 3.

So if you look at the recovery models of the databases again, you’ll see that they’ve all been changed to Simple.

Dir | FT Name, RecoveryModel –auto

But if you look at the recovery model for any of the databases in SSMS, you’ll see that it’s still Full. So why is that? Well, that’s because you’ve only changed the in-session version of the property and it hasn’t been pushed to the server. To push it to the server, you’ll need to call the Alter() method like this:

Dir | %{$_.RecoveryModel = 3; $_.Alter()}

And now if you go to SSMS, you’ll see the values have been changed. This is what the Dirty column in the properties collection tells you. It tells you whether the value has been pushed to the server. It’s a lot like how writing to pages works. When you write data to a page, it gets written in memory, and it’s not until a checkpoint comes around that it actually writes the page to disk. In fact, we say that the checkpoint writes “dirty” pages to disk—dirty pages being those that have been changed, but not hardened to disk yet.

It’s the same thing here. The in-session page is dirty. That is, it hasn’t been written to the server yet. You can prove this very easily. After the first line where you made the change previously, but you didn’t call the Alter() method, view the properties and see what you find in the Dirty column. Do it exactly like this:

Dir | %{$_.RecoveryModel = 3}

$a = dir | select -first 1 ## just return the 1st one to avoid the loop. 

$a.properties | FT Name, Dirty -auto

You will see that RecoveryModel is set to True, which is dirty. The change has not been pushed to the server. Now run it again and call the Alter() method afterward, then inspect the properties again.

Dir | %{$_.RecoveryModel = 3; $_.Alter()} 

$a = dir | select -first 1 ## just return the 1st one to avoid the loop. 

$a.properties | FT Name, Dirty -auto

You can see that Dirty has been set back to False because the change has now been pushed to the server. You can verify this in SSMS now. Be very careful with this concept though. A common mistake beginners make is that they confuse properties with methods, and they often think that methods work the same way. They don’t.

So to change a property, you have to call the Alter() method to save the change, but when you call a method—for example, Drop()—you don’t need to call the Alter() method. The change is made on the server when the object is in the pipeline. So don’t confuse changing properties with calling methods or you’ll be very sorry one day.

You can use this method of looking at your dirty properties anywhere you see an object with properties and a property collection. I know I started this post talking about instance-level properties, but I wanted to show you that it’s the same at all levels so I gave you the example working with databases.

Now we can go back and do the exact same thing at the instance level and change something there.

$a = dir | ?{$_.name –eq “SDLSEANM”} 

$a.BackupDirectory = “F:\MyBackups” 

$a.properties | FT Name, Dirty -auto 

So you see, nothing has really changed. We can still inspect the Dirty column like we did at the database level. And of course, that means that we still have to call the Alter() method when making the change to begin with. Only here we have it set to a variable, so it may be best to separate it into two calls. Here’s what the final version looks like:

$a = dir | ?{$_.name –eq “SDLSEANM”} 

$a.BackupDirectory = “F:\MyBackups” 

$a.Alter()

$a.properties | FT Name, Dirty –auto

Alright, that’s it for the Dirty column. Now let’s talk briefly about the Expensive column. There’s not a lot of advice I can give you about it, but I want you to know what it means. Expensive means that the particular property is more expensive to gather. Now, there’s nothing like a good tautological definition—but what do you want from me? That’s really what it means. But logically, what does “expensive” mean? Well unfortunately, it doesn’t mean anything tangible in this case—nothing that is documented anyway. If you look up Property.Expensive Property on MSDN it says:

“The boundary between expensive properties and inexpensive properties is an arbitrary value.”

Frankly, I was hoping for a little more than that, but it pretty much jives with what I’ve seen in my testing and in my experience. I haven’t had any trouble pulling back any of the properties marked as expensive, so the value really must be completely arbitrary.

However, that’s not to say that if you were pulling back a lot of values, that you wouldn’t see some performance issues. I’ve only ever tested this on jobs and pulling back an expensive property (say Category) hasn’t proven to be any more expensive than anything else. But maybe if I had 10,000 jobs, I might start seeing some lag. If you notice these properties being slower on your system, you know how to investigate it now. You can query on whether it’s marked as Expensive, and then exclude it from your collection and see if the time improves. If it does, that one really is expensive for you.

Alright, that’s it for this time.

~Sean

Thank you, Sean. Come back tomorrow for another guest blog by Sean.

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 Network Adapter Power State

$
0
0

Summary: Use Windows PowerShell to find network adapter power states.

Hey, Scripting Guy! Question How can I use Windows PowerShell 3.0 in Windows 8 to identify network adapter power states?

Hey, Scripting Guy! Answer Use the Get-NetAdapterPowerManagement function.

Use PowerShell to Clean-up SQL Server Jobs

$
0
0

Summary: SQL Server MVP, Sean McCown, shows how to use Windows PowerShell to clean-up SQL Server jobs.

Microsoft Scripting Guy, Ed Wilson, is here. Today we wrap up the weekend with the last of three blogs by guest blogger, Sean McCown. You can read more about Sean and his previous guest blogs on the Hey, Scripting Guy! Blog site.

Take it away Sean …

In the previous two blogs of this series, Use PowerShell to Work with SQL Server Instance-Level Properties and Use PowerShell to Examine Dirty and Expensive SQL Server Properties, I talked about working with server objects so you could inventory or standardize your environment. Today, I’m going to run through a few operations that you may want to perform on your jobs because they’re some of the objects you’re going to work with the most. And even better, I’m going to show you how to go from a regular line to code to an actual script that you can run your enterprise with. I’m going to go a little faster here than I have in the past two posts because if you’ve read the others, you should be getting better at this.

First let’s pull up a list of the jobs on our box. Start by simply connecting to the provider and navigating to the Jobs node. I’m going to do this by right-clicking the Jobs folder in SSMS, and then clicking Start PowerShell.

Image of menu

Let’s start with a scenario I find myself in from time-to-time. This will demonstrate the power of the Shell better than anything else. We’ve got a situation where an employee left. He owned a bunch of jobs on a bunch of servers, and we need to set his replacement to be the owner of all these jobs. For this exercise we’ll say that there are 10 servers and around 50 jobs on each server. So this is definitely something you wouldn’t want to do by hand.

We’ll start by pulling up a list of jobs on the first server to make sure that our query is correct.

Dir | %{$_.OwnerLoginName –eq “NicC”} ## Notice I’m filtering by only jobs he owns.

Now I need to change the owner to RobertD. Actually, that’s easy enough.

Dir | %{$_.OwnerLoginName –eq “NicC”} | %{$_.OwnerLoginName = “RobertD”}

And if you remember from the last post, this won’t fly because I’ve only changed the owner in the current Windows PowerShell session, not on the server. Remember, I have to call the Alter() method to make that happen.

Dir | %{$_.OwnerLoginName –eq “NicC”} | %{$_.OwnerLoginName = “RobertD”; $_.Alter(); $_.Name}

Notice that I’m not only calling the Alter() method, but I’m also printing the name of the job to the screen every time so I’m not just staring at a blinking cursor wondering if anything’s going on. That’s a great start, but I haven’t met my goal yet, have I? No, I still need to make this work on several boxes; otherwise, what’s the point, right?

First we need to get a server list. Because we can’t do this against different servers until we know what those servers are. And because we’re DBAs, I’m going to pull the data from a table where it belongs. So I’m going to get the data from my Servers table and put it into a variable to make it easy to work with.

$ServerList = invoke-sqlcmd –ServerInstance “MgmtRepoServer” –database “MgmtRepoDB” –query “Select ServerName from dbo.Servers where application = ‘ThisGuysApp’”

You can see that I didn’t put a lot of thought into database design, but I chose the object names in the query to be more instructive than anything. However, now we have our server list. So let’s put it into our script and see where that puts us.

$ServerList | %{

Dir | %{$_.OwnerLoginName –eq “NicC”} | %{$_.OwnerLoginName = “RobertD”; $_.Alter(); $_.Name}

}

We plugged this into our script and it clearly won’t work. We need a couple things to make this work. First, although we manually connected to the server to run our initial job query, we need to have the script make the connection for us. And second, we need that connection to be dynamic. That is, it needs to connect to each of the boxes listed in the $ServerList variable.

We’ll take care of the connection first:

$ServerList | %{

CD SQLSERVER:\SQL\SDLSEANM\DEFAULT\JobServer\Jobs;

Dir | %{$_.OwnerLoginName –eq “NicC”} | %{$_.OwnerLoginName = “RobertD”; $_.Alter(); $_.Name}

}

As the script reads, when we cycle through the server names in $ServerList, we’re going to connect to the same server again and again. So now we have to take care of the second issue, which is making that server connection dynamic.

$ServerList | %{

$currServer = $_.ServerName;

$currServer; ## Print each server as they come through so you can tell which one you’re on.

CD SQLSERVER:\SQL\$currServer\DEFAULT\JobServer\Jobs;

Dir | %{$_.OwnerLoginName –eq “NicC”} | %{$_.OwnerLoginName = “RobertD”; $_.Alter(); $_.Name}

}

Now I’ve done what I need to do. I’ve set the $currServer variable to the current server name being passed in by $ServerList, and I’ve replaced SDLSEANM with that variable. Now we’ll connect to each separate server as it comes through our loop.

As far as this situation goes, we’ve accomplished what we set out to do, but I want to give you some advice—and I really can’t stress this enough. Whenever you write a process in Windows PowerShell, always write it to go against several boxes. It costs you nothing. As you can see it’s just a couple extra lines of code, and now you can run your enterprise with it. Even if you’ve only got a single box to run it against right now, you never know when you’ll need to do it against many boxes.

And now that we’ve got the basics down for how to do something useful with jobs on a bunch of servers, let’s look at some other things you can do. The cool thing is that all you have to do is replace the dir line in the script above, and all of these will work as-is. This is how you should be building these types of scripts. Make templates and the specifics fold in nicely. This is a job template, btw.

Let’s plug in another command to change the job category. This is really useful when you want to organize all of your maintenance jobs into the same category so you can filter them more easily.

$ServerList = invoke-sqlcmd –ServerInstance “MgmtRepoServer” –database “MgmtRepoDB” –query “Select ServerName from dbo.Servers where application = ‘ThisGuysApp’”

$ServerList | %{

$currServer = $_.ServerName;

$currServer; ## Print each server as they come through so you can tell which one you’re on.

CD SQLSERVER:\SQL\$currServer\DEFAULT\JobServer\Jobs;

Dir | %{$_.Name –match “Reindex”} | %{$_.Category = “Maintenance”; $_.Alter(); $_.Name}

}

This is a simple example, but it gets the point across. I took all the jobs where the name contained the word Reindex and put them in the Maintenance category.

This brings up another good example though. I had to search on anything that contained Reindex. I shouldn’t have to do that because reindex routines should all have the same names across all your boxes—as much as possible anyway. So to that end, now let’s change the names of all the reindex routines and standardize them so we know what we’re looking at when you go to each box. This will be a simple example again. It’s sometimes not quite this simple, but you can build in your own logic.

$ServerList = invoke-sqlcmd –ServerInstance “MgmtRepoServer” –database “MgmtRepoDB” –query “Select ServerName from dbo.Servers where application = ‘ThisGuysApp’”

$ServerList | %{

$currServer = $_.ServerName;

$currServer; ## Print each server as they come through so you can tell which one you’re on.

CD SQLSERVER:\SQL\$currServer\DEFAULT\JobServer\Jobs;

Dir | %{$_.Name –match “Reindex”} | %{$_.Name = “ReindexAllDBs”; $_.Alter(); $_.Name}

}

There’s no way I could give you a complete solution here because I don’t know what your server looks like, but this is a framework for you to use to build your own scripts for working with jobs.

Now that I have standardized reindex routines I want to disable all of those stupid maintenance plan jobs that do reindexing. I’m going to disable all of the jobs that belong to maintenance plans.

$ServerList = invoke-sqlcmd –ServerInstance “MgmtRepoServer” –database “MgmtRepoDB” –query “Select ServerName from dbo.Servers where application = ‘ThisGuysApp’”

$ServerList | %{

$currServer = $_.ServerName;

$currServer; ## Print each server as they come through so you can tell which one you’re on.

CD SQLSERVER:\SQL\$currServer\DEFAULT\JobServer\Jobs;

Dir | %{$_.Name –match “Subplan”} | %{$_.IsEnabled = $False; $_.Alter(); $_.Name}

}

Depending on the version of SQL Server you’re using, you may have to filter your jobs by other criteria, but this is what it worked out to be on my local box.

You can see how easy it is to build a simple script that will do things against multiple boxes. If you do it right, you’ll have it as a template where you can make minor changes to get new functionality. The way I walked you through building this process is how you should do it every time. Start by getting the basic functionality you need and then expand it little-by-little until you have what you need.

Remember that you may not be able to fully automate everything you do, but that’s OK. I’m also a big fan of semiautomated processes. Anything that gives me back any amount of time is alright in my book. You may not be able to fully automate it with your current knowledge or state of technology, but that doesn’t mean that it’ll stay that way. You may learn something in two months that’ll show you how to do what you’re looking for. So if you cannot currently automate something fully, don’t let that discourage you from doing what you can.

~Sean

Thank you, Sean, for three days of awesomeness. I really appreciate you sharing your time and knowledge. Join us tomorrow for a great blog post by Windows PowerShell MVP, Chad Miller—also about SQL Server and Windows PowerShell. Cool stuff. Trust me.

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

Ed Wilson, Microsoft Scripting Guy

PowerTip: Use PowerShell to Troubleshoot PowerShell

$
0
0

Summary: Use Windows PowerShell to troubleshoot errors with Windows PowerShell.

Hey, Scripting Guy! Question How can I use Windows PowerShell to troubleshoot issues with Windows PowerShell?

Hey, Scripting Guy! Answer Use the Get-WinEvent cmdlet to parse the Windows PowerShell log. For example, to find all errors in a Windows PowerShell log:

In Windows PowerShell 3.0:

Get-WinEvent -LogName *powershell* | where leveldisplayname -eq 'error'

In Windows PowerShell 2.0:

Get-WinEvent -LogName *powershell* | where {$_.LevelDisplayName -eq 'error'}

You can also group by the error ID to get an idea of how often a particular error appears:

In Windows PowerShell 3.0:

Get-WinEvent -LogName *powershell* | where leveldisplayname -eq 'error' | group id

In Windows PowerShell 2.0:

Get-WinEvent -LogName *powershell* | where {$_.LevelDisplayName -eq 'error'} | group id

 

Viewing all 3333 articles
Browse latest View live




Latest Images