Tuesday, October 14, 2014

Everyday Powershell - Part 29 - Add-SMTPAlias

It's funny. When you think about how many clicks you need to go through to add an SMTP alias to an Exchange mailbox... There's a lot of clicks! Or you could just use this function and be done in 5 seconds.

function Add-SMTPAlias{
    <# Add-SMTPAlias
    .SYNOPSIS Adds smtp alias to a given mailbox

    .DESCRIPTION

    .PARAMETER alias
    Users Alias

    .PARAMETER
    newsmtpaddress desired email alias

    .EXAMPLE
    Add-IPToSMTPRelay -alias someuser -newsmtpaddress someusersotheremail@someserver.com 
    #>

    [CmdletBinding()]
    param (
        [Parameter(mandatory=$true,ValueFromPipeline=$true)]
        $alias,
        [Parameter(mandatory=$true,ValueFromPipeline=$true)]
        [mailaddress]$newsmtpaddress
    )
    process{
        $emailaddresses = (get-mailbox $alias).emailaddresses
        $emailaddresses += $newsmtpaddress.ToString()
        try{
            Set-Mailbox $alias -EmailAddresses $emailaddresses -ErrorAction stop
        }
        catch{
            write-warning ("Problem setting email address " + $_)
        }
    }
}

5 minutes or 5 seconds, the choice is yours GUI fans. Maybe it's time to give in to the power of the shell?

Wednesday, September 10, 2014

Everyday Powershell - Part 28 - Add-IPToSMTPRelay

Here's one that came up when we'd provisioned a new powershell integration server and couldn't send email from it!

Turns out our email server's weren't allowing it relay. So we needed to add it's IP to the RemoteIPRanges parameter and as Paul Cunningham  points out this can be tricky.

function Add-IPToSMTPRelay{
    <#
    Add-IPToSMTPRelay
    .SYNOPSIS
    Adds an IP address to the allow anonymous SMTP relay Receive connector on TMAIL

    .DESCRIPTION
    You will need to know your receive connectors name... use get-recieveconnector We only use one so it's been hard coded into the funtion, this could be parameterised if you need
    
    .PARAMETER
    IP IP Address to add to allowed anonymous SMTP relay 

    .EXAMPLE
    Add-IPToSMTPRelay 192.168.1.1
    #>

    [CmdletBinding()]
    param (
        [Parameter(mandatory=$true,ValueFromPipeline=$true)]
        [ipaddress]$ip
    )
    process{
        $remoteipranges = (Get-ReceiveConnector "smtp relays").remoteipranges
        $remoteipranges += ($ip).ToString()
        Set-ReceiveConnector "smtp relays" -RemoteIPRanges $remoteipranges
    }
}

You may be saying... "WOAH WOAH WOAH this looks different to your usual posts! What's all this function param process stuff?"

Well as you can see we wrapped up the powershell in a Function. The Comment block at the top is the help. The Param block defines any parameters in this case we've just got $IP. The process block is what actually happens and would be what we'd normally post.

This allows us to embed the function in a module and then stick the module in our powershell profile so it's always available!

This is far better than digging around in a scripts folder we'll dig into setting up a module in the next few weeks.

Friday, September 5, 2014

Everyday Powershell - Part 27 - Uploading files to a FTP server

Full credit to Goyuix for his awesome example on StackOverflow which I am shamlessly ripping off for this Post!

$logpath = "C:\SomePath"
$ftpserver = "ftp://someFTP.com//"
$user = "User"
$password = "Password"
$ftppath = "Somefolder"
$todayslogs = Get-ChildItem $logpath | where {$_.CreationTime -gt ((get-date).AddDays(-1))}

