Tried to move mails from Inbox to PST, but they vanished - powershell

I have written a simple script with the aim to backup Inbox, Archived and Sent mails to an attached PST datafile.
It seems it works fine: the script found source folders from my email account, it found destination folders on the attached PST, it found the mails to be backupped (ie I have deeply checked out the $namespace object).
When I have reopened my Outlook client I got an unpleasant surprise. As expected, my mails aren't stored in my email account, but none of them appear in my attached PST.
What's happened???
Edit: alternatively, could you suggest to me any dcumentation about Outlook MAPI for Powershell? Google returns to me weak results
$outlook = New-Object -ComObject Outlook.Application
$namespace = $outlook.GetNameSpace("MAPI")
$SourceDest = [ordered]#{}
$SourceDest['Inbox'] = 'PSTfolder1'
$SourceDest['Archive'] = 'PSTfolder2'
$SourceDest['Sent'] = 'PSTfolder3'
foreach ($from_folder in $SourceDest.Keys) {
$to_folder = $SourceDest[$from_folder]
$Source = $namespace.Folders['mymail#account.com'].Folders[$from_folder]
$Dest = $namespace.Folders['myPST'].Folders[$to_folder]
$Messages = $Source.Items
foreach ($msg in $Messages) {
[void]$msg.Move($Dest)
}
}

Was Outlook running at the time the script was executed? Do you see emails being moved?
Keep in mind that Move is a function that returns the new item, it does not return void. Not that it should matter in your case.
What does matter is that you are modifying the collection (since Move deletes the original) as you are looping through its items. Use a back loop from Items.Count down to 1.

Related

Powershell, Outlook hidden mailbox management.

anyone have any idea what I may need to change. I have the code below that works fine. But recently I've decided to hide the mailbox from the address book. This has cause the below script to stop working.. If I unhide the mailbox it works again. But I would prefer to keep it hidden.
quick description of what it does.
it gets a shared mailbox then a specific folder in the inbox folder and then gets all the emails in that folder.
$Outlook = New-Object -comobject Outlook.Application
$namespace = $Outlook.GetNameSpace("MAPI")
$olRecipient = $namespace.CreateRecipient("sharedmailbox#mail.com")
$SInbox = $namespace.GetSharedDefaultFolder($olRecipient,"6")
$targetFolder = $SInbox.Folders('targetfolder')
$Completedfolder = $targetFolder.Folders("Complete")
$Emails = #()
$Emails = $targetfolder.Items
The recipient created by CreateRecipient cannot be resolved if the user is hidden from GAL. And if it cannot be resolved, GetSharedDefaultFolder will not work either.

creating comobject for AccessObjects

I am trying to create a powershell 'AccessObject' comobject for my MS Access app. Basically, I will trying to create a powershell script that gets queries in a database and the tables and/or queries a particular query depends on. To do that i will need to have an instance of the MS Access 'AccessObject' and 'DependencyInfo' classes in my powershell script. I have attached a snippet of the function i intend to use. This is not the complete function, please note. All i want is to know how to create an instance of the DependencyInfo and AccessObjects in powershell.
function getQueries([string] $database)
{
$dbEng = New-Object -ComObject DAO.DBEngine.120
$AccessApp= New-Object -ComObject Access.Application
$Dependency = $AccessApp.DependencyInfo
$AccessObject=$AccessApp.AccessObjects
...
}
All i want is to know how to create an instance of the DependencyInfo
and AccessObjects in powershell.
The following creates a new Access process, opens a local accdb file, and retrieves the dependencies for a given form:
$db = new-object -ComObject 'Access.Application'
$db.OpenCurrentDatabase('C:\temp\deezNutz.accdb')
$dependency_info = $db.Application.CurrentProject().AllForms('frm_person').GetDependencyInfo()
foreach ($dependency in $dependency_info.Dependencies) { $dependency.FullName }
$db.CloseCurrentDatabase()
$db.Application.DoCmd.Quit()
If you're trying to pro grammatically manipulate the objects in a Microsoft Access database e.g., forms, reports, queries, etc. Your best bet is to search for solutions using VBA then convert those to Powershell. For this example, I first wrote the solution in VBA then converted it to Powershell.
Thanks #Lord Adam. This was really helpful. In my case i had to modify the logic a little bit:
$AccessApp= New-Object -ComObject 'Access.Application'
$AccessApp.OpenCurrentDatabase($database)
$AccessApp.Application.SetOption("Track Name AutoCorrect Info", $true)
$QryDependency = $AccessApp.Application.CurrentData.AllQueries.Item($query.Name).GetDependencyInfo()
ForEach($di in $QryDependency.Dependencies)
{
$QryObjects= $QryObjects + $di.Name +","
}

