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

PowerTip: Use PowerShell to Find WMI Classes

0
0

Summary: Learn how to use Windows PowerShell to find WMI classes.

Hey, Scripting Guy! Question How can I use Windows PowerShell to find WMI classes related to the BIOS on my computer.

Hey, Scripting Guy! Answer Use the Get-CimClass cmdlet and search for the wildcard term *bios*:

Get-CimClass *bios*


Weekend Scripter: Authentication Silos Part 2

0
0

Summary: Microsoft PFE, Ian Farr, continues his series about using Windows PowerShell to work with Authentication Policy Silos.

Microsoft Scripting Guy, Ed Wilson, is here. Welcome back today, guest blogger, Ian Farr. If you missed yesterday's post I suggest that you read it before reading todays post: Authentication Silos: Part 1.

Yesterday, I left you by saying that we would explore a bit in my lab today. From my lab:

Image of command output

Notice that both the msDS-AuthNPolicySiloMembersBL and msDS-AssignedAuthNPolicySilo attributesare populated with the distinguished name of the new Authentication Policy Silo for each User and Computer object returned.  

On each User and Computer object, the AuthenticationPolicySilo attribute is also populated. Let’s look at a couple of accounts from the demo:

Get-ADUser -Identity ianfarr -Properties AuthenticationPolicySilo; Get-ADComputer -Identity HALODC01 -Properties AuthenticationPolicySilo

Image of command output

Sanity check two

We now have:

  • All domain admins in the Protected Users group.
  • An enforced Authentication Policy that restricts User TGTs to two hours and has an access control condition that only allows users to authenticate from devices within a named Authentication Policy Silo.
  • An enforced Authentication Policy Silo that references the aforementioned Authentication Policy.
  • All read-write domain controllers permitted to use the Authentication Policy Silo.
  • All domain admins permitted to use the Authentication Policy Silo.
  • The Authentication Policy Silo associated with the read-write domain controller accounts.
  • The Authentication Policy Silo associated with the Domain Admin accounts.

 I’m sane! Time to test.

Testing

The first thing I need to do is reboot the computer accounts within the silo to renew their TGTs. With that done, I can begin my testing!

Here’s what happens when I try to access a server outside of the Authentication Policy Silo with a Domain Admin account.

Image of error message

The failed logon is also captured in a new event log called "Microsoft-Windows-Authentication/AuthenticationPolicyFailures-DomainController". 

Let’s have a look:

(Get-WinEvent -LogName "Microsoft-Windows-Authentication/AuthenticationPolicyFailures-DomainController" | Select-Object -First 1).Message

Image of command output

Notice the Authentication Policy Information section of the event message. It has our silo name, policy name, and applicable TGT lifetime value. I can also see what device refused the logon in the Device Information section.

Now, let’s access a domain controller (HALODC02) with the same account. And, there you go...I’m logged on! Trust me!

For the doubters, let’s have a look at the new "Microsoft-Windows-Authentication/ProtectedUserSuccesses-DomainController" log:

(Get-WinEvent -LogName "Microsoft-Windows-Authentication/ProtectedUserSuccesses-DomainController" -FilterXPath "*[System[EventID=303]]" | Select-Object -First 1).Message

Image of command output

This is a Protected Users group-related event. The FilterXPath parameter of Get-WinEvent cmdlet is used to filter on event ID 303 (“A Kerberos ticket-granting-ticket (TGT) was issued for a member of the Protected User group.”) Notice the Authentication Policy information.

Let’s check out the logged-on user’s TGT lifetime:

klist tgt | Select-String –SimpleMatch time

Image of command output

We can see that the TGT has the expected two hour duration, as per the Authentication Policy.

Finally (because this is all claims driven), let’s look at the user’s claims:

whoami /claims

Image of command output

Notice how the Claim ID and its value match the condition that we configured in the User section of the Authentication Policy.

That’s all folks. In my test lab, my Domain Admin accounts can only log on to my read-write domain controllers.

With all of this funky credential protection stuff introduced in Active Directory in Windows Server 2012 R2, I suspect those elderly, bearded gentlemen are already busy writing a new chapter for the book of Active Directory wisdom…or, perhaps not!

~Ian

Thanks, Ian, for once again sharing your time and knowledge.

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

Ed Wilson, Microsoft Scripting Guy 

PowerTip: Use PowerShell to Find WMI Classes for Creating

0
0

Summary: Learn to find WMI classes that permit creating stuff.

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

Hey, Scripting Guy! Answer Use the Get-CimClass cmdlet, and specify the MethodName of create:

Get-CimClass -MethodName create

Troubleshooting a PowerShell SQL Server Script

0
0

Summary: Microsoft Scripting Guy, Ed Wilson, helps a reader with a Windows PowerShell and SQL Server scripting issue.

Microsoft Scripting Guy, Ed Wilson, is here. This morning I am sipping a nice cup of Darjeeling tea. I added a bit of peppermint leaves, spearmint leaves, orange peel, licorice root, and a cinnamon stick. The result is very refreshing, with just a little bit of a kick to help jump start one's scripting. I was reviewing my scripter@microsoft.com email this morning, and I ran across the following question:

Hey, Scripting Guy! Question Hey, Scripting Guy! Is there good training content available for Windows PowerShell basics and an introduction to Windows PowerShell for SQL Server?

—PB

I engaged in a rather long conversation. This post is the result of that conversation.

Round one:

  Hello PB,

Sure! There are lots of great resources for learning Windows PowerShell and SQL Server scripting. Check out these Hey, Scripting Guy! Blog posts. I have more than three dozen posts there.

Round two:

Thanks Ed!

I went through some of the links and they were very useful. But I am looking for some good training content for Windows PowerShell basics and an introduction to Windows PowerShell for SQL Server.

  My Windows PowerShell Scripting page in the Script Center has over a dozen hours of training for Windows PowerShell basics, but there is nothing specific to SQL Server and Windows PowerShell other than the information I gave you earlier.

Round three:

Hi Ed,

I went through your links and learned a quite a bit. Thanks for the good references. I’m running into an issue when reading data from a data table. Can you please help me find what I’m doing wrong here:

foreach ($Row in $SrvList.Rows)

{

  write-host ("value is : ", $Row[0][1])

}

  $srvlist.rows contains a collection of rows. $row is your enumerator, and you should be able to list $row instead of trying to index into the collection. Inside your loop, $row will have members, you can use $row | Get-Member to find those members, which will probably include column headings. Access the items via the members instead of by index number. 

You can also directly access rows by using $srvlist. Use $srvlist.rows[0] for the first row, and pipe this to Get-Member to see the members. Hope this helps.

Round four:

Hi Ed,

I tried $SrvList.Rows[0]  | Get-Member, and it is giving the following error message:

Image of command output

Here’s the full script:

Function GetServerList

{

$sqlConnection = new-object System.Data.SqlClient.SqlConnection "Server=localhost;Database=SSISPROTOSERVERS;Integrated Security=sspi"

$sqlConnection.Open()

 

#Create a command object

$sqlCommand = $sqlConnection.CreateCommand()

$sqlCommand.CommandText = "select * from ServerList"

 

$adapter = New-Object System.Data.SqlClient.SqlDataAdapter $sqlcommand

$dataset = New-Object System.Data.DataSet

 

$adapter.Fill($dataSet) | out-null

 

# Close the database connection

$sqlConnection.Close()

 

$dataTable = new-object "System.Data.DataTable" "ServerList"

$dataTable = $dataSet.Tables[0]

 

return $dataTable

}

$SrvList = GetServerList

 

Write-Host("There are ", $SrvList.Count, " SQL Servers in the list")

