Exchange online - move messages with powershell - powershell

I have a huge mailbox (~50Gb) with plenty of messages in 'Inbox' . I'm looking for moving certain messages received before particular date to another folder at the same mailbox.
I have tried to do it from Outlook Windows app but it seems too slow and I can't do it for all the messages at once. Outlook just crash.
Is there any way to perform the task from Exchange Powershell ? I can certainly create server side rule but how to apply it to the messages already in 'Inbox' ?
New-InboxRule -Name Testmove2018 -Mailbox test -MoveToFolder "MailboxName:\2018" -ReceivedAfterDate "09/01/2015"
Oleg

The Exchange Online powershell module still allows you to copy and delete messages matching a search with Search-Mailbox, but you must copy them elsewhere first. Use an empty mailbox:
# First, check your search filter using EstimateOnly or LogOnly:
Search-Mailbox myUser#domain.com -SearchQuery 'Subject:Move Me' -EstimateResultOnly
# Copy the items to a temp mailbox and delete from the primary
# (EXO does not allow copying search results to same mailbox)
# param block just for readability:
$Params = #{
Identity = 'myUser#domain.com'
SearchQuery = 'Subject:Move Me'
TargetMailbox = 'temp#domain.com'
TargetFolder = 'Temp'
DeleteContent = $true
}
$TempResult = Search-Mailbox #Params
# Now move back to original mailbox
$Params.Identity = 'temp#domain.com'
$Params.TargetMailbox = 'myUser#domain.com'
$Params.TargetFolder = 'Moved'
$MoveResult = Search-Mailbox #Params
Then just make sure the number of emails is equal. EXO can take a while to get synced up, so if it's not finding all the email to move back, just give it a while and run the second search again:
If ($TempResult.ResultItemsCount -eq $MoveResult.ResultItemsCount) {
"We're good!"
} Else {
Write-Warning "Move results not equal!"
$TempResult
$MoveResult
}
Note that this is the 'Old' method, which is already retired and may be removed in the future. The new method is supposed to be using the New-ComplianceSearch style commands, but they have some limitations and aren't built for this purpose.
In my opinion, the "easiest" way is to open the mailbox in the browser > search for what you want > scroll down to load all the messages if needed > select all > move to > "move to a different folder..."

Related

Powershell - How to filter an attachment before download