foreach($log in $todayslogs){
    $uploadpath = $ftpserver + $ftppath + "/" + $log.Name 
    # create the FtpWebRequest and configure it
    $ftp = [System.Net.FtpWebRequest]::Create($uploadpath)
    $ftp = [System.Net.FtpWebRequest]$ftp
    $ftp.Method = [System.Net.WebRequestMethods+Ftp]::UploadFile
    $ftp.Credentials = new-object System.Net.NetworkCredential($user,$password)
    $ftp.UseBinary = $true
    $ftp.UsePassive = $true
    # read in the file to upload as a byte array
    $content = [System.IO.File]::ReadAllBytes($log.FullName)
    $ftp.ContentLength = $content.Length
    # get the request stream, and write the bytes into it
    $rs = $ftp.GetRequestStream()
    $rs.Write($content, 0, $content.Length)
    # be sure to clean up after ourselves
    $rs.Close()
    $rs.Dispose()
}

This one was written to automate the upload of some logs. It happens every day so that's why we just (get-date).addays(-1) but that where on get-childitem can be anything you like.

One thing to note is the hardcoded password. You may not want to do this, depends on your security requirements. You can save the credentials in another file as a secure.string if you like. There's probably a post coming about that.

Tuesday, July 29, 2014

Everyday Powershell - Part 26 - Check Server last boot time

Here's a quickie that was useful this week.
$servers = Get-ADComputer -filter {OperatingSystem -like "*Server*"}
$report = @()
foreach ($server in $servers){
    $temp = "" | Select Name, Lastboot
    $temp.name = $server.dnshostname
    if(test-connection $temp.name -Count 1 -quiet){
        $temp.lastboot = [datetime]::ParseExact(((Get-wmiobject -ComputerName $temp.name Win32_operatingSystem).lastbootuptime).Substring(0,12), "yyyyMMddHHmm", $null)
    }
    else{
        $temp.lastboot = $null
    }
    $report += $temp
}
$report | sort name

Original inspiration was from here;

You see the great thing with powershell is once you know how to do something on one server you can do it on many servers relatively easily. To support some of our older fleet we had to change that query from CIM to WMI and meant some trickery with the Datetime but that’s all pretty simple stuff.

This could be combined with the server shutdown comments script from part 11 to get the reason for the last reboot too!

Monday, July 28, 2014

Run Need For Speed Rivals at 60FPS

So I watched the Need for Speed movie on the weekend... Despite being pretty bad it did impart a longing to drive exotic cars through scenic landscapes.

So I grabbed the latest game in the franchise; Need for Speed Rivals. It was a PS4 Launch title and runs on the Frostbite engine that powers battlefield games. So it should be a really good bunch of eye candy running nice and smooth on the new(ish) rig!

So after loading it up and sitting through an eternity of tutorial videos I got into the game... What's this? It's bloody capped at 30FPS!!!

This is an atrocity in the modern age of gaming! Capping performance of a PC title to run at a lower frame rate to, presumably, maintain compatibility with the console versions.

Luckily before returning the game I did a quick Google and found that the arbitrary limit on the frame rate could be be changed with two command line arguments...

-GameTime.MaxSimFps 60 -GameTime.ForceSimRate 60

MaxSimFps tells the game to run at 60 FPS but everything runs twice as fast if you don't set ForceSimRate to 60 as well.

Game runs exactly as I expected when run with those parameters and I'm now doing what I wanted, blasting past gorgeous scenery in insanely overpowered status symbols...

However it is of great concern that a big studio would release a AAA title on PC with the frame-rate limited like that. A key benefit of PC high end gaming is high resolutions with high frame rates.

When a big publisher decides that the PC market should be stuck on the same frame-rates as their console users, it should be ringing alarm bells for performance enthusiasts everywhere.

Friday, July 25, 2014

Everyday Powershell - Part 25 - Monitor for CryptoWall

So the bastards got us! Yup we got Cryptowalled!

We caught the infection before it did too much damage, isolated the PC, configured the firewall to block the IPs it was trying to get to and restored the pwnd files from backup. Business as usual really.

But what do we do to monitor for this in the future?

Whacked this together quickly;
$protectedpaths = "\\server1\someshare", "\\server2\someother"
$filename = "-Canary-.txt"
$foldername = "\-----AAA--TOP-----\"
$canarystring = "If this can be read everything is ok"
$logpath = "C:\scripts\cryptolockercanary.txt"
$recipients = "someadmin@mail.com"
$smtpserver = "somemailserver"

$report = @()