$SrvList | format-table -AutoSize

 

foreach ($Row in $SrvList.Rows)

{

  $SrvList.Rows[0]  | Get-Member 

}

What does $dataTable return? What happens when we pipe it to Get-Member? What does $rows return?

  You should be able to type $dataTable, and it will return your DataTable object. $rows should return the Rows object. If they don't, you are not making the connection and returning proper data. You need to backtrack all the way to $sqlconnection, which should return a connection object. Pipe it to Get-Member also.

Remember your scoping, so that $sqlconnection only exists inside the function. You need to add a line after you make the connection:

$sqlconnection | Get-Member

You do not need Return because Windows PowerShell always returns an object from a function, but Return makes the script easier to read, so it is no problem leaving it there.

Round five:

Hi Ed,

These two statements return fine with the actual data:

Write-Host("There are ", $SrvList.Count, " SQL Servers in the list")

2

$SrvList | format-table -AutoSize

Image of command output

But I can’t display the rows.

foreach ($Row in $SrvList.Rows)

{

      write-host ("Rows : ")

      $Row

}

  Pipe SrvList to Get-Member. You will see at least two properties: ServerName and InstanceName. To access the name, you need to use $row.ServerName.

Round six:

Thanks a lot Ed! Yup. Using $row.ServerName worked!

~PB

That is all there is to using Windows PowerShell to query from a SQL Server database. 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: Start PowerShell with No Profile

0
0

Summary: Start Windows PowerShell with no profile to troubleshoot scripts.

Hey, Scripting Guy! Question How can I use Windows PowerShell to troubleshoot a script that sometimes works and sometimes does not?

Hey, Scripting Guy! Answer Look for any external dependencies by starting Windows PowerShell with no profile.
           Use Start/Run, and in the dialog box, type:

PowerShell –noprofile

Troubleshoot PowerShell Script Output

0
0

Summary: Microsoft Scripting Guy, Ed Wilson, talks about troubleshooting output from a Windows PowerShell script.

Hey, Scripting Guy! Question Hey, Scripting Guy! First, I want to say that you guys absolutely rock! I mean, I never miss a single day reading your posts. They are always right on, and it is like somehow you read my mind. I cannot tell you how many times I have read your post in the morning, and then needed to do that very thing in the afternoon. I also know that you probably get hundreds of emails every day, so I don’t really expect an answer, but I thought that maybe...what the heck. So here goes:

I wrote a script that works perfectly. I am trying to reduce our security footprint by stopping services. For some reason, the number of services has exploded between Windows Server 2008 and Windows Server 2012 R2. I think, "The fewer things running, the better." So I am trying to see what I have by doing an audit. I thought if I get a list of what is running and what is not, I could compare them to see what is going on. Here is the script:

$services = Get-WmiObject win32_Service

$running = $services | where { $_.state -eq 'running' }

$stopped = $services | where { $_.state -eq 'stopped' }

Write-Host -ForegroundColor Green "There are " $running.Count " running services. They are here:"

$running | Format-Table name

Write-Host -ForegroundColor Red "There are " $stopped.Count " stopped services. They are here:"

$stopped | Format-Table name

The problem is not with the script, it works perfectly for what I want to do. The problem is that I want to write the data to a text file. Here is what I did, and it sort of works, but not really:

$file = "c:\fso\MyServiceStatus.txt"

$services = Get-WmiObject win32_Service

$running = $services | where { $_.state -eq 'running' }

$stopped = $services | where { $_.state -eq 'stopped' }

Write-Host -ForegroundColor Green "There are " $running.Count " running services. They are here:" |

    Out-file $file -Append

$running | Format-Table name |

    Out-file $file -Append

Write-Host -ForegroundColor Red "There are " $stopped.Count " stopped services. They are here:"  |

   Out-file $file -Append

$stopped | Format-Table name |

   Out-file $file -Append 

When I say it sort of works, I mean that it creates the text file, and it writes all of the service names to the file. But I do not get my header information. So I cannot really tell which services are running and which are stopped. It is really weird, and I am wondering if I have found a bug in Windows PowerShell. If you don't have an answer, maybe you can forward this to someone who can look at it. Thanks again for all you do.

—BP

Hey, Scripting Guy! Answer Hello BP,

Microsoft Scripting Guy, Ed Wilson, is here. This morning I am sitting in the living room sipping a cup of Earle Grey tea with a bit of jasmine in it. I am watching the rain coming down, and looking over my scripter@microsoft.com email.

BP, yes, I do get a lot of email. I also enjoy answering some of it. I am glad you enjoy reading my column and that you have found it useful. I am especially pleased that you are writing your own scripts and using Windows PowerShell to solve some of your issues.

Understanding different output streams

One concept that is a bit difficult to understand in Windows PowerShell is the concept of output streams. Windows PowerShell has several output streams. This can be especially useful for things like trying to redirect errors, verbose output, debug messages, or even warning messages. However, it is not useful for redirecting the Write-Host cmdlet.

Note  For a good discussion about the various output streams and redirecting output, see Understanding Streams, Redirection, and Write-Host in PowerShell, which was written by Honorary Scripting Guy, June Blender.

Basically what happens when I use Write-Host is that it goes only to the host (usually the Windows PowerShell console). Because Write-Host does not go to any of the output streams, I am unable to write the results to a text file.

Output stuff

Write-Host is a useful cmdlet to precisely produce messages to the Windows PowerShell console because it does not write to an output stream. This means that I can use Write-Host to display information in an interactive fashion to a person running a Windows PowerShell script, and this information will not go on to an output file. For example, if I want to prompt a user to “press any key to begin” I can use Write-Host. Or I can use Write-Host to display status messages such as “counting services” or some other such prompt.

Of course, I can also use Read-Host and Write-Progress to perform these types of tasks. Often in Windows PowerShell, I have more than one way of doing things. One of the things you may want to read about is Windows PowerShell best practices. I have written a collection of posts where I talk about various aspects of Windows PowerShell scripting. For more information, see these Hey, Scripting Guy! Blog posts.

One hint as to what might be happening with your script is that after I run it, the console output box only contains the results from the two Write-Host cmdlets. Everything else writes to the text file.

The easy change

The easiest fix to your script is to simply change Write-Host to Write-Output. Of course, when you do that, you also need to delete the –ForegroundColor portion of your script. Now when the script runs, it will write everything to the text file and nothing to the screen.

If you need to output to both the screen and the text file, you can use the Tee-Object cmdlet. For more information about Tee-Object, read How Can I Both Save Information in a File and Display It on the Screen?

There are a number of other changes that could be made to the script to make it a bit leaner, but that is a subject for another post.

BP, that is all there is to troubleshooting script output. 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 Create Two Column Output

0
0

Summary: Create a two column output of data by using Windows PowerShell.

Hey, Scripting Guy! Question How can I use Windows PowerShell to create output that has two columns?

Hey, Scripting Guy! Answer Use the Format-Wide cmdlet, and then use the –Column parameter to specify
           a number of columns, for example:

Get-Process | Format-Wide name -Column 2

Use PowerShell to Write Verbose Output

0
0

Summary: Microsoft Scripting Guy, Ed Wilson, talks about using Windows PowerShell to write verbose output.

 

Hey, Scripting Guy! Question Hey, Scripting Guy! The other day, you said that I should not use Write-Host. I even read that such usage is dangerous to the health of small canines in 2012 Scripting Games Commentary: STOP USING WRITE-HOST!.

—BP

Hey, Scripting Guy! Answer Hello BP,

