Modify the array text condition in an Outlook rule with PowerShell? - email

I work on a team, who manage a few hundred servers. We each take primary responsibility for about 100 servers. I am the new person on the team, so I have a rule "MyServers" in outlook that makes a special sound and moves emails in to the folder "MyServers", when an email comes in with the name of one of my servers in the subject or body. Servers come and go, and the responsible person changes occasionally. I can use the GUI to modify the list, but what I want to do is use PowerShell to modify the list of servers based on a data set from a SQL query on our table of whom belongs to what. (also would be helpful when covering for someone else.)
Per PowerShell - Managing an Outlook Mailbox with PowerShell, By Joe Leibowitz, March 2013 it is possible in theory. That article and the post Hey, Scripting Guy! How Can I Tell Which Outlook Rules I Have Created? December 15, 2009 by ScriptingGuy1 have taught me how to get outlook files into PowerShell to read and or write. The post Multiple Actions for one Outlook rule in Powershell has also been helpful.
I can find several examples of creating rules, mostly around email addresses. As I did more research (below) it seems like the I want to modify 'TextRuleCondition.Text' but I am not finding any example code that gets in to reading OR modifying rule conditions for a single existing rule.
Specifying Rule Conditions
TextRuleCondition Object (Outlook)
TextRuleCondition.Text Property (Outlook)
Optimally: I would like to go to the "MyServers" rule and change the array, from what it is to a new array that I will build with PowerShell, after getting the list from a SQL table.
##source https://blogs.technet.microsoft.com/heyscriptingguy/2009/12/15/hey-scripting-guy-how-can-i-tell-which-outlook-rules-i-have-created/
## Gets outlook rules on PC
#Requires -version 2.0
Add-Type -AssemblyName microsoft.office.interop.outlook
$olFolders = “Microsoft.Office.Interop.Outlook.OlDefaultFolders” -as [type]
$outlook = New-Object -ComObject outlook.application
$namespace = $Outlook.GetNameSpace(“mapi”)
$folder = $namespace.getDefaultFolder($olFolders::olFolderInbox)
$rules = $outlook.session.DefaultStore.<Some code here gets TextRuleCondition.Text for just MyServers>
$rules ## this displays the current value
##More code inserts the array that I built earlier (not actually built, yet as I don't know what it should look like)
$rules.Save() ## this saves the changes.
Everything I have found so far programmatically creates an entire new rule from PowerShell. Nothing indicates if it is, or is not possible to modify an existing rule. My Plan "B" would be to read the existing "MyServers" rule, modify the array, and overwrite the old rule with a new one. This is problematic as it limits options, only some conditions and actions can be created programmatically.

#setup
Add-Type -AssemblyName microsoft.office.interop.outlook
$olFolders = “Microsoft.Office.Interop.Outlook.OlDefaultFolders” -as [type]
$outlook = New-Object -ComObject outlook.application
$namespace = $Outlook.GetNameSpace(“mapi”)
#get all of the rules
$rules = $outlook.Session.DefaultStore.GetRules()
#get just the rule I care about
$myRule = $rules | ? { $_.Name -eq 'My Awesome Rule' }
#build my new array of text conditions
$textValues = #('ServerA', 'NewServerB')
#replace the existing value on my rule (this is assuming you are using BodyOrSubject, you can substitute Body, Subject, etc)
$myRule.Conditions.BodyOrSubject.Text = $textValues
#save all the rules
$rules.save()

Related

Accessing the "Date Last Saved" using PowerShell

I am attempting to access the Date Last Saved of an xls file using PowerShell. It is in the details page and is more of a hidden attribute of the file. Pic attached for reference.
EDIT: Thank you for the help. Both solutions work but I am in a constricted language mode so I can't use them :(
Went down a bit of a rabbit hole for this one but I found the below.
The attribute is not part of the file properties. It is part of the worksheets properties (as are many attributes).
Full credit goes to Ed Wilson and Craig Liebendorfer, Scripting Guys - https://devblogs.microsoft.com/scripting/hey-scripting-guy-how-can-i-read-microsoft-excel-metadata/
$excel = New-Object -ComObject Excel.Application
$excel.Visible = $false
$workbook = $excel.Workbooks.Open("C:\temp\Test.xlsx")
$binding = "System.Reflection.BindingFlags" -as [type]
Foreach($property in $workbook.BuiltInDocumentProperties){
if ([System.__ComObject].invokemember("name",$binding::GetProperty,$null,$property,$null) -eq "Last save time"){
[System.__ComObject].invokemember("value",$binding::GetProperty,$null,$property,$null)
}
}
$excel.quit()
Previously I had an answer discussing how to retrieve basic file info, but to access Office file info, you have to do a bit more work...
Using this answer from a previous question, I made a PowerShell function to make this easy for you.
Source Here on github
Usage
Get-OfficeFileInfo C:\temp\UsersOfabc.comDomain.xlsx
Name Exp
---- ---
Title
Subject
Author
Keywords
Comments
Template
Last author Stephen Owen
Revision number
Application name Microsoft Excel
Creation date 7/21/2021 11:30:51 AM
Last save time 7/21/2021 11:30:51 AM
Security 0
Category
Format
Manager
Company
Hyperlink base
Content type
Content status
Language
Document version
Getting the specific property you want
$fileInfo = Get-OfficeFileInfo C:\temp\UsersOfabc.comDomain.xlsx
$dateSaved = $fileInfo | ? Name -eq "Last save time"
C:\temp\> $dateSaved.Exp
Wednesday, July 21, 2021 11:30:51 AM

