Adding Applications to EMET

If you have been using EMET (Enhanced Mitigation Experience Toolkit ) toolkit from Microsoft you probably have had to need to add custom application, one of the main “culprits” is Flash Player with its ever changing name (filename contains it’s version number)
In EMET wildcards are only allowed in paths not filenames, so I wrote a little script to add applications to the EMET “Watch list” :)

It consist of 2 functions one to remove an application and one to add an application.

The below example will first remove all applications that starts with Flash, then it will add all .exe found in the path: ‘C:\Windows\System32\Macromed\Flash’, so if there were multiple versions of flash in there, they would all be removed.

Read More

Send SMS through bibob with Powershell

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]

Read More

Send SMS from Powershell through Unwire

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]

Read More

The (r)evolution of a script

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…

Read More

Translate Service DACL’s

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.

Read More

My account keeps getting locked out, where did I use it ??

A few weeks ago my admin account kept getting locked out, after I had changed my password.. So I assumed I had used it somewhere to either run a service or a scheduled task on a test server. But where???

So I decided to write a little PowerShell script to help me find out where, and give me a chance to be more proactive next time I need to change my Password, or the password of one of our service accounts.

So I decided to try to write a script that connects to all our servers, and list all services running under domain credentials, and do the same for scheduled tasks. In our environment we have an issue with some COM+ objects that are either running as a domain admin user, or as “interactive” meaning that a domain admin user has to be logged into a machine at all times for the app to work.. (Yes I have yelled and screamed at the developers).

So what this script does, it connects info from domain machines, and puts it in a Excel spreadsheet.
In order to run the script, you need to have Powershell V2, Quest AD cmdlet (preferably ver. 1.2 or above) and permissions on the remote machines to connect via WMI.

[ps]
#==========================================================================
# NAME: GetServiceScheduledtaskandCom.ps1
#
# AUTHOR: Claus T Nielsen
# Version: 0.9
# Date: 07/09/10
#
# COMMENT:
# This script is based on a script by Stephen Wheet, a lot of the code has been rewritten, and
# and a bunch of code has been added.
# The script connects to remote computers, and lists all services running under domain accounts,
# it does the same for Scheduled Tasks and COM+ Applications.
#
# The Script required the QUEST AD cmdlets and excel installed on the computer
#
# Missing features: IIS APP Pool Credentials, Logged In Users
#==========================================================================
$domainname = (Get-QADRootDSE.Domain).Domain.ToString()

$searchroot = "appension.local/"

$ErrorActionPreference = "SilentlyContinue"

[System.Threading.Thread]::CurrentThread.CurrentCulture = "en-US"
$ErrorActionPreference = "SilentlyContinue"
$ExcelAPP = New-Object -comobject Excel.Application
$ExcelAPP.visible = $True

$ExcelWB = $ExcelAPP.Workbooks.Add()
$ServiceSheet = $ExcelWB.Worksheets.Item(1)
$ScheduledSheet = $ExcelWB.Worksheets.Item(2)
$COMPlusSheet = $ExcelWB.Worksheets.Item(3)

$ServiceSheet.Name = "Services"
$ScheduledSheet.Name = "Scheduled Tasks"
$COMPlusSheet.Name = "COM+"

# Accounts to ignore
$IgnoreAcct =
"NT AUTHORITY\LOCAL SERVICE",
"NT AUTHORITY\LOCALSERVICE",
"LocalSystem",
".\*",
"NT AUTHORITY\NETWORK SERVICE",
"NT AUTHORITY\NetworkService"

$DefaultCOMObjects =
‘COM+ Utilities’,
‘COM+ QC Dead Letter Queue Listener’,
‘COM+ Explorer’,
‘COM+ Utilities (32 bit)’,
‘IIS Out-Of-Process Pooled Applications’,
‘.NET Utilities’

#Place Headers on out-put file