Connect to TFS via Powershell with different user credentials

I've written a Powershell script which connects to our TFS server, creates a workspace, downloads the latest source and performs a nightly build and release.
The issue I have is that it always connects with my own credentials, and from what I've read this is because I'm logged onto the machine as me. I've had a new domain user account created and we've given this admin permissions within TFS, however I'm having trouble making the script use these credentials.
Here's the part of the script which deals with the initial connection and workspace creation as it currently stands:
$subfolder = [System.Guid]::NewGuid().ToString()
$tfsServer = "http://tfsserver:8080/tfs/xyz"
$tfsCollection = [Microsoft.TeamFoundation.Client.TfsTeamProjectCollectionFactory]::GetTeamProjectCollection($tfsServer)
$tfsVersionCtrlType = [Microsoft.TeamFoundation.VersionControl.Client.VersionControlServer]
$tfsVersionCtrl = $tfsCollection.GetService([type] $tfsVersionCtrlType)
$tfsWorkspace = $tfsVersionCtrl.CreateWorkspace($subfolder, $tfsVersionCtrl.AuthenticatedUser)
For completeness, here's the rest of the "Get" logic:
$tfsWorkspace.Map($serverLocation, $localLocation)
$recursion = [Microsoft.TeamFoundation.VersionControl.Client.RecursionType]::Full
$versionSpec = [Microsoft.TeamFoundation.VersionControl.Client.VersionSpec]::Latest
$itemSpec = New-Object Microsoft.TeamFoundation.VersionControl.Client.ItemSpec($serverLocation, $recursion)
$fileRequest = New-Object Microsoft.TeamFoundation.VersionControl.Client.GetRequest($itemSpec, $versionSpec)
$getStatus = $tfsWorkspace.Get($fileRequest, [Microsoft.TeamFoundation.VersionControl.Client.GetOptions]::Overwrite)
The main problem I have when working with TFS via Powershell is the fact Microsoft keep changing their implementation, in some cases quite radically, and what we're left with is scraps of code posts and documentation littered around the internet (including here on SO) which refer to old addins and other now obsolete references which take you down a myriad of wrong paths.
Anyway, so I've had a play around with trying to create Windows credentials, PSCredentials, and the like (which don't seem to be accepted by anything), the old methods to supply an ICredential are now obsolete and I'm really not sure where to turn.
Basically, I just want to create a workspace, check out items, update files and check them back in - all as our new "tfsService" user account. Please help...
UPDATE:
Based on the answer from #Nick, I needed to make the following changes. Note the use of [System.Uri] which was required to get this working for me (not sure if that's a quirk of my setup as others didn't seem to require this). Also, I needed to put the constructor call for TfsTeamProjectCollection all on one line, as splitting it onto separate lines, as per Nick's example, wouldn't work for me either.
$cred = New-Object System.Net.NetworkCredential("username", "password", "domain")
$tfsCollection = New-Object Microsoft.TeamFoundation.Client.TfsTeamProjectCollection([System.Uri]$tfsServer, $cred)
#$tfsCollection = [Microsoft.TeamFoundation.Client.TfsTeamProjectCollectionFactory]::GetTeamProjectCollection($tfsServer)
$tfsVersionCtrlType = [Microsoft.TeamFoundation.VersionControl.Client.VersionControlServer]
$tfsVersionCtrl = $tfsCollection.GetService([type] $tfsVersionCtrlType)
$tfsWorkspace = $tfsVersionCtrl.CreateWorkspace($subfolder, $tfsVersionCtrl.AuthenticatedUser)
I cannot test this effectively but this seems to be a common answer on SO for this issue.
$cred = New-Object NetworkCredential("myuser", "myPassword", "mydomain")
$tfsCollection = New-Object Microsoft.TeamFoundation.Client.TfsTeamProjectCollection
(
$tfsServer,
$cred
)

Multiple Actions for one Outlook rule in Powershell

