Making a Shared Calendar Shortcut (WunderBar Hyperlink) with EWS and Powershell

Making a Shared Calendar Shortcut (WunderBar Hyperlink) with EWS and Powershell Shared Calendar Shortcuts eg

are an Outlook and OWA  characteristic that may be useful to automate if it’s essential deploy quite a few these to new (or present) mailboxes and you do not need to undergo the invitation/settle for process or manually including every shortcut.

Whereas there are not any supported operations in EWS for creating these sort of objects, it may be achieved by setting the Prolonged MAPI properties that represent the shortcut. The draw back of that is that it would not ever be thought-about supported if all of it goes horribly improper. The properties concerned within the shortcut are documented within the following protocol doc http://msdn.microsoft.com/en-us/library/ee157359(v=exchg.80).aspx.

For a few these properties the values it’s essential get cannot be obtained instantly in EWS so some others methods are wanted.

The PidTagWlinkAddressBookEID property accommodates the MAPI tackle E book EntryId for the shared Calendar your connecting to. The Deal with E book EntryID format is documented right here http://msdn.microsoft.com/en-us/library/ee160588%28v=exchg.80%29.aspx so to assemble this in EWS it’s essential get the LegacyExchangeDN from AutoDiscover after which appended the ProviderUID,Flag and Sort info which for a standard consumer would be the identical. (Be aware in case you are attempting to connect with an object different then a consumer mailbox.

The PidTagWlinkAddressBookStoreEID proeprty accommodates the MAPI StoreEntryID of the customers the place your creating the ShortCut. Whereas you may get this property in EWS the worth you get is not right as a result of EWS makes use of completely different wrapper and providerID values. So as an alternative utilizing the format documented in http://msdn.microsoft.com/en-us/library/ee203516%28v=exchg.80%29.aspx you’ll be able to assemble this which entails getting the LegacyExchangeDN and the ServerName for Autodiscover after which constructing the identifier.

The opposite properties are fairly static for shared calendars.

The ShortCuts them selves are saved in a Non_IPM_Subtree folder known as frequent views

(Particular thanks additionally to Neil Doody for serving to with property definitions on this submit and script)

So the next script takes two commandline parameter the primary is the Mailbox the place you need the shortcut to be created and the second is the Mailbox which has the Calendar you need the shortcut to level to so you’d run it like

./createWBarCal.ps1 [email protected] [email protected]

 The script will examine the CommonViews folder to see if a SharedFolder shortcut already exists for the goal Mailbox AddressId and FolderType and if no ShortCut exists and it’ll try to create one.

