Quantcast
Channel: Scripting Blog
Viewing all 2129 articles
Browse latest View live

PowerTip: Use PowerShell to Subtract Timespans

$
0
0

Summary: Subtract timespans by using Windows PowerShell.

Hey, Scripting Guy! Question How can I use Windows PowerShell to subtract 2 days and 14 hours from 12 days and 1 hour to determine the amount of time left on a project?

Hey, Scripting Guy! Answer Create two timespans using the New-Timespan cmdlet, and then subtract them from each other:

(New-TimeSpan -Days 12 -Hours 1) - (New-TimeSpan -Days 2 -Hours 14)


Weekend Scripter: Who are the Administrators?

$
0
0

Summary: Guest blogger, Bill Stewart, talks about using Windows PowerShell to find administrators.

Microsoft Scripting Guy, Ed Wilson, is here. Hello everyone. It is the weekend, and guest blogger, Bill Stewart is going to share his time and knowledge today. Bill is a scripting guru and a moderator for the Official Scripting Guys Forum...

As you know, an administrator of a Windows computer is a member of the computer’s local Administrators group. This is a special built-in group, so any user or group that’s a member of this special group is an administrator on the computer. We can see who the members of this group are by typing the command net localgroup Administrators at a cmd.exe or Windows PowerShell prompt.

Get local group membership by using ADSI

But what if we want to get the members of the local Administrators group on a remote computer? We can’t use the net localgroup command because it works only on the local computer. Since we’re talking Windows  PowerShell, you might be aware that you can use the [ADSI] type accelerator to connect to a group on a computer; for example:

$localAdminGroup = [ADSI] "WinNT://remotecomputer/Administrators,Group"

This creates a System.DirectoryServices.DirectoryEntry object that references the local Administrators group on the computer RemoteComputer. This is an object, so we should be able to call its Members method to view the members of the group:

$localAdminGroup.Members()

However, this produces some peculiar output:

System.__ComObject

...

The members of the group are COM objects, but Windows PowerShell doesn’t know what to do with them because there’s no type library that tells Windows PowerShell the interface for the objects (that is, what the properties and methods are). The workaround is to call the InvokeMember method of each COM object’s base type to retrieve the name property, like this:

$localAdminGroup.Members() | ForEach-Object {

  $_.GetType().InvokeMember("Name", "GetProperty", $NULL, $_, $NULL)

}

It’s ugly, but it works. But we have another issue. What if you’re using a non-English version of Windows? Or what if the Administrators group has been renamed? In either case, the ADSI technique isn’t going to work because the group might not be named Administrators.

Get Administrators group name by using WMI

The alternative is to use WMI. By using WMI, we can ask for the group by its SID, S-1-5-32-544, rather than by its name:

Get-WMIObject -Class Win32_Group `

  -Filter "LocalAccount=TRUE and SID='S-1-5-32-544'" `

  -Computer remotecomputer

This gives us output like this:

Caption                        Domain          Name            SID

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

REMOTECOMPUTER\Administrators  REMOTECOMPUTER  Administrators  S-1-5-32-544

Get-WMIObject supports credentials, so if you’re signed in with an account that doesn’t have permission to access the remote computer, you can create a credential object to make the connection:

$credential = Get-Credential

Get-WMIObject -Class Win32_Group `

  -Filter "LocalAccount=TRUE and SID='S-1-5-32-544'" `

  -ComputerName remotecomputer `

  -Credential $credential

Combine ADSI and WMI?

Now that we’ve connected to the actual Administrators group, we can use the Name property, with ADSI and script in the previous section, to get the members of the group:

$credential = Get-Credential

$computerName = "remotecomputer"

$wmiObject = Get-WMIObject `

  -Class Win32_Group `

  -Filter "LocalAccount=TRUE and SID='S-1-5-32-544'" `

  -ComputerName $computerName `

  -Credential $credential

$adminGroupName = $wmiObject.Name

$adminGroup = [ADSI] "WinNT://$computerName/$adminGroupName,Group"

$adminGroup.Members() | ForEach-Object {

  $_.GetType().InvokeMember("Name", "GetProperty", $NULL, $_, $NULL)

}

Now we’re getting somewhere. The output of this script is a list of names like this:

Administrator

Domain Admins

kendyer

...

This is good, but we’re missing something. We know that Administrator is probably a local account and Domain Admins is probably a domain group. But we don’t know if kendyer is a local account or a domain account. To avoid confusion, it would be really helpful if the output contained the domain name for domain accounts.

Also, there’s another issue with this approach: It’s relatively inefficient. We’re making an initial WMI connection to the remote computer to retrieve the Administrators group name, and then we’re making a second connection (connecting to the ADSI group object) to retrieve the group members. This may not be an issue on a fast network, but over slower connections, it doesn’t scale very well.

Use WMI to get members

To avoid making two separate connections to each remote computer, we can stick with WMI. When we connect to a computer and retrieve its Administrators group, we’re retrieving a .NET ManagementObject object:

$credential = Get-Credential

$computerName = "remotecomputer"

$wmiObject = Get-WMIObject `

  -Class Win32_Group `

  -Filter "LocalAccount=TRUE and SID='S-1-5-32-544'" `

  -ComputerName $computerName `

  -Credential $credential

".NET object type: {0}" -f $wmiObject.GetType().FullName

"WMI class: {0}" -f $wmiObject.__CLASS

The output of this command is:

.NET object type: System.Management.ManagementObject

WMI class: Win32_Group

WMI objects can be related to each other by using associator classes; in this case, WMI relates the Win32_Group class with its members by using the Win32_GroupUser associator class. The WMI GetRelated method can use the Win32_GroupUser class to retrieve the group’s members, like this:

$credential = Get-Credential

$computerName = "remotecomputer"

$wmiObject = Get-WMIObject `

  -Class Win32_Group `

  -Filter "LocalAccount=TRUE and SID='S-1-5-32-544'" `

  -ComputerName $computerName `

  -Credential $credential

$wmiObject.GetRelated("Win32_Account")

When I run this on my computer, though, the script seems to hang, and it never finishes. There are simply too many relationships in WMI for this call to the GetRelated method to complete in a reasonable amount of time. To get around this issue, I need to specify the parameters to the GetRelated method to tell WMI that I only want the members of this specific group—not every possible relationship:

$credential = Get-Credential

$computerName = "remotecomputer"

$wmiObject = Get-WMIObject `

  -Class Win32_Group `

  -Filter "LocalAccount=TRUE and SID='S-1-5-32-544'" `

  -ComputerName $computerName `

  -Credential $credential

$wmiObject.GetRelated("Win32_Account","Win32_GroupUser","","",

  "PartComponent","GroupComponent",$FALSE,$NULL)

In this case, I use all eight parameters for the GetRelated method to focus on the exact relationship between the Win32_Group object instance (in the variable $wmiObject) and its related Win32_Account object instances. When I run the previous script, I get the desired output:

AccountType : 512

Caption     : REMOTECOMPUTER\Administrator

Domain      : REMOTECOMPUTER

SID         : S-1-5-21...

FullName    :

Name        : Administrator

Caption : FABRIKAM\Domain Admins

Domain  : FABRIKAM

Name    : Domain Admins

SID     : S-1-5-21...

Improve WMI performance

I’ve run into a final snag, though. When I connect to a remote computer over a slow network connection, I get the group members, but the GetRelated method only outputs one group member at a time. To speed things up, I would prefer to retrieve multiple members at a time; say, in batches of 50 members.

To do this, I need to create a System.Management.EnumerationOptions object, set its BlockSize property to 50, and use this object as the final parameter of the GetRelated method:

$wmiEnumOpts = new-object System.Management.EnumerationOptions

$wmiEnumOpts.BlockSize = 50

$credential = Get-Credential

$computerName = "remotecomputer"

$wmiObject = Get-WMIObject `

  -Class Win32_Group `

  -Filter "LocalAccount=TRUE and SID='S-1-5-32-544'" `

  -ComputerName $computerName `

  -Credential $credential

$wmiObject.GetRelated("Win32_Account","Win32_GroupUser","","",

  "PartComponent","GroupComponent",$FALSE,$wmiEnumOpts)

Now, I get the same output, but it runs faster because the GetRelated method retrieves the local Administrator group’s membership in batches of 50 instead of one at a time.

User friendly output

When I use the GetRelated method, the object instances are output by the default formatter in Windows PowerShell. These output objects provide a lot of detail, but I only want to know three things:

  • The computer name where the local Administrators group resides
  • The name of the Administrators group (in case it’s not in English or has been renamed)
  • The name of the member of the group

To do this, I’ll use the Select-Object cmdlet to output customized objects that contain only the information I want:

$wmiEnumOpts = new-object System.Management.EnumerationOptions

$wmiEnumOpts.BlockSize = 20

$credential = Get-Credential

$computerName = "remotecomputer"

$wmiObject = Get-WMIObject `

  -Class Win32_Group `

  -Filter "LocalAccount=TRUE and SID='S-1-5-32-544'" `

  -ComputerName $computerName `

  -Credential $credential

$groupName = $wmiObject.Name

$wmiObject.GetRelated("Win32_Account","Win32_GroupUser","","",

  "PartComponent","GroupComponent",$FALSE,$wmiEnumOpts) |

  Select-Object `

  @{Name="ComputerName"; Expression={$_.__SERVER}},

  @{Name="Name"; Expression={$groupName}},

  @{Name="Member"; Expression={$_.Caption}}