foreach ($path in $protectedpaths){
    $temp = "" | select Time, Path, EncryptedStatus
    $pathwithfolder =  $path + $foldername
    $canarypath =  $pathwithfolder + $filename
    $temp.time = get-date
    $temp.path = $pathwithfolder
    if (test-path $canarypath){
        $test = Get-content $canarypath
        if ($test -eq $canarystring){
            $temp.EncryptedStatus = $false
        }
        else{
            $temp.EncryptedStatus = $true
            Send-MailMessage -to $recipients -From 
some@email.com  -Subject ("CryptoLockerCanary Has been changed! " + $temp.time) -BodyAsHtml ($temp | convertto-html | Out-String-SmtpServer $smtpserver
        }
    }
    else{
        mkdir $pathwithfolder
        out-file $canarypath -InputObject $canarystring
        Set-ItemProperty -Path $canarypath -Name attributes -Value ((Get-ItemProperty $canarypath).attributes -BXOR ([io.fileattributes]::Hidden))
        if (test-path $canarypath){
            Send-MailMessage -to $recipients -From 
some@email.com  -Subject ("CryptoLockerCanary file is not present - creating new one " + $temp.time) -BodyAsHtml ($temp | convertto-html | Out-String-SmtpServer $smtpserver
        }
        else{
            Send-MailMessage -to $recipients -From some@email.com -Subject ("CryptoLockerCanary file is not present and COULD NOT CREATE new one - Recomend investigation " + $temp.time) -BodyAsHtml ($temp | convertto-html | Out-String-SmtpServer $smtpserver
        }
    }
    $report += $temp
}
$report | export-csv -Path $logpath -Append -NoTypeInformation
$report

It'll take any folders defined in $protectedpaths and stick a "canary" file in there. If it sees a change to the canary file it'll send out emails to $recipients.

This is scheduled to run every five minutes so this way we'll get alerted really quickly if it get's through again.

Tuesday, July 8, 2014

Game Design brings out my evil side

Mwuhahahahahahaha!
Believe it or not... This is a Racing Game.
Island-Test6 a smooshing together of unity, world machine and some custom car scripts has allowed me to indulge in much deviousness.

What is the fine line between fun challenge and pure frustration? Well I don't know, but I'm trying my damnedest to find out!

Wednesday, June 25, 2014

Everyday Powershell - Part 24 - Send Email With Inline Image

Ever wanted to include inline images with an email you are sending out from a script?

We came across the requirement during our Exchange 2013 Migration.

This was ridiculously easy to achieve in Powershell 4.
Send-MailMessage -to someuser@someserver.com -From someotheruser@someserver.com -SmtpServer somemailserver -Subject "Test" -BodyAsHtml "TEST <br /><img src="attachement.png" />" -Attachments "C:\Scripts\attachement.png"

This is how we used to do it!
Add-PSSnapin Microsoft.Exchange.Management.Powershell.Admin -erroraction silentlyContinue
$smtpServer = "somemailserver"
$att = new-object Net.Mail.Attachment("c:\scripts\attachment.png")
$att.ContentId = "att"
$msg = new-object Net.Mail.MailMessage
$smtp = new-object Net.Mail.SmtpClient($smtpServer)
$body = "Some Text<br />
<img src="cid:att" />"

$msg.From = "someuser@manly.nsw.gov.au"
$msg.To.Add("someotheruser@manly.nsw.gov.au")
$msg.Subject = "Some Subject" + (get-date -format yyyyMMdd)
$msg.Body = $body
$msg.IsBodyHTML = $true
$msg.Attachments.Add($att)
$smtp.Send($msg)
$att.Dispose()

UPDATE - The HTML included in the script bugged out... Will fix shortly.

UPDATE 2 - reader sivaramsharma asked if send-mailmessage could handle multiple attachments, if this interests you too head over to Part 31 where we give a useful example.

Wednesday, May 28, 2014

Everyday Powershell - Part 23 - Check for old servers

Another simple AD cleanup script. But it's the simple things in life that count right?

You can run this script and it'll find any servers in your domain. It pings them and if they reply it sets .there to true otherwise it'll be null.
$servers = Get-ADComputer -filter {OperatingSystem -like "*Server*"}
$report = @()
foreach ($server in $servers)
{
    $temp = "" | select Name, There
    $temp.name = $server.DNSHostName
    $temp.there = if(Test-Connection $server.DNSHostName -Count 1){$true}
    $report += $temp
}
$report

Quick and dirty powershell to do something useful in a hurry. That's what we're all about here!


Tuesday, May 27, 2014

Oh Irony

Crash reporter crashed would you like to report this crash?


It's stuff like this that makes working in IT a comedy gold mine for those working in IT.

Wednesday, May 21, 2014

Everyday Powershell - Part 22 - Determine which mailboxes a user has fullaccess to

Had cause this week to figure out to which mailboxes a particular user had been assigned Full Access  permission.

Seemed a bit daunting, and would have taken us ages to audit just using the GUI. But it was actually very quick to get going in Powershell.
Add-PSSnapin *exchange*

$user = "TESTUSER"

$mailboxes = get-mailbox

foreach ($mailbox in $mailboxes)
{
    $mailboxpermission = Get-Mailboxpermission $mailbox
    foreach ($permission in $mailboxpermission)
    {
        $permission | where {$_.Accessrights -Contains "FullAccess" -and $_.user -like "*$user*"| select Identity
    }
}

This was a cracking use of Powershell right in the nick of time and made us seem really professional to our big boss. Really can't recommend enough getting some powershell tools into your frequently used toolbelt.

Once you start using it, it's amazing the things you start to dream up. Things you wouldn't have even thought to do before learning the power of the shell!

Friday, May 16, 2014

New rig is online!

Parts arrived by courier this morning...

Was planning to wait for the weekend to do the build. But being the passionate hardware enthusiast that I am, I couldn't wait. So I took a long lunch around at Kaggies shop and got to work.


The motherboard was in and out a few times to fit parts that I'd forgotten to mount. 


Doing the build in a proper work space was tops! Chris has all sorts of handy tools!


Mounting that water cooling system had me scratching my head for a while. Pretty sure I've done it wrong too. Might have a look a that manual again when I have a minute. I feel like there should be another Fan in the mix.


Builds always look so good right before the cables go in, don't they?


All done! All up it was about 2 hours including the Windows 8.1 install which was about 10 minutes! It's so fast and so Quiet. The Graphics card will change the sound profile quite a lot but we've got some plans for that too!







Wednesday, May 14, 2014

Everyday Powershell - Part 21 - List Mailboxes with disabled user accounts

Any organisation will have users coming and going all the time. As admins we've got to manage much of this process. Running regular reports like this can catch errors in onboarding and offboarding processes.

Add-PSSnapin *exchange*
Get-Mailbox | Get-aduser -identity {$_.samaccountname} -Properties enabled | where {$_.enabled -eq $False| Select Name

Very simple one liner script that'll list all mailboxes that have their associated users account disabled.

You could package this up into an email and send that to the relevant people on a regular basis.

Ahhh using Automation to make your team look more professional... Is there anything more rewarding?

Tuesday, May 13, 2014

New Rig Specs

So I'm building a new PC and decided to record the specs;

Fractal Design Define R4 Arctic White with Window
Gigabyte GA-Z97X-SOC Force LGA 1150 Motherboard
Intel Core i5 4670K Quad Core LGA 1150 3.4GHz CPU Processor
Corsair Vengeance Pro 8GB (2x 4GB) DDR3 2133MHz Memory
Samsung 840 EVO 250GB 2.5" SATA III SSD MZ-7TE250BW
Seasonic P-860XP Platinum Series 860W Power Supply
Thermaltake Water 3.0 Pro Liquid CPU Cooler
Gigabyte GeForce GTX 780 Ti 3GB WindForce 3X Video Card

It's going to be pretty special when it's done.

The CPU will be replaced by a Devils Canyon i7 once they become available.

It's my first foray into liquid cooling but using an all in one unit kinda makes it a more approachable concept.

Update! - Here are some photos from the build

Friday, May 9, 2014

Everyday Powershell - Part 20 - Rollback system time and launch a program

So there are times where loading an older application requires you to rollback the date of your system. Our UPS management console has had this odd requirement since 2014. So we threw this together to make life a little easier;

set-date 01/01/2009
Start-Process "c:\windows\system32\notepad.exe"
Start-sleep 15
set-date (invoke-command -ComputerName SOMECURRENTTIMESERVER -ScriptBlock {get-date})

You can update the process path in the second line, then save this as a .ps1 file and use it as a shortcut to launch time-bombed applications.

It does require the appropriate permissions on the sever we get the time from in the fourth line. It'll also need Powershell Remoting enabled.

Wednesday, April 16, 2014

Everyday Powershell - Part 19 - Migrate Transport Rules from Exchange 2007 to 2013

Today's script is a quick and dirty fix for a project we're in the middle of. Not really a script either, but it's the thought that counts.


#on a computer with exchange 2007 management tools
Add-PSSnapin *exchange*
export-transportrulecollection c:\scripts\exch2007transportrules.xml

#on a computer with exchange 2013 management tools
Add-PSSnapin *exchange*
[Byte[]]$Data = Get-Content -Path '\\someserver\c$\Scripts\exch2007transportrules.xml' -Encoding Byte -ReadCount 0
Import-TransportRuleCollection -FileData $Data
Export transport rules from Exchange 2007 and import them to 2013. 

To think our System Admin was going to copy these over by hand!

Full credit to Microsoft for this one it is all from http://support.microsoft.com/kb/2846555

Wednesday, March 26, 2014

Everyday Powershell - Part 18 - Set Address Details in Active Directory

Here's a request from one of our readers. He's tidying up a negelected AD he inherited and needed a way to apply consistent addresses to his AD User Objects.

Powershell is great at this stuff! We've decided to use group membership as a way to decide who should get what data, but this could be easily applied via OU.
<# For this to work setup a CSV in C:\Scripts call it Groups.csv
And fill it with data like this; 
SecurityGroup,Address,City,Zip,State,Country,Telephone
Group1,1 Some St,SomeTown,1122,SOMESTATE,AU,615551234
Group2,2 Some Other St,SomeOtherTown,2211,SOMESTATE,AU,615554321 #>

$csvdata =import-csv C:\Scripts\Groups.csv
foreach($row in $csvdata)
{
    $users= Get-ADGroupMember $row.securitygroup
    foreach($user in $users)
    {
        Set-ADUser $user -StreetAddress $row.Address -city $row.city -State $row.State -Postalcode $row.Zip -Country $row.Country -OfficePhone $row.Telephone
    }
}
Just a simple script with two loops, first one iterates through the rows in the CSV the second group steps through each user in the security group identified in the initial loop.

Nothing fancy but it fixed our readers problem. Such is the power of the shell!

Wednesday, March 19, 2014

Everyday Powershell - Part 17 - Using new-mailboxexportrequest with NON-US dates

Here's a crazy making problem!

The new-mailboxexportrequest commandlet will only accept dates in US formats. Roland Paix has made this post about the issue. The conclusion is switch your culture to EN-US before loading powershell. Sooo frustrating! But hey, something has to be the default datetime and Microsoft is an American company. No point getting emotional, let's just fix it.

Here's a neat trick you can use to set the Culture of the "Current Thread" you are running.

[System.Reflection.Assembly]::LoadWithPartialName("System.Threading")
[System.Reflection.Assembly]::LoadWithPartialName("System.Globalization")
[System.Threading.Thread]::CurrentThread.CurrentCulture = [System.Globalization.CultureInfo]::CreateSpecificCulture("en-us")

Add-PSSnapin *exchange*

$start = (get-date).adddays(-28)
$end = (get-date).adddays(-14)

$filter = "(Received -gt '"+$start+"') -and (Received -lt '"+$end+"')"
new-Mailboxexportrequest -mailbox SOMEMAILBOX -ContentFilter $filter -filepath C:\SOME.pst

This should trick new-mailboxexportrequest into believing that your dates are in the "correct" format!

Wednesday, March 12, 2014

Everyday Powershell - Part 16 - Monitoring Disk Performance in Powershell

Welcome back to another Everyday Powershell post. It's not powershell we POST every day, it's powershell you could USE everyday.

Today we look at get-counter which you should have a fiddle with if you get the chance. It does a lot more than what we've used it for here. But this may help you get the gist of running performance counters in powershell.

$disks = Get-WmiObject win32_logicaldisk | Where-Object {$_.DriveType -eq 3| select DeviceID
$report = @()
$i=1
foreach ($disk in $disks)
{
    Write-Progress -Activity "Mesuring disk performance" -Status $disk.DeviceID -PercentComplete (($i/$disks.Count)*100)
    $temp = "" | select Disk, Read, Write
    $temp.disk = $disk.deviceid
    $counter = "\LogicalDisk(" + ($disk.deviceid) + ")\Avg. Disk sec/Read"
    $temp.read = ((Get-Counter -Counter $counter).CounterSamples.cookedvalue)*1000
    $counter = "\LogicalDisk(" + ($disk.deviceid) + ")\Avg. Disk sec/Write"
    $temp.write = ((Get-Counter -Counter $counter).CounterSamples.cookedvalue)*1000
    $report += $temp
    $i++
}
$report

We've included the now ubiquitous write-progess to give a useful progress indicator. As well as the old $temp="" | select which sets up a hash table for us to dump data into.

Thursday, February 27, 2014

Everyday Powershell Returns! - Find exchange mailbox size

Fresh from an ass kicking at the Winter Scripting games we're back with another useful bit of Powershell!

We're building our Exchange 2013 environment now and have a burning need to see what our users mailboxes look like, in terms of the space they consume.

This little script did the trick for us and helped us plan our new information store layout.

Add-PSSnapin *exchange*

$users = Get-ADUser -searchbase "DC=somedomain,DC=com" -Filter * -Properties department

$report = @()
$count = $users.count
$i = 0
foreach ($user in $users)
{
    $i++
    $prog = ($i/$count* 100
    Write-Progress -Activity "Processing..." -percentcomplete $prog -CurrentOperation $user.samaccountname 
    $temp = ""| select Person, mailboxsize, itemcount, Team, Function
    $mailboxstats = Get-MailboxStatistics $user.samaccountname
    $temp.person = $user.samaccountname
    $temp.mailboxsize = $mailboxstats.totalitemsize.value.toMB()
    $temp.itemcount = $mailboxstats.itemcount
    $temp.team = $user.department
    $report += $temp
}
$report

Should be pretty self explanatory. Ask a question in the comments if you'd like clarification of anything. 

Wednesday, February 12, 2014

What I learnt at the Winter Scripting Games - Part 4

Great news!

We scored in the positives for event 2! Even got some good feeback from the judges. Great work Team Awesome...

Bad news!

We didn't get anything submitted for event 3. Missed an opportunity to capitalize on what we've learnt so far. Still there's event 4 to come.

We did get a decent function together for setting ACLs so event 3 wasn't a total write off.

For us it's more about have fun leaning challenges than the score anyway.

Wednesday, February 5, 2014

What I learned at the Winter Scripting Games - Part 3 - The Importance of Help

That’s what we learnt in event 1 of the scripting games. Our team of enthusiastic novices threw together a script that we thought met most of the requirements. It wasn't pretty and didn't include any comments or help.

Once the scoring was done we came out a -4. MINUS FOUR! Ouch.

Looking at the scoring criteria it became clear why. Much of the scoring seems to be around building the right kinds of good practice into your script.
We lost points on;
  • Appropriate use of Try/Catch
  • Appropriate use of verbose output including -Verbose support
  • Correct management of pipeline input
  • Appropriate use of parameter validation attributes
  • Comment- or file-based help
  • File names include date of production
  • Code shared between solutions
This is all pretty simple stuff that could almost be built into a script “template”. We’ll be working on that for future Events.

 So what we've learned is that the logic in your script isn't the best way to score points. I was pretty happy with our solution, give that it was produced by 3 novice scripters. OK 2 novices and me… A ‘Senior Novice’.


It was a great learning experience and the emotional kick of scoring NEGATIVE points made it clear how valuable it is to build this good practice stuff into our scripts.