I am trying to put together a small script that finds all emails in an org box that match a given search term and display the full path to each result.
While what I have works in my personal inbox and recurses through all subfolders, when I try and use it on the Org box it only searches the current directory and does not recurse.
Is there something I am missing that is unique to working with Org boxes?
$OL = New-Object -ComObject Outlook.Application
$NS = $OL.GetNameSpace('MAPI')
$MB = $NS.createRecipient('SomeOrgBox#mail.com')
if ($MB.Resolve) {
$Inbox = $NS.GetSharedDefaultFolder($MB,[Microsoft.Office.Interop.Outlook.OlDefaultFolders]::olFolderInbox)
} else {
Write-Warning "Unable to access inbox
"
pause
exit
}
Register-ObjectEvent -InputObject $OL -EventName "AdvancedSearchComplete" - Action {
if ($Args.Results) {
Write-Host " - Results -
"
Foreach ($Result in $Args.Results) {
Write-Host $Result.Subject
Write-Host $Result.Parent.FolderPath
}
Write-Host ""
Write-Host " - End of results -"
} else {
Write-Host " - No Results found -"
}
Write-Host ""
}
Function Search-Inbox {
$Search = $OL.AdvancedSearch("'"+$Inbox+"'", "urn:schemas:httpmail:subject like '%"+$Query+"%'", $true)
}
$Query = Read-Host " Enter Search Term "
Search-Inbox
Edit:
To clarify, this will be used on Windows machines and third-party tools are not an option.
Also, after some testing, it seems that how the Org box was added to Outlook plays a role in this issue.
If the account is associated with you through your personal account's advanced options, the search does not recurse.
If the Org box was added as an additional Email account through the 'Add Account' dialog recursion works just fine.
If I was the only one using this, I could just change how I have the account set up, but since other people have to use it as well, it's not the most ideal solution. For now, I'll run with it until a solution can be found for accounts set up the other way.
Related
I'm trying to give an assistant access to part of someone's O365 mailbox. She currently has foldervisible permissions on his inbox. And there is about 607 folders under the inbox that she needs access to without having anymore permissions to the inbox itself.
Below is the code I've tried to run. I've removed the domain name but otherwise the code is exact. I've run the code twice and gotten no errors and it runs for a quite a while. But once it's complete, there is no change in the permissions.
ForEach($f in (Get-EXOMailboxFolderStatistics -identity jjo | Where { $_.FolderPath.Contains("jjo:/Inbox/Case Files") -eq $True } ) ) {
$fname = "jjo:" + $f.FolderPath.Replace("/","\");
Add-MailboxFolderPermission $fname -User gka -AccessRights Owner
}
Try to narrow down what's working and not;
Get-EXOMailboxFolderStatistics -identity jjo -ErrorAction Stop | Where-Object { $_.FolderPath.Contains("jjo:/Inbox/Case Files") } | ForEach-Object {
Try {
$fname = "jjo:" + $_.FolderPath.Replace("/","\")
Write-Host "Amending: $fname ..."
Add-MailboxFolderPermission $fname -User gka -AccessRights Owner -ErrorAction Stop
Write-Host "Done"
}
Catch {
$_
}
}
Write-Host "Complete"
On the client side, Extended MAPI (C++ or Delphi) can be used modify the ACL table on the folder level. If using Redemption (I am its author) is an option (any language), you can use RDOFolder.ACL collection to modify the permissions. Something along the lines (VBA, off the op of my head):
ROLE_PUBLISH_EDITOR = &H4FB
set Session = CreateObject("Redemption.RDOSession")
Session.MAPIOBJECT = Application.Session.MAPIOBJECT
set AddressEntry = Session.AddressBook.GAL.ResolveName("The Other User Name")
set Folder = Session.GetDefaultFolder(olFolderInbox)
for each subFolder in Folder.Folders
set ACE = subFolder.ACL.Add(AddressEntry)
ACE.Rights = ROLE_PUBLISH_EDITOR
next
I have an issue with a PowerShell script written for SharePoint 2010, it is only working on one site collection http://company/, it lists all the pages using a Content Editor Web Part with Image Maps, once the loop goes to the next site collection it returns nothing.
As the script loops through all the other site collections, it returns nothing to $publishingPages - I can see $publishingWeb loading correctly and returning its data but I get nothing from:
$publishingWeb.GetPublishingPages($publishingWeb)
I've ran the script using different SharePoint accounts (Setup, Farm, Admin, etc), the results are always the same, I have no clue what I could be possibly doing wrong here!
Here's my code:
Start-SPAssignment -Global
$SPWebApp = Get-SPWebApplication "http://company/"
foreach ($site in $sites)
{
foreach ($web in $site.AllWebs)
{
Write-Host `n "-" $web.Url `n -ForegroundColor Green
if ([Microsoft.SharePoint.Publishing.PublishingWeb]::IsPublishingWeb($web))
{
$publishingWeb = [Microsoft.SharePoint.Publishing.PublishingWeb]::GetPublishingWeb($web)
$publishingPages = $publishingWeb.GetPublishingPages($publishingWeb)
foreach($page in $publishingPages)
{
$manager = $web.GetLimitedWebPartManager($page.Url, [System.Web.UI.WebControls.WebParts.PersonalizationScope]::Shared)
$allWebParts = $manager.WebParts
$onlyCEWP = $allWebParts | ? {$_.GetType() -eq [Microsoft.SharePoint.WebPartPages.ContentEditorWebPart]}
$imageMaps = $onlyCEWP | ? {$_.Content.InnerText -match '<map name='}
if ($imageMaps -ne $null)
{
Write-Host `t " - " $page.Url
}
}
}
$web.Dispose()
}
$site.Dispose()
}
Stop-SPAssignment -Global
There was nothing wrong with the script above, it turns our my customer had placed all pages outside the Pages library, therefor a script find pages once a library is empty :)
Here is the basis of my question, I am trying to remove a very large list of users from all site collections in multiple web applications. My overall goal is to remove roughly 50,000 users from almost 10,000 site collections (all site collections in about 15 web applications).
I currently have a way of doing this by having a list of users and a list of sites, and simply looping through the sites and removing each user from each site.
I'm wondering if there is a more efficient way of doing this, and or if there is a way to remove a user from a web application and have that cascade down through each of the site collections.
Below is what I currently have:
$users = get-content $userList
$sites = get-content $siteList
Write-Host "Getting all listed sites"
Write-Host "Getting all listed users"
$siteArray = #()
for ($i=0; $i -lt $sites.length; $i++)
{
try
{
$rmUserCount = 0
$userNotExist = 0
$failedRm = 0
Write-Host "Working on" $sites[$i]"..."
for ($c=0; $c -lt $users.length; $c++)
{
Write-Host "Attempting to remove user" $users[$c] -ForegroundColor Yellow
try
{
write-host $isuser
$isUser = Get-SPUser -Identity $users[$c] -web $sites[$i] -ErrorAction SilentlyContinue
}
catch
{
}
if($isUser)
{
try
{
Remove-SPUser -Identity $users[$c] -Web $sites[$i] -Confirm:$false
Write-Host "The user" $users[$c] "was removed from the site" $sites[$i] -ForegroundColor Green
$rmUserCount++
}
catch
{
Write-Host "The user still exists and was not removed" -ForegroundColor Red
$failedRm++
}
}
else
{
$userNotExist++
Write-Host "user" $users[$c] "does not exist on" $sites[$i]"... moving on"
}
$isuser=$null
}
$addInArrayOut = $rmUserCount.ToString() + " users removed from " + $sites[$i].ToString() + ", " + $userNotExist.ToString() + " users did not exist on the site, " + $failedRm.ToString() + " users failed to be removed"
$siteArray += $addInArrayOut
}
catch
{
Write-Host $_ $Error[0] -ForegroundColor Red
}
}
From my calculations using measure command, I can remove roughly 10 users per second, but this still means that it would take hundreds of days to complete this task, leaving me thinking that there must be a better way of doing this.
I tried adding the web application url as one of the sites in the array and it didn't work like I thought it would, as it didn't remove the user from each of the site collections within it.
If anyone has any idea of how to do this better, or a way of drastically speeding up what I have I would really appreciate it. Thanks!
I'm working on a TFS build with a pre-build PowerShell script that (in addition to building my app) automatically checks out a file where we maintain version, increments the build number, then checks the file in.
I have been able to do this, except that I get an error from the script which results in a partially successful build (orange). I need to have a fully successful (green) build.
Here's the check-in line (using TFS Power Tools for VS 2013):
New-TfsChangeset -Item $versionFile -Override "Automated" -Notes "Code Reviewer=tfs" -Comment "Automated"
The error I receive is that the changeset is not associated with a work item, but the -Override should handle that. The funny thing is that it checks in anyway.
Running locally on my machine instead of the build server, I get the same thing, except that I also see a line that says The policies have been overridden. This tells me that the override is working, but it still outputs the error.
I've tried adding -ErrorAction SilentlyContinue, but it has no effect.
I need one of three options:
A way to suppress output of the checkin error,
A way to create a work item and associate it to the checkin, or
Some other third option that will result in a green build.
Any ideas?
Credit goes to Eddie - MSFT for leading me the right direction, but I wanted to consolidate everything here.
WARNING This will check in all pending changes in the workspace.
Creating a new work item (source)
I did modify it quite a bit to support automation. It connects to TFS and generates a new work item.
function New-WorkItem()
{
# These *should* be registered in the GAC.
# The version numbers will likely have to change as new versions of Visual Studio are installed on the server.
Add-Type -Assembly "Microsoft.TeamFoundation.Client, Version=12.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"
Add-Type -Assembly "Microsoft.TeamFoundation.WorkItemTracking.Client, Version=12.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"
$server = "http://YOURTFSSERVER:8080/tfs"
$tfs = [Microsoft.TeamFoundation.Client.TeamFoundationServerFactory]::GetServer($server)
$type = [Microsoft.TeamFoundation.WorkItemTracking.Client.WorkItemStore]
$store = [Microsoft.TeamFoundation.WorkItemTracking.Client.WorkItemStore] $tfs.GetService($type)
$workItem = New-Object Microsoft.TeamFoundation.WorkItemTracking.Client.WorkItem($store.Projects[0].WorkItemTypes[0])
$workItem.Title = "Automated work item"
$workItem
}
Associating the work item and checking in
Slight modifications to the code from the link given by Eddie, we get the following:
function CheckIn()
{
param([Microsoft.TeamFoundation.WorkItemTracking.Client.WorkItem] $workItem)
$col = Get-TfsCollection("http://YOURTFSSERVER:8080/tfs/YOURCOLLECTION")
$vcs = Get-TfsVersionControlServer($col)
$ws = $vcs.GetWorkspace([System.IO.Path]::GetDirectoryName($anyPathInWorkspace))
$pc = $ws.GetPendingChanges()
$wici = Get-TfsWorkItemCheckinInfo($workItem)
$changeset = $ws.CheckIn($pc, "Automated check in", $null, $wici, $null)
}
That post doesn't tell you that Get-TfsCollection, Get-TfsVersionControlServer, and Get-TfsWorkItemCheckinInfo aren't defined. I had to find them.
I found the first two on http://nkdagility.com/powershell-tfs-2013-api-1-get-tfscollection-and-tfs-services/. I didn't have to change anything.
function Get-TfsCollection
{
param([string] $CollectionUrl)
if ($CollectionUrl -ne "")
{
#if collection is passed then use it and select all projects
$tfs = [Microsoft.TeamFoundation.Client.TfsTeamProjectCollectionFactory]::GetTeamProjectCollection($CollectionUrl)
}
else
{
#if no collection specified, open project picker to select it via gui
$picker = New-Object Microsoft.TeamFoundation.Client.TeamProjectPicker([Microsoft.TeamFoundation.Client.TeamProjectPickerMode]::NoProject, $false)
$dialogResult = $picker.ShowDialog()
if ($dialogResult -ne "OK")
{
#exit
}
$tfs = $picker.SelectedTeamProjectCollection
}
$tfs
}
function Get-TfsVersionControlServer
{
param([Microsoft.TeamFoundation.Client.TfsTeamProjectCollection] $TfsCollection)
$TfsCollection.GetService("Microsoft.TeamFoundation.VersionControl.Client.VersionControlServer")
}
But I couldn't find Get-TfsWorkItemCheckinInfo. The only Google hit was the kinook link from Eddie (and soon probably this answer). Here's what I came up with:
function Get-TfsWorkItemCheckinInfo
{
param([Microsoft.TeamFoundation.WorkItemTracking.Client.WorkItem] $workItem)
$wi = New-Object Microsoft.TeamFoundation.VersionControl.Client.WorkItemCheckinInfo($workItem, [Microsoft.TeamFoundation.VersionControl.Client.WorkItemCheckinAction]::Resolve)
$wi
}
Now we can use it
CheckIn (New-WorkItem)
That's it!
You can create a work item from PowerShell by following this article: http://halanstevens.com/blog/powershell-script-to-create-a-workitem/
Quote the code here for reference:
$key = Get-ItemProperty HKLM:\SOFTWARE\Microsoft\VisualStudio\8.0
$dir = [string] (Get-ItemProperty $key.InstallDir)
$dir += "PrivateAssemblies\"
$lib = $dir + "Microsoft.TeamFoundation.WorkItemTracking.Client.dll"
[Reflection.Assembly]::LoadFrom($lib)
$lib = $dir + "Microsoft.TeamFoundation.Client.dll"
[Reflection.Assembly]::LoadFrom($lib)
"Please enter your Team Foundation Server Name:"
$server = [Console]::ReadLine()
$server = $server.Trim()
"Connecting to " + $server + "..."
$tfs = [Microsoft.TeamFoundation.Client.TeamFoundationServerFactory]::GetServer($server)
$type = [Microsoft.TeamFoundation.WorkItemTracking.Client.WorkItemStore]
$store = [Microsoft.TeamFoundation.WorkItemTracking.Client.WorkItemStore] $tfs.GetService($type)
$workItem = new-object Microsoft.TeamFoundation.WorkItemTracking.Client.WorkItem($store.Projects[0].WorkItemTypes[0])
"Created a new work item of type " + $workItem.Type.Name
$workItem.Title = "Created by Windows PowerShell!"
$workItem.Save()
And then refer to this article to associate the work item to changeset: http://www.kinook.com/Forum/showthread.php?t=4502
Exchange Management Shell:
[PS] C:\Windows\system32>$AddressBook = Get-PublicFolderItemStatistics -Identity "\Shared Company Address Book"
[PS] C:\Windows\system32>$AddressBook [0] | format-list
RunspaceId : d8e95055-1f3e-4e7f-a1fc-d5b97ecbcb96
ServerName : MAILMAN
DatabaseName : Public Folder Database 0524088380
Subject : John Q User
PublicFolderName : \Company Address Book
LastModificationTime : 11/12/2012 2:57:49 PM
CreationTime : 11/12/2012 2:56:28 PM
HasAttachments : False
ItemType : IPM.Contact
MessageSize : 6.598 KB (6,756 bytes)
Identity : 000000001A447390AA6611CD9BC800AA002FC45A0900580787449ABF5E4891DD89178938242C0000000250AE00001BE1
A5309D57D5439914FD70BDC745C100000B8942FD0000
MapiIdentity : 000000001A447390AA6611CD9BC800AA002FC45A0900580787449ABF5E4891DD89178938242C0000000250AE00001BE1
A5309D57D5439914FD70BDC745C100000B8942FD0000
OriginatingServer : mailman.company.com
IsValid : True
[PS] C:\Windows\system32>
Okay... I'm trying to export the contacts in an Exchange Server 2010 Contact List. I can not, for the world of me, figure out how to get the "Data" out of this stupid thing.
If I do $AddressBook | Format-List, it lists all the contacts, so I'm know I'm in the right ballpark.
how can I get all of the information out of this list? Last Name, First Name, Email Address, Business Phone, etc.
This is an old post, but I have the same issue for a couple days and was unable to figure it out. So, I opted to use the road #WernerCD used and would like to add my PowerShell script that could help you in the case that you decide to go down this path.
Before starting, allow me to explain my issue. We have multiple folders with contacts in it. These contacts contain User Defined Fields that due to migration issues, were not added to their containing folder. While we use Outlook, we need to be able to see these fields on the current view. However, when we try to add the UDF columns, it only allows us to use the "User-defined fields in folder", which is empty.
Below is the PowerShell script that will check a public folder (and its subfolders) within Outlook, check every contact UserProperties (Equivalent for UDF), put them into an array, then it will check if each contact's UDF exist on its containing folder (UserDefinedProperties), and if it doesn't exist, it will added it as a Text field without a value.
Please keep in mind that all our contact folders are under a folder called Shared Contacts Folder.
Code
# Connection to Outlook
$Outlook = New-Object -com Outlook.Application
$Namespace = $outlook.GetNamespace("MAPI")
# "Location" of public folders (Change me#example.com)
$PublicFolder = $Namespace.Folders.Item("Public Folders - me#example.com")
$PublicFolders = $PublicFolder.Folders.Item("All Public Folders")
# Folder that you would like to check. We will check Shared Contacts under Shared Contacts Folder
$SharedContactsFolder = $PublicFolders.Folders.Item("Shared Contacts Folder")
$SharedContacts = $SharedContactsFolder.Folders.Item("Shared Contacts")
Write-Host ("<----------------------------------------------------------->") -foreground Yellow
function CheckContacts($MyFolder){
# Check if this folder has subfolder
If ( $MyFolder.Folders.Count -gt 0) {
Write-Host ("Folder '" + $MyFolder.Name + "' contains subfolders (" + $MyFolder.Folders.Count + ")") -BackgroundColor Yellow -foreground DarkBlue
Foreach ( $Subfolder in $MyFolder.Folders ) {
CheckContacts($Subfolder)
}
}
Write-Host ("Working on folder: " + $MyFolder.Name) -BackgroundColor White -foreground DarkBlue
$All_UDF = #()
# Check User Defined Fields (UDF) for each contact and add them to array
foreach ( $Contacts in $MyFolder.Items ) {
foreach ( $UDF in $Contacts.UserProperties ) {
# Check if field was previously added to array
If ($All_UDF -notcontains $UDF.Name) {
$All_UDF += $UDF.Name
}
}
}
# Add all UDF to Folder's UDF
Write-Host ("We will add the following UDF into '" + $MyFolder.Name + "': ") -foreground Green
Write-Host ($All_UDF -join "`n") -foreground Green
Foreach ( $UDF in $All_UDF ){
# Only add if UDF does not exist on folder's UDF
if( (CheckFolderUDF $MyFolder $UDF) -eq $false) {
# Add - Always add UDF as Text field (1)
Write-Host ("Adding '" + $UDF + "' to '" + $MyFolder.Name + "'")
$MyFolder.UserDefinedProperties.Add($UDF, 1)
}else{
Write-Host ("Already present: " + $UDF)
}
}
Write-Host ("<----------------------------------------------------------->") -foreground Yellow
}
Function CheckFolderUDF ( $MyFolder, $MyUDFName ) {
$Result = $false
Foreach ( $Folder_UDF in $MyFolder.UserDefinedProperties ){
If ( $Folder_UDF.Name -eq $MyUDFName ) {
$Result = $true
break
}
}
return $Result
}
# Start - Check Shared Contacts
CheckContacts($SharedContacts)
How do I run/test this code?
1) Open Windows PowerShell ISE (within Windows).
2) Copy the code above and paste it into the PowerShell ISE window.
3) Read and understand the code.
4) Modify as needed.
P.S.: I tried to add this a "comment", but I don't have enough points.
After much pain and suffering... and stumbling upon [this post]. This is in Powershell (not Exchange Powershell Console) and from my computer (not server MailMan):
$Outlook = New-Object -com Outlook.Application
$Namespace = $outlook.GetNamespace("MAPI")
$PublicFolder = $Namespace.Folders.Item("Public Folders - me#example.com")
$PublicFolders = $PublicFolder.Folders.Item("All Public Folders")
$AddressBook = $PublicFolders.Folders.Item("Company Address Book")
$Contacts = $AddressBook.Items
$Contacts | Select FullName
This actually pulls the contact information. I'm still looking at how to do it on the server side (Exchange Powershell Console), but this should be a good foundation to select the desired fields and push them into the database as I need.
I figure if I can figure out how to get the "Public Folders - dummy_user#example.com", I should be able to do the same thing on the server.
I also assume there is an easier way to do this (maybe by pull path, instead of one part at a time), but this does work.
Now to find out how to get UserDefinedFields....
Why don't you use Get-Contact instead of Get-PublicFolderItemStatistics?