As i discussed earlier than as this script is totally unsupported and for essentially the most half untested and will solely be thought-about protected for testing in a developmenttest setting. It additionally assume you might have Autodiscover working if not your this is not going to work nicely.

 I’ve put a download of the script right here the code itself seems like

  1. ## Get the Mailbox to Entry from the 1st commandline argument  
  2. $TargetCalendarMailbox = $args[1]  
  3. $MailboxName = $args[0]  
  4.   
  5. ## Load Managed API dll    
  6. Add-Sort -Path “C:Program FilesMicrosoftExchangeWeb Services2.0Microsoft.Exchange.WebServices.dll”    
  7.     
  8. ## Set Alternate Model    
  9. $ExchangeVersion = [Microsoft.Exchange.WebServices.Data.ExchangeVersion]::Alternate2010_SP2    
  10.     
  11. ## Create Alternate Service Object    
  12. $service = New-Object Microsoft.Alternate.WebServices.Information.ExchangeService($ExchangeVersion)    
  13.     
  14. ## Set Credentials to use two choices are availible Option1 to use explict credentials or Choice 2 use the Default (logged On) credentials    
  15.     
  16. #Credentials Choice 1 utilizing UPN for the home windows Account    
  17. $psCred = Get-Credential    
  18. $creds = New-Object System.Internet.NetworkCredential($psCred.UserName.ToString(),$psCred.GetNetworkCredential().password.ToString())    
  19. $service.Credentials = $creds        
  20.     
  21. #Credentials Choice 2    
  22. #service.UseDefaultCredentials = $true    
  23.     
  24. ## Select to ignore any SSL Warning points brought about by Self Signed Certificates    
  25.     
  26. ## Code From http://poshcode.org/624  
  27. ## Create a compilation setting  
  28. $Supplier=New-Object Microsoft.CSharp.CSharpCodeProvider  
  29. $Compiler=$Supplier.CreateCompiler()  
  30. $Params=New-Object System.CodeDom.Compiler.CompilerParameters  
  31. $Params.GenerateExecutable=$False  
  32. $Params.GenerateInMemory=$True  
  33. $Params.IncludeDebugInformation=$False  
  34. $Params.ReferencedAssemblies.Add(“System.DLL”) | Out-Null  
  35.   
  36. $TASource[email protected] 
  37.   namespace Native.ToolkitExtensions.Internet.CertificatePolicy{ 
  38.     public class TrustAll : System.Internet.ICertificatePolicy { 
  39.       public TrustAll() {  
  40.       } 
  41.       public bool CheckValidationResult(System.Internet.ServicePoint sp, 
  42.         System.Safety.Cryptography.X509Certificates.X509Certificates cert,  
  43.         System.Internet.WebRequest req, int downside) { 
  44.         return true; 
  45.       } 
  46.     } 
  47.   } 
  48. @   
  49. $TAResults=$Supplier.CompileAssemblyFromSource($Params,$TASource)  
  50. $TAAssembly=$TAResults.CompiledAssembly  
  51.   
  52. ## We now create an occasion of the TrustAll and connect it to the ServicePointManager  
  53. $TrustAll=$TAAssembly.CreateInstance(“Local.ToolkitExtensions.Net.CertificatePolicy.TrustAll”)  
  54. [System.Net.ServicePointManager]::CertificatePolicy=$TrustAll  
  55.   
  56. ## finish code from http://poshcode.org/624  
  57.     
  58. ## Set the URL of the CAS (Shopper Entry Server) to use two choices are availbe to use Autodiscover to discover the CAS URL or Hardcode the CAS to use    
  59.     
  60. #CAS URL Choice 1 Autodiscover    
  61. $service.AutodiscoverUrl($MailboxName,{$true})    
  62. “Using CAS Server : “ + $Service.url     
  63.      
  64. #CAS URL Choice 2 Hardcoded    
  65.     
  66. #$uri=[system.URI] “https://casservername/ews/exchange.asmx”    
  67. #$service.Url = $uri      
  68.     
  69. ## Elective part for Alternate Impersonation    
  70.     
  71. #$service.ImpersonatedUserId = new-object Microsoft.Alternate.WebServices.Information.ImpersonatedUserId([Microsoft.Exchange.WebServices.Data.ConnectingIdType]::SmtpAddress, $MailboxName)   
  72.   
  73. perform GetAutoDiscoverSettings{  
  74.     param (  
  75.             $adEmailAddress = “$( throw ’emailaddress is a mandatory Parameter’ )”,  
  76.             $Credentials = “$( throw ‘Credentials is a mandatory Parameter’ )”  
  77.           )  
  78.     course of{  
  79.         $adService = New-Object Microsoft.Alternate.WebServices.AutoDiscover.AutodiscoverService($ExchangeVersion);  
  80.         $adService.Credentials = $Credentials  
  81.         $adService.EnableScpLookup = $false;  
  82.         $adService.RedirectionUrlValidationCallback = {$true}  
  83.         $UserSettings = new-object Microsoft.Alternate.WebServices.Autodiscover.UserSettingName[] 3  
  84.         $UserSettings[0] = [Microsoft.Exchange.WebServices.Autodiscover.UserSettingName]::UserDN  
  85.         $UserSettings[1] = [Microsoft.Exchange.WebServices.Autodiscover.UserSettingName]::InternalRpcClientServer  
  86.         $UserSettings[2] = [Microsoft.Exchange.WebServices.Autodiscover.UserSettingName]::UserDisplayName  
  87.         $adResponse = $adService.GetUserSettings($adEmailAddress , $UserSettings);  
  88.         return $adResponse  
  89.     }  
  90. }  
  91. perform GetAddressBookId{  
  92.     param (  
  93.             $AutoDiscoverSettings = “$( throw ‘AutoDiscoverSettings is a mandatory Parameter’ )”  
  94.           )  
  95.     course of{  
  96.         $userdnString = $AutoDiscoverSettings.Settings[[Microsoft.Exchange.WebServices.Autodiscover.UserSettingName]::UserDN]  
  97.         $userdnHexChar = $userdnString.ToCharArray();  
  98.         foreach ($factor in $userdnHexChar) {$userdnStringHex = $userdnStringHex + [System.String]::Format(“{0:X}”, [System.Convert]::ToUInt32($factor))}  
  99.         $Supplier = “00000000DCA740C8C042101AB4B908002B2FE1820100000000000000”  
  100.         $userdnStringHex = $Supplier + $userdnStringHex + “00”  
  101.         return $userdnStringHex  
  102.     }  
  103. }  
  104. perform GetStoreId{  
  105.     param (  
  106.             $AutoDiscoverSettings = “$( throw ‘AutoDiscoverSettings is a mandatory Parameter’ )”  
  107.           )  
  108.     course of{  
  109.         $userdnString = $AutoDiscoverSettings.Settings[[Microsoft.Exchange.WebServices.Autodiscover.UserSettingName]::UserDN]  
  110.         $userdnHexChar = $userdnString.ToCharArray();  
  111.         foreach ($factor in $userdnHexChar) {$userdnStringHex = $userdnStringHex + [System.String]::Format(“{0:X}”, [System.Convert]::ToUInt32($factor))}   
  112.         $serverNameString = $AutoDiscoverSettings.Settings[[Microsoft.Exchange.WebServices.Autodiscover.UserSettingName]::InternalRpcClientServer]  
  113.         $serverNameHexChar = $serverNameString.ToCharArray();  
  114.         foreach ($factor in $serverNameHexChar) {$serverNameStringHex = $serverNameStringHex + [System.String]::Format(“{0:X}”, [System.Convert]::ToUInt32($factor))}  
  115.         $flags = “00000000”  
  116.         $ProviderUID = “38A1BB1005E5101AA1BB08002B2A56C2”  
  117.         $versionFlag = “0000”  
  118.         $DLLFileName = “454D534D44422E444C4C00000000”  
  119.         $WrappedFlags = “00000000”  
  120.         $WrappedProviderUID = “1B55FA20AA6611CD9BC800AA002FC45A”  
  121.         $WrappedType = “0C000000”  
  122.         $StoredIdStringHex = $flags + $ProviderUID + $versionFlag + $DLLFileName + $WrappedFlags + $WrappedProviderUID + $WrappedType + $serverNameStringHex + “00” + $userdnStringHex + “00”  
  123.         return $StoredIdStringHex  
  124.     }  
  125. }  
  126.   
  127.   
  128. perform hex2binarray($hexString){  
  129.     $i = 0  
  130.     [byte[]]$binarray = @()  
  131.     whereas($i -le $hexString.size – 2){  
  132.         $strHexBit = ($hexString.substring($i,2))  
  133.         $binarray += [byte]([Convert]::ToInt32($strHexBit,16))  
  134.         $i = $i + 2  
  135.     }  
  136.     return ,$binarray  
  137. }  
  138. perform ConvertId($EWSid){      
  139.     $aiItem = New-Object Microsoft.Alternate.WebServices.Information.AlternateId        
  140.     $aiItem.Mailbox = $MailboxName        
  141.     $aiItem.UniqueId = $EWSid     
  142.     $aiItem.Format = [Microsoft.Exchange.WebServices.Data.IdFormat]::EWSId;        
  143.     return $service.ConvertId($aiItem, [Microsoft.Exchange.WebServices.Data.IdFormat]::StoreId)       
  144. }   
  145.   
  146. #PropDefs   
  147. $pidTagStoreEntryId = new-object Microsoft.Alternate.WebServices.Information.ExtendedPropertyDefinition(4091, [Microsoft.Exchange.WebServices.Data.MapiPropertyType]::Binary)  
  148. $PidTagNormalizedSubject = new-object Microsoft.Alternate.WebServices.Information.ExtendedPropertyDefinition(0x0E1D,[Microsoft.Exchange.WebServices.Data.MapiPropertyType]::String);   
  149. $PidTagWlinkType = new-object Microsoft.Alternate.WebServices.Information.ExtendedPropertyDefinition(0x6849, [Microsoft.Exchange.WebServices.Data.MapiPropertyType]::Integer)  
  150. $PidTagWlinkFlags = new-object Microsoft.Alternate.WebServices.Information.ExtendedPropertyDefinition(0x684A, [Microsoft.Exchange.WebServices.Data.MapiPropertyType]::Integer)  
  151. $PidTagWlinkOrdinal = new-object Microsoft.Alternate.WebServices.Information.ExtendedPropertyDefinition(0x684B, [Microsoft.Exchange.WebServices.Data.MapiPropertyType]::Binary)  
  152. $PidTagWlinkFolderType = new-object Microsoft.Alternate.WebServices.Information.ExtendedPropertyDefinition(0x684F, [Microsoft.Exchange.WebServices.Data.MapiPropertyType]::Binary)  
  153. $PidTagWlinkSection = new-object Microsoft.Alternate.WebServices.Information.ExtendedPropertyDefinition(0x6852, [Microsoft.Exchange.WebServices.Data.MapiPropertyType]::Integer)  
  154. $PidTagWlinkGroupHeaderID = new-object Microsoft.Alternate.WebServices.Information.ExtendedPropertyDefinition(0x6842, [Microsoft.Exchange.WebServices.Data.MapiPropertyType]::Binary)  
  155. $PidTagWlinkSaveStamp = new-object Microsoft.Alternate.WebServices.Information.ExtendedPropertyDefinition(0x6847, [Microsoft.Exchange.WebServices.Data.MapiPropertyType]::Integer)  
  156. $PidTagWlinkGroupName = new-object Microsoft.Alternate.WebServices.Information.ExtendedPropertyDefinition(0x6851, [Microsoft.Exchange.WebServices.Data.MapiPropertyType]::String)  
  157. $PidTagWlinkStoreEntryId = new-object Microsoft.Alternate.WebServices.Information.ExtendedPropertyDefinition(0x684E, [Microsoft.Exchange.WebServices.Data.MapiPropertyType]::Binary)  
  158. $PidTagWlinkGroupClsid = new-object Microsoft.Alternate.WebServices.Information.ExtendedPropertyDefinition(0x6850, [Microsoft.Exchange.WebServices.Data.MapiPropertyType]::Binary)  
  159. $PidTagWlinkEntryId = new-object Microsoft.Alternate.WebServices.Information.ExtendedPropertyDefinition(0x684C, [Microsoft.Exchange.WebServices.Data.MapiPropertyType]::Binary)  
  160. $PidTagWlinkRecordKey = new-object Microsoft.Alternate.WebServices.Information.ExtendedPropertyDefinition(0x684D, [Microsoft.Exchange.WebServices.Data.MapiPropertyType]::Binary)  
  161. $PidTagWlinkCalendarColor = new-object Microsoft.Alternate.WebServices.Information.ExtendedPropertyDefinition(0x6853, [Microsoft.Exchange.WebServices.Data.MapiPropertyType]::Integer)  
  162. $PidTagWlinkAddressBookEID = new-object Microsoft.Alternate.WebServices.Information.ExtendedPropertyDefinition(0x6854,[Microsoft.Exchange.WebServices.Data.MapiPropertyType]::Binary)  
  163. $PidTagWlinkROGroupType = new-object Microsoft.Alternate.WebServices.Information.ExtendedPropertyDefinition(0x6892,[Microsoft.Exchange.WebServices.Data.MapiPropertyType]::Integer)  
  164. $PidTagWlinkAddressBookStoreEID = new-object Microsoft.Alternate.WebServices.Information.ExtendedPropertyDefinition(0x6891,[Microsoft.Exchange.WebServices.Data.MapiPropertyType]::Binary)  
  165.   
  166.   
  167. #Get the TargetUsers Calendar  
  168. # Bind to the Calendar Folder  
  169. $fldPropset = new-object Microsoft.Alternate.WebServices.Information.PropertySet([Microsoft.Exchange.WebServices.Data.BasePropertySet]::FirstClassProperties)    
  170. $fldPropset.Add($pidTagStoreEntryId);  
  171. $folderid= new-object Microsoft.Alternate.WebServices.Information.FolderId([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::Calendar,$TargetCalendarMailbox)     
  172. $TargetCalendar = [Microsoft.Exchange.WebServices.Data.Folder]::Bind($service,$folderid,$fldPropset)  
  173. #Verify for present ShortCut for TargetMailbox  
  174. #Get AddressBook Id for TargetUser  
  175. Write-Host (“Getting Autodiscover Settings Target”)  
  176. Write-Host (“Getting Autodiscover Settings Mailbox”)  
  177. $adset = GetAutoDiscoverSettings -adEmailAddress $MailboxName -Credentials $creds  
  178. $storeID = “”  
  179. if($adset -is [Microsoft.Exchange.WebServices.Autodiscover.AutodiscoverResponse]){  
  180.     Write-Host (“Get StoreId”)  
  181.     $storeID = GetStoreId -AutoDiscoverSettings $adset  
  182. }  
  183. $adset = $null  
  184. $abTargetABEntryId = “”  
  185. $adset = GetAutoDiscoverSettings -adEmailAddress $TargetCalendarMailbox -Credentials $creds  
  186. if($adset -is [Microsoft.Exchange.WebServices.Autodiscover.AutodiscoverResponse]){  
  187.     Write-Host (“Get AB Id”)  
  188.     $abTargetABEntryId = GetAddressBookId -AutoDiscoverSettings $adset  
  189.     $SharedUserDisplayName =  $adset.Settings[[Microsoft.Exchange.WebServices.Autodiscover.UserSettingName]::UserDisplayName]  
  190. }  
  191. Write-Host (“Getting CommonVeiwFolder”)  
  192. #Get CommonViewFolder  
  193. $folderid = new-object Microsoft.Alternate.WebServices.Information.FolderId([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::Root,$MailboxName)     
  194. $tfTargetFolder = [Microsoft.Exchange.WebServices.Data.Folder]::Bind($service,$folderid)    
  195. $fvFolderView = new-object Microsoft.Alternate.WebServices.Information.FolderView(1)   
  196. $SfSearchFilter = new-object Microsoft.Alternate.WebServices.Information.SearchFilter+IsEqualTo([Microsoft.Exchange.WebServices.Data.FolderSchema]::DisplayName,“Common Views”)   
  197. $findFolderResults = $service.FindFolders($tfTargetFolder.Id,$SfSearchFilter,$fvFolderView)   
  198. if ($findFolderResults.TotalCount -gt 0){   
  199.     $ExistingShortCut = $false  
  200.     $cvCommonViewsFolder = $findFolderResults.Folders[0]  
  201.     #Outline ItemView to retrive simply 1000 Gadgets      
  202.     #Discover Gadgets that are unread  
  203.     $psPropset= new-object Microsoft.Alternate.WebServices.Information.PropertySet([Microsoft.Exchange.WebServices.Data.BasePropertySet]::FirstClassProperties)    
  204.     $psPropset.add($PidTagWlinkAddressBookEID)  
  205.     $psPropset.add($PidTagWlinkFolderType)  
  206.     $ivItemView =  New-Object Microsoft.Alternate.WebServices.Information.ItemView(1000)     
  207.     $ivItemView.Traversal = [Microsoft.Exchange.WebServices.Data.ItemTraversal]::Related  
  208.     $ivItemView.PropertySet = $psPropset  
  209.     $fiItems = $service.FindItems($cvCommonViewsFolder.Id,$ivItemView)      
  210.     foreach($Merchandise in $fiItems.Gadgets){  
  211.         $aeidVal = $null  
  212.         if($Merchandise.TryGetProperty($PidTagWlinkAddressBookEID,[ref]$aeidVal)){  
  213.                 $fldType = $null  
  214.                 if($Merchandise.TryGetProperty($PidTagWlinkFolderType,[ref]$fldType)){  
  215.                     if([System.BitConverter]::ToString($fldType).Exchange(“-“,“”) -eq “0278060000000000C000000000000046”){  
  216.                         if([System.BitConverter]::ToString($aeidVal).Exchange(“-“,“”) -eq $abTargetABEntryId){  
  217.                             $ExistingShortCut = $true  
  218.                             Write-Host “Found existing Shortcut”  
  219.                             ###$Merchandise.Delete([Microsoft.Exchange.WebServices.Data.DeleteMode]::SoftDelete)  
  220.                         }  
  221.                     }  
  222.                 }  
  223.             }                               
  224.     }  
  225.     if($ExistingShortCut -eq $false){  
  226.         If($storeID.size -gt 5 -band $abTargetABEntryId.size -gt 5){  
  227.             $objWunderBarLink = New-Object Microsoft.Alternate.WebServices.Information.EmailMessage -ArgumentList $service    
  228.             $objWunderBarLink.Topic = $SharedUserDisplayName    
  229.             $objWunderBarLink.ItemClass = “IPM.Microsoft.WunderBar.Link”    
  230.             $objWunderBarLink.SetExtendedProperty($PidTagWlinkAddressBookEID,(hex2binarray $abTargetABEntryId))    
  231.             $objWunderBarLink.SetExtendedProperty($PidTagWlinkAddressBookStoreEID,(hex2binarray $storeID))    
  232.             $objWunderBarLink.SetExtendedProperty($PidTagWlinkCalendarColor,-1)  
  233.             $objWunderBarLink.SetExtendedProperty($PidTagWlinkFlags,0)  
  234.             $objWunderBarLink.SetExtendedProperty($PidTagWlinkGroupName,“Shared Calendars”)  
  235.             $objWunderBarLink.SetExtendedProperty($PidTagWlinkFolderType,(hex2binarray “0278060000000000C000000000000046”))    
  236.             $objWunderBarLink.SetExtendedProperty($PidTagWlinkGroupClsid,(hex2binarray “B9F0060000000000C000000000000046”))    
  237.             $objWunderBarLink.SetExtendedProperty($PidTagWlinkROGroupType,-1)  
  238.             $objWunderBarLink.SetExtendedProperty($PidTagWlinkSection,3)    
  239.             $objWunderBarLink.SetExtendedProperty($PidTagWlinkType,2)    
  240.             $objWunderBarLink.IsAssociated = $true  
  241.             $objWunderBarLink.Save($findFolderResults.Folders[0].Id)  
  242.             Write-Host (“ShortCut Created for – “ + $SharedUserDisplayName)  
  243.         }  
  244.         else{  
  245.             Write-Host (“Error with Id’s”)  
  246.         }  
  247.     }  
  248. }  

Leave a Reply