BGINFO - A Posh Recreation
Recently I have been building a lot of Windows Servers in different environments - one
Grumpy Admin here – as we know Grumpy admin isn’t one to not accept a challenge – Where it is legal and sensible. The challenge of introducing my ex-wife to an industrial sized wood chipper was sadly rejected after a long hard consultation! I can’t create or enter a pssession from jail!
Grumpy Admin has been doing some work for a friend, and part of this work meant, using the Exchange Web Services Managed API to access mailboxes from PowerShell!
In the normal office banter way, Grumpy Admin was expressing how neat it was and useful – The others didn’t and lacked the imagination to see how useful it could actually be! So they challenged me to come with a useful usage case for this. I think they just wanted a use case, not a proof in concept but then I am Grumpy Admin and it is what I like to do!
Took me about 5 mins to think of something, that was actually useful and here was my offering to them!
We have a script that runs once a month or so – currently the script emails us when it complete with the results, which we then make an entry on to the shared calendar to show everyone that the script ran on that date. Easy to keep track and people can see when the script ran. There might be smarter ways of doing it, but it was in place and being done like this before my time! It is just one of the many processes I dislike! They seem to think automation is a bad thing…. Like I would replace them with a script if I could!
So the concept I came up with just to show to get the script to connect to the exchange mailbox and insert a calendar entry itself when it is finished! This sort of thing could be used quite widely for example, Vemmae backup has PowerShell cmdlets, what stopping us from once a day putting the results of the backups and the details of tapes written in that backup in to a calendar – saves me having to log them each day! I digress, so I want a script to write to a Exchange Calandar!
Now of course, the first thing anyone does when given a challenge is to Google it! I’m lazy, I find in the first hit on Google some brilliant code and examples, people have been doing this for years. Tell me an IT guy that doesn’t use Google in this manner!
This blog entry I find is from 2011! So this isn’t anything new! His usage case is exactly the same as what I intend to do!
http://mikepfeiffer.net/2011/01/creating-calendar-items-with-powershell-and-the-ews-managed-api/
Seriously what is the point of redoing work if it is already done! As long as you credit the original author – in this case it’s Mike Pfeiffer. Smarter than me! Well to be honest most of the world is smarter than I am!
So he has this new-calendaritem cmdlet! I will just lift this and use it at the end of my script! Simple As, mission done and I can get more coffee!
Now let’s give it a quick test before hand to prove it works, the we can celebrate!
So let’s cut and paste and run that script – Naturally, have a read through make sure you understand the code and what the implications are DON’T just run any old code off the internet as that would be DUMB!
OH no – a sea of PowerShell red – what the hell is going on – well it clear – that is was written for 2011 – the version of the EWS is out of date – So all that we need to do is to update the path to the EWS DLL file J
New
Let then re-run this and see if it worked, perfect – no sea of PowerShell Red!
Let’s log into the accounts mailbox via OWA and check the calendar!
WHAM! it did! Worked first time, that is fantastic, but again proves it’s not my code! Problem solved – thank you Google and Thank you Mike Pfeiffer – you saved me about 30 seconds per month!
Mikes blog is great worth check out, and this script simply works – copying code is all well and good and solves the problem at hand and sometimes that is what you need, but it doesn’t improve our understanding or learning.
In this instance we want to learn about handling the calendar via EWS – so let’s break his script down and work out what is doing!
function New-CalendarItem { [CmdletBinding()] param( [Parameter(Position=1, Mandatory=$true)] $Subject, [Parameter(Position=2, Mandatory=$true)] $Body, [Parameter(Position=3, Mandatory=$true)] $Start, [Parameter(Position=4, Mandatory=$true)] $End, [Parameter(Position=5, Mandatory=$false)] $RequiredAttendees, [Parameter(Position=6, Mandatory=$false)] $OptionalAttendees, [Parameter(Position=7, Mandatory=$false)] $Location, [Parameter(Position=8, Mandatory=$false)] $Impersonate )
This should be basic and clear to everyone, he’s named his function quite sensibly and got the default binding in there and all the properties as parameters. Moving on as we are quite familiar with this structure.
Add-Type -Path "C:\Program Files\Microsoft\Exchange\Web Services\2.0\Microsoft.Exchange.WebServices.dll"
This adds the EWS dll file, as explained above we had to modify the path for the latest version of the EWS install but again – we are not a total stranger to the add-type method.
$sid = [System.Security.Principal.WindowsIdentity]::GetCurrent().User.Value
This does what you think it does, it gets the current user’s security identifier and puts it inside of the variable $sid
$user = [ADSI]"LDAP://<SID=$sid>"
Again this is a simple line, you should be able to work out what It does – if not I will give you a clue – it will link the SID to the active directory account!
Next,
$service = New-Object Microsoft.Exchange.WebServices.Data.ExchangeService -ArgumentList ([Microsoft.Exchange.WebServices.Data.ExchangeVersion]::Exchange2010_SP1)
Here the script is creating a new object based on a EWS type and he calling it with the argument specifying the exchange version – There must be some backwards compatibility code built in as it saying “exchange2010_sp1” but my test system is running exchange2013_sp1 – Maybe I could change this… but I’m a lazy admin! It still worked! MEH!
$service.AutodiscoverUrl($user.Properties.mail)
This sets the EWS url to the that of the user’s mailbox – This is a AD object property as we know we stored the AD account of the current user in $user! This is the key as EWS needs to know the service it is connecting to! I could hard code this and it will be something like – you can turn the Auto discover to $false and specify the $service.URL
https://simexch.sim.local/EWS/Exchange.asmx
However, it is best practice and recommended to use the auto discover, as different user might be on different exchange servers etc.
if($Impersonate) { $ImpersonatedUserId = New-Object Microsoft.Exchange.WebServices.Data.ImpersonatedUserId -ArgumentList ([Microsoft.Exchange.WebServices.Data.ConnectingIdType]::SmtpAddress),$Impersonate $service.ImpersonatedUserId = $ImpersonatedUserId }
This allows us to use the impersonate user features of Exchange, it allows you to impersonate another user so opening their mailbox, send on their behalf etc etc – might be useful if the calendar is somewhere else or attached to someone else mail box! But it has to be configured on the exchange and of course the account given the correct delegation rights etc!
Follow this link for more information about impersonation! Useful!
https://msdn.microsoft.com/en-us/library/office/dd633680(v=exchg.80).aspx
$appointment = New-Object Microsoft.Exchange.WebServices.Data.Appointment -ArgumentList $service $appointment.Subject = $Subject $appointment.Body = $Body $appointment.Start = $Start $appointment.End = $End
This creates a new appointment, and uses the information that we provided as parameters at the start of the function!
if($RequiredAttendees) {$RequiredAttendees | %{[void]$appointment.RequiredAttendees.Add($_)}} if($OptionalAttendees) {$OptionalAttendees | %{[void]$appointment.RequiredAttendees.Add($_)}} if($Location) {$appointment.Location = $Location}
A couple of logical if statements here! It then cycles through the options in a %{} ( foreach ) loop and does the required actions on the object, in this case either add requiredattendess or optionalattendess to our appointment object! simple, and brilliant!
and then if we provided a location as one of them parameters, it will add that into the object as well 🙂
and finally, it executes
$appointment.Save([Microsoft.Exchange.WebServices.Data.SendInvitationsMode]::SendToAllAndSaveCopy)
There ends the function… and actually a great little addition to my script/function collection, so once again, credit to Mark who wrote this back in 2011.
Just goes to prove my point, that chances are someone has already solved most of the problems you could think of – it’s one thing to copy code and use it, after all that is why we put in the public domain but you got to credit the author!
Not saying given 30 mins, I couldn’t of produce a similar working function after all the MSDN pages is quite good
https://msdn.microsoft.com/en-us/library/office/dd633661(v=exchg.80).aspx
But I am a Lazy Grumpy Admin – Why work harder than you should!
Hazzy