I am using a PowerShell script to download attachment from an email which has multiple attachment.
If I use below statement it will download all the attachments.
# Find Unread mail messages
$UnreadMessages = $Inbox.Items | where-object {$_.Unread -and $_.SenderEmailAddress -imatch "usa"}
I want to download only a specific attachment using below statement but it gives nothing.
# Find Unread mail messages
$UnreadMessages = $Inbox.Items | where-object {$_.Unread -and $_.SenderEmailAddress -imatch "usa" -and $_.Attachments -imatch "Backlog"}
Please help me correct this statement
Firstly, your code will cause all Inbox messages to be downloaded and processed by your script. This is like a SELECT statement in SQL without a WHERE clause - as bad as it gets performance wise.
Use Items.Find/FindNext or Items.Restrict (see https://learn.microsoft.com/en-us/office/vba/api/outlook.items.find) - let the server/message store do the work. For your first query, use
#SQL=("urn:schemas:httpmail:read" = 0) AND ("http://schemas.microsoft.com/mapi/proptag/0x0065001F" like '%usa%')
For the second query, OOM won't let you search on the attachment name even though Extended MAPI (C++ or Delphi only) exposes that functionality (create RES_SUBRESTRICTION on PR_MESSAGE_ATTACHMENTS and specify PR_ATTACH_LONG_FILENAME as the search property). You can of course use only your first query and loop over the query matches, for each entry looping through each Attachment object in the MailItem.Attachments collection - far from ideal, but still better than no restriction at all.
If using Redemption (I am its author - it is an Extended MAPI wrapper and can be used from any language) is an option, it allows to use Attachments in queries. Something like the following (off the top of my head, VBA):
set Session = CreateObject("Redemption.RDOSession")
Session.MAPIOBJECT = Application.Session.MAPIOBJECT
set Folder = Session.GetDefaultFolder(Outlook.OlDefaultFolders.olFolderInbox)
set restrItems = Folder.Items.Restrict(" (UnRead = 'true') AND (""http://schemas.microsoft.com/mapi/proptag/0x0065001F"" like '%usa%') AND (Attachments LIKE '%Backlog%')")
for each item in restrItems
Debug.Print item.Subject
next

disable enumeration other user accounts in active directory

Huhu,
is it possible to disable the search for other Users in an AD? In this picture i am logged in as "normal" User.
Get-ADuser search
Here is a Picture of our AD structure.
AD Structure
So i don't want that a User can find another User in the Users OU by powershell (get-aduser)
is that possible?
I hope you have enough informations to understand my issue.
Regards
This is not really a thing in Windows proper. The default in AD is all users read. Secondly, anyone in AD is likely to have an email alias and thus search where they use PowerShell or not by their email alias/SMTP address, and should be for email lookups, so, IMHO, this is a futile use case.
One does not need Get-ADUser to find a user in AD. One has been able to do this since AD has been around and well before PowerShell was ever a thing using older scripting methods with .bat/.cmd/.vbs/WMI/ADSI or .Net directly and that is how it was done before PowerShell was ever a thing.
If you don't what a user using a specific cmdlet/cmdlets, then you need to implement restrictions via 'PowerShell just enough administration (JEA)'
Again, One does not need to use PowerShell to scan and get info from AD, a well documented and used thing, for folks who have PowerShell disabled (or tried to) in their environments.
Example:
How Can I Get a List of All the Users in an OU and Its Sub-OUs?
VBScript:
On Error Resume Next
Const ADS_SCOPE_SUBTREE = 2
Set objConnection = CreateObject(“ADODB.Connection”)
Set objCommand = CreateObject(“ADODB.Command”)
objConnection.Provider = “ADsDSOObject”
objConnection.Open “Active Directory Provider”
Set objCommand.ActiveConnection = objConnection
objCommand.Properties(“Page Size”) = 1000
objCommand.Properties(“Searchscope”) = ADS_SCOPE_SUBTREE
objCommand.CommandText = _
“SELECT Name FROM ‘LDAP://ou=finance,dc=fabrikam,dc=com’ WHERE objectCategory=’user'”
Set objRecordSet = objCommand.Execute
objRecordSet.MoveFirst
Do Until objRecordSet.EOF
Wscript.Echo objRecordSet.Fields(“Name”).Value
objRecordSet.MoveNext
Loop
Or
ADDS PowerShell (CMDLET, ADSI & .Net) to Expedite Your Tasks
ADSI:
<#
PowerShell ADSI(Active Directory Services Interface) commands
1. How to find the users property.
#>
$users1 = [ADSI]"LDAP://cn=copy,cn=users,dc=contoso,dc=com"
$users1 | select *
# 2. How to find the Group members for a Group.
$test = [ADSI]"LDAP://CN=test,CN=Users,DC=contoso,DC=com"
$test.Member |
ForEach-Object {[ADSI]"LDAP://$_"} |
select samccountname, samaccounttype
# 3. Listing an OU Contents
$ou=[ADSI]"LDAP://ou=tech,dc=contoso,dc=com"
$ou.PSBase.Children
$ou.PSBase.Children | Format-Table sAMAccountName
So this worked for me:
I just got it working by unchecking the "List Contents" from the "authenticated users" of the "Users" OU and I did not recognized any side effects so far.
Rights of Authenticated Users
And the "normal" User can't see the other users anymore by a query.
Tested with powershell: AD-GetUser and CMD "net user"
Query Result
So my problem is solved if there won't be any side effects in the future.
I will let you know.
Cheers

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.

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

PowerShell Script to sync members of distribution group with SharePoint group

Im in a SharePoint environment where:
distribution lists are in use instead of security lists
can not switch to security list
distribution lists are nested
no user profile sync
So to sync my new SharePoint group "something" I was thinking of running a PowerShell script regulary to fetch all the members hierarchically and put them in the SharePoint group, including checks. Unfortunately I cant install the AD add-in so I installed the Quest AD add-in.
update: I noticed that direct syncing took too long. So I have decided to split the solution: the first AD script output all nested distribution groups (Get-QADGroupMember -Indirect -Type 'group'), although this takes as long as getting all users, this feels safer, since probably this doesnt have to been run that much. Then script 2 read the groups and creates a csv file per group, this is certainly not unique, but with tens of thousands of users, this is the safest way. Then script 3 (underneath) reads the csv files with users and update the SharePoint user group. Then script 4 reads again the usergroup and deletes all the one where the date (in notes) is not equal to the most recent date.
<#
.SYNOPSIS
Syncs All Users in AD distribution Group To SharePoint group:
- Adds new Users
- Updates display name, email when changed
- Deleted Users no longer present
#>
# --------------------------------------------------------
# Variables to Change / Set:
# --------------------------------------------------------
$site = "http://abc-def-ghi:7777"
$SPgroup = "MYCOOLSPGROUP"
$ADgroup = "MYADGROUP"
# --------------------------------------------------------
# Add-SSuser : adds a user to Sharepoint Group
# --------------------------------------------------------
function Add-SSUser {
Param( [string]$account,
[string]$displayname,
[string]$email,
[string]$sitecollectionurl,
[string]$group,
[string]$date)
Get-SPWeb -Identity $sitecollectionurl | foreach-object {
$identityClaimWindowsNTAccount = "i:0#.w|$account"
[Microsoft.SharePoint.SPUser] $ssUser =
$_.EnsureUser($identityClaimWindowsNTAccount)
$_.Groups.GetByName($group).AddUser($ssUser)
$_.Groups.GetByName($group).Update()
Set-SPUser -Identity $ssUser -Web $_.Url -Group $group
# Update properties
$ssUser.Email = $email
$ssUser.Name = $displayname
$ssUser.Notes = "[[$date]]"
$ssUser.Update()
#Write-Host $ssUser.Xml
}
}