[ps]
Function Change-ScheduledTask {
Param($XMLIn)
$ShortDate = [regex]"(?<shortdate>[0-9]{4}[/.-](?:1[0-2]|0[1-9])[/.-](?:3[01]|[12][0-9]|0[1-9])T(?:2[0-3]|[01][0-9])[:.][0-5][0-9][:.][0-5][0-9])(?<digits>\.\d*)"

foreach ($line in $XMLIn){
If ($line -match "$ShortDate") {
$line = [regex]::Replace($line, $ShortDate, $($Matches["Shortdate"]))
}
$line
#$Script:outfile += $line + "`n"
}
}

Function Remove-ScheduledTask {

}

Function New-ScheduledTask {

}

$schtask = Schtasks.exe /query /s "localhost" /V /FO CSV | ConvertFrom-Csv

if ($schtask) {

Foreach ($sch in $schtask) {

if ($Sch."Run As User" -like "$($domainname)*") {
$sch
}
}

$outfile = ""
Change-scheduledtask $(Get-Content c:\temp\TestTask.xml)
[/ps]

I thought I had posted this script more than a year ago, but apparently I have not. It is quite similar to the script, I posted recently sending SMS messages through unwire, this just allows bibob customers to send messages through bibobs online gateway.

One thing to notice is that you are required to convert your password in an MD5 hash, before you send it. Here is a function to convert your plain passwords into an MD5 hash (Adapted from a script by Dennis Damen)

[ps]
Function New-MD5Hash {
param(
[Parameter(Position=0, Mandatory=$true)]
[System.String]
$inputString
)
$cryptoServiceProvider = [System.Security.Cryptography.MD5CryptoServiceProvider];
$hashAlgorithm = new-object $cryptoServiceProvider
$hashByteArray = $hashAlgorithm.ComputeHash($([Char[]]$inputString));
foreach ($byte in $hashByteArray) { if ($byte -lt 16) {$result += “0{0:X}” -f $byte } else { $result += “{0:X}” -f $byte }}
write-host $result
}
[/ps]
Example of usage:

Here is the bibob send sms script.
[ps]
<#
.SYNOPSIS
Send SMS through Bibob (Danish mobile phone company)

.DESCRIPTION
Send SMS Text messages through bibob SMS Gateway services

.PARAMETER Content
The text that the message should contain (Strings above 160 Chars will be split) [Mandatory]

.PARAMETER Recipient
The recipient phonenumber (8 digits) [Mandatory]

.EXAMPLE
PS C:\>Send-SMS -Content "Hello handsome" -Recipient 42424242

.INPUTS
System.String,System.Int32

.OUTPUTS
Sends SMS text message

.NOTES

.LINK
about_functions_advanced

.LINK
about_comment_based_help

#>

function Send-SMS {
[CmdletBinding()]
param(
[Parameter(Position=0, Mandatory=$true)]
[System.String]
$Content= "Hello Handsome",
[Parameter(Position=1)]
[System.Int32]
$Recipient=42424242
)
begin {
If ($Recipient -notmatch ‘^(\d{8})$’ )
{Write-Host "Error in Phone Number"
break
}
$Username = "42424242" # The same as your login to bibob.dk (You cannot spoof you sender number, so it is used as sender as well)
$MD5Password = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

}

process {
$objHTTP = New-Object -ComObject MSXML2.XMLHTTP
$SoapServer = "https://www.bibob.dk/SmsSender.asmx"

$XMLString = @"
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<SendMessage xmlns="http://www.bibob.dk/">
<cellphone>$Username</cellphone>
<password>$MD5Password</password>
<smsTo>
<string>$recipient</string>
</smsTo>
<smscontents>$content</smscontents>
<fromNumber>$username</fromNumber>
</SendMessage>
</soap:Body>
</soap:Envelope>
"@

$objHTTP.open(‘POST’, $SoapServer, $False)

$objHTTP.setRequestHeader(‘Man’, ‘POST’ + ‘ ‘ + $SoapServer + ‘ HTTP/1.1’)
$objHTTP.setRequestHeader(‘Host’, ‘www.bibob.dk’)
$objHTTP.setRequestHeader(‘Content-Type’, ‘text/xml; charset=utf-8’)
$objHTTP.setRequestHeader(‘Content-Length’, $($XMLString.Length))
$objHTTP.setRequestHeader(‘SOAPAction’, ‘http://www.bibob.dk/SendMessage’)

$objHTTP.send($XMLString)
}
}

[/ps]

At work we have bought an SMS gateway service through the danish company Unwire, so after we the deal was signed we got the information on how to send email through their services, so in order to test I wrote a quick Powershell script to test it.

Of course all company/Unwire specific data has been removed, since it relates to our company account.

[ps]
<#
.SYNOPSIS
Send SMS through Unwire GW Server

.DESCRIPTION
Send SMS Text messages through Unwire.com SMS Gateway services

.PARAMETER Content
The text that the message should contain (Strings above 160 Chars will be split) [Mandatory]

.PARAMETER Recipient
The recipient phonenumber (Either 8 or 10 digits) [Mandatory]

.PARAMETER Sender
The name/number that shows up on the recipients phone (Maximum 11 digits/chars)

.EXAMPLE
PS C:\>Send-SMS -Content "Hello handsome" -Recipient 42424242

.EXAMPLE
PS C:\> Send-SMS -Content "Hello handsome" -Recipient 42424242 -Sender "IT Helpdesk"

.INPUTS
System.String,System.Int64,System.String

.OUTPUTS
Sends SMS text message

.NOTES

.LINK
about_functions_advanced

.LINK
about_comment_based_help

#>

function Send-SMS {
[CmdletBinding()]
param(
[Parameter(Position=0, Mandatory=$true)]
[System.String]
$Content= "Hello There",
[Parameter(Position=1, Mandatory=$true)]
[System.Int64]
$Recipient=42424242,
[Parameter(Position=2, Mandatory=$false)]
[System.String]
$Sender="IT-Drift"
)
begin {

If ( $Recipient -notmatch ‘^(\d{8}|\d{10})$’ )
{Write-Host "Error in Phone Number"
break
}
If ($Sender.Length -gt 11)
{Write-Host "Sender name too long"
break
}
[Void] [System.Reflection.Assembly]::LoadWithPartialName("System.Web")
$objHTTP = New-Object -ComObject MSXML2.XMLHTTP

$User = "xxxxx"
$Password = "xxxxxx"
$AppNr = "xxxxx"
$smsc = "xxxxxx"
$Price = "xxxxx"

$Message = [System.Web.HttpUtility]::UrlEncode([System.Text.Encoding]::GetEncoding("ISO-8859-1").GetBytes("$Content"))
$SenderName = [System.Web.HttpUtility]::UrlEncode([System.Text.Encoding]::GetEncoding("ISO-8859-1").GetBytes("$Sender"))

[String] $unwireURL = "xxxxxxxx"
[String] $UnwireServer = "xxxxxxxx"
[String] $UnwireHost ="xxxxxxxxxx"

}

process {

$PushString = @"
user=$user&password=$password&to=$Recipient&text=$Message&smsc=$smsc&price=$Price&type=text&appnr=$AppNr&from=$SenderName
"@

$objHTTP.open(‘POST’, $UnwireServer, $False)

$objHTTP.setRequestHeader(‘POST’, $unwireURL + ‘ HTTP/1.1’)
$objHTTP.setRequestHeader(‘Host’, $UnwireHost)
$objHTTP.setRequestHeader(‘User-Agent:’, ‘CP agent’)
$objHTTP.setRequestHeader(‘Content-Type’, ‘application/x-www-form-urlencoded’)
$objHTTP.setRequestHeader(‘Content-Length’, $($XMLString.Length))
$objHTTP.send("$PushString")
$objHTTP.status
$objHTTP.statusText

}
}

[/ps]

As stated in a previous post, I have been working on a script to change passwords on scheduled tasks.

I had hoped to find some time to clean up the code, and write some error handling, but a few people asked for the code, so here it is in alpha alpha edition.

ToDo:
Tidy up code
Add some error handling
Add code to update-scheduledtasks created in 2k8(R2) GUI

Description:

The script is assuming you have  Powershell V2, and Quest AD cmdlets.

Here is a picture of the GUI when you run the script.

The Quest AD cmdlets are used to connect to AD, to get a list of computers to check for scheduled tasks

image

Search Account:  Search for scheduled tasks running under the supplied account, you can use wildcards. If you supply DomainName\* (Default value), then it will search for scheduled tasks running under a domain account, you Search Root: The DN path from where you want to build your computer list

OSName: Default value is *server* it’s the value that is the same for all windows servers… So with this value set, it will only return servers from AD

Filter MachineName: Default value is “*” which returns all servers,  example “Exchan*” would return all servers whose names start with “Exchan”

PassWord: Enter the new password for the scheduled tasks.

The Run scheduled tasks with current credentials is not working, since it is not supported against 2003 servers and below, so I remove the functionality.

 

Here is an example of a result.

image

You can click on one and edit the username if you like, otherwise you can just mark the ones you want to change password on, holding down ctrl and click each one.

image

Press Change PWD

 

As stated above, there is no particular error handling,and it takes a while to run depending on how many servers you have.  (I hope to find some time to update the script, to a more “production” ready state soon)

[ps]
########################################################################
# Code Generated By: SAPIEN Technologies, Inc., PrimalForms 2009 v1.1.11.0
# Generated On: 29-01-2011 23:05
# Generated By: CTN
########################################################################

#———————————————-
#region Application Functions
#———————————————-

function OnApplicationLoad {
#Note: This function runs before the form is created
#Note: To get the script directory in the Packager use: Split-Path $hostinvocation.MyCommand.path
#Note: To get the console output in the Packager (Windows Mode) use: $ConsoleOutput (Type: System.Collections.ArrayList)
#Important: Form controls cannot be accessed in this function
#TODO: Add snapins and custom code to validate the application load

return $true #return true for success or false for failure
}

function OnApplicationExit {
#Note: This function runs after the form is closed
#TODO: Add custom code to clean up and unload snapins when the application exits

$script:ExitCode = 0 #Set the exit code for the Packager
}

#endregion

#———————————————-
# Generated Form Function
#———————————————-
function GenerateForm {

#———————————————-
#region Import Assemblies
#———————————————-
[void][reflection.assembly]::Load("System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")
[void][reflection.assembly]::Load("System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")
[void][reflection.assembly]::Load("mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")
[void][reflection.assembly]::Load("System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")
#endregion

#———————————————-
#region Generated Form Objects
#———————————————-
[System.Windows.Forms.Application]::EnableVisualStyles()
$form1 = New-Object System.Windows.Forms.Form
$progressbar1 = New-Object System.Windows.Forms.ProgressBar
$label9 = New-Object System.Windows.Forms.Label
$SchTaskPWD = New-Object System.Windows.Forms.TextBox
$label8 = New-Object System.Windows.Forms.Label
$button4 = New-Object System.Windows.Forms.Button
$label7 = New-Object System.Windows.Forms.Label
$SchTaskUsr = New-Object System.Windows.Forms.TextBox
$button3 = New-Object System.Windows.Forms.Button
$label6 = New-Object System.Windows.Forms.Label
$srchMachineNameTXT = New-Object System.Windows.Forms.TextBox
$label5 = New-Object System.Windows.Forms.Label
$SrchOSNameTXT = New-Object System.Windows.Forms.TextBox
$label4 = New-Object System.Windows.Forms.Label
$SrchRootTXT = New-Object System.Windows.Forms.TextBox
$label2 = New-Object System.Windows.Forms.Label
$SrchAccountTxt = New-Object System.Windows.Forms.TextBox
$label1 = New-Object System.Windows.Forms.Label
$textbox1 = New-Object System.Windows.Forms.TextBox
$button2 = New-Object System.Windows.Forms.Button
$datagridview1 = New-Object System.Windows.Forms.DataGridView
$button1 = New-Object System.Windows.Forms.Button
$tooltip1 = New-Object System.Windows.Forms.ToolTip
$InitialFormWindowState = New-Object System.Windows.Forms.FormWindowState
#endregion Generated Form Objects

#———————————————-
# User Generated Script
#———————————————-

$FormEvent_Load={
#TODO: Initialize Form Controls here
$erroractionpreference = "SilentlyContinue"
Add-PSSnapin *quest*
$domainname = (Get-QADRootDSE).Domain.ToString()
$SrchAccountTxt.Text = "$domainname" + "*"

$SrchRootTXT.Text = (Get-QADRootDSE).DefaultNamingContextDN.ToString()
$SrchOSNameTXT.Text = "*Server*"
$srchMachineNameTXT.Text = "*"
$SchTaskUsr.text = $env:userdomain + "\" + $env:username
$array= new-object System.Collections.ArrayList
$Tasks = @()

}

$handler_button1_Click={
#TODO: Place custom script here

$Servers = get-QADComputer -SearchRoot $SrchRootTXT.Text -OSName $SrchOSNameTXT.Text -Name $srchMachineNameTXT.Text # | where {$_.name -like $($srchMachineNameTXT.Text)}
$progressbar1.Maximum = $Servers.Length

Foreach ($server in $Servers ) {
$serverFQDN = $server.dnsname

$progressbar1.value += 1

$schtask = Schtasks.exe /query /s $serverFQDN /V /FO CSV | ConvertFrom-Csv
#Write-Host "****************** $($server.name) Scheduled Tasks ******************"
if ($schtask) {

Foreach ($sch in $schtask) {
#$Sch."Run As User"
if ($Sch."Run As User" -like "$($domainname)*") {
#Write-Host $serverFQDN ($Sch.TaskName).replace("\","") $Sch.’Run As User’

$SchedTasks = New-Object System.Object
$SchedTasks | Add-Member -type NoteProperty -name ServerName -value $serverFQDN
$SchedTasks | Add-Member -type NoteProperty -name "Scheduled Task" -value ($Sch.TaskName).replace("\","")
$SchedTasks | Add-Member -type NoteProperty -name NextRun -value $Sch.’Next Run Time’
$SchedTasks | Add-Member -type NoteProperty -name Username -value $sch.’Run As User’
$Tasks += $SchedTasks
$SchedTasks

}

}

}

}

$array.AddRange($tasks)
$datagridview1.DataSource = $array
$datagridview1.AutoResizeColumns()

}
$handler_button2_Click={
foreach ($row in $datagridview1.SelectedRows) {
#[System.Windows.Forms.MessageBox]::Show($row.Cells[0].Value)
$StrCmd = schtasks.exe /change /s "$($row.Cells[0].Value)" /RU "$($row.Cells[3].Value)" /RP "$($textbox1.text)" /TN ""$($row.Cells[1].Value)""
#Invoke-Expression $strCmd #|Out-File c:\temp\userlog.txt
Write-Host $LastExitCode
Write-Host $StrCmd
Clear-Variable StrCmd -ErrorAction SilentlyContinue

}

}

$handler_label1_Click={
#TODO: Place custom script here

}

$handler_label2_Click={
#TODO: Place custom script here

}

$handler_button3_Click={
#TODO: Place custom script here
if ($textbox1.PasswordChar -notlike "0" ) {$textbox1.PasswordChar = $null ;$textBox1.Text = $textBox1.Text}
else {$textbox1.PasswordChar = "*";$textBox1.Text = $textBox1.Text}

}

$handler_label7_Click={
#TODO: Place custom script here

}

$handler_tooltip1_Popup=[System.Windows.Forms.PopupEventHandler]{
#Event Argument: $_ = [System.Windows.Forms.PopupEventArgs]
#TODO: Place custom script here

}

$ShowHelp={
#TODO: Place custom script here
#display popup help
Switch ($this.name) {
"SrchAccountTxt" {$tip="Examples:
n Domainname\AdminGuy Search for a specific account.n DomainName\admin* For all usersnames in the domain that starts with admin n Domainname\* All Domain users"}
"SrchRootTXT" {$tip="Enter DN for the OU, that you want to be the base of the AD search"}
"SrchOSNameTXT" {$tip="Enter a complete or partial machinename that you want to search for. Wildcards are permitted n Exhange* will find all servers whos name starts with 'Exchange'"}
default {$tip="No help found for $($this.name)"}
}
$tooltip1.SetToolTip($this,$tip)

}

$handler_button4_Click={
#TODO: Place custom script here
if ($SchTaskPWD.PasswordChar -notlike "
0" ) {$SchTaskPWD.PasswordChar = $null ;$SchTaskPWD.Text = $SchTaskPWD.Text}
else {$SchTaskPWD.PasswordChar = "*";$SchTaskPWD.Text = $SchTaskPWD.Text}
}

#———————————————-
# Generated Events
#———————————————-

$Form_StateCorrection_Load=
{
#Correct the initial state of the form to prevent the .Net maximized form issue
$form1.WindowState = $InitialFormWindowState
}

#———————————————-
#region Generated Form Code
#———————————————-
#
# form1
#
$form1.Controls.Add($progressbar1)
$form1.Controls.Add($label9)
$form1.Controls.Add($SchTaskPWD)
$form1.Controls.Add($label8)
$form1.Controls.Add($button4)
$form1.Controls.Add($label7)
$form1.Controls.Add($SchTaskUsr)
$form1.Controls.Add($button3)
$form1.Controls.Add($label6)
$form1.Controls.Add($srchMachineNameTXT)
$form1.Controls.Add($label5)
$form1.Controls.Add($SrchOSNameTXT)
$form1.Controls.Add($label4)
$form1.Controls.Add($SrchRootTXT)
$form1.Controls.Add($label2)
$form1.Controls.Add($SrchAccountTxt)
$form1.Controls.Add($label1)
$form1.Controls.Add($textbox1)
$form1.Controls.Add($button2)
$form1.Controls.Add($datagridview1)
$form1.Controls.Add($button1)
$form1.ClientSize = New-Object System.Drawing.Size(897,567)
$form1.DataBindings.DefaultDataSourceUpdateMode = [System.Windows.Forms.DataSourceUpdateMode]::OnValidation
$form1.Name = "form1"
$form1.Text = "Change Scheduled Task Passwords v. 0.0.1"
$form1.add_Load($FormEvent_Load)
#
# progressbar1
#
$progressbar1.DataBindings.DefaultDataSourceUpdateMode = [System.Windows.Forms.DataSourceUpdateMode]::OnValidation
$progressbar1.Location = New-Object System.Drawing.Point(34,528)
$progressbar1.Name = "progressbar1"
$progressbar1.Size = New-Object System.Drawing.Size(829,23)
$progressbar1.TabIndex = 23
#
# label9
#
$label9.DataBindings.DefaultDataSourceUpdateMode = [System.Windows.Forms.DataSourceUpdateMode]::OnValidation
$label9.Location = New-Object System.Drawing.Point(505,98)
$label9.Name = "label9"
$label9.Size = New-Object System.Drawing.Size(108,23)
$label9.TabIndex = 22
$label9.Text = "Schtasks Password"
#
# SchTaskPWD
#
$SchTaskPWD.DataBindings.DefaultDataSourceUpdateMode = [System.Windows.Forms.DataSourceUpdateMode]::OnValidation
$SchTaskPWD.Location = New-Object System.Drawing.Point(619,95)
$SchTaskPWD.Name = "SchTaskPWD"
$SchTaskPWD.PasswordChar = ‘*’
$SchTaskPWD.Size = New-Object System.Drawing.Size(161,20)
$SchTaskPWD.TabIndex = 21
#
# label8
#
$label8.DataBindings.DefaultDataSourceUpdateMode = [System.Windows.Forms.DataSourceUpdateMode]::OnValidation
$label8.Location = New-Object System.Drawing.Point(505,64)
$label8.Name = "label8"
$label8.Size = New-Object System.Drawing.Size(108,23)
$label8.TabIndex = 20
$label8.Text = "Schtasks username"
#
# button4
#
$button4.DataBindings.DefaultDataSourceUpdateMode = [System.Windows.Forms.DataSourceUpdateMode]::OnValidation
$button4.Location = New-Object System.Drawing.Point(786,93)
$button4.Name = "button4"
$button4.Size = New-Object System.Drawing.Size(81,23)
$button4.TabIndex = 19
$button4.Text = "Toggle PWD"
$button4.UseVisualStyleBackColor = $True
$button4.add_Click($handler_button4_Click)
#
# label7
#
$label7.DataBindings.DefaultDataSourceUpdateMode = [System.Windows.Forms.DataSourceUpdateMode]::OnValidation
$label7.Location = New-Object System.Drawing.Point(506,36)
$label7.Name = "label7"
$label7.Size = New-Object System.Drawing.Size(218,23)
$label7.TabIndex = 18
$label7.Text = "Run Schtasks with following credentials"
#
# SchTaskUsr
#
$SchTaskUsr.DataBindings.DefaultDataSourceUpdateMode = [System.Windows.Forms.DataSourceUpdateMode]::OnValidation
$SchTaskUsr.Location = New-Object System.Drawing.Point(619,63)
$SchTaskUsr.Name = "SchTaskUsr"
$SchTaskUsr.Size = New-Object System.Drawing.Size(161,20)
$SchTaskUsr.TabIndex = 17
#
# button3
#
$button3.DataBindings.DefaultDataSourceUpdateMode = [System.Windows.Forms.DataSourceUpdateMode]::OnValidation
$button3.Location = New-Object System.Drawing.Point(439,123)
$button3.Name = "button3"
$button3.Size = New-Object System.Drawing.Size(79,23)
$button3.TabIndex = 16
$button3.Text = "Toggle PWD"
$button3.UseVisualStyleBackColor = $True
$button3.add_Click($handler_button3_Click)
#
# label6
#
$label6.DataBindings.DefaultDataSourceUpdateMode = [System.Windows.Forms.DataSourceUpdateMode]::OnValidation
$label6.Location = New-Object System.Drawing.Point(169,92)
$label6.Name = "label6"
$label6.Size = New-Object System.Drawing.Size(106,23)
$label6.TabIndex = 15
$label6.Text = "Filter MachineName"
#
# srchMachineNameTXT
#
$srchMachineNameTXT.DataBindings.DefaultDataSourceUpdateMode = [System.Windows.Forms.DataSourceUpdateMode]::OnValidation
$srchMachineNameTXT.Location = New-Object System.Drawing.Point(284,92)
$srchMachineNameTXT.Name = "srchMachineNameTXT"
$srchMachineNameTXT.Size = New-Object System.Drawing.Size(148,20)
$srchMachineNameTXT.TabIndex = 14
#
# label5
#
$label5.DataBindings.DefaultDataSourceUpdateMode = [System.Windows.Forms.DataSourceUpdateMode]::OnValidation
$label5.Location = New-Object System.Drawing.Point(172,66)
$label5.Name = "label5"
$label5.Size = New-Object System.Drawing.Size(101,23)
$label5.TabIndex = 13
$label5.Text = "OS Name"
#
# SrchOSNameTXT
#
$SrchOSNameTXT.DataBindings.DefaultDataSourceUpdateMode = [System.Windows.Forms.DataSourceUpdateMode]::OnValidation
$SrchOSNameTXT.Location = New-Object System.Drawing.Point(284,66)
$SrchOSNameTXT.Name = "SrchOSNameTXT"
$SrchOSNameTXT.Size = New-Object System.Drawing.Size(148,20)
$SrchOSNameTXT.TabIndex = 12
#
# label4
#
$label4.DataBindings.DefaultDataSourceUpdateMode = [System.Windows.Forms.DataSourceUpdateMode]::OnValidation
$label4.Location = New-Object System.Drawing.Point(172,41)
$label4.Name = "label4"
$label4.Size = New-Object System.Drawing.Size(100,23)
$label4.TabIndex = 11
$label4.Text = "SearchRoot "
#
# SrchRootTXT
#
$SrchRootTXT.DataBindings.DefaultDataSourceUpdateMode = [System.Windows.Forms.DataSourceUpdateMode]::OnValidation
$SrchRootTXT.Location = New-Object System.Drawing.Point(284,39)
$SrchRootTXT.Name = "SrchRootTXT"
$SrchRootTXT.Size = New-Object System.Drawing.Size(148,20)
$SrchRootTXT.TabIndex = 10
#
# label2
#
$label2.DataBindings.DefaultDataSourceUpdateMode = [System.Windows.Forms.DataSourceUpdateMode]::OnValidation
$label2.Location = New-Object System.Drawing.Point(172,14)
$label2.Name = "label2"
$label2.Size = New-Object System.Drawing.Size(100,23)
$label2.TabIndex = 8
$label2.Text = "Search Accounts"
#
# SrchAccountTxt
#
$SrchAccountTxt.DataBindings.DefaultDataSourceUpdateMode = [System.Windows.Forms.DataSourceUpdateMode]::OnValidation
$SrchAccountTxt.Location = New-Object System.Drawing.Point(284,12)
$SrchAccountTxt.Name = "SrchAccountTxt"
$SrchAccountTxt.Size = New-Object System.Drawing.Size(148,20)
$SrchAccountTxt.TabIndex = 7
#
# label1
#
$label1.DataBindings.DefaultDataSourceUpdateMode = [System.Windows.Forms.DataSourceUpdateMode]::OnValidation
$label1.Location = New-Object System.Drawing.Point(172,124)
$label1.Name = "label1"
$label1.Size = New-Object System.Drawing.Size(100,23)
$label1.TabIndex = 6
$label1.Text = "New Password"
#
# textbox1
#
$textbox1.DataBindings.DefaultDataSourceUpdateMode = [System.Windows.Forms.DataSourceUpdateMode]::OnValidation
$textbox1.Location = New-Object System.Drawing.Point(284,124)
$textbox1.Name = "textbox1"
$textbox1.PasswordChar = ‘*’
$textbox1.Size = New-Object System.Drawing.Size(148,20)
$textbox1.TabIndex = 5
#
# button2
#
$button2.DataBindings.DefaultDataSourceUpdateMode = [System.Windows.Forms.DataSourceUpdateMode]::OnValidation
$button2.Location = New-Object System.Drawing.Point(71,42)
$button2.Name = "button2"
$button2.Size = New-Object System.Drawing.Size(89,23)
$button2.TabIndex = 4
$button2.Text = "Change PWD"
$button2.UseVisualStyleBackColor = $True
$button2.add_Click($handler_button2_Click)
#
# datagridview1
#
$System_Windows_Forms_DataGridViewCellStyle_16 = New-Object System.Windows.Forms.DataGridViewCellStyle
$System_Windows_Forms_DataGridViewCellStyle_16.Alignment = [System.Windows.Forms.DataGridViewContentAlignment]::MiddleLeft
$System_Windows_Forms_DataGridViewCellStyle_16.BackColor = [System.Drawing.Color]::FromArgb(255,240,240,240)
$System_Windows_Forms_DataGridViewCellStyle_16.Font = New-Object System.Drawing.Font("Microsoft Sans Serif",8.25,0,3,1)
$System_Windows_Forms_DataGridViewCellStyle_16.ForeColor = [System.Drawing.Color]::FromArgb(255,0,0,0)
$System_Windows_Forms_DataGridViewCellStyle_16.SelectionBackColor = [System.Drawing.Color]::FromArgb(255,51,153,255)
$System_Windows_Forms_DataGridViewCellStyle_16.SelectionForeColor = [System.Drawing.Color]::FromArgb(255,255,255,255)
$System_Windows_Forms_DataGridViewCellStyle_16.WrapMode = [System.Windows.Forms.DataGridViewTriState]::True
$datagridview1.ColumnHeadersDefaultCellStyle = $System_Windows_Forms_DataGridViewCellStyle_16
$datagridview1.DataBindings.DefaultDataSourceUpdateMode = [System.Windows.Forms.DataSourceUpdateMode]::OnValidation
$System_Windows_Forms_DataGridViewCellStyle_17 = New-Object System.Windows.Forms.DataGridViewCellStyle
$System_Windows_Forms_DataGridViewCellStyle_17.Alignment = [System.Windows.Forms.DataGridViewContentAlignment]::MiddleLeft
$System_Windows_Forms_DataGridViewCellStyle_17.BackColor = [System.Drawing.Color]::FromArgb(255,255,255,255)
$System_Windows_Forms_DataGridViewCellStyle_17.Font = New-Object System.Drawing.Font("Microsoft Sans Serif",8.25,0,3,1)
$System_Windows_Forms_DataGridViewCellStyle_17.ForeColor = [System.Drawing.Color]::FromArgb(255,0,0,0)
$System_Windows_Forms_DataGridViewCellStyle_17.SelectionBackColor = [System.Drawing.Color]::FromArgb(255,51,153,255)
$System_Windows_Forms_DataGridViewCellStyle_17.SelectionForeColor = [System.Drawing.Color]::FromArgb(255,255,255,255)
$System_Windows_Forms_DataGridViewCellStyle_17.WrapMode = [System.Windows.Forms.DataGridViewTriState]::False
$datagridview1.DefaultCellStyle = $System_Windows_Forms_DataGridViewCellStyle_17
$datagridview1.Location = New-Object System.Drawing.Point(34,165)
$datagridview1.Name = "datagridview1"
$System_Windows_Forms_DataGridViewCellStyle_18 = New-Object System.Windows.Forms.DataGridViewCellStyle
$System_Windows_Forms_DataGridViewCellStyle_18.Alignment = [System.Windows.Forms.DataGridViewContentAlignment]::MiddleLeft
$System_Windows_Forms_DataGridViewCellStyle_18.BackColor = [System.Drawing.Color]::FromArgb(255,240,240,240)
$System_Windows_Forms_DataGridViewCellStyle_18.Font = New-Object System.Drawing.Font("Microsoft Sans Serif",8.25,0,3,1)
$System_Windows_Forms_DataGridViewCellStyle_18.ForeColor = [System.Drawing.Color]::FromArgb(255,0,0,0)
$System_Windows_Forms_DataGridViewCellStyle_18.SelectionBackColor = [System.Drawing.Color]::FromArgb(255,51,153,255)
$System_Windows_Forms_DataGridViewCellStyle_18.SelectionForeColor = [System.Drawing.Color]::FromArgb(255,255,255,255)
$System_Windows_Forms_DataGridViewCellStyle_18.WrapMode = [System.Windows.Forms.DataGridViewTriState]::True
$datagridview1.RowHeadersDefaultCellStyle = $System_Windows_Forms_DataGridViewCellStyle_18
$datagridview1.Size = New-Object System.Drawing.Size(829,356)
$datagridview1.TabIndex = 2
#
# button1
#
$button1.DataBindings.DefaultDataSourceUpdateMode = [System.Windows.Forms.DataSourceUpdateMode]::OnValidation
$button1.Location = New-Object System.Drawing.Point(71,12)
$button1.Name = "button1"
$button1.Size = New-Object System.Drawing.Size(89,23)
$button1.TabIndex = 1
$button1.Text = "Get Servers"
$button1.UseVisualStyleBackColor = $True
$button1.add_Click($handler_button1_Click)
#
# tooltip1
#
#endregion Generated Form Code

#———————————————-

#Save the initial state of the form
$InitialFormWindowState = $form1.WindowState
#Init the OnLoad event to correct the initial state of the form
$form1.add_Load($Form_StateCorrection_Load)
#Show the Form
return $form1.ShowDialog()

} #End Function

#Call OnApplicationLoad to initialize
if(OnApplicationLoad -eq $true)
{
#Create the form
GenerateForm | Out-Null
#Perform cleanup
OnApplicationExit
}

[/ps]

I have been working on a script for changing passwords on scheduled tasks in our domain, in that process I have run into a few kinks with scheduled tasks in server 2008(R2).

I tried different ways of manipulating the scheduled tasks, but ended up using schtasks.exe from the commandline, so I wrote some Powershell code to wrap around the schtasks.exe to connect to multiple machines.. But I kept getting errors on some servers, so I decided to dig into the issue. After a lot of testing I figured out, that scheduled tasks created from the Task Scheduler GUI, could not be changed with schtasks.exe from the commandline.
So I had to dig a little deeper, and figure out what was happening, and how to work around it. The problem can be seen if you export the scheduled task into XML.

The first picture here is a scheduled task created from the command line.

image

 

This one is from a scheduled task created from the Task scheduler GUI (Notice the date format contains 6 or 7 decimals)

image

Apparently schtasks.exe does not like the date/time format in a task created in the GUI, but if you export the scheduled task to XML change remove the “decimals” and reimport the scheduled task, you can now manage it from the command line with schtasks.exe (How was this not caught in testing ??)

 

So to avoid going out to all 2008(R2) machines and do it manually, I wrote a small Powershell script to dump a scheduled task to XML and change the date/time and reimport it.

[ps]
Function Update-ScheduledTask {
param(
[Parameter(Position=1,Mandatory=$true)]
$SchedName ,
[Parameter(Position=2,Mandatory=$false)]
$ComputerName = "Localhost"
)

$ShortDate = [regex]"(?<ShortDate>[0-9]{4}[/.-](?:1[0-2]|0[1-9])[/.-](?:3[01]|[12][0-9]|0[1-9])T(?:2[0-3]|[01][0-9])[:.][0-5][0-9][:.][0-5][0-9])(?<Digits>\.\d*)"
Remove-Item "$Env:TEMP\$($SchedName).xml"
$XMLIn = schtasks /query /s $Computername /tn $SchedName /xml

foreach ($line in $XMLIn){
If ($line -match "$ShortDate") {
$line = [regex]::Replace($line, $ShortDate, $($Matches["Shortdate"]))
}
If ($line.length -gt 1) {
$line | Out-File -Append -FilePath "$Env:TEMP\$($SchedName).xml"}
}
schtasks /Create /tn $SchedName /XML "$Env:TEMP\$($SchedName).xml" /f
}
[/ps]

Currently I am using a free plan with Dynamsoft for source control with PowerGui, you get 25 MB for free, which will last me a long time at my current speed of writing PowerShell scripts Smile

 

Here is a quick guide to setting up an account.

Go to http://www.dynamsoft.com/products/SAWHosted_FreePlan.aspx and sign up for a free account.

Just fill in your information, and wait for the confirmation email. When the mail arrives you have to click the link to activate your account, on that page there is a link to download the Dynamsoft Source AnyWhere Hosted Server Manager:

http://www.dynamsoft.com/downloads/sawhosted_download.aspx

I my case running on Windows I choose the Windows version Smile

 

image

Download and install the client.

It prompts you if you want to make it your default Source Control provider, which in my case I choose yes, since this is my private machine.

SNAGHTML562d4a8

 

You then fire you PowerGui Pro, and go to Tools –> Options

image

Under options you choose  “Version Control” and in Current provider you choose Dynamsoft SourceAnyware Hosted then click advanced.

SNAGHTML5665897

In the General Pane I choose a new path for saving Temp files.

SNAGHTML5687a49

Under External Programs I put in the path for PowerGUI Pro, in my Case: C:\Program Files (x86)\Quest Software\PowerGUI Pro\ScriptEditor.exe

(I use WinMerge for file comparison, so that is what you see in the image below WinMerge can be downloaded here: http://winmerge.org/ )

SNAGHTML56e1b87

If you do not want to view the DynamSoft Login log every time you login.

SNAGHTML573002b

First time you try to save a script in PowerGui Pro, it will prompt you if you want to check it into your Source Control system.

A login box will appear, fill in the fields with the data from your welcome mail, and choose if you want to be prompted for a password, everything you check something in/out.

SNAGHTML57787e1

You get prompted to choose a Repository, in this case just choose “default”

SNAGHTML57a1a7e

In my case I have a few existing Projects, but I choose to create a new Project called “Test Project”

SNAGHTML57b4852SNAGHTML589f256

You then get prompted to put in a comment

SNAGHTML58aa56f

In PowerGui you now have an extra menu called “Version Control” and you can see a mark on each tab if your script is in the source control and if it is checked in or out.

image (The arrow on the tab is blue if the script is checked in, and red if it is checked out)

I have checked the script out (If you try to edit the script, it will automatically prompt you to check out the script), I made some changes, and try to check it in again.

image

image

You get prompted to enter a comment, after that is entered the file is checked back in.. (You can leave to comment field blank if you do not want to write something)

SNAGHTML590ed57

You can manage your software projects from the DynamSoft application.

Go to start menu and choose Dynamsoft SourceAnywhere Hosted

Sign in with your username/Password

 

First thing do to is go to Tools –> Options –> External Programs

 

SNAGHTML5bbb316

As you can see my test project is now in my list of projects.

image

One thing I have noticed is that Dynamsoft default checks files in as Binary files, which means it does not allow you to do a text comparison of the two, in order to change that  right click the .ps1 file and choose properties.

image

Then you change the type from Binary to Mergeable, you can now compare your different versions of checked in files.

SNAGHTML5a0136e

 

Right click the file you want to “compare”, then choose  “Show History”, you get a lot of options to choose from, in most cases the default options are enough. Click OK

 

In this example I have made an initial version of the script then made some changes and checked it back in.

I highlight both files and choose Diff

SNAGHTML5a42317

This will bring up WinMerge and show the differences (This is a horrible example of file comparison I Know)

 

image

 

This guide should be enough to get you started…  Smile

 

Edit:

 

Here is a better example of script a script comparison.

SNAGHTML5a9248d

image

As those of you who a following my blog know, I have been fortunate enough to become a Microsoft MVP in Powershell, and because of that I have been allowed on the Powershell MVP mailing list, an let me tell, there are some amazing people out there, doing some amazing stuff. Some of the stuff floating around I don’t even have a clue what does 😉

But people are also very helpful, and quick to jump in if someone has a problem or even if someone just posts a piece of code they find useful, people tend to jump in with suggestions/additions to make the script even better… Here is an example of a script that went from 14 lines of code to 334 in half a day.

Tome Tanasovski wrote a quick and dirty script that connects to the MVP site, and cycles through all the categories, and returns how many MVP’s there are in each. He ended off by saying, that the results where not entirely correct, since some MVP has hidden the profile from the public list, meaning you would have to be logged in to see them.

[ps]
$web = New-Object System.Net.WebClient
$regex = [regex]'(?m)<a href=\"(\/communities\/mvp.aspx\?product=1&competency=(\S+))\"’
$objects = @()
$return = $web.DownloadString(‘http://mvp.support.microsoft.com/communities/mvp.aspx’)
$regex.Matches($return) |% {
$obj = New-Object psobject
$obj |Add-Member NoteProperty -Name ‘Group’ -Value ($_.Groups[2].Value -replace ‘\+’,’ ‘)
if ($web.DownloadString((‘http://mvp.support.microsoft.com’ + $_.Groups[1].Value)) -match ‘Results 1 \- \d+ of (\d+)’) {
$obj |Add-Member NoteProperty -Name ‘Count’ -Value ([int]$matches[1])
}
$objects += $obj
}

$objects |sort Count -Descending |ogv
[/ps]

So I decided to write some simple IE automation that would log you you on to the Microsoft passport site, and count all the MVP’s, including those who do not have a public profile.

[ps]
$URI = "https://login.live.com/ppsecure/secure.srf?lc=1033&sf=1&id=259214&ru=https://mvp.support.microsoft.com/communities/mvp.aspx&tw=0&fs=0&kv=0&cb=wizid%3df4502d34-3b8f-4a04-b741-289e08aa1782%26lcid%3d1033%26returnurl%3dhttps%253a%252f%252fmvp.support.microsoft.com%252fcommunities%252fmvp.aspx%26brand%3dMicrosoft&cbcxt=&wp=MCMBI&wa=wsignin1.0&wreply=https://mvp.support.microsoft.com/communities/mvp.aspx&wlsu=1"
$ie = new-object -com "InternetExplorer.Application"
$ie.visible = $false
$ie.navigate($URI)
while ($ie.Busy -eq $true) { Start-Sleep -Milliseconds 1000; }
$doc = $ie.Document
$ie.Document.Body.InnerText.ToString()
$user = $doc.getElementByID("Login")
$user.value = "xxxxxxxxxx"
$pass = $doc.getElementByID("Passwd")
$pass.value = "xxxxxxxxxx"
$Logon = $doc.getElementByID("SI")
$Logon.click()
while ($ie.LocationURL -ne "https://mvp.support.microsoft.com/communities/mvp.aspx") { Start-Sleep -Milliseconds 1000; }
while ($ie.Busy -eq $true) { Start-Sleep -Milliseconds 1000; }
#$ie.Document.Body.InnerHTML.ToString()

$regex = [regex]'(?m)<A href=\"(\/communities\/mvp.aspx\?product=1&amp;competency=(\S+))\"’
$objects = @()

$return = $ie.Document.Body.InnerHTML.ToString()

$regex.Matches($return)|% {
$obj = New-Object psobject
$obj |Add-Member NoteProperty -Name ‘Group’ -Value ($_.Groups[2].Value -replace ‘\+’,’ ‘)
$URI = "https://mvp.support.microsoft.com$($_.Groups[1].Value -replace ‘amp;’,”)"
while ($ie.Busy -eq $true) { Start-Sleep -Milliseconds 1000; }
$ie.navigate($URI)
$URI
while ($ie.Busy -eq $true) { Start-Sleep -Milliseconds 1000; }
if (($ie.Document.Body.InnerText.ToString()) -match ‘Results 1 \- \d+ of (\d+)’) {
$MVPs=""; $MVPs = [int]$matches[1];$MVPs; $obj |Add-Member NoteProperty -Name ‘Count’ -Value ($MVPs)

}
$objects += $obj
}
$objects |sort Count -Descending |ogv
[/ps]

Then Michael B Smith jumped in on the thread, because he thought my script needed some error handling, which is completely right, and came up with his own version of the script.
[ps]
Param(
[switch]$gridview,
[string]$username,
[string]$password
)

[System.Reflection.Assembly]::LoadWithPartialName("System.Web") | out-null

$ie = $null

try
{
if ($username)
{
$URI = "https://login.live.com/ppsecure/secure.srf?lc=1033&sf=1&id=259214&ru=https://mvp.support.microsoft.com/communities/mvp.aspx&tw=0&fs=0&kv=0&cb=wizid%3df4502d34-3b8f-4a04-b741-289e08aa1782%26lcid%3d1033%26returnurl%3dhttps%253a%252f%252fmvp.support.microsoft.com%252fcommunities%252fmvp.aspx%26brand%3dMicrosoft&cbcxt=&wp=MCMBI&wa=wsignin1.0&wreply=https://mvp.support.microsoft.com/communities/mvp.aspx&wlsu=1"
$ie = new-object -com "InternetExplorer.Application"
$ie.visible = $false
$ie.navigate($URI)
while ($ie.Busy -eq $true) { Start-Sleep -Milliseconds 1000; }
$doc = $ie.Document
$ie.Document.Body.InnerText.ToString()

$user = $doc.getElementById( "i0116" ) ## login
if( $user )
{
$user.value = $username
}
else
{
"Document element named ‘Login’ not found"
}

$pass = $doc.getElementById( "i0118" ) ## passwd
if( $pass )
{
$pass.value = $password
}
else
{
"Document element named ‘Passwd’ not found"
}

$Logon = $doc.getElementById( "i0011" ) ## SI
if( $Logon )
{
$Logon.click()
}
else
{
"Document element named ‘SI’ not found"
}

$count = 0
while( $ie.LocationURL -ne "https://mvp.support.microsoft.com/communities/mvp.aspx" )
{
if( $ie.LocationURL -eq "https://mvp.support.microsoft.com/mvpinvalidsignin.aspx" )
{
write-error "Invalid username / password combination"
return
}

Start-Sleep -Milliseconds 1000
if( $count -gt 15 )
{
"Location URL is $($ie.LocationURL)"
}
$count++
}

$count = 0
while( $ie.Busy -eq $true )
{
Start-Sleep -Milliseconds 1000
if( $count -gt 15 )
{
"IE still busy after $count seconds"
}
$count++
}

#$ie.Document.Body.InnerHTML.ToString()
}

$regex = [regex]'(?m)<A href=\"(\/communities\/mvp.aspx\?product=1&amp;competency=(\S+))\"’
$objects = @()

$return = $ie.Document.Body.InnerHTML.ToString()
$regex.Matches($return)|% {
$obj = New-Object psobject
$obj |Add-Member NoteProperty -Name ‘Group’ -Value ([System.Web.HttpUtility]::UrlDecode( $_.Groups[2].Value ))

$URI = "https://mvp.support.microsoft.com$($_.Groups[1].Value -replace ‘amp;’,”)"
while ($ie.Busy -eq $true) { Start-Sleep -Milliseconds 1000; }
$ie.navigate($URI)
$URI
while ($ie.Busy -eq $true) { Start-Sleep -Milliseconds 1000; }
if (($ie.Document.Body.InnerText.ToString()) -match ‘Results 1 \- \d+ of (\d+)’) {
$MVPs=""; $MVPs = [int]$matches[1];$MVPs; $obj |Add-Member NoteProperty -Name ‘Count’ -Value ($MVPs)
}
$objects += $obj
}

" "
"Total MVP Competencies $($objects.Count)"
if( $gridview )
{
$objects |sort Count -Descending |ogv
}
else
{
$objects |sort Count -Descending
}

}
finally
{
if( $ie )
{
$ie.Quit()
$ie = $null
}
}

[/ps]

Finally Kirk Munro decided to go “all in” and more or less rewrite the entire script into reusable functions, and using PSCredentials for more secure handling of the password, rewrote the logic to speed up the script, made it possible to only check a single MVP group and lots more.

[ps]
function Get-MvpCount {
[CmdletBinding()]
param(
[Parameter(Position=0,Mandatory=$false)]
[ValidateNotNullOrEmpty()]
[System.String[]]
$Competency = @(‘*’),

[Parameter(Mandatory=$false)]
[ValidateRange(0,60)]
[System.Int32]
$Timeout = 15,

[Parameter(Mandatory=$false)]
[ValidateNotNull()]
[System.Management.Automation.Credential()]
$Credential = [System.Management.Automation.PSCredential]::Empty
)

#region Initialize and load helper module.
$helperModule = New-Module -ScriptBlock {
#region Initialize local variables.
$ie = $null
#endregion

function Show-IEWindow {
#region Create a new hidden instance of Internet Explorer if one does not exist.
if (-not $script:ie) {
$script:ie = New-Object -ComObject ‘InternetExplorer.Application’
}
#endregion

#region Show the IE window.
$script:ie.Visible = $true
#endregion
}

function Hide-IEWindow {
#region Create a new hidden instance of Internet Explorer if one does not exist.
if (-not $script:ie) {
$script:ie = New-Object -ComObject ‘InternetExplorer.Application’
}
#endregion

#region Hide the IE window.
$script:ie.Visible = $false
#endregion
}

function Open-IEWebSite {
[CmdletBinding()]
param(
[Parameter(Position=0,Mandatory=$true)]
[ValidateNotNullOrEmpty()]
[System.String]
$Uri,

[Parameter(Position=1,Mandatory=$false)]
[ValidateNotNullOrEmpty()]
[System.String[]]
$ErrorUri,

[Parameter(Mandatory=$false)]
[ValidateRange(0,60)]
[System.Int32]
$Timeout = 15
)
#region Create a new hidden instance of Internet Explorer if one does not exist.
if (-not $script:ie) {
$script:ie = New-Object -ComObject ‘InternetExplorer.Application’
$script:ie.Visible = $false
}
#endregion

try {
#region Navigate to the specified web site.
Write-Progress -Activity ‘Loading a web page’ -Status "Loading $Uri…"
$script:ie.Navigate($Uri)
#endregion

#region Wait until IE has finished loading the web site.
$count = 0
do {
if ($Timeout -and ($count -gt ($Timeout * 1000))) {
throw "The $Uri web page failed to load in $Timeout seconds."
}
if ((-not $script:ie.Busy) -and $ErrorUri) {
foreach ($failedLocationUrl in $ErrorUri) {
if ($script:ie.LocationURL -like $failedLocationUrl) {
throw "The $Uri web page failed to load ($($script:ie.LocationURL))."
}
}
}
Start-Sleep -Milliseconds 500
$count++
} while ($script:ie.Busy)
#endregion
}
finally {
if ($script:ie.Busy) {
$script:ie.Stop()
}
Write-Progress -Activity ‘Loading a web page’ -Status "Loading $Uri…" -Completed
}
}
function Test-IEWebSiteElement {
[CmdletBinding()]
param(
[Parameter(Position=0,Mandatory=$true)]
[ValidateNotNullOrEmpty()]
[System.String]
$Name
)
#region Raise an error if you haven’t opened a web site yet.
if (-not $script:ie.Document) {
throw ‘You must open a web site using Open-IEWebSite before you can invoke a button on the site.’
}
#endregion

#region Return true if the element exists; false otherwise.
[bool]$script:ie.Document.getElementById($Name)
#endregion
}

function Set-IEWebSiteFieldData {
[CmdletBinding()]
param(
[Parameter(Position=0,Mandatory=$true)]
[ValidateNotNullOrEmpty()]
[System.Collections.Hashtable]
$DataMap
)
#region Raise an error if you haven’t opened a web site yet.
if (-not $script:ie.Document) {
throw ‘You must open a web site using Open-IEWebSite before you can set field data.’
}
#endregion

#region Load the data into the appropriate fields.
foreach ($key in $DataMap.Keys) {
if ($field = $script:ie.Document.getElementById($key)) {
$field.Value = $DataMap[$key]
} else {
throw "Field element ‘$key’ was not found on $($script:ie.LocationURL)."
}
}
#endregion
}
function Invoke-IEWebSiteButton {
[CmdletBinding()]
param(
[Parameter(Position=0,Mandatory=$true)]
[ValidateNotNullOrEmpty()]
[System.String]
$Name,

[Parameter(Position=1,Mandatory=$false)]
[ValidateNotNullOrEmpty()]
[System.String[]]
$ErrorUri,

[Parameter(Mandatory=$false)]
[ValidateRange(0,60)]
[System.Int32]
$Timeout = 15
)
#region Raise an error if you haven’t opened a web site yet.
if (-not $script:ie.Document) {
throw ‘You must open a web site using Open-IEWebSite before you can invoke a button on the site.’
}
#endregion

try {
#region Click on the specified button.
Write-Progress -Activity ‘Clicking a web site button’ -Status "Clicking on the $Name button on $($script:ie.LocationURL)…"
if ($button = $script:ie.Document.getElementById($Name)) {
$button.Click()
} else {
throw "Button ‘$Name’ was not found on $($script:ie.LocationURL)."
}
#endregion

#region Wait until IE has finished loading the web site.
$count = 0
do {
if ($Timeout -and ($count -gt ($Timeout * 1000))) {
throw "The web page failed to load in $Timeout seconds."
}
if ((-not $script:ie.Busy) -and $ErrorUri) {
foreach ($failedLocationUrl in $ErrorUri) {
if ($script:ie.LocationURL -like $failedLocationUrl) {
throw "An error occurred ($($script:ie.LocationURL))."
}
}
}
Start-Sleep -Milliseconds 500
$count++
} while ($script:ie.Busy)
#endregion
}
finally {
if ($script:ie.Busy) {
$script:ie.Stop()
}
Write-Progress -Activity ‘Clicking a web site button’ -Status "Clicking on the $Name button on $($script:ie.LocationURL)…" -Completed
}
}
function Get-IEWebSiteMatch {
[CmdletBinding()]
param(
[Parameter(Position=0,Mandatory=$true)]
[ValidateNotNullOrEmpty()]
[System.Text.RegularExpressions.Regex[]]
$Regex
)
#region Raise an error if you haven’t opened a web site yet.
if (-not $script:ie.Document) {
throw ‘You must open a web site using Open-IEWebSite before you can invoke a button on the site.’
}
#endregion

#region Return all matches for the specified regular expression.
$innerHtml = $script:ie.Document.Body.InnerHTML.ToString()
foreach ($value in $RegEx) {
$value.Matches($innerHtml)
}
#endregion
}
function ConvertFrom-EncodedUrl {
[CmdletBinding()]
param(
[Parameter(Position=0,Mandatory=$true,ValueFromPipeline=$true)]
[ValidateNotNullOrEmpty()]
[System.String[]]
$InputObject
)
begin {
#region Load the System.Web assembly.
[System.Reflection.Assembly]::LoadWithPartialName(‘System.Web’) | Out-Null
#endregion
}
process {
#region Decode the values.
foreach ($value in $InputObject) {
[System.Web.HttpUtility]::UrlDecode($value)
}
#endregion
}
}

function ConvertTo-EncodedUrl {
[CmdletBinding()]
param(
[Parameter(Position=0,Mandatory=$true,ValueFromPipeline=$true)]
[ValidateNotNullOrEmpty()]
[System.String[]]
$InputObject
)
begin {
#region Load the System.Web assembly.
[System.Reflection.Assembly]::LoadWithPartialName(‘System.Web’) | Out-Null
#endregion
}
process {
#region Encode the values.
foreach ($value in $InputObject) {
[System.Web.HttpUtility]::UrlEncode($value)
}
#endregion
}
}

$ExecutionContext.SessionState.Module.OnRemove = {
if ($script:ie) {
try {
$script:ie.Quit()
}
finally {
$script:ie = $null
}
}
}
}
#endregion

#region Load the helper module.
$helperModule | Import-Module
#endregion

try {
#region Open the MVP website, using credentials if they were provided.
if ($Credential -and ($Credential -ne [System.Management.Automation.PSCredential]::Empty)) {
#region If the caller provided credentials, use Passport login before retrieving competency data.
Open-IEWebSite -Uri ‘https://login.live.com/ppsecure/secure.srf?lc=1033&sf=1&id=259214&ru=https://mvp.support.microsoft.com/communities/mvp.aspx&tw=0&fs=0&kv=0&cb=wizid%3df4502d34-3b8f-4a04-b741-289e08aa1782%26lcid%3d1033%26returnurl%3dhttps%253a%252f%252fmvp.support.microsoft.com%252fcommunities%252fmvp.aspx%26brand%3dMicrosoft&cbcxt=&wp=MCMBI&wa=wsignin1.0&wreply=https://mvp.support.microsoft.com/communities/mvp.aspx&wlsu=1’
if (Test-IEWebSiteElement -Name ‘i0011’) {
Set-IEWebSiteFieldData -DataMap @{
‘i0116′ = $Credential.UserName.Trim([Char[]]’ \’)
‘i0118’ = $Credential.GetNetworkCredential().Password
}
Invoke-IEWebSiteButton -Name ‘i0011’ -ErrorUri ‘https://mvp.support.microsoft.com/mvpinvalidsignin.aspx’
}
#endregion
} else {
#region Otherwise, simply open the MVP website.
Open-IEWebSite -Uri ‘https://mvp.support.microsoft.com/communities/mvp.aspx’
#endregion
}
#endregion

#region Process the compentencies that were requested.
Get-IEWebSiteMatch -Regex ‘(?m)<A href=\"(\/communities\/mvp.aspx\?product=1&amp;competency=(\S+))\"’
| Add-Member -MemberType ScriptProperty -Name DecodedUrl -Value {$this.Groups[2].Value | ConvertFrom-EncodedUrl} -PassThru

| Sort-Object -Property DecodedUrl
| ForEach-Object {
foreach ($value in $Competency) {
if ($_.DecodedUrl -like $value) {
Open-IEWebSite -Uri "https://mvp.support.microsoft.com$($_.Groups[1].Value -replace 'amp;','')"
if ($matches = Get-IEWebSiteMatch -Regex 'Results 1 \- \d+ of (\d+)') {
$competencyRecord = New-Object -TypeName System.Management.Automation.PSObject

| Add-Member -MemberType NoteProperty -Name Competency -Value $_.DecodedUrl -PassThru `
| Add-Member -MemberType NoteProperty -Name Count -Value $matches[0].Groups[1].Value -PassThru
$competencyRecord.PSTypeNames.Insert(0,’MVPCompetencyRecord’)
$competencyRecord
}
break
}
}
}
#endregion
}
finally {
$helperModule | Remove-Module
}
}
[/ps]

Remember this is not thoroughly tested code, this is just examples of different ways of doing things…

There was a post on the Minasi forum, where someone was trying to check which users had start/stop permissions on a given server. I knew SC would give the results, but it returns it in DACL format, which as far from readable by humans… So since I had some time to pass I decided to try to write a powershell script to translate the DACL into something human readable (We were having our floor lacquered, and the guy doing it, had a little extra lacquer, and decided to lacquer our hallway, thereby cutting me off from leaving the first floor of our house).

This function takes a service name and a computername as input, if no computername is given, it tries to look it up on the localmachine.

I considered putting this in the script I wrote for checking Services/Scheduled Tasks/COM+ objects, but it just gave me too much output, so I decided to put this in a separate script, and tried to put a GUI around it, since it has been a while since I have opened Primal Forms.

The script takes a username, it then connects to all servers in your domain, and checks to see if the username you typed in has a connection to that server, it does so by querying Win32_LoggedOnUser (You will be surprised by how many connections you have to different machines)

The script requires Powershell V2, Quest AD cmdlets.

[sourcecode lang=”Ps”]
########################################################################
# Code Generated By: SAPIEN Technologies, Inc., PrimalForms 2009 v1.1.9.0
# Generated On: 08-09-2010 22:12
# Generated By: CTN
########################################################################

#———————————————-
#region Application Functions
#———————————————-

function OnApplicationLoad {
#Note: This function runs before the form is created
#Note: To get the script directory in the Packager use: Split-Path $hostinvocation.MyCommand.path
#Note: To get the console output in the Packager (Windows Mode) use: $ConsoleOutput (Type: System.Collections.ArrayList)
#Important: Form controls cannot be accessed in this function
#TODO: Add snapins and custom code to validate the application load

return $true #return true for success or false for failure
}

function OnApplicationExit {
#Note: This function runs after the form is closed
#TODO: Add custom code to clean up and unload snapins when the application exits

$script:ExitCode = 0 #Set the exit code for the Packager
}

#endregion

#———————————————-
# Generated Form Function
#———————————————-
function GenerateForm {

#———————————————-
#region Import Assemblies
#———————————————-
[void][reflection.assembly]::Load("System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")
[void][reflection.assembly]::Load("System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")
[void][reflection.assembly]::Load("mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")
[void][reflection.assembly]::Load("System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")
#endregion

#———————————————-
#region Generated Form Objects
#———————————————-
[System.Windows.Forms.Application]::EnableVisualStyles()
$form1 = New-Object System.Windows.Forms.Form
$label5 = New-Object System.Windows.Forms.Label
$label4 = New-Object System.Windows.Forms.Label
$label3 = New-Object System.Windows.Forms.Label
$label2 = New-Object System.Windows.Forms.Label
$label1 = New-Object System.Windows.Forms.Label
$progressbar1 = New-Object System.Windows.Forms.ProgressBar
$listbox1 = New-Object System.Windows.Forms.ListBox
$CheckBTN = New-Object System.Windows.Forms.Button
$Usernametxt = New-Object System.Windows.Forms.TextBox
$InitialFormWindowState = New-Object System.Windows.Forms.FormWindowState
#endregion Generated Form Objects

#———————————————-
# User Generated Script
#———————————————-

$FormEvent_Load={
#TODO: Initialize Form Controls here

}

$handler_textbox1_TextChanged={
#TODO: Place custom script here

}

$handler_CheckBTN_Click={

#TODO: Place custom script here
$i = 1
$ErrorActionPreference ="silentlycontinue"
$Servers = get-QADComputer -SearchRoot ‘appension.local/’ -OSName "*Server*"

$progressbar1.Maximum(($Servers).length)
$label2.Text = $servers.length

Foreach ($server in $Servers) {
$users = get-WmiObject -computer $server.Name Win32_LoggedOnUser | select Antecedent -Unique
$label4.Text = $i
$i++
$label5.Text = $Server.name
$progressbar1.Increment("1")

Foreach ($user in $users) {

$domainname =([regex]::Match($user.Antecedent, ‘Domain="(?<domain>.*?)"’)).Groups["domain"].Value
$username = ([regex]::Match($user.Antecedent, ‘Name="(?<name>.*?)"’)).Groups["name"].Value
If ($username -like $Usernametxt.Text) {
#Write-Host "************* $($server.name) ********************"
$listbox1.Items.Add($Servers.name)
}
}
}
}

$handler_label1_Click={
#TODO: Place custom script here

}

$handler_listbox1_SelectedIndexChanged={
#TODO: Place custom script here

}

#———————————————-
# Generated Events
#———————————————-

$Form_StateCorrection_Load=
{
#Correct the initial state of the form to prevent the .Net maximized form issue
$form1.WindowState = $InitialFormWindowState
}

#———————————————-
#region Generated Form Code
#———————————————-
#
# form1
#
$form1.Controls.Add($label5)
$form1.Controls.Add($label4)
$form1.Controls.Add($label3)
$form1.Controls.Add($label2)
$form1.Controls.Add($label1)
$form1.Controls.Add($progressbar1)
$form1.Controls.Add($listbox1)
$form1.Controls.Add($CheckBTN)
$form1.Controls.Add($Usernametxt)
$form1.Text = "Check where user is logged in."
$form1.Name = "form1"
$form1.DataBindings.DefaultDataSourceUpdateMode = [System.Windows.Forms.DataSourceUpdateMode]::OnValidation
$form1.ClientSize = New-Object System.Drawing.Size(418,358)
$form1.add_Load($FormEvent_Load)
#
# label5
#
$label5.TabIndex = 8
$label5.Size = New-Object System.Drawing.Size(149,18)
$label5.Location = New-Object System.Drawing.Point(142,302)
$label5.DataBindings.DefaultDataSourceUpdateMode = [System.Windows.Forms.DataSourceUpdateMode]::OnValidation
$label5.Name = "label5"
#
# label4
#
$label4.TabIndex = 7
$label4.Size = New-Object System.Drawing.Size(31,18)
$label4.Location = New-Object System.Drawing.Point(61,303)
$label4.DataBindings.DefaultDataSourceUpdateMode = [System.Windows.Forms.DataSourceUpdateMode]::OnValidation
$label4.Name = "label4"
#
# label3
#
$label3.TabIndex = 6
$label3.Size = New-Object System.Drawing.Size(27,18)
$label3.Text = " of"
$label3.Location = New-Object System.Drawing.Point(84,303)
$label3.DataBindings.DefaultDataSourceUpdateMode = [System.Windows.Forms.DataSourceUpdateMode]::OnValidation
$label3.Name = "label3"
#
# label2
#
$label2.TabIndex = 5
$label2.Size = New-Object System.Drawing.Size(33,18)
$label2.Location = New-Object System.Drawing.Point(109,303)
$label2.DataBindings.DefaultDataSourceUpdateMode = [System.Windows.Forms.DataSourceUpdateMode]::OnValidation
$label2.Name = "label2"
#
# label1
#
$label1.TabIndex = 4
$label1.Size = New-Object System.Drawing.Size(42,18)
$label1.Text = "Server"
$label1.Location = New-Object System.Drawing.Point(24,303)
$label1.DataBindings.DefaultDataSourceUpdateMode = [System.Windows.Forms.DataSourceUpdateMode]::OnValidation
$label1.Name = "label1"
$label1.add_Click($handler_label1_Click)
#
# progressbar1
#
$progressbar1.DataBindings.DefaultDataSourceUpdateMode = [System.Windows.Forms.DataSourceUpdateMode]::OnValidation
$progressbar1.Size = New-Object System.Drawing.Size(382,23)
$progressbar1.TabIndex = 3
$progressbar1.Location = New-Object System.Drawing.Point(24,323)
$progressbar1.Name = "progressbar1"
#
# listbox1
#
$listbox1.FormattingEnabled = $True
$listbox1.Size = New-Object System.Drawing.Size(201,225)
$listbox1.DataBindings.DefaultDataSourceUpdateMode = [System.Windows.Forms.DataSourceUpdateMode]::OnValidation
$listbox1.Name = "listbox1"
$listbox1.Location = New-Object System.Drawing.Point(24,75)
$listbox1.TabIndex = 2
$listbox1.add_SelectedIndexChanged($handler_listbox1_SelectedIndexChanged)
#
# CheckBTN
#
$CheckBTN.TabIndex = 1
$CheckBTN.Name = "CheckBTN"
$CheckBTN.Size = New-Object System.Drawing.Size(75,23)
$CheckBTN.UseVisualStyleBackColor = $True
$CheckBTN.Text = "Check"
$CheckBTN.Location = New-Object System.Drawing.Point(150,20)
$CheckBTN.DataBindings.DefaultDataSourceUpdateMode = [System.Windows.Forms.DataSourceUpdateMode]::OnValidation
$CheckBTN.add_Click($handler_CheckBTN_Click)
#
# Usernametxt
#
$Usernametxt.Size = New-Object System.Drawing.Size(100,20)
$Usernametxt.DataBindings.DefaultDataSourceUpdateMode = [System.Windows.Forms.DataSourceUpdateMode]::OnValidation
$Usernametxt.Text = "Username"
$Usernametxt.Name = "Usernametxt"
$Usernametxt.Location = New-Object System.Drawing.Point(24,24)
$Usernametxt.TabIndex = 0
$Usernametxt.add_TextChanged($handler_textbox1_TextChanged)
#endregion Generated Form Code

#———————————————-

#Save the initial state of the form
$InitialFormWindowState = $form1.WindowState
#Init the OnLoad event to correct the initial state of the form
$form1.add_Load($Form_StateCorrection_Load)
#Show the Form
return $form1.ShowDialog()

} #End Function

#Call OnApplicationLoad to initialize
if(OnApplicationLoad -eq $true)
{
#Create the form
GenerateForm | Out-Null
#Perform cleanup
OnApplicationExit
}
[/sourcecode]