Microsoft Scripting Guy, Ed Wilson, is here. This morning I am sipping a cup of English Breakfast tea with a cinnamon stick and a squeeze of lemon. I am using my Surface Pro 2 to check my scripter@microsoft.com email, and I saw another email from you. I decide to crank up Alan Parsons on my 64-gig Zune HD, and dive right in.

Use Write-Verbose

To provide status updates, detailed tracking information, and the like, use the Write-Verbose cmdlet The cool thing about using Write-Verbose is that I never need to modify the script. For example, if I use a whole bunch of Write-Host statements, eventually I may become tired of the clutter. But with Write-Verbose, I can turn on the extra output only if I decide I need it. In addition, because Write-Verbose writes to the verbose output stream, I can decide if I want to capture that output. This makes it a great option.

Easy way to Write-Verbose

By default, Windows PowerShell is set up to use Write-Verbose. The issue is that out of the box, verbose messages do not display. The $VerbosePreference preference variable controls if Write-Verbose statements appear. By default, the value of $VerbosePreference is set to SilentlyContinue, which means the messages do not appear. Because this is a simple preference variable, all I need to do is to change it in my profile. Now all verbose messages will appear in the output. This is probably not a good idea. If I want to change it, the statement is this:

$VerbosePreference = "continue"

If I do not want to make the change in my profile (we really, really do not want to make this change in our profiles), I can easily change it in my script. To do this, I first read the old value of $verbosePreference, store that in a variable, and then change $verbosePreference to continue. Here is the command to do this:

  $oldverbose = $VerbosePreference

  $VerbosePreference = "continue"

Now, when I am done doing whatever I need to do, I simply set the old value. Here is that command:

$VerbosePreference = $oldverbose

To put this into a function, I create a switched parameter named $verbose, and if I call that parameter, I change the existing value of $verbosePreference to continue, and then I can use Write-Verbose to display progress, updates, and other information. Here is the complete function:

Function test-verbose

{

 Param ([switch]$verbose)

 if($verbose) {

   $oldverbose = $VerbosePreference

   $VerbosePreference = "continue" }

 Write-Verbose "verbose output"

 "Regular output"

 $VerbosePreference = $oldverbose

 }

This is pretty simple. When I call the function the first time, only regular output displays. When I call the function with –verbose, the verbose output appears along with the regular output. This is shown in the following image:

Image of command output

Easier way to verbose output

The point of the earlier command is to show you how the preference variables effect the way Write-Verbose works. (It is the same thing for other cmdlets, such as Write-Debug). But there is a slightly easier way to do this, and it is to use the [cmdletbinding()] attribute.

Note  There is a bit of confusion because that I must include Param() in my function—even if I am not accepting parameterized input to the function. The reason is that I am using [cmdletbinding()] as an attribute for my parameters. It does more than turn on automatic support for Write-Verbose. See the following topic (which, incidentally, does not say anything about Write-Verbose) for details: about_Functions_CmdletBindingAttribute.

All I need to do is add the [cmdletbinding()] attribute over the Param()statement, and I automatically gain support for Write-Verbose. Here is the command:

Function Test-VerboseTwo

{

 [cmdletbinding()]

 Param()

 Write-Verbose "Verbose output"

 "Regular output"

}

This is seven lines as opposed to ten lines of script. It saves me three lines. Of course, [cmdletbinding()] does more than automatically wire Write-Verbose, so the savings are potentially greater. To be honest, the script is not quite as easy to read, and [cmdletbinding()] (which does not appear via Tab expansion in the ISE, nor does it really tell me what it is doing) behaves more like a black box. But hey, there is a whole topic that tells me what this little attribute does, so it is not really a black box.

Here is the new function in action:

Image of command output

It is all good, and pretty simple. Perhaps best of all, no small canines were harmed.

BP, that is all there is to writing verbose output. Script Week will continue 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: Check Status of PowerShell Preference Variables

0
0

Summary: Learn to easily check the value assignments of Windows PowerShell preference variables.

Hey, Scripting Guy! Question How can I use Windows PowerShell to quickly check the value of Windows PowerShell preference variables?

Hey, Scripting Guy! Answer You can use the Windows PowerShell variable drive and look for variables that end with
           the word preferenceIn this example, dir is an alias for Get-Content:

dir variable:*preference 

Exclude Delayed Start Services when Checking Status with PowerShell

0
0

Summary: Windows PowerShell MVP, Mike Robbins, shows us how to use Windows PowerShell to check the status of Windows services and improve the accuracy of results.

Microsoft Scripting Guy, Ed Wilson, is here. Welcome back guest blogger, Mike Robbins.

Photo of Mike Robbins

Mike F Robbins is a senior systems engineer with 20 years of professional experience as an IT pro. Mike has provided enterprise computing solutions for educational, financial, health care, and manufacturing customers. He’s a Windows PowerShell MVP and self-proclaimed evangelist who uses Windows PowerShell on a daily basis to administer and manage Windows Server, Hyper-V, SQL Server, Exchange Server, SharePoint, Active Directory, Remote Desktop, EqualLogic storage area networks, AppAssure, and Backup Exec.

Mike is the winner of the advanced category in the 2013 PowerShell Scripting Games. He has written guest blog posts for the Hey, Scripting Guy! Blog, PowerShell.org, and PowerShell Magazine, and he is a contributing author of a chapter in PowerShell Deep Dives. Mike is the leader and cofounder of the Mississippi PowerShell User Group. Mike is also a frequent speaker at Windows PowerShell and SQL Server Saturday technology events. He blogs at mikefrobbins.com and can be found on twitter at @mikefrobbins.

Here’s Mike…

When it comes to the subject of checking the status of services with Windows PowerShell, the first cmdlet that comes to mind is Get-Service, so we’ll take a look at it first.

Get-Service -Name sppsvc |

Select-Object -Property *

Image of command output

As shown in the previous example, Get-Service has a Status property, but the issue is that it doesn’t have a property that tells us whether the service is set to be started automatically, so there’s no way of knowing whether the service should be running.

Because the Get-Service cmdlet doesn’t have properties that contain the necessary information to determine what the start mode of a service is, we’ll resort to using Windows Management Instrumentation (WMI). In this scenario, I’m going to maintain backwards compatibility with Windows PowerShell 2.0,  so I’ll use the Get-WmiObject cmdlet instead of Get-CimInstance cmdlet.

Get-WmiObject -Class Win32_Service -Filter {State != 'Running' and StartMode = 'Auto'}

Image of command output

In this example, notice the following:

  • Querying the Win32_Service WMI class with Windows PowerShell allows us to see the start mode of a service.
  • Best practices were followed because the command uses the Filter parameter to filter left instead of piping the information for all services to the Where-Object cmdlet.
  • In the results from WMI, the State property returns the value that was listed in the Status property in the results of Get-Service.

To learn more about the service that isn’t started, we need to determine if any additional properties exist that could provide us with additional information. Any cmdlet that produces output can be piped to the Get-Member cmdlet to determine what properties and methods exist, so that’s exactly what we’ll do in this scenario. We’re specifically looking for properties, so we’ll specify that via the MemberType property of Get-Member:

Get-WmiObject -Class Win32_Service -Filter {State != 'Running' and StartMode = 'Auto'} |

Get-Member -MemberType Properties

Image of command output

The DisplayName property looks promising to give us a friendlier name for this service:

Get-WmiObject -Class Win32_Service -Filter {State != 'Running' and StartMode = 'Auto'} |

Select-Object -Property DisplayName, Name, StartMode, State

Image of command output

To determine a little more information about this service, let’s take a look at it in the GUI. We’re specifically looking to see what the GUI says the startup type is.

Image of command output

