Adding Azure Information Protection Label to Powershell script using Outlook 2016 - azure

I'm trying to add a custom header for AIP's msip_labels to a Powershell script that I'm writing. I've figured out how to do this with .Net.SMTP using:
$message.Headers.Add("msip_labels","MSIP_Label_aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee_Enabled=True; MSIP_Label_aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee_SiteId=00000000-1111-2222-3333-444444444444; MSIP_Label_aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee_Owner=user2#domain.tld; MSIP_Label_aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee_SetDate=$((Get-Date).ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ss.fffffffZ")); MSIP_Label_aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee_Name=Internal; MSIP_Label_aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee_Application Microsoft Azure Information Protection; MSIP_Label_aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee_ActionId ffffffff-5555-6666-7777-888888888888; MSIP_Label_aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee_Extended_MSFT_Method Manual")
Based on research I've done this should work using Outlook 2016 in theory:
$Outlook = New-Object -ComObject Outlook.Application
$message = $Outlook.CreateItem(0)
$message.PropertyAccessor.SetProperty("http://schemas.microsoft.com/mapi/string/{00020386-0000-0000-C000-000000000046}/msip_labels", "MSIP_Label_aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee_Enabled=True; MSIP_Label_aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee_SiteId=00000000-1111-2222-3333-444444444444; MSIP_Label_aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee_Owner=user2#domain.tld; MSIP_Label_aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee_SetDate=$((Get-Date).ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ss.fffffffZ")); MSIP_Label_aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee_Name=Internal; MSIP_Label_aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee_Application=Microsoft Azure Information Protection; MSIP_Label_aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee_ActionId=ffffffff-5555-6666-7777-888888888888; MSIP_Label_aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee_Extended_MSFT_Method=Manual")
$message.To = "user1#domain.tld"
$message.Cc = "user3#domain.tld"
$message.Subject = "Report"
$message.HTMLBody = #"
<p><font face = "Calibri" size = "3">Hello World</p></font>
"#
$reportMessage.Send()
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($Outlook) | Out-Null
I confirmed this using a MAPI viewer that this is what's used in Outlook itself with other emails I've sent using just Outlook. But, when I tried running this in my script I get this error:
Exception setting "SetProperty": Cannot convert the "MSIP_Label_aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee_Enabled=True; MSIP_Label_aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee_SiteId=00000000-1111-2222-3333-444444444444;
MSIP_Label_aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee_Owner=user2#domain.tld; MSIP_Label_aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee_SetDate=$((Get-Date).ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ss.fffffffZ")); MSIP_Label_aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee_Name=Internal;
MSIP_Label_aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee_Application=Microsoft Azure Information Protection; MSIP_Label_aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee_ActionId=ffffffff-5555-6666-7777-888888888888;
MSIP_Label_aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee_Extended_MSFT_Method=Manual" value of type "string" to type "Object".
At C:\emailtest.ps1:21 char:1
+ $message.PropertyAccessor.SetProperty("http://schemas.microsoft ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodException
+ FullyQualifiedErrorId : RuntimeException
Which doesn't seem to make much sense it's suppose to be a string in the MAPI schema, so I'm not sure why it thinks it should be an object. I even tried converting those values to objects using ConvertFrom-String, but it didn't work. Any advice on this would be greatly appreciated.

Try to reduce the string length you pass as a value. Does it work correctly?
It seems you need to use a low-level API on which Outlook is based on - Extended MAPI. It doesn't have any restrictions on the string length unlike OOM if you use the OpenProperty method. Also, you may consider using any third-party wrapper around that API such as Redemption.
If you use PropertyAccessor, you must have good knowledge of exception handling logic. Below I list some roadblocks that you may run into:
The body and content of an attachment of an Outlook items are not accessible through PropertyAccessor.
The PropertyAccessor ignores any seconds of the date/time value
String properties are limited in size depending on the information store type.
Limitation: Personal Folders files (.pst) and Exchange offline folders files (.ost) cannot be more than 4,088 bytes.
Limitation: For direct online access to Exchange mailbox or Public Folders hierarchy, the limit is 16,372 bytes.
Binary properties only those whose values are under 4,088 byte can be retrieved or set. (If trying to use larger values, you get an out-of-memory error).
You may find the Don't stumble over a stone working with the PropertyAccessor and StorageItem classes in Outlook 2007 article helpful.

It worked for me by appending a null character at the end of the string ( "`0" ) This is required by type "PtypString", required by property.
https://learn.microsoft.com/en-us/openspecs/exchange_server_protocols/ms-oxcdata/0c77892e-288e-435a-9c49-be1c20c7afdb

Related

Outlook OfficeJS API cannot resolve content type if extension is in uppercase

OfficeJS does not seem to be able to resolve the content type of a file if the file extension is uppercase. This can be easily reproduced by attaching a file with an uppercase file name (e.g. FOO.JPEG) and executing the following code:
window.Office.context.mailbox.item.attachments.forEach(function(file) { console.log("name: " + file.name + " type:" + file.contentType);})
Is this a bug?
Office.context.mailbox.item.attachments returns Array of AttachmentDetails. The AttachmentDetails represents an attachment on an item from the server and contains properties of the attachment, for example name, size, etc. One of the property is contentType. All of them are just the Exchange properties and whatever Exchange has set into those properties, the JavaScript API will gives you. For example, the following is the attachment with small letters extension ...
Exchange has completed the PR_ATTACH_MIME_TAG_W property with content type. If you will use Office Js API contentType will return this value. In the same time, the attachment with all CAPS (on our Exchange server) would not have the PR_ATTACH_MIME_TAG_W at all ...
This happen because Exchange doesn't have mapping "JPEG" extension. Obviously if you would use Office Js API contentType function it will return empty string. This is exactly what you observe.
To add this mapping you would need to refer to your System Administrator. As long as the mapping will be added to your environment, Exchange will be resolving this file extensions and Office Js API will deliver this value to you.

how to create valid message object (LPMessage) in Extended MAPI

I have existing email client in MFC application (vs2008). I want to save an email to .msg file.
So I investigated and found one sample
Searched for reference of LPMessage and found that, they created from existing .msg file or message object. In my application I dont have message object from which I can build message object. I have to create it from scratch. I am not sure , that I am investigating in right direction or there are other simpler ways also. Can any one help me on this ?
Use StgCreateStorageEx(TGM_CREATE | STGM_TRANSACTED | STGM_READWRITE, STGFMT_DOCFILE) (or StgCreateDocfile) / OpenIMsgSession / OpenIMsgOnIStg / WriteClassStg.
See https://support.microsoft.com/en-us/help/171907/info-save-message-to-msg-compound-file

IIS 6 ServerBindings with WMI and Powershell v2

I've long had a bunch of VBS automations for IIS 6, including one that gets/sets complex server bindings on several farms of paired servers, each having dozens of apps, each app having 3-12 host headers. Each app has hostname, hostname-fullyqualified, and Disaster Recovery enabled hostname, so they can be a mess to maintain manually.
I did all my vbs stuff using ADSI, but I'm thinking WMI is probably more flexible than ADSI from a full server maintenance perspective. Please correct me if I'm wrong. So now I'm trying to move up to PowerShell + WMI to prepare for Windows 2008 + IIS 7.5. I'm enjoying the learning process, but I've hit a roadblock on this problem.
I can get/set all properties via WMI on my IIS 6 web servers, except ServerBindings. I feel like I'm close, but I'm missing some layer of containment, and I just can't get the objects I'm building to cast over to the right automation object.
The following code gets and reads the ServerBindings just fine. I simply can't figure out a way to write my changes back. Any advice is welcomed.
$objWMI = [WmiSearcher] "Select * From IISWebServerSetting"
$objWMI.Scope.Path = "\\" + $server + "\root\microsoftiisv2"
$objWMI.Scope.Options.Authentication = 6
$sites = $objWMI.Get()
foreach ($site in $sites)
{
$bindings = $site.psbase.properties | ? {$_.Name -contains "ServerBindings"}
foreach ($pair in $bindings.Value.GetEnumerator())
{
# The pair object is a single binding and contains the correct data
$pair
$pair.IP
$pair.Port
$pair.Hostname
# And this line will successfully erase the contents of
# the ServerBindings
$bindings.Value = #{}
# but I can't figure out what to do to update $bindings.Value
}
$site.Put()
}
I'm liking Powershell so far, so thanks for any help you're able to offer.
Alright. I got distracted with major disk failures. The fun never stops.
Anyway, the solution to this problem is simpler than I'd made it:
process
{
$bindings = $_.ServerBindings
foreach ($binding in $bindings)
{
$binding.IP = $ip
$binding.Port = $port
$binding.Hostname = $hostname
}
$_.ServerBindings = $bindings
$_.Put()
}
ServerBindings is an array, but it likes to be an array of its own kind. I was trying to build the array from scratch, but my home-rolled array didn't smell right to Powershell. So, pull the array out of ServerBindings into a new variable, manipulate the variable, then assign the manipulated variable back to the ServerBindings property. That keeps all the right typing in place. It's smooth as silk, and seems easier than ADSI.

Extending WSS3 task lists to support recurring reminders

WSS 3.0 will let me send an email to a group when a new task is added to a task list. What I would like to do is to run a weekly task that sends out reminders of tasks due within certain periods, i.e. 2 days, 7 days, 14 days etc. I thought the simplest way would be to build a little C# app that sits on the WS2K3 box and queries the WSS database. Any ideas on which tables I should be checking? More generally is there an overall schema for the WSS3 database system?
If anyone is aware of an existing solution with code please let me know.
Thx++
Jerry
My suggestions:
don't create a console app, create a class that inherits from SPJobDefinition.
set SPJobLockType.Job to this timer, this will grant that the job is executed only once in the whole farm, even if you are running multiple front-end servers
in the, timer job, open the SPSite, SPWeb objects you need, then find the SPList\
Using SPQuery filter out only the items you need - I believe, you will have to filter out the ones where Status!=Complete
Loop through the results collection (which will be of type SPListItemCollection, apply your rules, checking the DueDate and Datetime.Now, send the e-mails
Since a task is simply a SPListItem, it has a Properties property, which is actually a property bag - you can add whatever properties you need. So, add a property My_LastSentReminderDate. Use this property to check if you are not sending too much of "corporate spam" :-)
To install your SPJobDefinition in a SharePoint farm, you can use a PowerShell script. I can give you examples, if needed.
Don't forget to Threading.Thread.CurrentThread.CurrentCulture = Your_SPWeb_Instance.Locale, otherwise date comparisons may not work if the web has a different locale!
EDIT: This is how a typical reminder looks like in my applications:
Public Class TypicalTimer
Inherits SPJobDefinition
Public Sub New(ByVal spJobName As String, ByVal opApplication As SPWebApplication)
'this way we can explicitly specify we need to lock the JOB
MyBase.New(spJobName, opApplication, Nothing, SPJobLockType.Job)
End Sub
Public Overrides Sub Execute(ByVal opGuid As System.Guid)
'whatever functionality is there in the base class...
MyBase.Execute(Guid.Empty)
Try
Using oSite As SPSite = New SPSite("http://yourserver/sites/yoursite/subsite")
Using oWeb As SPWeb = oSite.OpenWeb()
Threading.Thread.CurrentThread.CurrentCulture = oWeb.Locale
'find the task list and read the "suspects"
Dim oTasks As SPList = oWeb.Lists("YourTaskListTitle")
Dim oQuery As New SPQuery()
oQuery.Query = "<Where><Neq><FieldRef Name='Status'/>" & _
"<Value Type='Choice'>Complete</Value></Neq></Where>"
Dim oUndoneTasks As SPListItemCollection = oTasks.GetItems(oQuery)
'extra filtering of the suspects.
'this can also be done in the query, but I don't know your rules
For Each oUndoneTask As SPListItem In oUndoneTasks
If oUndoneTask(SPBuiltInFieldId.TaskDueDate) IsNot Nothing AndAlso _
CDate(oUndoneTask(SPBuiltInFieldId.TaskDueDate)) < Now().Date Then
' this is where you send the mail
End If
Next
End Using
End Using
Catch ex As Exception
MyErrorHelper.LogMessage(ex)
End Try
End Sub
End Class
To register a timer job, I typically use this kind of a script:
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint")
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint.Administration")
[System.Reflection.Assembly]::LoadWithPartialName("Your.Assembly.Name.Here")
$spsite= [Microsoft.SharePoint.SPSite]("http://yourserver/sites/yoursite/subsite")
$params = [System.String]("This text shows up in your timer job list (in Central Admin)", $spsite.WebApplication
$newTaskLoggerJob = new-object -type Your.Namespace.TypicalTimer -argumentList $params
$schedule = new-object Microsoft.SharePoint.SPDailySchedule
$schedule.BeginHour = 8
$schedule.BeginMinute = 0
$schedule.BeginSecond = 0
$schedule.EndHour = 8
$schedule.EndMinute = 59
$schedule.EndSecond = 59
$newTaskLoggerJob.Schedule = $schedule
$newTaskLoggerJob.Update()
Any time you need something in sharepoint that is executed periodically, 99 times out of a 100 you'll need to build a TimerJob. These are scheduled tasks that run inside SharePoint and you can create your own, then using a feature + featurereceiver to actually "install" the timoerjob (definition) and assign it a schedule.
For more info: see Andrew Connell's article on TimerJobs.
P.S. Never query /update the databases related to SharePoint directly! This will make you "unsupported", i.e. if anything happens microsoft will charge (a lot of) money to come and fix it, instead of being able to ask for regular support. (if you are say an MSDN subscriber you get up to 4 free support calls a year).
Don't bother trying to go directly to the database. You will have a very hard time because it's undocumented, unsupported, and not recommended. SharePoint does in fact have a full featured object model though.
If you reference Microsoft.SharePoint.dll (located in the Global Assembly Cache of a machine with SharePoint installed on it) you can access the data that way. The objects you'll want to start with are SPSite, SPWeb, SPList, SPQuery, and SPListItem. All of which you can find very easily on http://msdn.microsoft.com in a search.
Another less-flexible but code-free possibility you could try is creating several different views that include upcoming tasks then via the GUI set up an alert for when items are added to that view.

COM Integration from ALBPM - Cannot find IDispatch for '{00020906-0000-0000-C000-000000000046}'

I am trying to use the Office COM components in order to create Word and Excel documents. Unfortunately I can not achieve this because I am getting an error.
Cannot find IDispatch for
'{00020906-0000-0000-C000-000000000046}'
in module
'{00020905-0000-0000-C000-000000000046}',
v8.3
I tried reinstalling Office, my application (ALBPM) and my interface (combsvc) but it is not working.
I want to know how can I install IDispatch, or how can I know if it is installed in the correct module. Some times the error says:
Cannot find IDispatch for
'{000209FF-0000-0000-C000-000000000046}'
... instead of
00020906-0000-0000-C000-000000000046
The code I'm using generate these errors is:
wordAppl.visible = false
wordDocs = wordAppl.documents
contratoTemplate = "C:\\albpmFiles\\mandatory\\aTemplate.doc"
// .doc template
convenioTemplate = "C:\\albpmFiles\\mandatory\\ConvenioModificatorio.doc"
// .doc template
saveContrato = "C:\\albpmFiles\\temp\\"
// where to save.
saveConvenio = "C:\\albpmFiles\\temp\\"
contratoName = "NewContact.doc"
wordDoc = open(wordDocs, fileName : contratoTemplate)
bookmark = item(wordDoc.bookmarks, index : "atrDescripcion")
insertAfter bookmark.range
using text = instSolicitud.atrDescripcion
bookmark = item(wordDoc.bookmarks, index : "atrObjProveedor_atrNombre")
insertAfter bookmark.range
using text = instSolicitud.atrObjProveedor.atrNombre
bookmark = item(wordDoc.bookmarks, index : "atrObjProveedor_atrDireccion")
insertAfter bookmark.range
using text = instSolicitud.atrObjProveedor.atrDireccion
filename = saveContrato + contratoName
end
// Extras - Fin
saveAs wordDoc
using fileName = filename
Any information you have about the IDispatch, or these registry entries, well be very appreciated, even if you can tell me where to find more info about this.
Thanks a lot.
Daniel.
From the error you get I assume that you are using Word 2003.
Have you made sure that the COM brigde service is correctly installed and running?
combsvc -install
combsvc -start
will register combsvc as service and then start it.
Please also have a look at the example for Word at the bottom of page 150 in the ALBPM Reference Guide.
The fact that it is sometimes working and sometimes could be an issue with ALBPM. Are you using the latest version and updates?
Another option - and quite frequent problem with Word automation - would be that the automated instance of Word is displaying a modal dialog box and is waiting for user interaction. You can switch of the display of modal dialogs by setting
Application.DisplayAlerts = 0
However, this will unfortunately not prevent all popups from being displayed.
Is there actually an instance of Word started? If so, can you make the Window visible and see if documents can be opened or if there is a popup blocking the application?
Daniel,
I'm taking a stab in the dark here. It looks like you're using BEA systems Aqualogic BPM which I have a feeling is a Java based tool. From digging about it looks like combsvc is actually a COM bridge service to allow ALBPM to speak to COM from Java:
http://edocs.bea.com/albsi/docs60/studio/index.html?t=studio/catalog/catalog_component/COM/c_COM_Bridge.html
I'm thinking this is your point of failure.
About your question on IDispatch, you don't actually install IDispatch. IDispatch is a interface used by COM to expose objects, methods and properties to late bound COM automation clients such as scripting languages (e.g. ASP or VBScript). It's part of the infrastructure of COM, if this was broken you'd see lots more problems with your machine.
I'd probably advise popping a question in here:
http://forums.oracle.com/forums/forum.jspa?forumID=560
To inspect installed COM Interfaces on your PC i suggest you download oleview.exe which is part of the Windows 2003 resource Kit
I actually have {00020906-0000-0000-C000-000000000046} but also no IDispatch interface and get a "Class not registered" error when trying to create an instance of it. My home PC doesn't have office installed just the Office tools which is most likely the cause.
In the past when automating Office Applications i was always able to talk to a version independent ProgID's such as "Excel.Application". Are you sure your referencing the right COM Objects ? Check it out in oleview or give us some more code to munch on :)
The code I am using is this, but I can not even see the first log, so I assume thereĀ“s an error with the conection, not with the code
wordAppl.visible = false
wordDocs = wordAppl.documents
contratoTemplate = "C:\\albpmFiles\\mandatory\\aTemplate.doc"
// .doc template
convenioTemplate = "C:\\albpmFiles\\mandatory\\ConvenioModificatorio.doc"
// .doc template
saveContrato = "C:\\albpmFiles\\temp\\"
// where to save.
saveConvenio = "C:\\albpmFiles\\temp\\"
contratoName = "NewContact.doc"
wordDoc = open(wordDocs, fileName : contratoTemplate)
bookmark = item(wordDoc.bookmarks, index : "atrDescripcion")
insertAfter bookmark.range
using text = instSolicitud.atrDescripcion
bookmark = item(wordDoc.bookmarks, index : "atrObjProveedor_atrNombre")
insertAfter bookmark.range
using text = instSolicitud.atrObjProveedor.atrNombre
bookmark = item(wordDoc.bookmarks, index : "atrObjProveedor_atrDireccion")
insertAfter bookmark.range
using text = instSolicitud.atrObjProveedor.atrDireccion
filename = saveContrato + contratoName
end
// Extras - Fin
saveAs wordDoc
using fileName = filename

Resources