I have a question about Outlook Rules in Powershell. I wrote some code that successfully stores any incoming e-mail from a certain sender to the deleted-items folder. I did this because when the mails enter the junk folder, the junk folder still has the counter token of mails, so in the end it will say I have 10-mails in the junk folder.
I want to avoid this by just throwing the incoming mails from that sender to the deleted-items folder and also marking the mail as "read" so that I don't see the clutter in the deleted items folder.
The question is really:
Can I add multiple actions to the same outlook rule in powershell? if so, how?
What is the syntax / code for the "run script" action?
My code so far:
$ol = New-Object -ComObject Outlook.Application
$ns = $ol.GetNamespace("MAPI")
$olFolders = "Microsoft.Office.Interop.Outlook.OlDefaultFolders"
$outlook = New-Object -ComObject outlook.application
$namespace = $Outlook.GetNameSpace("MAPI")
$inBox = $ns.GetDefaultFolder([Microsoft.Office.Interop.Outlook.OlDefaultFolders]::olFolderInbox)
$deleted = $ns.GetDefaultFolder([Microsoft.Office.Interop.Outlook.OlDefaultFolders]::olFolderDeletedItems)
$rules = $outlook.session.DefaultStore.GetRules()
$rule = $rules.create("Move mail: to DeletedItems", [Microsoft.Office.Interop.Outlook.OlRuleType]::olRuleReceive)
$rule_Address = $rule.Conditions.SenderAddress
$rule_Address.Enabled = $true
$rule_Address.Address = #("<Sender Address>")
$action = $rule.Actions.MoveToFolder
$action.Enabled = $true
[Microsoft.Office.Interop.Outlook._MoveOrCopyRuleAction].InvokeMember("Folder",[System.Reflection.BindingFlags]::SetProperty,$null, $action, $deleted)
$rules.Save()
This code works so far.
Please help.
Thanks!
Can I add multiple actions to the same outlook rule in powershell? if so, how?
Took a bit but I got a working test that uses multiple actions applied to a single rule. It is actually easy and you just need to repeat the steps you have already done and create a different action variable.
In my example, just showing the end of the code, I have added a action to display a message in the New Item Alert window.
...
$action = $rule.Actions.MoveToFolder
$action.Enabled = $true
$anotherAction = $rule.Actions.NewItemAlert
$anotherAction.Text = "I am awesome!"
$anotherAction.Enabled = $true
[Microsoft.Office.Interop.Outlook._MoveOrCopyRuleAction].InvokeMember("Folder",[System.Reflection.BindingFlags]::SetProperty,$null, $action, $deleted)
$rules.Save()
You quite possibly already tried something like this. If not there is an important reference for this that you need to be aware of.
What is the syntax / code for the "run script" action?
This is one of the actions you cannot programatically set as per this reference for Office 2007 or this one for Office 2010/2013. The tables are similar and rather large to include here but I will reference the one in your 2nd bullet.
Action : Start a script
Constant in olRuleActionType : olRuleActionRunScript
Supported when creating new rules programmatically? : No
Apply to olRuleReceive rules? : Yes
Apply to olRuleSend rules? : No
There are others as well where you are restricted. So you need to keep that in mind when you are making your rules.

How does Powershell access Word and Word objects?

I am using the following script to convery a directory full of txt and rtf documents to Word documents.
$global:word = new-object -comobject word.application
$word.Visible = $False
$srcfiles = \\Path\to\files
$savepath = \\Path\to\docfiles
$saveFormatDoc = [Enum]::Parse([Microsoft.Office.Interop.Word.WdSaveFormat], 0);
function saveas-document ($docs) {
"Opening $($docs.FullName)"
$opendoc = $word.documents.open($docs.FullName)
$savepath = "$docPath$($docs.BaseName)"
"Converting to $savepath.doc"
$opendoc.saveas([ref]"$savepath", [ref]$saveFormatDoc)
}
ForEach ($doc in $srcfiles) {
saveas-document -docs $doc
}
With the way it's coded right now, I think it's opening a new instance of Word, opening the rtf/txt document in Word, saving as a Word doc and repeating. I'm not quitting Word (unless there's an implicit $word.quit() in there somewhere. I fear I'm killing performance with multiple instances of Word, but don't know if that's a concern I need to have or not.
What's a more efficient way of coding this to open a single instance of Word for document handling, and what's the syntax for opening Word as an application rather than opening a series of documents that launch the Application based on their file associations (which is what I fear it's doing now)?
Your New-Object is outside your loop so you're only using the one instance. When you assign the New-Object to a variable (the object reference) and reuse it, that same object get's reused unless you destroy it or create another. Your script, in it's current form, is not creating a new object for each $doc in $srcfiles as you might be thinking.
With COM Objects, you are not launching the application, or dozens of them. You are opening a binary interface to libraries
You don't need or want to open a Com Object over and over, as that would cause a delay and performance hit.
You should close the Com-Object ($word.Quit()) at the end of the script though.