We’ve just discovered that querying WMI for the startup mode of services doesn’t differentiate services that are set to start automatically from the ones that are set to start automatically with a delayed start. This could be a problem because the delayed start services might return a false positive if we make the assumption that services set to automatic should be running. The delayed start services aren’t necessarily supposed to be running because, by definition, their startup is delayed.

To determine which services are set to start automatically, but not with a delayed start, we’ll need to query the registry:

Get-ItemProperty 'HKLM:\SYSTEM\CurrentControlSet\Services\*' |

Where-Object {$_.Start -eq 2 -and $_.DelayedAutoStart -ne 1} |

Select-Object -Property PSChildName

Image of command output

We’re specifically looking for services that have a value of 2 for the Start property. This can mean automatic or automatic with delayed start. We’re also looking for services that don’t have a value of 1 for the DelayedAutoStart property. The DelayedAutoStart property is created when a service is set to start automatically with a delayed start. If the service is changed later, that property will remain, but its value will be 0 if the service is set to start automatically (not a delayed start). If the service is set to manual, the Start property will be 3, and if it’s disabled, it will be 4.

Get-Item -Path 'HKLM:\SYSTEM\CurrentControlSet\Services\sppsvc'

Image of command output

Now we need to combine what we previously accomplished by querying WMI with the information we retrieved from the registry to eliminate the delayed start services. In the following example, I’ve stopped the Windows Audio and Print Spooler services on the local computer so we should receive some results, but the Software Protection service should be excluded this time.

Get-WmiObject -Class Win32_Service -Filter {State != 'Running' and StartMode = 'Auto'} |

ForEach-Object {Get-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Services\$($_.Name)" |

Where-Object {$_.Start -eq 2 -and $_.DelayedAutoStart -ne 1}} |

Select-Object -Property @{label='ServiceName';expression={$_.PSChildName}}

Image of command output

While we could accomplish the tasks we set out to accomplish with a one-liner as in the previous example. For remote machines, this command needs to run locally on the remote computer because the registry provider doesn’t natively support remote access. We’ll write a couple of reusable functions that use the Windows PowerShell remoting Invoke-Command cmdlet. These functions could be integrated into a Windows PowerShell script module.

Note  You can download the two functions that are used from this point forward from the Script Center Repository.

Get-MrAutoStoppedService -ComputerName dc01, sql01, sql02, web01

Image of command output

You could also retrieve the computer names from a text file (or from Active Directory) and pipe them to these functions. This time, we’ll use the Start-MrAutoStoppedService function to start the services that should be running. We’ll also use the Credential parameter to specify alternate credentials when connecting to the remote computers.

Get-Content -Path C:\Scripts\servers.txt |

Start-MrAutoStoppedService -Credential (Get-Credential)

Image of command output

Now when we check to see if any services on our servers aren’t running, nothing is returned because all of the services that should be running are running.

Get-Content -Path C:\Scripts\servers.txt |

Get-MrAutoStoppedService

Image of command output

We could schedule the Start-MrAutoStoppedService function to run periodically by using a scheduled task, and then use the following command to notify us via email if problems were found and action was taken.

if (Get-Content -Path C:\Scripts\servers.txt | Start-MrAutoStoppedService | Out-String -OutVariable Services) {  

    $Params = @{

        From = 'serverstatus@mikefrobbins.com'

        SmtpServer = 'mail.mikefrobbins.com'

        Subject = 'Server Service Status Update'

        To = 'helpdesk@mikefrobbins.com'

        Body = "The following services are configured with a startup type of Automatic. They were in the stopped state and they have now been started: `n $Services"

    }

    Send-MailMessage @Params

}

Image of message

~Mike

Thanks, Mike, for sharing your time and knowledge.

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

Ed Wilson, Microsoft Scripting Guy 

PowerTip: Use PowerShell to Display Invalid File Names

0
0

Summary: Use Windows PowerShell to display invalid file name characters.

Hey, Scripting Guy! Question How can I use Windows PowerShell to display information about invalid file name characters?

Hey, Scripting Guy! Answer Use the System.IO.Path class and call the GetInvalidFileNameChars static method:

[System.IO.Path]::GetInvalidFileNameChars()

I've Got a PowerShell Secret: Adding a GUI to Scripts

0
0

Summary: Microsoft PFE, Chris Conte, talks about adding a GUI for Windows PowerShell scripts.

Microsoft Scripting Guy, Ed Wilson, is here. Today Chris Conte is our guest blogger.

Chris Conte is a premier field engineer at Microsoft who grew up with a passion for computers and the creative arts. At age 12, he started programming by copying assembly game code on his dad's Commodore 64. Eventually, he parlayed that experience into learning about Visual Basic for Applications (VBA), ASP.NET, C#, and a variety of client scripting languages and frameworks. You can learn more about Chris via LinkedIn or Twitter @CrashConte.

Now, Chris...

This is not my original idea, but I’ve been able to document the methodology presented in this post. Before we start, I’d like the opportunity to thank my mentor, Donald Scott, and my colleague Michael Melone. Mike is the guru who figured this out, and Don and I created the helper cmdlet.

Windows Server admins have a lot of choices for automating routine tasks, but for those looking to stay up-to-date, the lingua franca is certainly Windows PowerShell. Windows PowerShell allows you to deal with objects instead of only strings and it has a syntax that C# and JavaScript programmers can easily relate to. In addition, with Microsoft heavily investing to propagate this awesome language throughout the ecosystem, you can be sure it's not going away soon.

So it’s settled, right? Learn Windows PowerShell and thrive. But honestly, I didn't get into Windows to work from the command line. I'm what you call a "GUI Guy." I love Windows, and as a developer, I have spent more than my share of time designing beautiful interfaces for the web. I believe that good design can have a huge influence on the success or failure of the underlying code. Don't get me wrong, I love the power and speed of scripted tasks. But that's why this "secret" is so special. You don't lose anything that Windows PowerShell has to offer.

What do you get when you mix XML, XPath, XAML, WPF, Windows PowerShell and a little creativity? Well, I say it's nothing short of magic. Even after you understand how it works, I hope you still get that little twinkle in your eyes that keeps the inner geek excited.

Image of chart

Tooling up

To follow along, you'll need to get a few tools. You've probably got these lying around. Here's what you'll need:

The Windows PowerShell ISE is an optional feature, and it requires .NET Framework 3.51 to be installed. To install the Windows PowerShell ISE on Windows Server 2008 R2, use the Add Features Wizard or run the following Windows PowerShell command:

Import-Module ServerManager; Add-WindowsFeature PowerShell-ISE

Visual Studio Express for Windows Desktop lets you take full advantage of Windows with XAML designers, a productive IDE, and a variety of programming languages.

Getting started

I'm going to presume a lot here. I'm going to assume you've got modern Windows Server admin skills and are a proficient Windows PowerShell scripter. I'm also going to assume you've heard of XML and XPath, event-driven programming, and some basics about object-oriented programming. If not, please get started with the following resources.

     Note  The mention of these resources is not an endorsement.  

WPF and XAML are important concepts to grasp for this methodology. To begin, let's review a bit about WPF and XAML (from the documentation). Here are some significant pieces of the documentation that should help you understand a bit more about why we are using them.

WPF is a next-generation presentation system for building Windows client applications. It is a resolution-independent and vector-based rendering engine. WPF extends the core with a comprehensive set of application development features that include Extensible Application Markup Language (XAML). WPF is included in the Microsoft .NET Framework, so you can build applications that incorporate other elements of the .NET Framework class library.

WPF exists as a subset of .NET Framework types that are for the most part located in the System.Windows namespace. You instantiate classes, set properties, call methods, and handle events by using your favorite .NET Framework programming language.

