• [Article 654]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]



  • [Article 651]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 :)



  • [Article 633]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]



  • [Article 616]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]



  • [Article 612]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]



  • [Article 601]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]



  • [Article 590]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]



  • [Article 573]Free online Source Control with PowerGui Pro.

    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