Archive for the “Scripting” Category
I recently came across the need to configure IP information on a Network interface using a script. This is definitely something that can be accomplished with NETSH, but I was looking for a way to do it with native PowerShell using Get-WmiObject. If there is a choice, I tend to prefer to stick with native Powershell Cmdlets and functions rather than running cmd based tools, but that is just my personal opinion and you can take it or leave it.
My goal was to be able to specify a Network Interface by name, ie “Local Area Connection” and pass it an IP address, mask, gateway, and 1 or 2 DNS servers. In order to accomplish this there were two WMI classes that I needed. The first is Win32_NetworkAdapter. The second is Win32_NetworkAdapterConfiguration. The real key (pun intended) was to figure out a way to join the information from these two classes so that I knew for sure that I was working with the same network interface. It turns out that both classes have a property called InterfaceIndex.
Win32_NetworkAdapter has a property called netconnectionid which is equal to the name of the Adapter, ie “Local Area Connection”.
With that information, here is a function that you can use to specify settings for a NIC based on the name of the NIC.
1: function Set-IPAddress {
2: param( [string]$networkinterface,
3: [string]$ip,
4: [string]$mask,
5: [string]$gateway,
6: [string]$dns1,
7: [string]$dns2,
8: [string]$registerDns = "TRUE"
9: )
10:
11:
12: #Start writing code here
13: $dns = $dns1
14: if($dns2){$dns ="$dns1,$dns2"}
15: $index = (gwmi Win32_NetworkAdapter | where {$_.netconnectionid -eq $networkinterface}).InterfaceIndex
16: $NetInterface = Get-WmiObject Win32_NetworkAdapterConfiguration | where {$_.InterfaceIndex -eq $index}
17: $NetInterface.EnableStatic($ip, $mask)
18: $NetInterface.SetGateways($gateway)
19: $NetInterface.SetDNSServerSearchOrder($dns)
20: $NetInterface.SetDynamicDNSRegistration($registerDns)
21:
22: }
Hope you find this useful.
Andy
7 Comments »
A while back on the Windows PowerShell Team blog, there was a post that describes how to force Get-Credential to prompt for a username and password in the console itself rather than popping up a Windows dialog box asking for a username and password.
The change is a key in the registry, and is permanent, unless of course you change it back to the original setting. The other minor complaint was that the there was no space between where the user needs to type a username and password, and the text prompting for the text.
1: 119 > $c = Get-Credential
2:
3: cmdlet Get-Credential at command pipeline position 1
4: Supply values for the following parameters:
5: Credential
6: PromptForCredential_UserAndy
7: PromptForCredential_Password********
8:
9: 120 >
You can see that the “PromptForCredential” and my username – “Andy” just run together.
You can accomplish this another way with much more control over the user experience and you don’t have to hack the registry.
So here is my quick and dirty function. I put in a very basic check to see if someone added their domain or not, and added it if necessary.
1: function Get-Cred {
2: Write-Host "";
3: $username = Read-Host "Enter username to access some resource (no domain required)"
4: if ($username -notlike "MYDOMAIN\*"){$username = "MYDOMAIN\$username"}
5:
6: Write-Host ""
7: $password = Read-Host -AsSecureString "Password to access some resource"
8:
9: $credential = New-Object System.Management.Automation.PSCredential($username,$password)
10: return $credential
11: }
And here it is in action:
1: 130 > $cred = Get-Cred
2:
3: Enter username to access some resource (no domain required): andy
4:
5: Password to access some resource: **************
6: 131 >
7: 131 > $cred.GetNetworkCredential() | fl *
8:
9:
10: UserName : andy
11: Password : secretpassword
12: Domain : MYDOMAIN
2 Comments »
There has been a lot of talk in the last couple weeks about using WPF to build UI’s for Powershell. Just to name a few
Powershell Team’s Series on WPF
Joel Bennet’s Huddled Masses Series on WPF
I think this is great and I can’t wait to see what we all come up with.
As we begin to build these graphical tools on top of Powershell, UI and user experience is going to be more and more important.
There is a great presentation over on dnrTV (Dot Net Rocks TV) where Mark Miller and Carl Franklin discuss the Science of Great User Experience. There are some really good nuggets in here that even as Powershell Scripters tweaking WPF, we can think about and use.
Also, if you are developer and use Visual Studio, you really should check out Dev Express’s CodeRush. This is the company that Mark works for and they really take User Experience for Visual Studio to the next level. It’s awesome.
No Comments »
For the last year or so I have been trying to get people I work with excited about Powershell. Some people love it right away, but for others, it is a bit overwhelming. That being said, I had an interesting experience the other day. In order to set the context of this, I must confess something. I have not been a big fan of PowerGui. My attitude was always something like this: “PowerShell is a SHELL, not a GUI, and the last thing in the world that I need is yet another GUI application.”
Don’t get me wrong here. It’s not that I thought PowerGui is a bad application, its just that I personally didn’t see the value in it for me and what I was doing with Powershell on a daily basis.
In spite of my point of view, I let my team know that PowerGui had RTM’d and forwarded a link to my co-workers.
Within about an hour I got an email from one of our Operations guys that included the following:
| “Dude, this is fricking AWESOME, I installed it with the Active Directory Quest management pack and E2K7 Management tools (32bit) on a Windows 2003 R2 box. Super sweet!” |
After reading that my attitude about PowerGui started to change Another 4 or 5 hours go by and I get another email from the same person:
| “WOW, I have being playing with some of the AD and E2K7 stuff, this tool kicks so much butt !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!” |
I would like end my confession with an apology to Dimitry and the rest of the PowerGui crew. I just wish I had started telling people about it earlier. You guys have created an awesome tool and it has absolutely helped me in getting more people in my organization using Powershell. Thank you very much for your hard work and all that you give to the Powershell Community.
By the way, I have been a die hard fan of the Quest AD Cmdlets from the very beginning.
As we – the Powershell Community – work together to get people on board with Powershell, we must remember and realize that what works well for us as individuals may not work well for others and vice versa. We must be able to step into the shoes of people we are working with, understand their concerns, and help them solve their problems, (with Powershell of course.)
8 Comments »
During the Scripting Games I found myself creating a lot of custom objects with properties that I could use to sort , select, take averages of, and a number of other cool things. Getting results into a Powershell object can make life a lot easier for a number of reasons.
There was one little piece I was missing. Not only did I want to create a single object, quite often I would want to put all the objects I created into a collection of objects. Did you know you can add collections of like objects to each other ?
1: PS 13 > $a = get-process
2: PS 14 > $a.count
3: 66
4: PS 15 > $b = get-process
5: PS 16 > $c = $a + $b
6: PS 17 > $c.count
7: 131
8: PS 18 >
The code above shows that I can add two collections of process objects together. Very cool.
So I tried doing this in the scripting games and came across a problem. For instance, in Event 3 we needed to tally up a bunch of votes. So what I really needed was to create a bunch of $vote objects and put them all together in a collection called $votes
Here’s what I came up with at first.
By the way, when I create PS Custom Objects I cheat and use the “” | Select-Object prop1, prop2 nomenclature. My easier than using new-object followed by a bunch of add-member commands.
1: $votes = "" | Select-Object v1,v2,v3,v4
2: foreach ($v in Get-Content votes.txt)
3: {
4: $vote = "" | Select-Object v1,v2,v3,v4;
5: $vote.v1,$vote.v2,$vote.v3,$vote.v4 = $v.split(",")
6: $votes += $vote
7: }
Looks nice and shiny until you run it I get the following error:
Method invocation failed because [System.Management.Automation.PSObject] doesn’t contain a method named ‘op_Addition’.
Not so shiny
The trick is that we $votes needs to be a collection of $vote objects, not another object identical to $vote.
So we instantiate $votes with a cast to [array] and life is good.
1: $votes = @()
2: foreach ($v in Get-Content votes.txt)
3: {
4: $vote = "" | Select-Object v1,v2,v3,v4;
5: $vote.v1,$vote.v2,$vote.v3,$vote.v4 = $v.split(",")
6: $votes = $votes + $vote
7: }
A quick update, thanks to Aleksandar. We should instantiate $votes as $votes = @(). I have updated the example above.
3 Comments »
Event 5 in the Scripting Games dealt with testing whether or not a password is secure or not. Among many qualifications, the password had to contain at least one digit, at least one upper case character and at least one lower case character. There also needed to be a check to see if the password contained any non-alphanumeric characters, such as $%^ or (.
All these could be dealt with using regular expressions, but regex skills are weak at best, so I always default to check and see if if someone has done it for me already. Sure enough, there are a bunch of static methods for System.Char that we can use.
1: [char]::IsUpper()
2: [char]::IsLower()
3: [char]::IsDigit()
4: [char]::IsLetterOrDigit()
From these we can build a filter that will pass the string if it meets the criteria. For the sake of example, lets build “Select-ContainsUpper”
1: # Create a filter that can be used in a pipeline
2: filter Select-ContainsUpper {
3:
4: # coerce the string into an array of chars and then pass each one to IsUpper
5: # IsUpper returns $TRUE or $FALSE for each char
6: # So ContainsUpper now contains an array of a bunch of Booleans
7: $containsUpper = $_.toCharArray() | % {[char]::IsUpper($_)}
8:
9: # if any char was uppercase, some element in ContainsUpper will be $TRUE
10: if ($containsUpper -contains $TRUE) {$_}
11: }
12:
13: "one","Two","5","ContainsUpperCase","all-lower-case" | Select-ContainsUpper
Building Select-ContainsDigit and the others is left as an exercise to the reader.
You could also quite easily build a function that takes the string as a password, but lately I have been big on using filters and then putting them all together in a nice pipeline for the sake of clarity and a nice overall PowerShelly look.
1 Comment »
Event six was a classic programming math problem. Find all the prime numbers in a given range. For this particular problem, the range was between 1 and 200.
Here’s my answer
1: filter select-prime
2: {
3: if ($_ -eq 1) {return $null}
4: for ($i=2;$i -le ([int][Math]::Sqrt($_));$i++)
5: {
6: if ($_ % $i -eq 0 ) {return}
7: }
8: $_
9: }
10: 1..200 | select-prime
It turns out that in order to find if a prime number we can use the modulus operator. This math operator simply returns the remainder when one number is divided by another
If a number n % x = 0 where x is not 1 or n, then the number will not be prime.
1: PS U:> 6 % 2
2: 0
3: PS U:> 5 % 3
4: 2
5: PS U:> 5 % 2.5
6: 0
7: PS U:> 9 % 2
8: 1
9: PS U:> 9 % 3
10: 0
So this will work if we just went from 2 to N. But it turns out that is way more work than necessary. We only need to go up to the Square Root of N and we can call it prime.
I really thought I was being super cool and iterated up the square root of the number. Turns out the calls into .NET to calculate the square root were to costly to make my efficiency efficient.
1: PS C:usersandysDesktop> cat C:usersandysDesktopprimes.ps1
2: filter select-primeQuickly
3: {
4: if ($_ -eq 1) {return $null}
5: for ($i=2;$i -le ([int][Math]::Sqrt($_));$i++)
6: {
7: if ($_ % $i -eq 0 ) {return}
8: }
9: $_
10: }
11:
12: filter select-primeSlowly
13: {
14: if ($_ -eq 1) {return $null}
15: for ($i=2;$i -le $_;$i++)
16: {
17: if ($_ % $i -eq 0 ) {return}
18: }
19: $_
20: }
21:
22: "Quickly"
23: ""
24: measure-command {1..200 | select-primeQuickly}
25: "Slowly"
26: ""
27: measure-command {1..200 | select-primeSlowly}
28: PS C:usersandysDesktop> C:usersandysDesktopprimes.ps1
29: Quickly
30:
31: Days : 0
32: Hours : 0
33: Minutes : 0
34: Seconds : 0
35: Milliseconds : 734
36: Ticks : 7346876
37: TotalDays : 8.5033287037037E-06
38: TotalHours : 0.000204079888888889
39: TotalMinutes : 0.0122447933333333
40: TotalSeconds : 0.7346876
41: TotalMilliseconds : 734.6876
42:
43: Slowly
44:
45: Days : 0
46: Hours : 0
47: Minutes : 0
48: Seconds : 0
49: Milliseconds : 346
50: Ticks : 3469459
51: TotalDays : 4.0155775462963E-06
52: TotalHours : 9.63738611111111E-05
53: TotalMinutes : 0.00578243166666667
54: TotalSeconds : 0.3469459
55: TotalMilliseconds : 346.9459
Looking at the results, what I thought was going to be quick was actually a lot slower! The version that calls into .NET ran in 736 milliseconds and the version that did not ran in 346 milliseconds.
Goes to show that when you are programming or scripting, what we think may be a gain in efficiency, may very well not be, as I learned in this exercise.
4 Comments »
The gentlemen over at the Power Scripting Podcast recently posted a tip on how to start processes in Powershell.
I had run into the same problem they had. How do you pass in both an executable and its arguments to the Start method of a System.Diagnostics.Process object.
Something like this works fine in Powershell:
[System.Diagnostics.Process]::Start(“calc”)
But as soon as you try to pass in something like “ipconfig /all” the thing blows up.
Turns out you can pass in arguments a couple of ways. The Powerscripting guys noted that if you pass in the argument of the executable as a second argument to Start, it will work great.
[System.Diagnostics.Process]::Start(“ipconfig”,”all”)
However, there is another way if you want to get a little bit more fancy and do things like hide windows or redirect output from StdOut or StdErr.
You can use a System.Diagnostic.ProcessStartInfo object. Using get-member we can take a look at all the options we have for such a thing.
1: PS 124 > $si = New-Object System.Diagnostics.ProcessStartInfo
2: PS 125 > $si | gm -type property | select Name
3:
4: Name
5: ----
6: Arguments
7: CreateNoWindow
8: Domain
9: EnvironmentVariables
10: ErrorDialog
11: ErrorDialogParentHandle
12: FileName
13: LoadUserProfile
14: Password
15: RedirectStandardError
16: RedirectStandardInput
17: RedirectStandardOutput
18: StandardErrorEncoding
19: StandardOutputEncoding
20: UserName
21: UseShellExecute
22: Verb
23: Verbs
24: WindowStyle
25: WorkingDirectory
Lots of goodness here that we can play with. Once you build up the ProcessStartInfo object, you pass that whole object in as the arg to the Start method of system.diagnostics.process.
I put together a quick function to show how this could be used more generically.
1: function Start-Proc {
2: param (
3: [string]$exe = $(Throw "An executable must be specified"),
4: [string]$arguments,
5: [switch]$hidden,
6: [switch]$waitforexit
7: )
8:
9: # Build Startinfo and set options according to parameters
10: $startinfo = new-object System.Diagnostics.ProcessStartInfo
11: $startinfo.FileName = $exe
12: $startinfo.Arguments = $arguments
13: if ($hidden){
14: $startinfo.WindowStyle = "Hidden"
15: $startinfo.CreateNoWindow = $TRUE
16: }
17: $process = [System.Diagnostics.Process]::Start($startinfo)
18: if ($waitforexit) {$process.WaitForExit()}
19:
20: }
21:
22: Start-Proc calc
23: Start-Proc calc -waitforexit
24: Start-Proc -exe ipconfig -arguments /all
25: Start-Proc ipconfig /all
26: Start-Proc ipconfig /all -hidden
I used the alias Start-Proc so that it would not collide with the PSCX Cmdlet Start-Process.
They have done the same writing their CmdLet in C#, but with all kinds of options.
1: PS 130 > gcm Start-Process | fl *
2:
3:
4: DLL : C:\Program Files (x86)\PowerShell Community Extensions\Pscx.dll
5: Verb : Start
6: Noun : Process
7: HelpFile : Pscx.dll-Help.xml
8: PSSnapIn : Pscx
9: ImplementingType : Pscx.Commands.StartProcessCommand
10: ParameterSets : {[[-Path] <String>] [[-Arguments] <String>] [-Verb <String>] [-WorkingDirectory <String>] [-Credential <PSCredential>] [-NoShellExecute] [-NoWindow]
11: [-WindowStyle <ProcessWindowStyle>] [-LoadUserProfile] [-WaitTimeout <Int32>] [-Boost] [-Priority <ProcessPriorityClass>] [-Verbose] [-Debug] [-Error
12: Action <ActionPreference>] [-ErrorVariable <String>] [-OutVariable <String>] [-OutBuffer <Int32>] [-WhatIf] [-Confirm], [[-ScriptBlock] <ScriptBlock>
13: ] [-NoProfile] [-WorkingDirectory <String>] [-Credential <PSCredential>] [-NoShellExecute] [-NoWindow] [-WindowStyle <ProcessWindowStyle>] [-LoadUser
14: Profile] [-WaitTimeout <Int32>] [-Boost] [-Priority <ProcessPriorityClass>] [-Verbose] [-Debug] [-ErrorAction <ActionPreference>] [-ErrorVariable <St
15: ring>] [-OutVariable <String>] [-OutBuffer <Int32>] [-WhatIf] [-Confirm]}
16: Definition : Start-Process [[-Path] <String>] [[-Arguments] <String>] [-Verb <String>] [-WorkingDirectory <String>] [-Credential <PSCredential>] [-NoShellExecute]
17: [-NoWindow] [-WindowStyle <ProcessWindowStyle>] [-LoadUserProfile] [-WaitTimeout <Int32>] [-Boost] [-Priority <ProcessPriorityClass>] [-Verbose] [-D
18: ebug] [-ErrorAction <ActionPreference>] [-ErrorVariable <String>] [-OutVariable <String>] [-OutBuffer <Int32>] [-WhatIf] [-Confirm]
19: Start-Process [[-ScriptBlock] <ScriptBlock>] [-NoProfile] [-WorkingDirectory <String>] [-Credential <PSCredential>] [-NoShellExecute] [-NoWindow] [-W
20: indowStyle <ProcessWindowStyle>] [-LoadUserProfile] [-WaitTimeout <Int32>] [-Boost] [-Priority <ProcessPriorityClass>] [-Verbose] [-Debug] [-ErrorAct
21: ion <ActionPreference>] [-ErrorVariable <String>] [-OutVariable <String>] [-OutBuffer <Int32>] [-WhatIf] [-Confirm]
22:
23: Name : Start-Process
24: CommandType : Cmdlet
25: Visibility : Public
5 Comments »
I thought event 2 was pretty cool. We have to take in a list of figure skaters and their scores, drop the highest and lowest score, average them, and assign a gold, silver and bronze medal to the top three competitors.
This problem was extremely conducive to using the power of the pipeline in Powershell.
1: $competitors = Get-Content c:\scripts\skaters.txt
2: [array]$totalScores = "" | Select-Object Name,Score,Medal
3:
4: foreach ($competitor in $competitors)
5: {
6: $scores = "" | Select-Object Name,Score,Medal
7: $scores.Name,$scores.Score = $competitor.Split(",")
8: $stats = $scores.Score | Measure-Object -Maximum -Minimum
9: $scores.Score = $scores.Score -replace $stats.Maximum
10: $scores.Score = $scores.Score -replace $stats.Minimum
11: $scores.Score = [math]::Round(($scores.Score | Measure-Object -Average).Average,2)
12: $totalScores += $scores
13: }
14: $winners = $totalScores | Sort-Object Score -Descending | Select-Object -First 3 $winners[0].Medal,$winners[1].Medal,$winners[2].Medal = "Gold","Silver","Bronze"
15: $winners | Format-Table -AutoSize
First we pull in the contents of the skaters.txt file. Next I use a little shortcut to create a custom PSObject using select-object. This creates a PSObject with three properties, a Name, a Score, and a Medal. This will become a collection of scores objects.
Using a foreach loop, we go through each competitor and create a new score object, assigning its values.Name to the name of the competitor and then the scores as an array in the .score property.
Please check out my post on multi variable assignment to see how this works.
I can pull the minimum and maximum using measure object and drop them. After that I use measure-object again to take the average and stuff it in the .score property.
I should mention here that /\/\o\/\/’s solution was pretty darn slick. I loved how he sorted and then select 1..5, essentially dropping the top and bottom score before he took the average.
The final item is to add the current score object to the collection of score objects called $totalScores.
Now it becomes quite easy to get the winners. We just sort the $totalScores collection by Score, select the top 3, and assign their .medal value accordingly.
Multi variable assignment is so cool. Ever since I found out about it it seems to appear in everything I do.
Have fun Powershelling
Andy
No Comments »
Now that the answers have been posted for the first 2 events, I thought I would share my answer for the first event.
My first version worked but on some queries would take up to 20 plus minutes to crunch to through the 32,000 + words.
Here is what I finally ended up with:
1: $wordlistlocation = "c:\scripts\wordlist.txt"
2: $phonenumber = Read-Host "Please Enter a phone number"
3: $numbers = [char[]]$phonenumber.ToString()
4:
5: filter Get-Letters {
6:
7: switch ($_) {
8: 2 {$letters = "ABC"}
9: 3 {$letters = "DEF"}
10: 4 {$letters = "GHI"}
11: 5 {$letters = "JKL"}
12: 6 {$letters = "MNO"}
13: 7 {$letters = "PRS"}
14: 8 {$letters = "TUV"}
15: 9 {$letters = "WXY"}
16: } # Switch
17:
18: return $letters
19:
20: } #Get-Letters
21:
22: $first,$second,$third,$fourth,$fifth,$sixth,$seventh = $numbers | Get-Letters
23:
24: $string1 = $first[0] + $first[1] + $first[2]
25: $string2 = $second[0] + $second[1] + $second[2]
26: $string3 = $third[0] + $third[1] + $third[2]
27: $string4 = $fourth[0] + $fourth[1] + $fourth[2]
28: $string5 = $fifth[0] + $fifth[1] + $fifth[2]
29: $string6 = $sixth[0] + $sixth[1] + $sixth[2]
30: $string7 = $seventh[0] + $seventh[1] + $seventh[2]
31:
32: $pattern = "^[" + $string1 + "][" + $string2 + "][" + $string3 + "][" + $string4 +"][" + $string5 + "][" + $string6 + "][" + $string7 + "]"
33:
34: Get-Content $wordlistlocation | Select-String -Pattern $pattern | Select -first 1
First I get the content of the wordlist file and then read in a phone number. The cast to an array of char[] was necessary to put them into an array as individual elements. It was interesting that I could not cast to an array of ints, but time was short and I was under pressure, and it worked
Next, I used switch to build a filter that returns the possible letters for any given number. If I pass in a 2, I get back a string of ABC.
Next I used multiple assignment to get all the possibilities for each number.
I used these to build a big ol’ regex pattern. It’s long but relatively simple. Basically just creates 7 chars using the [] for “this OR that OR the other thing.”
For example, if the first two numbers were 2 and 3, it would generate [abc][def] as the pattern.
Once I built up that pattern, I simply used get-content of the word list and piped that to Select-String and gave Select-String the the regex pattern I created.
In my first attempt, I created a list of all possibilities and then did a -match on them and compared to the word list file.
Using regex and Select-String brought my execution time down from 23 minutes to about 7 seconds, give or take.
No Comments »
|