WPF offers the ability to develop an application by using markup and code-behind, which is an experience that ASP.NET developers should be familiar with. You generally use XAML markup to implement the appearance of an application while using managed programming languages (code-behind) to implement its behavior.

XAML is an XML-based markup language that is used to implement an application's appearance declaratively. It is typically used to create windows, dialog boxes, pages, and user controls, and to fill them with controls, shapes, and graphics. Because XAML is XML-based, the UI that you compose with it is assembled in a hierarchy of nested elements known as an element tree.

Building the interface

To create the interface, fire up Visual Studio and create a new WPF project. The path is: File > New > Project.

 Image of menu

When the project loads, you will see a prebuilt Window. Simply drag-and-drop controls onto the canvas from common WPF controls. In the following screenshot, I've added a text box and a button. Notice the handles that surround the control. You are welcome to use these handles to resize the control.

Image of controls

Notice the XAML in the window under the design canvas. You can see a Label and Button control added to the Grid element. Use the XAML resource documentation if you want to get fancy, but you now have all the XAML you need to continue. Simply copy and XAML and paste it into a Notepad window. Remove the x:Class attribute from the Window element. Leave the XML namespace elements (xmlns and xmlns:x), Title, Height, and Width. Save the file with the .xaml extension (for example, MyForm.xaml). Here is the code:

<Window

  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

  Title="MainWindow" Height="350" Width="525">

 <Grid>

  <Label Content="Label" HorizontalAlignment="Left" Margin="68,38,0,0" VerticalAlignment="Top" Width="197"/>

  <Button Content="Button" HorizontalAlignment="Left" Margin="307,41,0,0" VerticalAlignment="Top" Width="75"/>

</Grid>

</Window>

Loading the dialog box

To make this easier (but admittedly more complex to understand), you'll need the following helper cmdlet to load and interpret the XAML. Simply save the following text as loadDialog.ps1:

[CmdletBinding()]

Param(

 [Parameter(Mandatory=$True,Position=1)]

 [string]$XamlPath

)

 

[xml]$Global:xmlWPF = Get-Content -Path $XamlPath

 

#Add WPF and Windows Forms assemblies

try{

 Add-Type -AssemblyName PresentationCore,PresentationFramework,WindowsBase,system.windows.forms

} catch {

 Throw "Failed to load Windows Presentation Framework assemblies."

}

 

#Create the XAML reader using a new XML node reader

$Global:xamGUI = [Windows.Markup.XamlReader]::Load((new-object System.Xml.XmlNodeReader $xmlWPF))

 

#Create hooks to each named object in the XAML

$xmlWPF.SelectNodes("//*[@Name]") | %{

 Set-Variable -Name ($_.Name) -Value $xamGUI.FindName($_.Name) -Scope Global

 }

LoadDialog walkthrough

This loadDialog helper cmdlet is extremely important, so let's break it down. This cmdlet accepts one parameter: the FileName and Path of the XAML file. The contents are loaded into a global variable (typed as XML) by using the following line:

[xml]$Global:xmlWPF = Get-Content -Path $XamlPath

Next, WPF and Windows Forms assemblies are loaded from the .NET Framework. These allow us to make sense of the XAML to display the dialog.

Add-Type -AssemblyName PresentationCore,PresentationFramework,WindowsBase,system.windows.forms

With the power of our loaded assemblies, we load the XML variable, xmlWPF, as a parameter into a XmlNodeReader method, and subsequently, XamlReader. The result is loaded into a global XAML-typed variable. Thanks to our loaded assemblies, our code now "understands" how to interpret this XAML (more about this later).

Last, we wire the XAML notes in the tree to Windows PowerShell variables. By using the power of XPath, we scan through all the XAML tree nodes to extract the Name attribute. This name is passed down the pipe and turned into a global variable.

$xmlWPF.SelectNodes("//*[@Name]") | %{

 Set-Variable -Name ($_.Name) -Value $xamGUI.FindName($_.Name) -Scope Global

 }

Those of you who are following intently have noticed that there isn't a Name attribute by default using Visual Studio. You'll need to add this manually. Simple enough. Simply edit the XAML file and add a Name attribute in the elements that you want to control via Windows PowerShell. Continuing with our example, I have added name "Label1" to our Label element and "Button1" to the Button element in the XAML.

  <Label Name="Label1" Content="Label" HorizontalAlignment="Left" Margin="68,38,0,0" VerticalAlignment="Top" Width="197"/>

 <Button Name="Button1" Content="Button" HorizontalAlignment="Left" Margin="307,41,0,0" VerticalAlignment="Top" Width="75"/>

Finally, notice that nothing is actually displaying our dialog box...yet! We're getting there.

Putting it all together

So, we've finally arrived. You're ready to build your HelloWorld.ps1. Of course, by now you've realized that to make this work, there are actually three files involved. This may seem like a lot, but as you build multiple scripts, this will make sense. You have your primary script named "HelloWorld.ps1", the loadDialog.ps1 helper cmdlet, and your XAML form (MyForm.xaml).

Image of flow chart

For clean separation, I recommend that you store your .ps1 files in a Scripts folder and the XAML forms in a Forms folder.

To review what's going on, your original Windows PowerShell script calls the loadDialog.ps1 helper cmdlet, which in turn, loads the XAML form into a variable and creates matching Windows PowerShell variables for all the elements in the tree, based on the Name attribute. Near the top of your HelloWorld.ps1 script, you should see a line like this:

#Required to load the XAML form and create the PowerShell Variables

.\loadDialog.ps1 -XamlPath '..\Forms\MyForm.xaml'

After this line successfully executes, we have access to the form via Windows PowerShell variables.

$Label1

$Button1

The properties to set and get are a matter of research on websites like MSDN or exploring the properties window inside of Visual Studio. For example, here is an example of the Button class.

Adding events

Remember when I mentioned event-driven programming? At this point, you'll want to wire-up an event to the button. Here's how:

#EVENT Handler

$button1.add_Click({

 $Label1.Content = "Hello World"

})

When the button is clicked, the code is setting the Content property of our Label1 Windows PowerShell variable with "Hello World." Because this is wired directly to the WPF form, we actually get to see the results on our screen.

Launching the window

With everything set, we are finally able to load the window by using the ShowDialog() method of our XAML object:

#Launch the window

$xamGUI.ShowDialog() | out-null

This must be the last statement in your script. From that point forward, everything is controlled via the events you have configured.

Execution policy

We have it all put together and we're ready to launch. If you haven't already, remember to set the execution policy so that you can run the script:

Set-ExecutionPolicy -ExecutionPolicy Unrestricted

Also, remember to run Windows PowerShell as an administrator. Otherwise you'll hit this error message:

Set-ExecutionPolicy : Access to the registry key

'HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\PowerShell\1\ShellIds\Microsoft.PowerShell' is denied. To change the execution policy for the default (LocalMachine) scope, start Windows PowerShell with the "Run as administrator" option. To change the execution policy for the current user, run "Set-ExecutionPolicy -Scope CurrentUser".

Kick off your HelloWorld script: .\HelloWorld.ps1

When the window loads, simply click the button to see the results. They should look like this:

Image of window

Methods like this don’t come along by chance. This mountain top of Windows PowerShell GUI is viewed on the shoulders of giant minds. My heartfelt thanks go to the engineering rock stars who made this possible: Microsoft senior premier field engineers, Mike Melone and Don Scott.

The methodology to create a Windows PowerShell GUI is somewhat advanced, but not beyond the reach of those who spend a great deal of time scripting Windows PowerShell solutions for others to use. I have personally used this method to create input validation and to control the choices of input from my users.