Note that I’m retrieving the group’s name before calling the GetRelated method so I can include it in the output objects.

There is one final “tweak” I want to include. If the account is a local account, I want to omit the computer name from the Member output property; that is, I want my Member property to contain Administrator rather than RemoteComputer\Administrator. To get this effect, I’ll use the Windows PowerShell -replace operator to replace the computer name from the Caption property with an empty string. To do this, let’s replace the final line of script:

@{Name="Member"; Expression={$_.Caption}}

with this instead:

@{Name="Member"; Expression={$_.Caption -replace "^$($_.__SERVER)\\", ""}}

This code gives me the final output I want:

ComputerName    Name            Member

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

REMOTECOMPUTER  Administrators  Administrator

REMOTECOMPUTER  Administrators  D1\Domain Admins

...

Put it all together

Now that we have the working script, all that’s left is to put it together so it’s easy to use. You can download the entire script from the TechNet Gallery, and it supports pipeline input for computer names: Get-LocalAdminGroupMember.ps1.

~Bill

Thanks Bill. Excellent post.

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

Ed Wilson, Microsoft Scripting Guy

PowerTip: Use PowerShell to Get WinSat Score on Windows 8.1

$
0
0

Summary:  Use Windows PowerShell to read the WinSat score on your computer running Windows 8.1.

Hey, Scripting Guy! Question How can I use Windows PowerShell to obtain the WinSat score from my computer running Windows 8.1, if I cannot find the utility I used on earlier versions of Windows?

Hey, Scripting Guy! Answer Use the Get-CimInstance cmdlet and read the values from the Win32_WinSat WMI class:

Get-CimInstance Win32_WinSAT

See How Naming Makes PowerShell Easy to Learn

$
0
0

Summary: Microsoft Scripting Guy, Ed Wilson, shares an excerpt from his book, Windows PowerShell 3.0 Step by Step, which explains how the Windows PowerShell naming convention makes it easy to learn.

Microsoft Scripting Guy, Ed Wilson, is here. Today I am happy to provide you with an excerpt from my book, Windows PowerShell 3.0 Step by Step, published by Microsoft Press.

Image of book cover

One of the great things about Windows PowerShell is the verb–noun naming convention. In Windows PowerShell, the verbs indicate an action to perform, such as Set to make a change or Get to retrieve a value. The noun indicates the item with which to work, such as a process or a service.

By mastering the verb–noun naming convention, you can quickly hypothesize what a prospective command might be called. For example, if you need to obtain information about a process, and you know that Windows PowerShell uses the verb Get toretrieve information, you can surmise that the command might very well be Get-Process. To obtain information about services, you try Get-Service…and once again, you are correct.

Note  When you are “guessing” Windows PowerShell cmdlet names, always try the singular form first. Windows PowerShell prefers the singular form of nouns. It is not a design requirement, but it is a strong preference. Therefore, the cmdlets are named Get-Service, Get-Process, and not Get-Services or Get-Processes.

To see the list of approved verbs, use the Get-Verb cmdlet. There are 98 approved verbs in Windows PowerShell 3.0. This number increases the 96 approved verbs from Windows PowerShell 2.0 by only two new verbs. The new verbs are Useand Unprotect. This is shown in the command that follows, where the Measure-Object cmdlet returns the count of the verbs:

PS C:\> (Get-Verb | Measure-Object).count
98

Analyzing Windows PowerShell verb grouping

With nearly 100 verbs, you may be asking yourself, “How does an array of 100 verbs assist in learning Windows PowerShell?” You would be correct in asking that question. Nearly 100 unrelated items are difficult to learn. However, the Windows PowerShell team grouped the verbs. For example, analyzing the common verbs reveals a pattern. The common verbs are listed here:

PS C:\> Get-Verb | where group -match 'common' | Format-Wide verb -auto
Add       Clear     Close     Copy      Enter    Exit     Find     Format   Get
Hide      Join      Lock      Move      New      Open     Optimize Pop      Push
Redo      Remove    Rename    Reset     Resize   Search   Select   Set      Show
Skip      Split     Step      Switch    Undo     Unlock   Watch

The pattern to the verbs emerges when analyzing the verbs: Add/Remove, Enter/Exit, Get/Set, Select/Skip, Lock/Unlock, Push/Pop, and so on. By learning the pattern to the common verbs, you quickly gain a handle on the Windows PowerShell naming convention.

By using the Windows PowerShell verb grouping, you can determine where to focus your efforts. The following command lists the Windows PowerShell verb grouping:

PS C:\> Get-Verb | select group -Unique
Group
-----
Common
Data
Lifecycle
Diagnostic
Communications
Security
Other

Analyzing Windows PowerShell verb distribution

Another way to get a handle on the Windows PowerShell cmdlets, is to analyze the verb distribution. Although there are nearly 100 approved verbs (not to mention rogue developers, who use unapproved verbs), only a fraction of them are utilized repeatedly—and some of them are not used at all in a standard Windows PowerShell installation. By using the Group-Object (group is an alias) and the Sort-Object cmdlet (sort is an alias), the distribution of the cmdlets quickly becomes evident. The following command shows the verb distribution:

Get-Command -CommandType cmdlet | group verb | sort count –Descending

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

Image of command output

The preceding output makes it clear that most cmdlets only use a few of the verbs. In fact, most of the cmdlets use only one of ten verbs. This is shown here:

PS C:\> Get-Command -CommandType cmdlet | group verb | sort count -Descending | select -First 10
Count Name                      Group
----- ----                      -----
   94 Get                       {Get-Acl, Get-Alias, Get-AppLockerFileInformation...
   48 Set                       {Set-Acl, Set-Alias, Set-AppLockerPolicy, Set-Aut...
   38 New                       {New-Alias, New-AppLockerPolicy, New-CertificateN...
   30 Remove                    {Remove-AppxProvisionedPackage, Remove-BitsTransf...
   15 Add                       {Add-AppxProvisionedPackage, Add-BitsFile, Add-Ce...
   11 Invoke                    {Invoke-BpaModel, Invoke-CimMethod, Invoke-Comman...
   11 Import                    {Import-Alias, Import-Certificate, Import-Clixml,...
   11 Export                    {Export-Alias, Export-Certificate, Export-Clixml,...
   10 Test                      {Test-AppLockerPolicy, Test-Certificate, Test-Com...
   10 Enable                    {Enable-ComputerRestore, Enable-JobTrigger, Enabl...

In fact, of 436 cmdlets, 278 of the cmdlets use only one of ten verbs. This is shown here:

PS C:\> (Get-Command -CommandType cmdlet | measure).count
436
PS C:\> $count = 0 ; Get-Command -CommandType cmdlet | group verb | sort count -Desce
nding | select -First 10 | % { $count += $_.count ; $count }
94
142
180
210
225
236
247
258
268
278

Therefore, all you need to do is to master 10 verbs, and you will have a good handle on more than half of the cmdlets that ship with Windows PowerShell 3.0.

That is all there is to using Windows PowerShell to explore cmdlet naming. 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

Find Hidden Message Embedded in Image

$
0
0

Summary: Learn how to find a hidden message embedded in an image.

Microsoft Scripting Guy, Ed Wilson, is here. Today we have an extra post from Shane Nelson with a recap of an interesting challenge presented to us at our Charlotte PowerShell User Group.

Shane is an associate Windows system administrator for a large-scale healthcare provider in the southeast U.S. He has worked in IT for since 2008. Along with his team, he helps manage and support Windows servers and Microsoft Exchange.

Take it away, Shane…

Windows PowerShell MVP, Jim Christopher, presented us with a challenge: Find the hidden message that he embedded in the image he provided. The image was a 640x480 seemingly black rectangle. And because most of us in the group aren’t Windows PowerShell veterans like the Scripting Guy, Jim gave us two clues:

1. He showed us how to load an image into Windows PowerShell:

$image = [System.Drawing.Image]::FromFile('C:\powershell\stegan1.png')

2. He hinted that when we are look for a hidden message in an image, we need to examine the bits.

What are the “bits” of an image? The pixels!

So we needed to find a way to examine all the pixels in an image. Fortunately, our $image variable came with a GetPixel method, as seen from its Get-Member output.
When we called the GetPixel method, for instance on the first pixel (0,0), we saw its red, green, and blue values.
PS C:\powershell> $image.GetPixel(0,0)

R             : 0

G             : 0

B             : 0

A             : 255

IsKnownColor  : False

IsEmpty       : False

IsNamedColor  : False

IsSystemColor : False

Name          : ff000000

In this case, they were all zero, which indicates that this pixel was black. Now we needed to iterate through every pixel to see what was different. We did this by iterating through each pixel in a row and then iterating through each row (and in turn through each of its pixels).

for ($h=0;$h -lt $image.Height;$h++)

{

   for ($w=0;$w -lt $image.Width;$w++)

   {

       $image.GetPixel($w,$h)

   }

}

Here the inner for loop iterates through the objects in a row while the outer for loop iterates through the rows.

When we watched this output for a time, we noticed that some of the pixels had R=1 instead of R=0. This didn’t help us much, however, because the GetPixel output merely showed us screens and screens of the RGB values of each pixel. We wanted to see what was different.

We decided to create a map of the red pixels (the ones that were changed) so that we could let our human eyes notice any differences.

$pixelmap = @()


for ($h=0;$h -lt $image.Height;$h++)

{

   $line = ""

   for ($w=0;$w -lt $image.Width;$w++)

   {

       $line += ($image.GetPixel($w,$h)).R

   }

   $pixelmap += $line

}

Here we created a pixel map by concatenating each red value in a row to the $line variable and then concatenating each $line value to the $pixelmap variable.

Redirecting our pixel map to a text document with $pixelmap > pixelmap.txt, we were then able to open the text containing all of the R values, zoom out as far as we could, and see the hidden message:

Image of message

Happy Holidays, scripters!

~Shane

Thanks, Shane!

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 Resolve DNS Names

$
0
0

Summary: Use Windows PowerShell to find IP addresses for DNS names.

Hey, Scripting Guy! Question How can I use Windows PowerShell to resolve a DNS name to an IP address?

Hey, Scripting Guy! Answer With Windows PowerShell 4.0 in Windows 8.1, use the Resolve-DnsName command,
          and then select the IP4address from the results:

(Resolve-DnsName www.microsoft.com).ip4address

Use PowerShell to Create Registry Keys

$
0
0

Summary: Use Windows PowerShell to create and manage registry keys.

Microsoft Scripting Guy, Ed Wilson, is here. Today I am happy to provide you with an excerpt from my book, Windows PowerShell 3.0 Step by Step, published by Microsoft Press.

Image of book cover

Note  The registry contains information that is vital to the operation and configuration of your computer. Serious problems could arise if you edit the registry incorrectly. Therefore, it is important to back up your system prior to attempting to make any changes. For information about backing up your registry, see article 322756 in the Microsoft Knowledge Base. For general information about working with the registry, see article 310516.

Creating a new registry key by using Windows PowerShell is the same as creating a new file or a new folder. All three processes use the New-Item cmdlet. In addition, you might use the Test-Path cmdlet to determine if the registry key already exists. You may also want to change your working location to one of the registry drives. If you do this, you might use the Push-Location, Set-Location, and Pop-Location cmdlets. This is, of course, the long way of doing things…

Just the steps

  1. Store the current working location by using the Push-Location cmdlet.
  2. Change the current working location to the appropriate registry drive by using the Set-Location cmdlet.
  3. Use the Test-Path cmdlet to determine if the registry key already exists.
  4. Use the New-Item cmdlet to create the new registry key.
  5. Use the Pop-Location cmdlet to return to the starting working location.

The following example creates a new registry key named hsg off the HKEY_CURRENT_USERS software registry hive. It illustrates each of the five steps previously detailed.

Push-Location
Set-Location HKCU:
Test-Path .\Software\test
New-Item -Path .\Software -Name test
Pop-Location

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

Image of command output

The short way to create a new registry key

It is not always necessary to change the working location to a registry drive when you create a new registry key. In fact, it is not even necessary to use the Test-Path cmdlet to determine if the registry key exists. If the registry key already exists, an error generates. If you want to overwrite the registry key, use the Forceparameter.

Note  How you choose to deal with an already existing registry key is one of those design decisionsthat confront IT pros who venture very far into the world of scripting. Software developers are very familiar with these types of decisions, and they usually deal with them during the analyzing requirements portion of the development lifecycle. IT pros who open the Windows PowerShell ISE first, and think about the design requirements second, become easily stymied, or else write-in problems. For more information about this, see my Microsoft Press book, Windows PowerShell 2.0 Best Practices.

The following example creates a new registry key named test in the HKCU:\Software location. Because the command includes the full path, it does not need to execute from the HKCU drive. Because the command uses the Forceswitched parameter, the command overwrites the HKCU:\Software\test registry key if it already exists.

New-Item -Path HKCU:\Software -Name test –Force

Just the steps: The short way to create a new registry key

  1. Include the full path to the registry key to create.
  2. Use the Forceparameter to overwrite any existing registry key of the same name.

In the following image, the first attempt to create a test registry key fails because it already exists. The second command uses the Forceparameter, which causes the command to overwrite the existing registry key, and therefore it succeeds without creating an error.

Image of command output

Setting the default value for the key

The previous examples do not set the default value for the newly created registry key. If the registry key already exists (as it does in this specific case), use the Set-Item cmdlet to assign a default value to the registry key. The steps to accomplish this are shown here:

Just the steps: Assign a default value to an existing registry key

  1. Use the Set-Item cmdlet and supply the complete path to the existing registry key.
  2. Supply the default value in the Valueparameter of the Set-Item cmdlet.

The following command assigns the value “test key” to the default property value of the hsg registry key contained in the HKCU:\Software location.

Set-Item -Path HKCU:\Software\test -Value "test key"

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 Display Routing Table

$
0
0

Summary: Use Windows PowerShell to display the routing table.

Hey, Scripting Guy! Question How can I use Windows PowerShell to display the routing table?

Hey, Scripting Guy! Answer In your computer running Windows 8.1, use Windows PowerShell 4.0, and call the Get-NetRoute command.


Use PowerShell to Create Remote Session

$
0
0

Summary: Learn how to use Windows PowerShell to create a remote management session.

Microsoft Scripting Guy, Ed Wilson, is here. Today I am happy to provide you with an excerpt from my new book, Windows PowerShell 3.0 Step by Step, published by Microsoft Press.

Image of book cover

For a simple configuration on a single remote machine, entering a remote Windows PowerShell session is the answer. To enter a remote Windows PowerShell session, use the Enter-PSSession cmdlet to create an interactive remote Windows PowerShell session on a target machine. If you do not supply credentials, the remote session impersonates your current sign-in info.

The following output illustrates connecting to a remote computer named dc1. When the connection is established, the Windows PowerShell prompt changes to include the name of the remote system. The Set-Location (sl is an alias) cmdlet changes the working directory on the remote system to c:\. Next, the Get-WmiObject cmdlet retrieves the BIOS information on the remote system. The Exitcommand exits the remote session, and the Windows PowerShell prompt returns to the default.

PS C:\> Enter-PSSession -ComputerName dc1
[dc1]: PS C:\Users\Administrator\Documents> sl c:\
[dc1]: PS C:\> gwmi win32_bios

SMBIOSBIOSVersion : A01
Manufacturer      : Dell Computer Corporation
Name              : Default System BIOS
SerialNumber      : 9HQ1S21
Version           : DELL   - 6

[dc1]: PS C:\> exit
PS C:\>

The good thing is that when using the Windows PowerShell transcript tool via Start-Transcript, the transcript tool captures output from the remote Windows PowerShell session and output from the local session. Indeed, all commands typed appear in the transcript. The following commands illustrate beginning a transcript, entering a remote Windows PowerShell session, typing a command, exiting the session, and stopping the transcript.

PS C:\> Start-Transcript
Transcript started, output file is C:\Users\administrator.IAMMRED\Documents\PowerShe
ll_transcript.20120701124414.txt
PS C:\> Enter-PSSession -ComputerName dc1
[dc1]: PS C:\Users\Administrator\Documents> gwmi win32_bios

SMBIOSBIOSVersion : A01
Manufacturer      : Dell Computer Corporation
Name              : Default System BIOS
SerialNumber      : 9HQ1S21
Version           : DELL   - 6

[dc1]: PS C:\Users\Administrator\Documents> exit
PS C:\> Stop-Transcript
Transcript stopped, output file is C:\Users\administrator.IAMMRED\Documents\PowerShe
ll_transcript.20120701124414.txt
PS C:\>

The following image contains a copy of the transcript from the previous session.

Image of transcript

If you anticipate making multiple connections to a remote system, use the New-PSSession cmdlet to create a remote Windows PowerShell session. New-PSSession permits you to store the remote session in a variable, and it provides you with the ability to enter and leave the remote session as often as required, without the additional overhead of creating and destroying remote sessions.

In the commands that follow, a new Windows PowerShell session is created via the New-PSSession cmdlet. The newly created session is stored in the $dc1 variable. Next, the Enter-PSSession cmdlet is used to enter the remote session by using the stored session. A command retrieves the remote host name, and the remote session is exited via the Exitcommand. Next, the session is re-entered, and the last process is retrieved. The session is exited once again. Finally, the Get-PSSession cmdlet retrieves Windows PowerShell sessions on the system, and all sessions are removed via the Remove-PSSession cmdlet.

PS C:\> $dc1 = New-PSSession -ComputerName dc1 -Credential iammred\administrator
PS C:\> Enter-PSSession $dc1
[dc1]: PS C:\Users\Administrator\Documents> hostname
dc1
[dc1]: PS C:\Users\Administrator\Documents> exit
PS C:\> Enter-PSSession $dc1
[dc1]: PS C:\Users\Administrator\Documents> gps | select -Last 1

Handles  NPM(K)    PM(K)      WS(K) VM(M)   CPU(s)     Id ProcessName
-------  ------    -----      ----- -----   ------     -- -----------
    292       9    39536      50412   158     1.97   2332 wsmprovhost

[dc1]: PS C:\Users\Administrator\Documents> exit
PS C:\> Get-PSSession

 Id Name            ComputerName    State         ConfigurationName     Availability
 -- ----            ------------    -----         -----------------     ------------
  8 Session8        dc1             Opened        Microsoft.PowerShell     Available

PS C:\> Get-PSSession | Remove-PSSession
PS C:\>

Join me tomorrow when I will talk about understanding the While statement.

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 Net Route with PowerShell

$
0
0

Summary: Use Windows PowerShell to find a net route to a remote host.

Hey, Scripting Guy! Question How can I use the Find-NetRoute function in Windows 8.1 to find a route to a remote host if I do not have the IP address?

Hey, Scripting Guy! Answer Use the Resolve-DnsName command to find the IP address of the remote host,
          and feed the results to the Find-NetRoute command:

Find-NetRoute -RemoteIPAddress (Resolve-DnsName www.microsoft.com).ip4address

Understanding the PowerShell While Statement

$
0
0

Summary: Learn how to use the While statement in Windows PowerShell.

Microsoft Scripting Guy, Ed Wilson, is here. Today I am happy to provide you with an excerpt from my new book Windows PowerShell 3.0 Step by Step, published by Microsoft Press.

Image of book cover

In VBScript, you had the While…Wend loop. An example of using the While…Wend loop is the WhileReadLineWend.vbs script. The first thing you do in the script is create an instance of the FileSystemObject, and store it in the objFSO variable. You then use the OpenTextFile method to open a test file, and you store that object in the objFile variable.

You then use the While…Not …Wend construction to read one line at a time from the text stream and display it on the screen. You continue to do this until you are at the end of the text stream object. A While … Wend loop continues to operate as long as a condition is evaluated as True. In this example, as long as you are not at the end of the stream, you will continue to read the line from the text file. The WhileReadLineWend.VBS script is shown here: 

WhileReadLineWend.vbs
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objFile = objFSO.OpenTextFile("C:\fso\testfile.txt")

While Not objFile.AtEndOfStream
 WScript.Echo objFile.ReadLine
Wend

Constructing the While statement

As you probably have already guessed, you have the same kind of construction available to you in Windows PowerShell. The While statement in Windows PowerShell is used in the same way that the While…Wend statement was used in VBScript.

In the DemoWhileLessThan.ps1 script, you first initialize the variable $i to be equal to 0. You then use the While keyword to begin the While loop. In Windows PowerShell, you must include the condition that will be evaluated inside a set of parentheses. For this example, you determine the value of the $i variable with each pass through the loop.

If the value of $i is less than the number 5, you will perform the action that is specified inside the braces, which set off the script block. In VBScript, the condition that is evaluated is positioned on the same line with the While statement, but no parentheses are required.

Although this is convenient from a typing perspective, it actually makes the code a bit confusing to read. In Windows PowerShell, the statement is outside the parentheses, and the condition is clearly delimited by the parentheses. In VBScript, the action that is performed is added between two words: While and Wend. In Windows PowerShell, there is no Wend statement, and the action to be performed is positioned inside a pair of braces.

Although shocking at first to user’s coming from a VBScript background, the braces are always used to contain code. This is what is called a script block, and they are used everywhere. As soon as you are used to seeing them, you will find them with other language statements also. The good thing is that you do not have to look for items such as the keyword Wend, or the keyword Loop (from the Do…Loop fame).

Understanding expanding strings

In Windows PowerShell, there are two kinds of strings: literal strings and expanding strings. In the DemoWhileLessThan.ps1 script you use the expanding string (signified when you use the double quotation mark ( “ ). (The literal string uses the single quotation mark ( ‘ ). You want to display the name of the variable, and you want to display the value that is contained in the variable.

This is a perfect place to showcase the expanding string. In an expanding string, the value that is contained in a variable is displayed to the screen when a line is evaluated. As an example, consider the following script. You assign the value 12 to the variable $i. You then put $i inside a pair of double quotation marks, which creates an expanding string. When the line “$i is equal to $i” is evaluated, you obtain “12 is equal to 12,” which, although true, is barely illuminating. This is shown here:

PS C:\> $i = 12
PS C:\> "$i is equal to $i"
12 is equal to 12
PS C:\>

Understanding literal strings

What you probably wanted to do is display both the name of the variable and the value that is contained inside it. In VBScript, you would have to use concatenation. For this example to work, you have to use the literal string as shown here:

PS C:\> $i = 12
PS C:\> '$i is equal to ' + $i
$i is equal to 12
PS C:\>

If you want to use the advantage of the expanding string, you have to suppress the expanding nature of the expanding string for the first variable. To do this, you use the escape character, which is the grave accent (or backtick) as shown here:

PS C:\> $i = 12
PS C:\> "`$i is equal to $i"
$i is equal to 12
PS C:\>

In the DemoWhileLessThan.ps1 script, you use the expanding string to print the status message of the value of the $i variable during each trip through the While loop. You suppress the expanding nature of the expanding string for the first $i variable so you can see which variable you are talking about. As soon as you have done this, you increment the value of the $i variable by one. To do this, you use the $i++ syntax. This is identical to saying the following:

$i = $i + 1

The advantage is that the $i++ syntax is less typing. The DemoWhileLessThan.ps1 script is shown here:

DemoWhileLessThan.ps1
$i = 0
While ($i -lt 5)
 {
  "`$i equals $i. This is less than  5"
  $i++
 } #end while $i lt 5

When you run the DemoWhileLessThan.ps1 script, you receive the following output.

$i equals 0. This is less than  5
$i equals 1. This is less than  5
$i equals 2. This is less than  5
$i equals 3. This is less than  5
$i equals 4. This is less than  5
PS C:\>

A practical example of using the While statement

Now that you know how to use the While loop, let’s examine the WhileReadLine.ps1 script. The first thing you do is initialize the $i variable and set it equal to 0. You then use the Get-Content cmdlet to read the contents of the testfile.txt and to store the contents into the $fileContents variable. 

Use the While statement to loop through the contents of the text file. You do this as long as the value of the $i variable is less than or equal to the number of lines in the text file. The number of lines in the text file is represented by the Length property. Inside the script block, you treat the contents of the $fileContents variable like it is an array (which it is), and you use the $i variable to index into the array to print the value of each line in the $fileContents variable. You then increment the value of the $i variable by one. The WhileReadLine.ps1 script is shown here:

WhileReadLine.ps1
$i = 0
$fileContents = Get-Content -path C:\fso\testfile.txt
While ( $i -le $fileContents.length )
 {
  $fileContents[$i]
  $i++
 }

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 Show IP Interface Overview

$
0
0

Summary: Use Windows PowerShell to provide an overview of IP interfaces.

Hey, Scripting Guy! Question How can I use Windows PowerShell to provide me with a quick overview of the IP interfaces on my computer?

Hey, Scripting Guy! Answer In your computer running Windows 8.1, use Windows PowerShell 4.0, and call the Get-NetIPInterface command.   

Using the PowerShell Contains Operator

$
0
0

Summary:  Microsoft Scripting Guy, Ed Wilson, talks about using the Windows PowerShell Contains operator to work with arrays.

Microsoft Scripting Guy, Ed Wilson, is here. Today I am happy to provide you with an excerpt from my book Windows PowerShell 3.0 Step by Step, published by Microsoft Press.

Image of book cover

Examine contents of an array

To verify input that is received from the command line, you can use the Contains operator to examine the contents of an array of possible values. This following technique illustrates an array of three values that is created and stored in the variable $noun. The Contains operator is then used to see if the array contains "hairy-nosed wombat". Because the $noun variable does not have an array element that is equal to the string "hairy-nosed wombat," the Contains operator returns False.

PS C:\> $noun = "cat","dog","rabbit"
PS C:\> $noun -contains "hairy-nosed wombat"
False
PS C:\>

If an array contains a match, the Contains operator returns True, as shown here:

PS C:\> $noun = "cat","dog","rabbit"
PS C:\> $noun -contains "rabbit"
True
PS C:\>

The Contains operator returns True only when there is an exact match. Partial matches return False. This is shown here:

PS C:\> $noun = "cat","dog","rabbit"
PS C:\> $noun -contains "bit"
False
PS C:\>

The Contains operator is case insensitive. Therefore, it will return True when matched, regardless of case:

PS C:\> $noun = "cat","dog","rabbit"
PS C:\> $noun -contains "Rabbit"
True
PS C:\>

If you need to perform a case sensitive match, you can use the case sensitive version of the Contains operator, -ccontains. As shown here, it will return True only if the case of the string matches the value that is contained in the array:

PS C:\> $noun = "cat","dog","rabbit"
PS C:\> $noun -ccontains "Rabbit"
False
PS C:\> $noun -ccontains "rabbit"
True
PS C:\>

In the Get-AllowedComputers.ps1 script, a single command-line parameter is created, which is used to hold the name of the target computer for the WMI query. The computer parameter is a string, and it receives the default value from the environmental drive. This is a good technique because it ensures that the script will have the name of the local computer, which could then be used to produce a report of the results. If you set the value of the computer parameter to LocalHost, you never know what computer the results belong to. This is shown here:

Param([string]$computer = $env:computername)

The Get-AllowedComputer function is used to create an array of permitted computer names and to check the value of the $computer variable to see if it is present. If the value of the $computer variable is present in the array, the Get-AllowedComputer function returns True. If the value is missing from the array, the Get-AllowedComputer function returns False.

The array of computer names is created by using the Get-Content cmdlet to read a text file that contains a list of computer names. The text file, servers.txt, is a plain ASCII text file that has a list of computer names on individual lines, as shown in the following image:

Image of note

A text file of computer names is easier to maintain than a hard-coded array that is embedded into the script. In addition, the text file can be placed on in central shared folder, and it can be used by many different scripts. The Get-AllowedComputer function is shown here:

Function Get-AllowedComputer([string]$computer)
{
 $servers = Get-Content -path c:\fso\servers.txt
 $servers -contains $computer
} #end Get-AllowedComputer function

Because the Get-AllowedComputer function returns a Boolean value (True/False) it can be used directly in an IF statement to determine if the value that is supplied for the $computer variable is on the permitted list. If the Get-AllowedComputer function returns True, the Get-WmiObject cmdlet is used to query for BIOS information from the target computer. This is shown here:

if(Get-AllowedComputer -computer $computer)
 {
   Get-WmiObject -class Win32_Bios -Computer $computer
 }

On the other hand, if the value of the $computer variable is not found in the $servers array, a string that states the computer is not an allowed computer is displayed:

Else
 {
  "$computer is not an allowed computer"
 }

Following is the complete Get-AllowedComputer.ps1 script:

Get-AllowedComputer.ps1
Param([string]$computer = $env:computername)

Function Get-AllowedComputer([string]$computer)
{
 $servers = Get-Content -path c:\fso\servers.txt
 $servers -contains $computer
} #end Get-AllowedComputer function

# *** Entry point to Script ***

if(Get-AllowedComputer -computer $computer)
 {
   Get-WmiObject -class Win32_Bios -Computer $computer
 }
Else
 {
  "$computer is not an allowed computer"
 }

Test for properties

You are not limited to only testing for specified computer names in the Get-AllowedComputer function. All you need to do is add additional information to the text file, as shown in the following image:

Image of note

A couple of modifications to the Get-AllowedComputer.ps1 script are all that is required to turn it into the Get-AllowedComputerAndProperty.ps1 script. The first is to add an additional command line parameter to allow the user to choose which property to display:

Param([string]$computer = $env:computername,[string]$property="name")

Next, the signature to the Get-AllowedComputer function is changed to permit passing the Property name. Instead of directly returning the results of the Contains operator, the returned values are stored in variables. The Get-AllowedComputer function first checks to see if the $servers array contains the computer name. It then checks to see if the $servers array contains the Property name. Each of the resulting values is stored in variables. The two variables are then added together, and the result returns to the calling code. When two Boolean values are added together, only the “True and True” case is equal to True. This is shown here:

PS C:\> $true -and $false
False
PS C:\> $true -and $true
True
PS C:\> $false -and $false
False
PS C:\>

Following is the revised Get-AllowedComputer function:

Function Get-AllowedComputer([string]$computer, [string]$property)
{
 $servers = Get-Content -path c:\fso\serversAndProperties.txt
 $s = $servers -contains $computer
 $p = $servers -contains $property
 Return $s -and $p
} #end Get-AllowedComputer function

The if statement is used to determine if both the computer value and the property value are contained in the allowed list of servers and properties. If the Get-AllowedComputer function returns True, the Get-WmiObject cmdlet is used to display the chosen property value from the selected computer. This is shown here:

if(Get-AllowedComputer -computer $computer -property $property)
 {
   Get-WmiObject -class Win32_Bios -Computer $computer |
   Select-Object -property $property
 }

If the computer value and the property value are not on the permitted list, the Get-AllowedComputerAndProperty.ps1 script displays a message that states there is a non-permitted value:

Else
 {
  "Either $computer is not an allowed computer, `r`nor $property is not an allowed property"
 }

The complete Get-AllowedComputerAndProperty.ps1 script is shown here:

Get-AllowedComputerAndProperty.ps1
Param([string]$computer = $env:computername,[string]$property="name")

Function Get-AllowedComputer([string]$computer, [string]$property)
{
 $servers = Get-Content -path c:\fso\serversAndProperties.txt
 $s = $servers -contains $computer
 $p = $servers -contains $property
 Return $s -and $p
} #end Get-AllowedComputer function

# *** Entry point to Script ***

if(Get-AllowedComputer -computer $computer -property $property)
 {
   Get-WmiObject -class Win32_Bios -Computer $computer |
   Select-Object -property $property
 }
Else
 {
  "Either $computer is not an allowed computer, `r`nor $property is not an allowed property"
 }

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 Get List of Authorized Root Certificates

$
0
0

Summary: Use Windows PowerShell to get a list of authorized root certificates for the current user.

Hey, Scripting Guy! Question How can I examine the authorized root certificates for the current user?

Hey, Scripting Guy! Answer Use the Get-ChildItem cmdlet (dir is an alias) and explore the cert:\CurrentUser\AuthRoot folder:

dir Cert:\CurrentUser\AuthRoot

Getting Started with PowerShell: The Basics of WMI

$
0
0

Summary: Microsoft Scripting Guy, Ed Wilson, talks about querying WMI in this excerpt of his book, Windows PowerShell 3.0 First Steps.

Microsoft Scripting Guy, Ed Wilson, is here. Today I have the last excerpt from my new Microsoft Press book, Windows PowerShell 3.0 First Steps.

Image of book cover

In most situations, when you use WMI, you are performing some sort of query. Even when you are going to set a particular property, you still need to execute a query to return a dataset that enables you to perform the configuration. (A dataset includes the data that come back to you as the result of a query, that is, it is a set of data.) There are several steps involved in performing a basic WMI query:

  1. Connect to WMI by using the Get-WMIObjectcmdlet.
  2. Specify a valid WMI class name to query.
  3. Specify a value for the namespace—omit the Namespace parameter to use the default root\cimv2 namespace.
  4. Specify a value for the ComputerNameparameter—omit the ComputerNameparameter to use the default value of LocalHost.

Windows PowerShell makes it easy to query WMI. In fact, at its most basic level, the only thing required is gwmi(alias for the Get-WmiObject cmdlet) and the WMI class name. An example of this simple syntax is shown here, along with the associated output:

PS C:\> gwmi win32_bios
SMBIOSBIOSVersion : BAP6710H.86A.0064.2011.0504.1711
Manufacturer      : Intel Corp.
Name              : BIOS Date: 05/04/11 17:11:33 Ver: 04.06.04
SerialNumber      :
Version           : INTEL  - 1072009

However, there are more properties available in the Win32_Bios WMI class than the five displayed in the previous output. The reason for the limited output that is displayed from the command is a custom view of the Win32_Bios class defined in the types.ps1xml file that resides in the Windows PowerShell home directory on your system. The following command uses the Select-String cmdlet to search the Types.ps1xml file to see if there is any reference to the WMI class Win32_Bios.

Select-String -Path $pshome\*.ps1xml -SimpleMatch "Win32_Bios"

In the following image, several Select-String commands display results when a special format exists for a particular WMI class. The last query (for the Win32_CurrentTime WMI class) does not return any results, indicating that no special formatting exists for this class.

Image of command output

The Select-String queries shown in the previous image indicate that there is a special formatting for the Win32_Bios, Win32_DesktopMonitor, and Win32_Service WMI classes. The Types.ps1xml file provides information to Windows PowerShell that tells it how to display a particular WMI class. When an instance of the Win32_Bios WMI class appears, Windows PowerShell uses the DefaultDisplayPropertySetconfiguration to display only five properties. The portion of the Types.ps1xml file that details these five properties is shown here:

                    <PropertySet>
                        <Name>DefaultDisplayPropertySet</Name>
                        <ReferencedProperties>
                            <Name>SMBIOSBIOSVersion</Name>
                            <Name>Manufacturer</Name>
                            <Name>Name</Name>
                            <Name>SerialNumber</Name>
                            <Name>Version</Name>
                        </ReferencedProperties>
                    </PropertySet>

The complete type definition for the Win32_Bios WMI classis shown in following image:

Image of command output

Special formatting instructions for the Win32_Bios WMI class indicate that there is an alternate property set available—a property set that is in addition to the DefaultDisplayPropertySet. This additional property set, named PSStatus, contains the four properties in the PropertySetshown here:

            <PropertySet>
                <Name>PSStatus</Name>
                <ReferencedProperties>
                    <Name>Status</Name>
                    <Name>Name</Name>
                    <Name>Caption</Name>
                    <Name>SMBIOSPresent</Name>
                </ReferencedProperties>
            </PropertySet>

Finding the PSStatusproperty set is more than a simple academic exercise, because it can be used directly with Windows PowerShell cmdlets such as Select-Object (select is an alias), Format-List (fl is an alias),or Format-Table (ft is an alias). The following commands illustrate this technique:

gwmi win32_bios | select psstatus
gwmi win32_bios | fl psstatus
gwmi win32_bios | ft psstatus 

Unfortunately, you cannot use the alternate property set, PSStatus, to select the properties via the Propertyparameter. Therefore, the following command fails.

gwmi win32_bios -Property psstatus

That is all there is to using Windows PowerShell to query WMI.  Join me tomorrow when I will have a guest post from Yuri Diogenes, Security Series: Using PowerShell to Enable BYOD—Part 1.

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 Special Formatting from Types XML File in WMI

$
0
0

Summary: Use special formatting from the types .xml file to choose properties in Windows PowerShell and WMI.

Hey, Scripting Guy! Question How can I use Windows PowerShell to find the status of the BIOS on my computer without viewing the default output?

Hey, Scripting Guy! Answer Use the special PSStatus view when you select properties:

Get-WmiObject win32_bios | select psstatus

Security Series: Using PowerShell to Enable BYOD–Part 1

$
0
0

Summary: Guest blogger and security expert, Yuri Diogenes, talks about using Windows PowerShell to enable BYOD.

Microsoft Scripting Guy, Ed Wilson, is here. Today’s guest blogger is Yuri Diogenes, who is bringing us the beginning of a new security series. Yuri is a senior knowledge engineer, and he is a coauthor of the book Windows Server 2012 Security from End to Edge and Beyond. You can follow him on Twitter at @YuriDiogenes.

Image of book cover

Take it away, Yuri…

The explosion of mobile devices and how users are connecting to resources online changed the way users consume information, and this change has arrived in the enterprise model. In the past, you could easily say that user Bob would always sign in from the UserBobPC computer. This allowed IT to control the computer (device) the way they wanted, and to control the user’s profile—hardening and authorizing, based on the need-to-know security principle.

However, with the demand from users to use their own devices to access corporate information, the concept that one user will always use one device, which is managed by IT, doesn’t fit anymore. This new model is called “bring your own device” (BYOD).

This series of posts will focus in three core BYOD scenarios that use Windows Server 2012 R2 technologies that can be enabled by using Windows PowerShell. If you want to know more about BYOD, read the General Considerations Regarding BYOD section in Bring Your Own Device (BYOD) Survival Guide for Microsoft Technologies.

Scenario 1: Know devices before users access resources

One of the biggest challenges that IT faces with the BYOD model is the lack of control of users’ devices. Even worse, IT has no idea who is bringing the device or if the device is rogue. Before you think about a management solution for user-owned devices, you should ask yourself the following questions:

  • How can I track who is bringing an unmanaged device?
  • How can I authenticate the device according to the user that is bringing the device?
  • How can I track this device?

These simple questions can be answered with the capabilities that are built-in to Windows Server 2012 R2, and rapidly enabled by using Windows PowerShell. To know more about the device, you can leverage the Device Registration Service (DRS) in Windows Server 2012 R2. This registration can be performed from devices that are connected within the corporate network (corpnet) or devices that are connecting from an external network, such as the Internet, as shown here:

Image of flow chart

As part of this registration process, a certificate is installed on the device, and a new device object is created in Active Directory. The following image shows an example of a record in Active Directory:

Image of record

This device object establishes a link between a user and a device, making it known to IT. It allows the device to be authenticated, effectively creating a seamless second-factor authentication. In return for registering their devices and making them known to IT, users gain access to corporate resources that were previously not available outside of their domain-joined computers.

Scenario definition

Contoso IT wants to embrace BYOD. As a first step toward this strategy, they want to understand the footprint of the users’ devices. Initially they want to enable device registration only for devices that are connected through the on-premises corporate network.

The steps to enable device registration by using Windows PowerShell are pretty straightforward. However, there are some prerequisites that must be in place before enabling this capability. Ensure that the following actions were completed before you enable DRS:

  • Install a server certificate on the local computer container of the server that will receive the Active Directory Federation Services (AD FS) server role.
  • Install and configure the AD FS role in the server running Windows Server 2012 R2 (by running the Install-AdfsFarm cmdlet).
  • Configure the Domain Name System (DNS) Server service with an alias CNAME resource record for the AD FS farm and a host (A) resource record for the AD FS nodes.

DRS is installed by running the Install-AdfsFarm cmdlet, and it will use the same service account credentials that are used by AD FS. When the environment is ready, you can prepare Active Directory for DRS by running the following command once for the entire ADFS farm:

Initialize-ADDeviceRegistration -ServiceAccountName contoso\<serviceaccountname>

Following is an example of this operation, where the service account name is adfsfarm:

Image of command output

After you finish this operation, you should run the following cmdlet in each AD FS node in the farm:

Enable-AdfsDeviceRegistration

Following is an example of this operation:

Image of command output

Validate the device

After the operation is complete, you can use the Get-AdfsDeviceRegistration cmdlet to verify the ObjectDN and its parameters, as shown here:

Image of command output

At this point, you can connect a device running Windows 8.1 in the corporate network and use the Workplace Join capability to join and register the device. For more information about this operation, see Walkthrough Guide: Workplace Join with a Windows Device.

To set up your environment for this functionality, you can follow the instructions in Set up the lab environment for AD FS in Windows Server 2012 R2. See you next time!

~Yuri

Thank you, Yuri, for 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

Solving Office Holiday Gift-Giving Conundrums with PowerShell

$
0
0

Summary: Microsoft Scripting Guy, Ed Wilson, talks about a project to assign secret pals at work. He ropes one of the managers into the project.

During my one-on-one last week with my manager, Adina, she mentioned doing something to help people who work from remote locations feel more like part of the team. Because I work remotely, I was all ears. She had a great idea: Assign random secret pals. You have to find out all you can about the secret pal, and then send them something that is unique to them—their interests, hobbies, or tastes. Of course, the present is something small, which also makes it a bit of a challenge. Although, maybe not. I mean, anyone who reads the Hey, Scripting Guy! Blog for very long knows that I enjoy different kinds of teas, and I am especially partial to Anzac biscuits—neither of which need be especially expensive.

So it is that kind of thing. And of course, I was in. Then Adina came up with a real kicker, “I wonder if you could write a Windows PowerShell script to do that?”

So I opened my big mouth and said, “Of course, I could write it. But with Windows PowerShell, the task would be so easy, that even a manager could write it.”

Now I had Adina’s attention. We discussed it a bit, and decided that Michel would be perfect for the task because as he has nearly no experience with Windows PowerShell. I offered, of course, to help with pointers. But before I got too involved, Michel had already solved the task at hand. I now turn you over to Michel LaFantano, a senior managing editor on our team, and I will let him tell you about his experience. Take it away Michel…

Our team at Microsoft includes many remote workers—some who work in non-Redmond Microsoft offices, and some who work from their homes around the country. This helps make for a diverse team, and it also lends a certain “open 24 hours” feeling to our group because there is pretty much always someone awake and online.

But it also means that remote workers rarely get the chance to hang-out in person, and we usually miss the morale events held on the Redmond campus. To help remedy that a bit this year, our director decided to hold a remote worker gift exchange. But how do we pick names from the fish bowl when people are dispersed across ten time zones?

Well, we figured a little Windows PowerShell script might help solve the issue. Being a resourceful kind of guy, I figured I would take a look in the Script Repository (or scriptorium, as I like to call it) and see if I could find something similar to leverage in creating our own “remote-gift-giver-partner-picker” script.

What I found was beyond all expectations, because I found a script that one of our awesome users had written for this exact purpose. Our scripter, who goes by the handle “CathersalCathersalCathersal,” wrote it for his family’s Secret Santa event, and it is called the “Virtual Hat.” Take a look:

Virtual Hat Windows PowerShell script

# This script will randomly draw a name out of a virtual "hat."

#

# This script selects two names from an array in a random fashion.

# Because the people will exchange gifts, it is important that no

# person is assigned to themselves.

#

# Instructions: Just change the names below to the names you want and run it.

 

$NameArray = New-Object System.Collections.ArrayList

 

$NameArray.Add("Syed")

$NameArray.Add("Kim")

$NameArray.Add("Sam")

$NameArray.Add("Hazem")

$NameArray.Add("Pilar")

$NameArray.Add("Terry")

$NameArray.Add("Amy")

$NameArray.Add("Pamela")

$NameArray.Add(“Julie")

$NameArray.Add("David")

$NameArray.Add("Robert")

$NameArray.Add("Shai")

$NameArray.Add("Ann")

$NameArray.Add("Mason")

$NameArray.Add("Sharon")

 

$numberOfNames = $namearray.count

$ArrayCopy = $namearray.clone()

 

for($i=0; $i -lt $numberOfNames; $i++)

{

    $NameToRemove = get-random -input $NameArray

    While ( $NameToRemove -eq $ArrayCopy[$i])  # make sure people don't pick themselves.

    {

        $NameToRemove = get-random -input $NameArray

    }

    Write-host $ArrayCopy[$i]"picks $NameToRemove"

    $NameArray.Remove($NameToRemove)  # Remove the name so it doesn't get picked again.

}

When I run the script, the output in the following image appears in the Windows PowerShell ISE:

Image of command output

Simple, yet elegant, don’t you think? And all we had to do to use it was add the names of our remote team members and we were good to go.

We all know that Windows PowerShell is an awesome tool in datacenters and at home, but who knew that it could also save the holidays? Issue solved. Back to you, Ed…

Turn an array into a hash table

While Michel was busy finding a script to use, I had also begun to tackle the issue. I figured it would be pretty simple because the Get-Random cmdlet is a powerful tool. In fact, we use the Get-Random cmdlet when handing out prizes at PowerShell Saturday and at Windows PowerShell User Group meetings. So I am familiar with this tool.

If all I wanted to do was randomize a list of names, that is two lines of code (it could even be one if I do not store the array of names in its own variable). But I wanted a two-column output. At first I thought I could use Format-Wide and specify two columns. But after messing around with it for about a minute, I saw that for some reason, Format-Wide does not like to convert an array into two-column output.

So I decided to convert my array of names into a hash table. Here is the script I wrote to do this:

# ArrayNamesToHashTable.ps1

# ed wilson, msft

# Secret Pals blog posting

# HSG-12-6-13-b

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

$names = 'Syed', 'Kim', 'Sam', 'Hazem', 'Pilar', 'Terry', 'Amy', 'Greg',

    'Pamela', 'Julie', 'David', 'Robert', 'Shai', 'Ann', 'Mason', 'Sharon'

 

$rndNames = Get-Random -InputObject $names -Count ($names.count)

$hash = @{} ; $i = 0

Do {

 $hash.Add($rndNames[$i],$rndNames[($i+1)])

 if($i -le $names.count){$i+=2}}

 While ($i -le $names.count-1)

 $hash

When I run it, I get a nice two-column output, as shown in the following image:

Image of command output

So in the end, it took me more time than it did for a manager to “write a script.” All told, I spent about an hour messing around with this script. But I imagine that I had more fun writing my script than Michel did. After all, I used the Windows PowerShell ISE and he used Bing.

You can find the script in the Script Repository: Randomly draw names out of a virtual "hat."

Join me tomorrow when I will have 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 Display Network Adapter Bindings

$
0
0

Summary:  Use Windows PowerShell to display network adapter bindings.

Hey, Scripting Guy! Question How can I use Windows PowerShell in Windows 8 to display network adapter bindings?

Hey, Scripting Guy! Answer Use the Get-NetAdapter cmdlet to retrieve your network adapter, and pipe it to Get-NetAdapterBinding:

Get-NetAdapter -Name ethernet | Get-NetAdapterBinding -AllBindings

Weekend Scripter: Create an HTML Server Report with PowerShell

$
0
0

Summary: Guest blogger, Matthew Kerfoot, talks about using Windows PowerShell to create an HTML server report.

Microsoft Scripting Guy, Ed Wilson, is here. Today I would like to welcome a new guest blogger, Matthew Kerfoot…

Photo of Matthew Kerfoot

I'm a Windows PowerShell enthusiast with strong interests in automating systems administration. I write a blog called The Overnight Admin where I like to share Windows PowerShell scripts, tips, and functions. I also tweet about Windows PowerShell at @mkkerfoot                         

The following function utilizes WMI to retrieve various information from local and remote machines and then outputs that data into a nicely formatted HTML file for easy viewing: Write-HTML.

Keep in mind that PSRemoting must be enabled, and a proper execution policy must be set to run scripts:

Enable-PSRemoting –Force ; Set-ExecutionPolicy RemoteSigned -Force

My Write-HTML function is about 60 lines of code, and I will not be able to touch on everything. My goal is to make the whole script as readable as possible for someone who has little to no experience with Windows PowerShell.

The first line turns this script into a function by bundling all the code into the function Write-HTML{}, which has a Help file. Because this is bundled into a function, all we have to do (after pasting the code into an elevated Windows PowerShell command prompt) is type “Write-HTML” to produce the following report about your local machine:

Image of report

function Write-HTML{

<#

.SYNOPSIS

Creates an HTML file on the Desktop of the local machine full of detailed   system information.

.DESCRIPTION

    Write-HTML utilizes WMI to retrieve information related to the physical hardware of the machine(s), the available `

disk space, when the machine(s) last restarted and bundles all that information up into a colored HTML report.

.EXAMPLE

   Write-HTML -Computername localhost, SRV-2012R2, DC-01, DC-02

   This will create an HTML file on your desktop with information gathered from as many computers as you can access remotely

#>

I’ve added the CmdletBinding attribute to the script, which by itself, allows the use of all common parameters including Verbose, Debug, ErrorAction, ErrorVariable, WarningAction, WarningVariable, OutBuffer, and OutVariable.

However, by adding SupportsShouldProcess=$True into the CmdletBinding attribute, we can now test what this script would do if we ran it—for example, if we type Write-HTML -WhatIf,it won’t make any changes, but it will output to the console what the script would have done.

[CmdletBinding(SupportsShouldProcess=$True)]

param( [Parameter(Mandatory=$false,

ValueFromPipeline=$true)]

[string]$FilePath = "C:\users\$env:USERNAME\desktop\Write-HTML.html",

[string[]]$Computername = $env:COMPUTERNAME,

After some of the more common parameters, there is a parameter called $Css, which is where the CSS code has been added to manipulate the HTML file. This is where we can change the color of the report, and format the table headers and data.

$Css='<style>table{margin:auto; width:98%}

     Body{background-color:Orange; Text-align:Center;}

     th{background-color:black; color:white;}

     td{background-color:Grey; color:Black; Text-align:Center;} </style>' )

The script begins with a verbose output of what this script is going to do.  This will only be shown if the –Verbose or –WhatIf parameter is added after the function. The Begin, Process, and End blocks are not needed, so I like to use these to organize the script. This allows you to break up the sections of your scripts and collapse the sections within the Windows PowerShell ISE.

Begin{Write-Verbose "HTML report will be saved $FilePath"}

Next, the Process section of the script starts. My script has five variables defined within the Process brackets, which allows me to compact all the code after each equals sign into each variable, for example $Hardware, $PercentFree, $Restarted, $Stopped, and $Report. Each variable is accomplishing a specific task and will later be used with the creation of the HTML file.

Let’s examine the first variable within the Process block: $Hardware. First, all WMI information related to Win32_ComputerSystem is queried for each $ComputerName that is specified. By default, this function will only run against the local host, but you can add the -ComputerName  parameter, and specify all the computers you would like the function to run against.

After querying the selected computers, the WMI object is filtered for the objects specified. In this case, it would be: Name, Domain, Manufacture, Model, NumberofLogicalProccessors. But there is one more object that is being specified. This one is a little harder to see—we are also looking for TotalPhysicalMemory.  In this case, I am taking the object and dividing it by 1 GB to give me a more readable output. After it is divided by 1 GB, the output is formatted with {0:N0}, which represents a number in the one’s place or rounded to the nearest whole number (for example, {0:N3} would give you an output like 7.833).

Process{$Hardware=Get-WmiObject -class Win32_ComputerSystem -ComputerName $Computername |

     Select-Object Name,Domain,Manufacturer,Model,NumberOfLogicalProcessors,

     @{ Name = "Installed Memory (GB)" ; Expression = { "{0:N0}" -f( $_.TotalPhysicalMemory / 1gb ) } } |

     ConvertTo-Html -Fragment -As Table -PreContent "<h2>Hardware</h2>" | Out-String

When all the Win32_ComputerSystem information is gathered and filtered with Select-Object, everything is pushed through the pipeline to ConvertTo-Html -Fragment -As Table -PreContent "<h2>Available Disk Space</h2>" | Out-String.

Most of this is pretty straightforward, except the –PreContent parameter, which contains the section’s header information to be displayed above the hardware table. Out-String is the perfect cmdlet for creating HTML documents because it allows Windows PowerShell to convert the objects into an array of strings, yet it will only return one string by default. This gives us the availability to use variables such as $Hardware later in the script within the –Body of the HTML file.

$PercentFree=Get-WmiObject Win32_LogicalDisk -ComputerName $Computername |

     Where-Object { $_.DriveType -eq "3" } | Select-Object SystemName,VolumeName,DeviceID,

     @{ Name = "Size (GB)" ; Expression = { "{0:N1}" -f( $_.Size / 1gb) } },

     @{ Name = "Free Space (GB)" ; Expression = {"{0:N1}" -f( $_.Freespace / 1gb ) } },

     @{ Name = "Percent Free" ; Expression = { "{0:P0}" -f( $_.FreeSpace / $_.Size ) } } |

     ConvertTo-Html -Fragment -As Table -PreContent "<h2>Available Disk Space</h2>" | Out-String

$Restarted=Get-WmiObject -Class Win32_OperatingSystem -ComputerName $Computername | Select-Object Caption,CSName,

     @{ Name = "Last Restarted On" ; Expression = { $_.Converttodatetime($_.LastBootUpTime) } } |

     ConvertTo-Html -Fragment -As Table -PreContent "<h2>Last Boot Up Time</h2>" | Out-String

$Stopped=Get-WmiObject -Class Win32_Service -ComputerName $Computername |

     Where-Object { ($_.StartMode -eq "Auto") -and ($_.State -eq "Stopped") } |

     Select-Object SystemName,DisplayName,Name,StartMode,State,Description |

     ConvertTo-Html -Fragment -PreContent "<h2>Services currently stopped that are set to autostart</h2>" | Out-String

The $Report variable is used to design the body and header of the report. The –Body parameter is where everything comes together—all of the previously created variables are placed within quotation marks so they get resolved. Without quotation marks, the body of the report would say: $Hardware $PercentFree $Restarted $Services $Stopped $Css. This is also where the -Title is defined, which changes what the browser tab will say when you view the report.

$Report=ConvertTo-Html -Title "$Computername" `

     -Head "<h1>PowerShell Reporting<br><br>$Computername</h1><br>This report was ran: $(Get-Date)" `

     -Body "$Hardware $PercentFree $Restarted $Services $Stopped $Css"}

The End block is where everything should come together and get cleaned up or formatted. Here, we’re taking the contents of $Report,which has already been converted to HTML,and piping it to the $FilePath (your desktop) specified at the beginning of the script. When complete, Invoke-Expression $FilePath will open the file in your default browser.

End{$Report | Out-File $Filepath  ; Invoke-Expression $FilePath  } }

Remember to paste the code into an elevated command prompt, and you can test the script first with Write-HTML –WhatIf.

You can download the whole function from the Script Center Repository: How to make an HTML server report with PowerShell or on SkyDrive: Write-HTML.

I invite you to follow me on Twitter, Facebook, and Google +. If you have any questions, feel free to email me at mkkerfoot@gmail.com.

~Matthew

Thanks for sharing your time and knowledge, Matthew. 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 2129 articles
Browse latest View live


Latest Images

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