$ServiceSheet.Cells.Item(1,1) = "Server"
$ServiceSheet.Cells.Item(1,2) = "Service"
$ServiceSheet.Cells.Item(1,3) = "Account"

$ScheduledSheet.Cells.Item(1,1) = "Server"
$ScheduledSheet.Cells.Item(1,2) = "Task"
$ScheduledSheet.Cells.Item(1,3) = "Next Run Time"
$ScheduledSheet.Cells.Item(1,4) = "Account"

$COMPlusSheet.Cells.Item(1,1) = "Server"
$COMPlusSheet.Cells.Item(1,2) = "COM+ Name"
$COMPlusSheet.Cells.Item(1,3) = "Identity"

$d = $ServiceSheet.UsedRange
$d.Interior.ColorIndex = 19
$d.Font.ColorIndex = 11
$d.Font.Bold = $True

$l = $ScheduledSheet.UsedRange
$l.Interior.ColorIndex = 19
$l.Font.ColorIndex = 11
$l.Font.Bold = $True

$q = $COMPlusSheet.UsedRange
$q.Interior.ColorIndex = 19
$q.Font.ColorIndex = 11
$q.Font.Bold = $True

$intRowSer = 2
$intRowSch = 2
$intRowCom = 2

#Get all the servers from the specified OU
$Servers = get-QADComputer -SearchRoot $searchroot -OSName "*Server*" #| where {$_.name -like "appo*"} # change the container based on site.

Foreach ($server in $Servers ) {
#$ErrorActionPreference = "SilentlyContinue"
$serverFQDN = $server.dnsname
#Test ping server to make sure it’s up before querying it
if (Test-Connection $serverFQDN -Quiet -Count 1){
Write-Host "n ************* $serverFQDN Online n" -ForegroundColor Red
Write-Host "****************** $($server.name) Services ******************"
# Get service info
$error.clear()
$services = gwmi win32_service -computer $serverFQDN -property name, startname, caption |
% {

$name = $_.Caption
$Acctname = $_.StartName
If ( $IgnoreAcct -notcontains $Acctname )
{
Write-host "$Name $Acctname"
$ServiceSheet.Cells.Item($intRowSer, 1) = $serverFQDN
$ServiceSheet.Cells.Item($intRowSer, 2) = $name
$ServiceSheet.Cells.Item($intRowSer, 3) = $Acctname
$intRowSer++
} #end If

} #end ForEach

#Write log if no access
if (!$?) {
$ServiceSheet.Cells.Item($intRowSer, 1) = $serverFQDN
$ServiceSheet.Cells.item($intRowSer, 2).Interior.ColorIndex = 3
$ServiceSheet.Cells.Item($intRowSer, 2) = "Access Denied/Offline"
$ServiceSheet.Cells.Item($intRowSer, 3) = $Error
#$errmsg = "$serverFQDN,No RPC server,ACCESS DENIED"
Write-Host $Error
$intRowSer++
} # end Error

$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’

$ScheduledSheet.Cells.Item($intRowSch,1) = $serverFQDN
$ScheduledSheet.Cells.Item($intRowSch,2) = ($sch.TaskName).replace(‘\’,”)
$ScheduledSheet.Cells.Item($intRowSch,3) = $Sch.’Next Run Time’
$ScheduledSheet.Cells.Item($intRowSch,4) = $sch.’Run As User’
$intRowSch++

}

}
}
Write-Host "****************** $($server.name) COM+ Applications ******************"
#$targetComputer = $serverFQDN
$comAdmin = New-Object -comobject COMAdmin.COMAdminCatalog
$comAdmin.Connect($serverFQDN)
$apps = $comAdmin.GetCollection("Applications")

$apps.Populate()