This is a very basic example, but WPF forms can be very sophisticated. The fun part is the creativity you bring that will help empower and guide others to be successful without getting stuck at the command line.

The files demonstrated in this blog post are available for download from the Scripting Guys Script Repository: PowerShell GUI.

~Chris

Thanks for this innovative post, Chris.

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

Ed Wilson, Microsoft Scripting Guy 

PowerTip: Use PowerShell to Create Temporary File Name

0
0

Summary: Use Windows PowerShell to create a temporary file name.

Hey, Scripting Guy! Question I need to output data to a file, but I do not need a permanent file name and location because
           the file will be later deleted. How can I easily use Windows PowerShell to create a temporary file name?

Hey, Scripting Guy! Answer Use the GetTempFileName static method from the System.IO.Path .NET Framework class:

[System.IO.Path]::GetTempFileName()

Weekend Scripter: Fixing PowerShell GUI Examples

0
0

Summary: Microsoft PowerShell MVP, Dave Wyatt, talks about using Windows PowerShell GUI example code.

Microsoft Scripting Guy, Ed Wilson, is here. Welcome back guest blogger, Dave Wyatt. To read more of Dave’s previous guest posts, see these Hey, Scripting Guy! Blog posts.

Dave is a Windows PowerShell MVP and a member of the board of directors at PowerShell.org. He works in IT, and he mainly focuses on automation, configuration management, and production support of computers running the Windows Server operating system and point-of-sale systems for a large retail company.

Blog: http://davewyatt.wordpress.com/
Twitter: @MSH_Dave

Take it away, Dave...

In the early days of Windows PowerShell, there was a series of “Windows PowerShell Tip of the Week” posts about how to create GUIs with Windows PowerShell, including:

  • Creating a Custom Input Box
  • Creating a Graphical Date Picker
  • Selecting Items From a List Box
  • Multi-Select List Boxes – And More!

These examples worked until Windows PowerShell 3.0 was released. If you tried to run them after that, they would not work. I can’t tell you how many times I’ve seen people asking questions about why the code based on these examples doesn’t work. This week, we released updated versions of these topics on TechNet because people still seem to be interested in writing GUIs.

I’m not going to spend a lot of time talking about most of the code in these examples. I’m going to focus on why they stopped working, and discuss some other cleanup I did on the code. The main reason they no longer work has to do with variable scope. All four examples had code similar to this:

OKButton.Add_Click({$x=$objTextBox.Text;$objForm.Close()})

This line assigns a script block as an event handler for the button’s Click event. When the button is clicked, the results of $objTextBox.Text are assigned to variable $x, and the form is closed. However, this variable assignment is local to the script block. It doesn’t modify any variable named $x in the parent scope where the rest of the code is running.

I honestly can’t tell you why it worked in Windows PowerShell 1.0 and Windows PowerShell 2.0, or why that behavior changed. However, the current behavior in Windows PowerShell 3.0 is more in line with what I would expect to see. Script blocks are supposed to have their own scope, and variable assignments are local unless you specify otherwise.

There is a very quick-and-dirty way to “fix” these scripts. You can change the variable assignments to use a scope modifier, for example:

$script:x=$objTextBox.Text

However, I decided to clean up these scripts a bit, and I took a different approach. You can see the complete scripts on GitHub: WinFormsExampleUpdates. Here’s a summary of the changes I made:

  • I got rid of all the KeyPreview and KeyDown bits, in addition to the Click events. These sections of code were intended to allow you to press ENTER or Escape to activate the same effects as the OK and Cancel buttons. However, Windows Forms already has a much simpler way of accomplishing this. You simply assign the buttons to the form’s AcceptButton and CancelButton properties:
    $form.AcceptButton = $OKButton
    $form.CancelButton = $CancelButton
  • I stopped throwing away the dialog’s results. In the original script, the return value of the ShowDialog() method was discarded by casting it to [void]. However, this return value is normally intended to tell you how the user closed the form (OK, Cancel, Abort, Retry, Ignore, Yes, or No) depending on what buttons you’ve shown.

    You also need to assign these dialog result values to the corresponding buttons. If the user pressed OK, the code inside the “If” block performs basically the same thing that the old Click and KeyDown event code used to do in each example. This avoids the scope issue that caused the old scripts to fail in Windows PowerShell 3.0. The code that checks the user’s input no longer happens in a separate event handler script block.

$OKButton.DialogResult = [System.Windows.Forms.DialogResult]::OK
$CancelButton.DialogResult = [System.Windows.Forms.DialogResult]::Cancel

$result = $form.ShowDialog()
if ($result –eq [System.Windows.Forms.DialogResult]::OK)
{
 # Examine the form controls to retrieve the user’s selection
}

  • I renamed many of the variables. Names like $objForm, $objListBox, and $dtmDate are an old VBScript convention, and they are generally discouraged in Windows PowerShell. These variables became $form, $listBox, and $date, respectively.
  • I replaced the calls to Assembly.LoadWithPartialName() with Add-Type instead. The LoadWithPartialName() method is deprecated, and it is often not necessary anyway. The Add-Type cmdlet maintains a table that maps the short names of many built-in .NET Framework assemblies to their full names. This allows you to do things like this:

Add-Type -AssemblyName System.Windows.Forms

Here’s the end result of the first example (a custom input box):

Image of command output

If you’d like to read the updated Windows PowerShell GUI-building topics on TechNet, they’re right here:

Thanks to Gaby Kaplan on the documentation team for updating the topics on TechNet.

~Dave

Thank you, Dave, for taking time to share your time and knowledge.

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

Ed Wilson, Microsoft Scripting Guy 

PowerTip: Wire Up PowerShell GUI Buttons

0
0

Summary: Learn to use the OK and Cancel buttons in a Windows PowerShell GUI.

Hey, Scripting Guy! Question How can I use Windows PowerShell to get my GUI’s OK and Cancel buttons to trigger when
           the user presses ENTER or Escape?

Hey, Scripting Guy! Answer Assign the OK and Cancel button objects to your form’s AcceptButton and CancelButton properties:

$form.AcceptButton = $OKButton
$form.CancelButton = $CancelButton


Weekend Scripter: A Hidden Gem in the PowerShell Ocean: Get-PSCallStack

0
0

Summary: Microsoft PowerShell MVP, Jeff Wouters, talks about using the Get-PSCallStack Windows PowerShell cmdlet.

Microsoft Scripting Guy, Ed Wilson, is here. Today Windows PowerShell MVP, Jeff Wouters, is back with another guest blog post. To read more of Jeff's previous guest posts, see these Hey, Scripting Guy! Blog posts.

And now, Jeff...

I once tweeted the following:

Image of quote

This blog post is all about that quote.

Some time ago, I found myself in a situation where I needed to create a generic function that knew what other function or script called it. Let me explain...

I wanted to write a function to log information to the screen, to a SQL Server database and/or to a file.
So if function one calls the logging function, the logging function needed to figure that it was called by function one.

After searching and failing because I was overthinking it, I finally gave up and asked for help from my fellow Windows PowerShell MVPs.

As always, I got a reply. In this case, the reply was from Oisin Grehan, with exactly the answer I was looking for: Get-PSCallStack.

The Get-PSCallStack cmdlet gets the current call stack. It was initially written to be used with the Windows PowerShell debugger. However, it can be used outside the debugger to get the call stack inside a script or function. This is exactly what the doctor ordered!

There are many places you can get a call stack, so let’s go through them one-by-one.

A script

When called from a script, the default output only provides you with the properties Command, Arguments, and Location. However, there are more properties hidden that you can show by using the Select-Object cmdlet:

Image of command output

So now let’s turn that into a reusable function and look at the call stack again:

Image of command output

That’s too much information! I only want the information from the command in the script, and not the function itself.

If the call stack only has a single entry, I want that entry. But if there are two (or more), I want the last entry, right?

function Get-Execution {

    $CallStack = Get-PSCallStack | Select-Object -Property *

    if ($CallStack.Count -eq 1) {

        $CallStack[0]

    } else {

        $CallStack[($CallStack.Count - 1)]

    }

}

Note  Remember that an array starts counting at 0, so selecting the first object in an array would be $VariableName[0].

Calling from a function in a script

The previous code will work when calling the Get-Execution function directly from the script. But what if you, like me, prefer to write functions—so you’ll have a function that calls Get-Execution. You’ll want the entry in the call stack for that function. Let’s see what happens:

Image of command output

It works! You can trust me when I write that it keeps working—no matter how large the tree of functions calling functions—it keeps working.

But wait, there’s more!

Get-Execution in a module

Reusable functions are nice, but having to copy them into all my scripts is not something I would prefer. So what if you place Get-Execution in a module—let’s say a module with a lot of useful functions that you use in many of your scripts?
This would negate the need to copy the code into all your scripts. Simply load the module, and all the functions in that mode are available to you. Let’s see if it still works:

Image of command output

Yes, it still works!

Now let’s try a function in the script calling Get-Execution that’s inside the module:

Image of command output

Yup, still works like a charm.

When will it not work?

This function will not work when calling from a command prompt or in the Windows PowerShell ISE from an unsaved file or selection. In those situations, no call stack is available.

In the case of the command prompt, I’ve found a difference in the behavior in Windows PowerShell 2.0 versus the later versions.

In Windows PowerShell 2.0, no output was given if no call stack was found. Additionally, no error was given. So I was able to check the variable to have no value by comparing to $null. In The later versions of Windows PowerShell, I found that this wasn’t an option because Get-PSCallStack provided output when no call stack was found:

Image of command output

Let’s check for an empty call stack, and if no call stack is found, we'll provide an error message:

That would mean we’re done, right?

Wrong…

When is the default data not enough?

I am actually missing one property in the output (although the data itself is available): The location of the script!

The current output provides the path, but only combined with the name of the script. I want a separate property with the path of the script. Here’s the code that also outputs the location of the script by taking it from ScriptName:

function Get-Execution {

    $CallStack = Get-PSCallStack | Select-Object -Property *

    if (

         ($CallStack.Count -ne $null) -or

         (($CallStack.Command -ne '<ScriptBlock>') -and

         ($CallStack.Location -ne '<No file>') -and

         ($CallStack.ScriptName -ne $Null))

    ) {

        if ($CallStack.Count -eq 1) {

            $Output = $CallStack[0]

            $Output | Add-Member -MemberType NoteProperty -Name ScriptLocation -Value $((Split-Path $_.ScriptName)[0]) -PassThru

        } else {

            $Output = $CallStack[($CallStack.Count - 1)]

            $Output | Add-Member -MemberType NoteProperty -Name ScriptLocation -Value $((Split-Path $Output.ScriptName)[0]) -PassThru

        }

    } else {

        Write-Error -Message 'No callstack detected' -Category 'InvalidData'

    }

}

Use case?

Why would I want to write such a function?

I like to log the actions of my scripts to a file or to a database—maybe even to the event log. So when I deliver a script to a customer, I make use of this Get-Execution function to mention which file or script did something. Let’s take an example...

I’ve got a Write-Log function, which combined with Get-Execution, can be used like this:

$Execution = Get-Execution

Write-Log -Database 'Logging' `

-DBServer 'SQLDB01' `

–Status ‘Error’ `

-Time ((Get-Date -format o) -replace ':','.') `

-Message "$($Execution.Command) at $($Execution.Location)"

~Jeff

Thanks for sharing, Jeff.

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

Ed Wilson, Microsoft Scripting Guy 

PowerTip: Use PowerShell to Display Square Root of Number

0
0

Summary: Use Windows PowerShell to display the square root of a number.

Hey, Scripting Guy! Question How can I figure out the square root of a number when I am writing a Windows PowerShell script?

Hey, Scripting Guy! Answer Use the Sqrt static method from the System.Math class. This example obtains the square root of the number 9:

[math]::Sqrt(9)

Use PowerShell to Create Bulk Users for Office 365

0
0

Summary: Microsoft Scripting Guy, Ed Wilson, talks about creating bulk users in Office 365.

Microsoft Scripting Guy, Ed Wilson, is here. This morning I spent a bit of time talking to my mentee. It is really cool, because yesterday I spent some time talking to my mentor. So it is sort of like the circle completing itself.

I have been getting quite a few questions about Office 365 and Windows PowerShell here lately, so I thought I would spend a bit of time talking about that.

Note  I have written many Hey, Scripting Guy! Blog posts about this topic, including Getting Started with the Office 365 PowerShell, Use PowerShell to Explore Office 365 Installation, and Use PowerShell to Manage Office 365 Users. For more information about the fundamental tasks related to Office 365 and Windows PowerShell, refer to this entire series of Hey, Scripting Guy! Blog posts.

Connecting to Office 365

The first thing to do is to make the connection to the Office 365 tenant installation.

I first store my credentials in an XML file so I can easily reuse them. When I call the Get-Credential cmdlet, a dialog box appears. I type my credentials in the box, and they are now stored in an XML file. I can import the credentials by using the Import-Clixml cmdlet. I use the reconstituted Credential object to make my connection. Here are the commands I type:

PS C:\> Get-Credential "admin@ScriptingGuy.OnMicrosoft.Com" |

>>> Export-Clixml C:\fso\ScriptingGuyCredential.xml

PS C:\> $cred = Import-Clixml C:\fso\ScriptingGuyCredential.xml

PS C:\> Connect-MsolService -Credential $cred

As shown in the following image, no output appears in the Windows PowerShell console:

Image of command

Note  To update the Azure Active Directory (Azure AD) module, you must first uninstall it via the Control Panel. After you do that, you can install it from the TechNet Library: Install the Azure AD Module.

Creating a bunch of users via a CSV file

Today, I am going to create a CSV file and use it to create a bunch of users for my Office 365 installation. I am not going to discuss the license assignment, license options, or usage location assignments. I will look at these tomorrow. For today, I just want to create a bunch of users.

To create a user in Office 365 by using the Azure AD module, I use the New-MSOlUser cmdlet. The syntax for this looks an awfully lot like the New-User cmdlet in the standard Active Directory module. There are not quite as many options, but the process is very similar. Here is the syntax for the New-MSOlUser cmdlet:

NAME

    New-MsolUser

SYNOPSIS

    Adds a new user to Windows Azure Active Directory.

SYNTAX

    New-MsolUser -DisplayName <string> -UserPrincipalName <string>

    [-AlternateEmailAddresses <string[]>] [-AlternateMobilePhones <string[]>]

    [-BlockCredential <Boolean>] [-City <string>] [-Country <string>] [-Department

    <string>] [-Fax <string>] [-FirstName <string>] [-ForceChangePassword <Boolean>]

    [-ImmutableId <string>] [-LastName <string>] [-LicenseAssignment <string[]>]

    [-LicenseOptions <LicenseOption[]>] [-MobilePhone <string>] [-Office <string>]

    [-Password <string>] [-PasswordNeverExpires <Boolean>] [-PhoneNumber <string>]

    [-PostalCode <string>] [-PreferredLanguage <string>] [-State <string>]

    [-StreetAddress <string>] [-StrongPasswordRequired <Boolean>] [-TenantId <Guid>]

    [-Title <string>] [-UsageLocation <string>] [<CommonParameters>]