Search emails by subject using powershell

All,
I'm stuck at the following. I get list of emails in my inbox, and I need to search emails that contain specific string in subject (and then parse body of that email). I'm stuck at getting right syntax for filtering emails by subject.
I have this:
$Outlook = New-Object -comObject Outlook.Application
$namespace = $Outlook.GetNameSpace("MAPI")
$inbox = $namespace.GetDefaultFolder([Microsoft.Office.Interop.Outlook.OlDefaultFolders]::olFolderInbox)
$emails = $inbox.items
$subjectComparisonExpression = "Meeting topic is: "
But then none of these work:
#1
$inbox.items | Where-Object {$_.Subject -like $subjectComparisonExpression} | Write-Host($_.Subject)
#2
$myemails = $inbox.items | Where-Object {$_.Subject -like $subjectComparisonExpression}
Write-Host($myemails.count)
#3
$myemails = $emails | Where {$_.Subject -like $subjectComparisonExpression}
Write-Host($myemails.count)
How do I get list of emails where subject contains $subjectComparisonExpression?
Use the Find/FindNext or Restrict methods of the Items class to find items that correspond to your conditions. The Restrict method is an alternative to using the Find method or FindNext method to iterate over specific items within a collection. The Find or FindNext methods are faster than filtering if there are a small number of items. The Restrict method is significantly faster if there is a large number of items in the collection, especially if only a few items in a large collection are expected to be found. You can read more about them in the following articles:
How To: Use Find and FindNext methods to retrieve Outlook mail items from a folder (C#, VB.NET)
How To: Use Restrict method to retrieve Outlook mail items from a folder
Problem was with '-like' keyword. Replaced it with '-match' and it works.

PowerShell SetAccessRule for ACL failing

The script I've done creates a folder and security groups that is later added to the folder in question with special permissions. The problem is that the script fails on "$acl.SetAccessRule($rule_modify)" and complains about the identity.
Error:
"Exception calling "SetAccessRule" with "1" argument(s): "Some or all identity references could not be translated."
If I run the script row by row by simply using copy/paste from ISE into a regular PowerShell window everything goes through without errors using the same location and user.
This is the important bit that isn't working.
#Get ACL list
$acl = Get-Acl -Path $Path
$acl.SetAccessRuleProtection($false,$false)
#Add permission for modify
$set_modify = "INTRA\FIL_$($Department)_$($Group)_Modify", 'DeleteSubdirectoriesAndFiles, Write, ReadAndExecute, Synchronize', 'ContainerInherit, ObjectInherit', 'None', 'Allow'
$rule_modify = New-Object -TypeName System.Security.AccessControl.FileSystemAccessRule -ArgumentList $set_modify
$acl.SetAccessRule($rule_modify)
$acl | Set-Acl $path
I had basicly the same issue but with "AddAccessRule".
$Acl.AddAccessRule($Ar)
My Script failed with the above mentioned error.
Just as Patrik Persson mentioned, in my case it was also because AD was slow on showing new groups and the Ar could not be added until the security group showed up in my AD.
So i added a try/catch with a do/until loop to my script which i want to share:
do {
$check = 'ok'
try {
$Acl.AddAccessRule($Ar)
} catch [System.Management.Automation.RuntimeException] {
$_.Exception.Message
$check = 'error'
Start-Sleep -Seconds 2
}
} until (
$check -eq 'ok'
)
So the loop continues until AD registers the security group correctly. After that the Ar is added to the acl and my script continues as expected.
I have found the solution and it fails because ActiveDirectory is too slow to recognize that the security group is created propery before adding it to the ACL.
Solution I went with was to add a 10 second sleep after groups and folder was created and it now works as intended.
You might want to expand all the strings you're using to build your security group name into a clean variable - I find that can be touchy. Make sure that $secgroup contains the right string value when you've constructed it.
You can create the rule together with the object type on one line as well.
$secgroup = "INTRA\FIL_$($Department)_$($Group)_Modify"
$modifyRule = New-Object System.Security.AccessControl.FileSystemAccessRule($secgroup,'DeleteSubdirectoriesAndFiles, Write, ReadAndExecute, Synchronize','ContainerInherit, ObjectInherit','None','Allow')
By the way, if essentially you want your users to have Modify rights on the contents without being able to delete the parent folder, it should work if you set the InheritOnly flag (I haven't tested it).
$modifyRule = New-Object System.Security.AccessControl.FileSystemAccessRule($secgroup,'Modify, Synchronize', 'ContainerInherit, ObjectInherit','InheritOnly','Allow')
Had a similar issue while creating shares on remote file servers.
At first, I'd used the proposed solution (start-sleep), but it was not good enough as it significantly increased the time consumed while processing a lot of shares.
It turns out that you may use SID while defining your ACE and operation is instant:
$TempSID = (Get-ADGroup "FIL_$($Department)_$($Group)_Modify").SID
$PermissionModify = "Write, Read, ListDirectory, ReadAndexecute, DeleteSubdirectoriesAndFiles"
$Inherit = [system.security.accesscontrol.InheritanceFlags]"ContainerInherit, ObjectInherit"
$Propagation = [system.security.accesscontrol.PropagationFlags]"None"
$Type = "Allow"
$modifyRule = New-Object System.Security.AccessControl.FileSystemAccessRule($TempSID, $PermissionModify, $Inherit, $Propagation, $Type)
Seems that Active Directory needs some time (in my case 2 seconds) to translate SID to the group name (in DOMAIN\groupName format).
Another case: I have got the error Exception calling SetAccessRule... because I have set the account without the domain, so - with the question's example - I have put .\FIL_$($Department)_$($Group)_Modify instead of INTRA\FIL_$($Department)_$($Group)_Modify.

EWS Category search

i've a script on powershell to manage mailbox using EWS, however i'm not able to user the current filters and filter certain categories.
I would like filter categories that start by _ or * and apply to my current filters
$sfRead = New-Object Microsoft.Exchange.WebServices.Data.SearchFilter+IsEqualTo([Microsoft.Exchange.WebServices.Data.EmailMessageSchema]::IsRead, $True)
$WIPSubject = New-Object Microsoft.Exchange.WebServices.Data.SearchFilter+ContainsSubstring([Microsoft.Exchange.WebServices.Data.ItemSchema]::Subject, "Assigned")
$sfNot = New-Object Microsoft.Exchange.WebServices.Data.SearchFilter+Not($WIPSubject)
$sfCollection = new-object Microsoft.Exchange.WebServices.Data.SearchFilter+SearchFilterCollection([Microsoft.Exchange.WebServices.Data.LogicalOperator]::And)
$sfCollection.add($sfRead)
$sfCollection.add($sfNot)
If you want to do a wildcard search on the Subject then I would suggest that you use AQS for queries instead https://msdn.microsoft.com/en-us/library/office/dn579420(v=exchg.150).aspx . SearchFilters don't support wildcards you have the ContainsSubString filter which will find partial string matches https://msdn.microsoft.com/en-us/library/office/dd633645(v=exchg.80).aspx which is the closest.

Unable to set Outlook email UnRead property to false via MAPI with Powershell

I don't appear to be able to write changes to Outlook via MAPI, the .UnRead variable is being set correctly to false within the script if you Write-Output it, but the variables don't appear to manipulate the actual .PST file. The select produces the correct emails, so read access to the .PST is fine.
Here is the code I am using to retrieve a list of unread emails from a PST folder, and set one of them to read:
$Outlook = new-object -comobject "Outlook.Application";
$Mapi = $Outlook.getnamespace("mapi");
$Pst = $Mapi.Folders.Item("Personal Folders")
$Folder = $Pst.Folders.Item("Test")
$Emails = $Folder.Items | Select UnRead, SenderEmailAddress, Subject, ReceivedTime, Body | Where {$_.Unread -eq "True"}
$Emails[1].UnRead = $false
Most examples I have seen say to place the variable in brackets, e.g.
$($Emails)[1].UnRead = $false
But this has made no difference for me.
Interestingly I get a 'method not found' error when I try to use the .delete() as well, hence I think I must be missing something.
Many thanks in advance for any advice.
Call MailItem.Save.
Do not loop through all items in a folder, use Items.Find/FindNext or Items.Restrict.
You've change property of your own object, but not on mail server