foreach ($app in $apps) {
If ( $IgnoreAcct -notcontains ($app.Value("Identity")) -and ( $DefaultCOMObjects -notcontains ($app.Value("Name")) )) {
$q.Cells.Item($intRowCom,1) = $serverFQDN
$q.Cells.Item($intRowCom,2) = $app.Value("Name")
$q.Cells.Item($intRowCom,3) = $app.Value("Identity")
Write-Host $app.Value("Name") $app.Value("Identity")
$intRowCom++
}
}
}

Else
{
Write-Host "$serverFQDN ************* OFFLINE"
} #end Else
}
#end ForEach
$d.EntireColumn.AutoFit()
$l.EntireColumn.AutoFit()
$q.EntireColumn.AutoFit()
[/ps]

Read More

Output a sortable HTML table

A while back I posted a script that would take whatever input it got, into a sortable HTML table in a file.

Last night my family was here, and they wanted to see the Eurovision song contest thing, so I had some time to rewrite the script into a PowerShell V2 Advanced function, that supports pipeline input.

There are some examples in the code on how to use the function

I do not know what has happened to code highlighter, but it has suddenly decided to mess up the code, so it is not working on the site, so here is a link to the file.

Out-HTMLTable

Example of output:

Out-HTMLTable example

download | new post

Read More

Remoting examples from my presentation

Example #1

[ps]
#Sessions

#Single Session
$Session = New-PSSession -ComputerName "computername"

#Multiple Sessions

$Session = New-PSSession -ComputerName "comp1","comp2"…..

#Remove all Sessions

Remove-PSSession *

#Remove Named Session

Remove-PSSession $Session

#Using $session
Invoke-Command {ipconfig} -Session $Session

Enter-PSSession -Session $session
[/sourcecode]

Example #2

[sourcecode lang="PosH"]
$session = New-PSSession -ComputerName "compname"

Invoke-Command {Import-Module ActiveDirectory} -Session $session

Import-PSSession -Session $session -Module ActiveDirectory -Prefix "ctn"

notepad (Get-Module).path

Get-Command *ctn*

[/ps]

Read More

Quest AD Cmdlet examples from my Presentation

Example #1
List users that expires within X days
[ps]
$DaysToExpire = 14
$MaxPassAge = (Get-QADObject (Get-QADRootDSE).defaultNamingContextDN).MaximumPasswordAge.days
[array]$a = Get-QADUser -Enabled -PasswordNeverExpires:$false -SizeLimit 0 -Email * |Select-Object Name,Email,@{Name="Expires";Expression={ $MaxPassAge – $_.PasswordAge.days }} | where {$_.Expires -lt 0} | Sort-Object expires
[/ps]

Example #2
Locate that Hyper-V Host
[ps]
Get-QADComputer | Where {$_.OSname -match "2008"} | % { Get-Service -ComputerName $_.Name} | where {$_.Displayname -match "hyper"} | select Machinename, Displayname

#Kirk Munroe pointed out that Hyper-V actually registers ans SCP (Service Connection Point)
Get-QADObject -Name ‘Microsoft Hyper-V’ -Type serviceConnectionPoint | Get-QADComputer -Identity {$_.ParentContainerDN}
[/ps]

Example #3
Create test users and OU’s in AD
[ps]
#Gets the default naming context
$RootDN = (Get-QADRootDSE).DefaultNamingContextDN
#Name of OU going to be created in the root of "AD"
$RootOUName = "MinasiTest"
#Create "root" OU
new-qadObject -ParentContainer $RootDN -type ‘organizationalUnit’ -NamingProperty ‘ou’ -name $RootOUName
#Create sub OU’s, create as many as you like, you only need to change the -name property in the end of the line
new-qadObject -ParentContainer "OU=$RootOUName,$RootDN" -type ‘organizationalUnit’ -NamingProperty ‘ou’ -name ‘Administrators’
new-qadObject -ParentContainer "OU=$RootOUName,$RootDN" -type ‘organizationalUnit’ -NamingProperty ‘ou’ -name ‘Marketing’
new-qadObject -ParentContainer "OU=$RootOUName,$RootDN" -type ‘organizationalUnit’ -NamingProperty ‘ou’ -name ‘Sales’
new-qadObject -ParentContainer "OU=$RootOUName,$RootDN" -type ‘organizationalUnit’ -NamingProperty ‘ou’ -name ‘IT’
new-qadObject -ParentContainer "OU=$RootOUName,$RootDN" -type ‘organizationalUnit’ -NamingProperty ‘ou’ -name ‘HR’