To use the cmdlet, it is as simple as filling in the blanks. Here is a quick example:

New-MsolUser -UserPrincipalName "ScriptingWife@ScriptingGuy.OnMicrosoft.Com" -City

 Charlotte -State NC -Country USA -DisplayName ScriptingWife

In this command, I did not assign a password. The cmdlet returns a user object, and that user object includes a password. I could then pipe the information from the returned object to Send-SMTPMail to send the user information and password to the user. The cool thing is that I do not have to create a password, nor do I have to supply one when I create the new user. Here is the output from the command in the Windows PowerShell console:

Image of command output

When I go to the Office 365 tenant admin site, I can see that the Scripting Wife user has indeed been created:

Image of webpage

Read a CSV file and create the users

So now, I want to read a CSV file and create a bunch of users. To do this, first I create a CSV file. I like to use Microsoft Excel for doing this because it provides a good interface, and the menu offers me the option to save as a CSV file.

I was hoping that I could splat the users from the CSV file to the New-MSOLUser cmdlet, but unfortunately that generates an error. Therefore, I will have to pipe my users via the Foreach-Object cmdlet. The cmdlet doesn’t accept positional parameters very well either, and so I have to do everything manually. This is shown here:

$users = Import-Csv C:\fso\Office365Users.CSV

$users | ForEach-Object {

New-MsolUser -UserPrincipalName $_.UserPrincipalName -City $_.city -State $_.State -Country $_.Country -DisplayName $_.DisplayName }

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

Image of command output

Removing a group of users

If I need to remove a group of users, I can once again use the CSV file. In fact, if I want to remove the users I just created, it is super simple. All I need to do is to use the Remove-MsolUser cmdlet instead of the New-MSOlUser cmdlet. I want also want to use the –Force parameter. If I do not, the command will prompt for each user, which is probably not what I want to do. Here is the command without the –Force parameter:

PS C:\> $users = Import-Csv C:\fso\Office365Users.CSV

PS C:\> $users | ForEach-Object { Remove-MsolUser -UserPrincipalName $_.userprincipalname}

Confirm

Continue with this operation?

[Y] Yes  [N] No  [S] Suspend  [?] Help (default is "Y"):

So instead, I use the –Force parameter as shown here:

$users | ForEach-Object { Remove-MsolUser -UserPrincipalName $_.userprincipalname -Force}

No output returns from this command.

That is all there is to using the Azure AD module to create a group of users. Office 365 Week will continue 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 List Time Zones Defined on System

0
0

Summary: Use Windows PowerShell to list time zones that are defined on a system.

Hey, Scripting Guy! Question How can I use Windows PowerShell to produce a list of all the time zones that are defined on my system?

Hey, Scripting Guy! Answer Use the System.TimeZone .NET Framework class, and call the GetTimeZones static method:

[System.TimeZoneInfo]::GetSystemTimeZones()

Use PowerShell to Force Office 365 Online Users to Change Passwords

0
0

Summary: Use Windows PowerShell to force Office 365 online users to change their passwords.

Hey, Scripting Guy! Question Hey, Scripting Guy! I used your technique from yesterday to create a bunch of Office 365 users online, and now I want to force them to change their passwords. If I could also make them use a complex password, that would be great. Can you help me out Oh, Scripting One?

—SW

Hey, Scripting Guy! Answer Hello SW,

Microsoft Scripting Guy, Ed Wilson, is here. Things are becoming interesting around the Scripting House this week. This is because yours truly, the Scripting Guy, has a birthday coming up in a few weeks. It is always fun trying to figure out what the Scripting Wife is going to do for my birthday. One time, she lured me into the Smokey Mountains and invited a bunch of my friends from Cincinnati for a surprise birthday party. She really pulled it off, and I had no idea until my friends began to show up at the cabin. Now, I certainly am not expecting a surprise birthday party in the Smokey Mountains, but who knows. She gets pretty creative sometimes. So far, I haven’t a clue.

I am sitting on our front porch, sipping a cup of Hawaiian organic tea, and using my Surface 2 Pro to peruse the scripter@microsoft.com email. SW, I ran across your email, and I thought it would be a great follow-up to yesterday’s Hey, Scripting Guy! Blog post.

Note  You should read yesterday's post, Use PowerShell to Create Bulk Users for Office 365, before you begin today's post.

Second things first

After I connect to my Office 365 tenant installation by using the Azure Active Directory (Azure AD) module (see yesterday’s post to learn about this technique), I can force my users to use a strong password. To do this, I use the Set-MSOlUser cmdlet. Here is the syntax for that cmdlet:

Image of command output

The StrongPasswordRequired parameter is a Boolean parameter—On/Off, True/False, Yes/No, Wax on/Wax off.

I am going to use the same CSV file that I used yesterday to make my password modifications. One advantage to using a CSV file for user creation is it can also be used for user management. To force all the users to use a strong password, I use the following commands:

$users = Import-Csv C:\fso\Office365Users.CSV

$users |

foreach { Set-MsolUser -UserPrincipalName $_.userprincipalname -StrongPasswordRequired:$true}

The first command reads my CSV file into a variable named $users. The second command pipes the objects in the $users cmdlet to the Foreach-Object cmdlet, and then inside script block, I use the Set-MSOlUser cmdlet to force each user to use a strong password.

Now I need to verify that the command worked. To do this, I use the following command:

$users |

foreach {Get-MsolUser -UserPrincipalName $_.userprincipalname | select displayname, strongpasswordrequired}

Here I use the same $users variable, and I pipe the objects to the Foreach-Object cmdlet. Inside the script block, I call the Get-MSOlUser cmdlet for each of the users. I display the DisplayName and the StrongPasswordRequired properties.

The two commands and their associated output are shown in the following image:

Image of command output

Force a password change

There is a specific user password cmdlet. It is the Set-MSOlUserPassword cmdlet. It provides the ability to set a new password and to force a password change. The syntax is shown here:

Image of command output

The syntax to force a password is very similar to the command I used earlier to force all the users to use a strong password. It is shown here:

$users |

foreach { Set-MsolUserPassword -UserPrincipalName $_.userPrincipalName -ForceChangePassword:$true}

Combine both commands

Instead of running two commands, I can combine the two commands as follows:

$users |

foreach {

Set-MsolUserPassword -UserPrincipalName $_.userPrincipalName -ForceChangePassword:$true;

Set-MsolUser -UserPrincipalName $_.userprincipalname -StrongPasswordRequired:$true}

Note  This is a single command that I broke up into multiple lines for easier reading.

In all of this, I have not felt the need to write a single Windows PowerShell script. But I could just as easily copy and paste everything into the following single script:

Get-Credential "admin@ScriptingGuy.OnMicrosoft.Com" |

Export-Clixml C:\fso\ScriptingGuyCredential.xml

$cred = Import-Clixml C:\fso\ScriptingGuyCredential.xml

Connect-MsolService -Credential $cred

$users = Import-Csv C:\fso\Office365Users.CSV

$users |

   foreach {

     Set-MsolUserPassword -UserPrincipalName $_.userPrincipalName -ForceChangePassword:$true

     Set-MsolUser -UserPrincipalName $_.userprincipalname -StrongPasswordRequired:$true}

Here is a better formatted version of the script.

Image of command output

SW, that is all there is to using Windows PowerShell to force your Office 365 users to change their passwords and to make them use complex passwords. Office 365 Week will continue 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 

Viewing all 3333 articles
Browse latest View live




Latest Images