• Checking Site sizes in SharePoint 2007 and 2010

    Our Sharepoint admin asked me to help him write a script, that found out how much space each DocumentLibrary in our sharepoint farm took up, so after some googeling I found that I could use the StorageManagementInformation Method on the SPSite object, so I cam up with this little script

    [ps]
    #First we load the SharePoint assembly
    [void][System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint")

    #Then we create a function that returns tje SPSite
    function Get-SPSite($url){
    return new-Object Microsoft.SharePoint.SPSite($url)
    }

    $site = Get-SPSite http://URL
    # We use the StorageManagementInformation Method on the $SPSite object, StorageManagementInformation returns a DataTable, and takes 4 input values
    # System.Data.DataTable StorageManagementInformation(Microsoft.SharePoint.SPSite+StorageManagementInformationType ltVar, Microsoft.SharePoint.SPSite+StorageManagementSortOrder sordVar, Microsoft.SharePoint.SPSite+StorageManagementSortedOn soVar, System.UInt32 nMaxResults)
    #
    # ltVar: What kind of storage management information to display
    # List = 1
    # DocumentLibrary = 2
    # Document = 3
    # sordVar: the direction in which the items are to be sorted
    # Increasing = 0×10
    # Decreasing = 0×11
    # soVar: whether the items are sorted by size or by date
    # Size=0
    # Date = 1
    # nMaxResults: the number of results to return

    $DT = $site.StorageManagementInformation(2,0×11,0,$(($site.allwebs).count));
    $DT | Select @{Label="Size"; Expression={[INT]($_.Size/1MB)}},Directory | out-gridview
    $site.Dispose()
    [/ps]

    Seems as if I forgot the last line, where I dispose of the Site object, I have added that now.



  • Updating .Htacces file based on Apache log files

    I am still seeing massive amounts of referal traffic hitting my site, eating up my bandwidth.. I did not get time to update my .htaccess file for the last 2 days.. and within the last 24 hours I have had more than 6000 hits, generating in almost 24.000 pageviews… Generating more than 1 GB worth of traffic (So at that speed I will reach my 10 GB limit soon)

    Looking through the Apache logs, figuring out which sites I get most referral traffic from, getting the hostnames, transforming them into a format that can be used by the Apache rewrite engine in the .htaccess file has been time consuming. So I decided that some powershell magic, might speed up the process a bit.

    [ps]
    function Select-FileDialog
    {
    param(
    [string]$Title,
    [string]$Directory,
    [string]$Filter="All Files (*.*)|*.*")
    [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms") | Out-Null
    $objForm = New-Object System.Windows.Forms.OpenFileDialog
    $objForm.InitialDirectory = $Directory
    $objForm.Filter = $Filter
    $objForm.Title = $Title
    $Show = $objForm.ShowDialog()
    If ($Show -eq "OK")
    {
    Return $objForm.FileName
    }
    Else
    {
    Write-Error "Operation cancelled by user."
    }
    }

    #Function to create the http rewrite rules.

    Function Create-Rewrite {
    Param (
    $Hostname
    )

    $HtaRule = "RewriteCond %{HTTP_REFERER} ^http://" + "$($hostname.replace(".","\."))" +" [OR]"
    $script:BlockList += $HtaRule
    }

    Function add-htaccess {
    Param (
    $HtaRules
    )
    (Get-Content $htaccess) | foreach-object {
    $_
    if ($_ -match "RewriteEngine") {
    if (!(Select-String -simplematch "$htarules" -Path $htaccess))
    {
    $HtaRules
    }
    }

    } | set-Content $tempFile
    Copy-Item $tempFile $htaccess
    }

    Function Upload-Ftp {
    Param ([Parameter(Position=0, Mandatory=$true)]
    [ValidateNotNullOrEmpty()]
    [System.String]
    $FTPHost,
    [Parameter(Position=1)]
    [ValidateNotNull()]
    $File
    )
    $webclient = New-Object System.Net.WebClient
    $uri = New-Object System.Uri($ftphost)

    "Uploading $File…"

    $webclient.UploadFile($uri, $File)
    }

    #Variables
    $log = Select-FileDialog -Title "Select an Apache logfile"
    $htaccess = "c:\Temp\.htaccess"
    $tempFile = [IO.Path]::GetTempFileName()
    $URLCount = 15
    $FTPUsername = "Username"
    $FTPPassword = "PassW0rd"

    $BlockList = ""
    #Create list of sites to block
    $script:BlockList = @()

    #Get the list of URLS in the the logfile, capturing each element into different named capturing groups

    $urls = Select-String ‘^(?<client>\S+)\s+(?<auth>\S+\s+\S+)\s+\[(?<datetime>[^]]+)\]\s+"(?:GET|POST|HEAD) (?<file>[^ ?"]+)\??(?<parameters>[^ ?"]+)? HTTP/[0-9.]+"\s+(?<status>[0-9]+)\s+(?<size>[-0-9]+)\s+"(?<referrer>[^"]*)"\s+"(?<useragent>[^"]*)"$’ $log |
    Select -Expand Matches | Foreach { $_.Groups["referrer"].value }

    #Output statistics for the referer hostnames (Only show top 15)
    $urls | group | ForEach -begin { $total = 0 }
    -process { $total += $_.Count; $_ } |Sort Count | Select Count, Name |
    Add-Member ScriptProperty Percent { "{0,15:0.00}%" -f (100*$this.Count/$Total) } -Passthru | select -Last $URLCount

    #Getting the base hostnames from the complete URLS, and outputs statistics to the screen.

    $hosts = $urls | Select-String '\b[a-z][a-z0-9+\-.]*://([a-z0-9\-._~%!$&()*+,;=]+@)?(?<host>[a-z0-9\-._~%]+|\[[a-z0-9\-._~%!$&()*+,;=:]+\])' |
    Select -Expand Matches | Foreach { $_.Groups["host"].value } | group | sort count | where {($_.name -notlike "*xipher.dk*") -and ($_.Count -gt 100)} |
    ForEach -begin { $total = 0 }

    -process { $total += $_.Count; $_ } | Sort Count | Select Count, Name |
    Add-Member ScriptProperty Percent { "{0,10:0.00}%" -f (100*$this.Count/$Total) } -Passthru

    Write-Host "List of root hostnames"

    $hosts

    Foreach ($Url in $hosts) {

    Create-Rewrite $url.Name
    }

    Foreach ($Block in $script:BlockList) {
    add-htaccess $Block
    }

    notepad $htaccess

    $script:BlockList

    Upload-Ftp -FTPHost "ftp://$($FTPUsername):$($FTPPassword)@xipher.dk/httpdocs/.htaccess" -File $htaccess
    Upload-Ftp -FTPHost "ftp://$($FTPUsername):$($FTPPassword)@xipher.dk/httpdocs/WordPress/.htaccess" -File $htaccess
    [/ps]

    Unfortunately my current hosting company, does not allow me to download the log files via FTP, but I have to connect to the Parallels interface and download it manually.. (I have not had the time looking into automating this part yet, so this is still a manual step)
    That is why I added a little function to use a GUI to pick the access_log file.

    [ps]
    function Select-FileDialog
    {
    param(
    [string]$Title,
    [string]$Directory,
    [string]$Filter="All Files (*.*)|*.*")
    [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms") | Out-Null
    $objForm = New-Object System.Windows.Forms.OpenFileDialog
    $objForm.InitialDirectory = $Directory
    $objForm.Filter = $Filter
    $objForm.Title = $Title
    $Show = $objForm.ShowDialog()
    If ($Show -eq "OK")
    {
    Return $objForm.FileName
    }
    Else
    {
    Write-Error "Operation cancelled by user."
    }
    }
    [/ps]

    I then call the function like this:

    [ps]
    $log = Select-FileDialog -Title "Select an Apache logfile"
    [/ps]

    A little Regex magic runs through the logfiles, and captures the different elements into different named capturing groups, in this step, I expand all referrer hostnames, and put them into the $urls variable

    [ps]
    $urls = Select-String ‘^(?<client>\S+)\s+(?<auth>\S+\s+\S+)\s+\[(?<datetime>[^]]+)\]\s+"(?:GET|POST|HEAD) (?<file>[^ ?"]+)\??(?<parameters>[^ ?"]+)? HTTP/[0-9.]+"\s+(?<status>[0-9]+)\s+(?<size>[-0-9]+)\s+"(?<referrer>[^"]*)"\s+"(?<useragent>[^"]*)"$’ $log |
    Select -Expand Matches | Foreach { $_.Groups["referrer"].value }
    [/ps]
    I modified a script by Joel Bennet, to get a little statistics as well, since there can be 1000′s of hostnames, I have selected only to output top 15 by default (using the $URLCount variable.

    [ps]
    $urls | group | ForEach -begin { $total = 0 }
    -process { $total += $_.Count; $_ } |Sort Count | Select Count, Name |
    Add-Member ScriptProperty Percent { "{0,15:0.00}%" -f (100*$this.Count/$Total) } -Passthru | select -Last $URLCount
    [/ps]

    Then I loop through all the hostnames, and extract the base domain name, using regex again. (Here I choose to ignore all traffic from my own domain name Xipher.dk, and I choose only to look for referral domains, that have generated 100 hits or more

    [ps]
    $hosts = $urls | Select-String '\b[a-z][a-z0-9+\-.]*://([a-z0-9\-._~%!$&()*+,;=]+@)?(?<host>[a-z0-9\-._~%]+|\[[a-z0-9\-._~%!$&()*+,;=:]+\])' |
    Select -Expand Matches | Foreach { $_.Groups["host"].value } | group | sort count | where {($_.name -notlike "*xipher.dk*") -and ($_.Count -gt 100)} |
    ForEach -begin { $total = 0 }

    -process { $total += $_.Count; $_ } | Sort Count | Select Count, Name |
    Add-Member ScriptProperty Percent { "{0,10:0.00}%" -f (100*$this.Count/$Total) } -Passthru
    [/ps]

    The script expects to find a .htaccess file in c:\temp containing at least the following two lines:

    RewriteEngine On
    RewriteRule (.*) http://%{REMOTE_ADDR}/$ [R=301,L]



  • Finally got confirmation.. Grown men do cry when their Bitlocker encrypted SSD disk dies ;(

    Had another visit from the ever present Mr Murphy, I have had a Lenovo T420 sitting on my desk for a while, as a replacement for my T410.. Finally last night I had some time to start moving over data to the new laptop. So I boot up both laptops, both have Windows Updates ready to install.. So I thought, I might as well, let it update, while I went out to get me something to drink. When I get back, my “old” laptop would’nt boot.. The SSD disk had suddenly stopped working. Even after 1 day at work, I have found that I have a lot of ad-hoc scripts, that I had not backed up… I am going on 7 weeks paternity leave next friday, so I had written a bunch of documentation, for my colleagues to have..

    So sorry to all those people I have made fun of over time, for having elaborate backup solutions for their laptops :)



  • Testing simple theme

    I am currently having some problems with my site, apparently it gets linked to be a lot of spam sites, resulting in more than 4000 visits in the last 24 hours. These “visitors” have generated over 1GB worth of traffic, and there is a 10GB cap on my current account.. So that this rate my site will be closed in 10 days :(



  • Fix 2008R2 scheduled tasks

    [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]



  • 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]



  • 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]



  • Change username/password on scheduled tasks.

    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]



  • Scheduled Tasks Server 2008(R2)

    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]