#
#
#
#Enter the DN path of the OU you want to add users to
#Note I manually put in the DN of an OU in my test environment, you will have to put one in that exists in your environment

#$OuPath = ‘OU=Aprismo Users,DC=aprismo,DC=test’
$OuPath = "OU=$RootOUName,$RootDN"

#Number of users to create in each OU

$No = 5

Function AddUsers
{
param([string]$OUDN, [string]$OUName)

foreach($user in 1..$No)
{
$UserName = "Test$OUName$User"
$UserPassword = "P@ssw0rd123!"
$ExpiresOn = $((Get-Date).AddMonths(1))
$userDescription = "User added for Performance Testing on $(Get-date) and Expires on $ExpiresOn"

$user = New-QADUser -name $UserName
-SamAccountName $UserName

-Description $UserDescription
-ParentContainer $OUDN

-UserPassword $UserPassword

$user | Set-QADUser -AccountExpires ((Get-Date).AddMonths(1))
}
}

#This foreach loops through all OU’s "below" $OUPath
Foreach ($OU in Get-QADObject -SearchRoot $OUPath -Type ‘organizationalUnit’){
Addusers $OU.DN $OU.Name
}

[/pa]

Example #4
Rename Groups in AD
[ps]
Get-QADGroup -Name "xyz*" | %{Set-QADGroup -Identity $_ -SamAccountName ($_.Name).Replace("xyz","abc") -whatif ;Rename-QADObject -Identity $_ -NewName ($_.Name).Replace("xyz","abc") -WhatIf }
[/ps]

Example #5
Output a list of servers in a Excel documents, with ping stats and if the machine is disabled/enabled
[ps][/ps]
#$serverlist = Get-QADComputer -LdapFilter ‘(!(userAccountControl:1.2.840.113556.1.4.803:=2))’ | where {$_.Osname -like “*server*”}
$serverlist = Get-QADComputer -IncludedProperties pwdLastSet -SizeLimit 0 | where {$_.Osname -like “*server*”}

$erroractionpreference = “SilentlyContinue”
$a = New-Object -comobject Excel.Application
$a.visible = $True

$b = $a.Workbooks.Add()
$c = $b.Worksheets.Item(1)

$c.Cells.Item(1,1) = “Machine Name”
$c.Cells.Item(1,2) = “OS Name”
$c.Cells.Item(1,3) = “IP Address”
$c.Cells.Item(1,4) = “Ping Status”
$c.Cells.Item(1,5) = “Password last set”
$c.Cells.Item(1,6) = “Enabled/Disabled”
$c.Cells.Item(1,7) = “Physical/Virtual”

$d = $c.UsedRange
$d.Interior.ColorIndex = 19
$d.Font.ColorIndex = 11
$d.Font.Bold = $True

$intRow = 2

$colComputers = $serverlist
foreach ($strComputer in $colComputers)
{
$c.Cells.Item($intRow, 1) = $strComputer.Name

$ping = new-object System.Net.NetworkInformation.Ping
$Reply = $ping.send($strComputer.Name)

if ($Reply.status –eq “Success”)
{
$machineType = Get-WmiObject -ComputerName $strComputer.Name -Class Win32_BIOS

If ($strComputer.AccountIsDisabled) {$enab = “Disabled”} else {$enab = “Enabled”}
if ($strComputer.pwdLastSet -le (Get-Date).AddDays(-90)) {$age = “Older than 90 Days” ; $fgColor = 3} else {$age = “Less than 90 days”; $fgColor = 0}

if ($machineType.Serialnumber -like “*vmware*”) {$type = “VMware”}
Elseif ($machineType.Version -like “*VRTUAL*”) {$type = “Hyper-V”}
Elseif (!($machineType.Version)) {$type = “N/A”}
else {$type = “Physical”}

$c.Cells.Item($intRow, 2) = $strComputer.OSName
$c.Cells.Item($intRow, 3) = $Reply.Address.ToString()
$c.Cells.Item($intRow, 4) = “Online”
$c.Cells.item($intRow, 5).Interior.ColorIndex = $fgColor
$c.Cells.Item($intRow, 5) = $age
$c.Cells.Item($intRow, 6) = $enab
$c.Cells.Item($intRow, 7) = $type
$Reply = “”
$intRow = $intRow + 1

}
else
{
$machineType = Get-WmiObject -ComputerName $strComputer.Name -Class Win32_BIOS

If ($strComputer.AccountIsDisabled) {$enab = “Disabled”} else {$enab = “Enabled”}
if ($strComputer.pwdLastSet -le (Get-Date).AddDays(-90)) {$age = “Older than 90 Days” ; $fgColor = 3} else {$age = “Less than 90 days”; $fgColor = 0}
if ($machineType.Serialnumber -like “*vmware*”) {$type = “VMware”}
Elseif (!($machineType.Version)) {$type = “N/A”}
Elseif ($machineType.Version -like “*VRTUAL*”) {$type = “Hyper-V”}
else {$type = “Physical”}

$c.Cells.Item($intRow, 2) = $strComputer.OSName
$c.Cells.Item($intRow, 3) = $Reply.Address.ToString()
$c.Cells.Item($intRow, 4) = “Offline”
$c.Cells.item($intRow, 5).Interior.ColorIndex = $fgColor
$c.Cells.Item($intRow, 5) = $age
$c.Cells.Item($intRow, 6) = $enab
$c.Cells.Item($intRow, 7) = $type
$Reply = “”

$intRow = $intRow + 1

}
$d.EntireColumn.AutoFit()

}
[ps][/ps]

Read More

.Net Examples from Presentation

Example #1 (From Lee Holmes’ Powershell cookbook)

“Compile C# code on the fly”
[ps]

$code =@’
using System.Management.Automation;

[Cmdlet("Write", "InputObject")]
public class MyWriteInputObjectCmdlet : Cmdlet
{
[Parameter]
public string Parameter1;

[Parameter(Mandatory = true, ValueFromPipeline=true)]
public string InputObject;

protected override void ProcessRecord()
{
if (Parameter1 != null)
WriteObject(Parameter1 + ":" + InputObject);
else
WriteObject(InputObject);
}
}
‘@

Add-Type -TypeDefinition $code -OutputAssembly .\ExampleModule.dll

Import-Module .\ExampleModule.dll

[/ps]

Example #2 (From Lee Holmes’ Powershell cookbook)
Showing how static functions are available directly

[ps]
$source = @"
public class BasicTest
{
public static int Add(int a, int b)
{
return (a + b);
}

public int Multiply(int a, int b)
{
return (a * b);
}
}
"@

Add-Type -TypeDefinition $source

[BasicTest]::Add(4, 3)

$basicTestObject = New-Object BasicTest
$basicTestObject.Multiply(5, 2)
[/ps]

Example #3
Access .Net function to move mouse, in order to prevent screensaver from kicking in.
[ps]
Function Move-Mouse {

param($minutes = 60)

for ($i = 0; $i -lt $minutes; $i++) {
Start-Sleep -Seconds 60
$Pos = [System.Windows.Forms.Cursor]::Position
[System.Windows.Forms.Cursor]::Position = New-Object System.Drawing.Point((($Pos.X) + 1) , $Pos.Y)

}
}

[/ps]

Read More