How to specify a subfolder of Inbox using Powershell - powershell

I'm trying to access a subfolder of "Inbox" named "subfolder" in outlook (2010) using Powershell.
$olFolderInbox = 6
$outlook = new-object -com outlook.application;
$ns = $outlook.GetNameSpace("MAPI");
$inbox = $ns.GetDefaultFolder($olFolderInbox)
# how do I specify a subfolder that's inside Inbox???
# I mean, "Inbox\subfolder" where "subfolder" is the name of the subfolder...
How do I specify this subfolder?
I'm sure this is really simple, which is why I am about to "lose it." Thanks in advance!
*Later in my code,
I search the body for a "searchterm" and send the results to a text file if there's a match. The following code works for my Inbox:
$inbox.items | foreach {
if($_.body -match "searchterm") {$_.body | out-file -encoding ASCII foo.txt} # prints to file...
Instead of the inbox, I want to look at the subfolder of inbox as described above...
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
EDIT:
$olFolderInbox = 6
$outlook = new-object -com outlook.application;
$ns = $outlook.GetNameSpace("MAPI");
$inbox = $ns.GetDefaultFolder($olFolderInbox)
$targetfolder = $inbox.Folders | where-object { $_.name -eq "Subfolder" }
$targetfolder.items | foreach {
if($_.body -match "keyword") {$_.body | out-file -Append -encoding ASCII foo.txt} # keyword match prints body to file...
}
OK, I think this works now...
I don't know what I was doing wrong, although it's literally my first day using Powershell, so it's no surprise, really.

$targetfolder = $inbox.Folders | where-object { $_.name -eq "subfolder" }
$targetfolder.items | where-object { $_.body -match "keyword" } | % { $_.body } # can then redirect the body to file etc.
EDIT: not sure why your newest edit wouldn't work. Yours looks to be similar in construction to the one I have above, which I verified against my own mailbox.
EDIT EDIT: Be sure that if you're using out-file, you append the results rather than overwriting with each match.

Try using the Where-Object cmdlet to filter the Folders returned from $inbox.Folders.
$Subfolder = $inbox.Folders | Where-Object -FilterScript { (Split-Path -Path $_.FolderPath -Leaf) -eq 'Subfolder' }
Here is an alternative / short-hand version of the above. This won't be quite as reliable, since you could have another folder called MySubfolder that's distinct from Subfolder.
$Subfolder = $inbox.Folders | ? { $_.FolderPath.EndsWith('Subfolder') }

Related

How to get powershell to attach certain file types from a directory and the latest 3 files?

I have this working partially, I can get powershell to attach the latest 3 files in this directory, but I cannot get it to only pick files based on file type or extension. My issue is that these files have no extension and are actually "files".
Can anyone help me get this script to only pick "files"
# Start-Process Outlook
$outlook = New-Object -comObject Outlook.Application
$message = $outlook.CreateItem(0)
$message.To = "test#test.com" # for multiple email use ';' as separator
#$message.CC = "" # for multiple email use ';' as separator
$message.Subject = "Files $((get-date).adddays(-1).toshortdatestring())"
$message.Body = "Attached."
$path = "C:\Downloads"
#$lastest = Get-ChildItem -Path $path -File | Sort-Object -Property LastAccessTime -Descending | Select-Object -First 3
Get-ChildItem -Path $path -File | Sort-Object -Property LastAccessTime -Descending | % { if ($_.Extension -eq ".file") {Send-Mail $_.FullName} }
foreach ($file in $lastest)
{
$message.Attachments.Add($file.FullName)
}
$message.Display()

Powershell script for count files and folders and send email

I found one script to calculate the number of files in a folder(looks Ok), but for some reason it doesn't work. The goal is that if the number of files in the folder exceeds five, I get an automatic notification about it.
If anyone can help me, I'm not very good at scripts, this is script that I found and doesn't work:
$Output = "c:\Test\*"
If (Get-Content -Path $Output | Where-Object {$_.Count -gt10})
{
$MailArgs = #{
'To' = "my#mail.com"
'From' = "some#mail.com"
'Subject' = "some text"
'Body' = "Number of files"
'SmtpServer' = "smtp"
}
Send-MailMessage #MailArgs
}
Replace this:
(Get-Content -Path $Output | Where-Object {$_.Count -gt 10})
with this:
((Get-ChildItem $Output | Measure-Object).Count -gt 10)

batch parsing data after a character/text from a word file in powershell

I am processing a batch of word documents.
I've successfully been able to extract the data that I wanted to a text file using this code:
$info = gci 'C:\Users\xxx\xxx' -Recurse -File *.doc -Include *lol -Exclude *poo*) | ForEach-Object {
Get-Content ($_.fullName ) | Where-Object { $_.Contains("Date:")}
Get-Content ($_.fullName ) | Where-Object { $_.Contains("Name:")}
}
$info > C:\Users\xxx.txt
This creates a text file like this-
Date: 1/11/2011
Name Joe Shmoe
For each found document...
I would like to remove the "Date:" and "Name:" part of the output for later extraction to an Excel file.
I've tried multiple methods using the $name.Split(':') followed by $name2 = $name.Substring($name.IndexOf(':') +1) and returning $name2 Heck, I've tried a ton of things. The best I could get was a complete iteration through each of the 100 files (with different names/dates) but only one name and date was returned 100 times. Could someone please help me out with this? Thank you!
This should accomplish your goal:
#Requires -Version 3
$Params = #{
Path = 'C:\Users\xx\xx'
Filter = '*.doc'
Include = '*lol'
Exclude = '*poo*'
File = $True
Recurse = $True
}
Get-ChildItem #Params |
ForEach-Object {
(Get-Content -Path $_.FullName |
Where-Object { $_ -match '(date)|(name):' }) -replace '(date)|(name):'
} |
Out-File -FilePath 'C:\Users\xxx.txt'

Save Email as .MSG to Local Folder using Powershell

I have created a rule to move emails from inbox to subfolder "Task" .I am able to move the all emails from subfolder "Task" to another subfolder "Complete" in outlook but can anyone assist me in copying the emails as .msg file locally to a pre-defined folder.Below is the powershell code.
$olFolderInbox = 6;
$GetOutlook = New-Object -com "Outlook.Application";
$olName = $GetOutlook.GetNamespace("MAPI")
$olxEmailFolder = $olName.GetDefaultFolder($olFolderInbox)
$SubFolders = $olxEmailFolder.Folders | ? { $_.Name -match 'Tasks' };
$TargetFolder = $olxEmailFolder.Folders.Item('Completed')
$SubFolders.Items |
ForEach-Object -Process {
$psitem.Move($TargetFolder)
}
I am unsure if you can save an email to a .msg, however, you can save it as a .htm using the modified code
$olFolderInbox = 6
Add-Type -assembly 'Microsoft.Office.Interop.Outlook'
$GetOutlook = New-Object -ComObject 'Outlook.Application'
$olName = $GetOutlook.GetNamespace('MAPI')
$olxEmailFolder = $olName.GetDefaultFolder($olFolderInbox)
$SubFolders = $olxEmailFolder.Folders | Where-Object -FilterScript {
$_.Name -match 'Tasks'
}
$TargetFolder = $olxEmailFolder.Folders.Item('Completed')
$SubFolders.Items |
ForEach-Object -Process {
$psitem.HTMLBody | Set-Content C:\test\email.htm
$psitem.Move($TargetFolder)
}

Why doesn't the ForEach-Object process all of the share names in my folder security analysis PowerShell script?

I am fairly new to PowerShell, and wrote a script to analyze network shares, dump it to CSV and import it to SQL. Our NAS device has several hidden shares, so I specify the (NAS) server name, a string of share names to search, and the folder depth that I want to search. (like 3 or 4 levels for quick testing).
The script tries to convert the security permissions to show simple "List, Read or Modify" access to folders. Can this user/group "list" the files, view the files, or modify them? The user info is put into a comma-separated list for each access type.
I suspect that although the code is functional, it may not be very efficient and I wonder if there are some significant improvements that could be made?
To deal with long pathnames, I use the "File System Security PowerShell Module 3.2.3" which appends a "2" to several modules, like "Get-ChildItem2".
I used to just specify one share folder, and I'm also wondering if my For-Each-Object that processes multiple shares has introduced a bug in how the objects are handled. It seems to use a lot more memory and slows down, and doesn't seem to process the last share in the list properly.
Here is the code: (split into 3 pieces)
# This script reads through the specified shares on the server and creates a CSV file containing the folder information
# The data is written to a SQL server
$Server = '\\MyServer'
$Shares = 'data$,share$'.Split(',')
$Levels = 99 # specify 3 or 4 for faster testing with less info
$ScanDate = Get-Date -Format 'yyyy-MM-dd HH:mm:ss'
$CSVFile = 'C:\FolderInfo\' + $ScanDate.Replace(':','-') + '.csv'
Write-Debug "ScanDate will be set to: $ScanDate"
Write-Debug "Data will be written to: $CSVFile"
$Separator = ',' # Separate the AD groups
$ListRights = 'ListDirectory,GenericExecute'.Split(',')
$ReadRights = 'Read,ReadAndExecute,GenericRead'.Split(',')
$ModifyRights = 'CreateFiles,AppendData,Write,Modify,FullControl,GenericAll'.Split(',')
$ErrorPref = 'Continue'
$ErrorActionPreference = $ErrorPref
$DebugPreference = 'Continue'
$DataBase = 'Folders.dbo.FolderInfo'
Function Get-Subs {
Param([String]$Path,[Byte]$Depth)
$CurrentDepth = $Path.Length - $Path.Replace('\','').Length
new-object psobject -property #{Path=$Path} # Object 'Path' is for the pipe output
If ( $CurrentDepth -lt ($Depth + 1) ) {
Get-ChildItem2 -Path $Path -Directory | ForEach {
Get-Subs $PSItem.FullName $Depth }
}
}
The next line has a commented out line of code that I was using to test how it is processing multiple share names, and it works properly, but the remaining code below seems to mess up on the last sharename in the list.
$Shares | ForEach-Object {Get-Subs (Resolve-Path $Server\$_).ProviderPath $Levels} | Get-Item2 | #ForEach-Object { new-object psobject -property #{Path=$_.FullName} } | Select Path
And the remaining code: (I hope this breakage doesn't confuse everyone :)
ForEach-Object {
$ListUsers = #()
$ReadUsers = #()
$ModifyUsers = #()
$Folder = $PSItem.FullName
Write-Debug $Folder
$Inherited = $true
try {$Owner = (Get-NTFSOwner -Path $Folder).Owner.AccountName.Replace('MyDomain\','')
}
catch {Write-Debug "Access denied: $Folder"
$Owner = 'access denied'
$Inherited = $false
}
$Levels = $Folder.Length - $Folder.Replace('\','').Length - 3 # Assuming \\server\share as base = 0
Get-NTFSAccess $Folder | Where { $PSItem.Account -ne 'BUILTIN\Administrators' } | ForEach-Object {
$Account = $PSItem.Account.AccountName.Replace('MyDomain\','')
$Rights = $PSItem.AccessRights -split(',')
If ($PSItem.IsInherited -eq $false) {$Inherited = $false}
IF ($PSItem.InheritanceFlags -eq 'ContainerInherit') { # Folders only or 'ContainerInherit, ObjectInherit' = Folders and Files
If (#(Compare -ExcludeDifferent -IncludeEqual ($ListRights)($Rights)).Length -and $Account) {$ListUsers += $Account}
If (#(Compare -ExcludeDifferent -IncludeEqual ($ReadRights)($Rights)).Length -and $Account) {$ListUsers += $Account}
If (#(Compare -ExcludeDifferent -IncludeEqual ($ModifyRights)($Rights)).Length -and $Account) {$ListUsers += $Account
Write-Debug "Modify anomaly found on Container only: $Account with $Rights in $Folder"
}
}
Else {
If (#(Compare -ExcludeDifferent -IncludeEqual ($ListRights)($Rights)).Length -and $Account) {$ListUsers += $Account}
If (#(Compare -ExcludeDifferent -IncludeEqual ($ReadRights)($Rights)).Length -and $Account) {$ReadUsers += $Account}
If (#(Compare -ExcludeDifferent -IncludeEqual ($ModifyRights)($Rights)).Length -and $Account) {$ModifyUsers += $Account}
}
}
$FileCount = Get-ChildItem2 -Path $Folder -File -IncludeHidden -IncludeSystem | Measure-Object -property Length -Sum
If ($FileCount.Sum) {$Size = $FileCount.Sum} else {$Size = 0}
If ($FileCount.Count) {$NumFiles = $FileCount.Count} else {$NumFiles = 0}
$ErrorActionPreference = 'SilentlyContinue'
Remove-Variable FolderInfo
Remove-Variable Created, LastAccessed, LastModified
$ErrorActionPreference = $ErrorPref
$FolderInfo = #{} # create empty hashtable, new properties will be auto-created
$LastModified = Get-ChildItem2 -Path $Folder -File | Measure-Object -property LastWriteTime -Maximum
IF ($LastModified.Maximum) {$FolderInfo.LastModified = $LastModified.Maximum.ToString('yyyy-MM-dd hh:mm:ss tt')}
else {$FolderInfo.LastModified = $PSItem.LastWriteTime.ToString('yyyy-MM-dd hh:mm:ss tt')}
$LastAccessed = Get-ChildItem2 -Path $Folder -File | Measure-Object -property LastAccessTime -Maximum
IF ($LastAccessed.Maximum) {$FolderInfo.LastAccessed = $LastAccessed.Maximum.ToString('yyyy-MM-dd hh:mm:ss tt')}
else {$FolderInfo.LastAccessed = $PSItem.LastAccessTime.ToString('yyyy-MM-dd hh:mm:ss tt')}
$Created = Get-ChildItem2 -Path $Folder -File | Measure-Object -Property CreationTime -Maximum
IF ($Created.Maximum) {$FolderInfo.Created = $Created.Maximum.ToString('yyyy-MM-dd hh:mm:ss tt')}
else {$FolderInfo.Created = $PSItem.CreationTime.ToString('yyyy-MM-dd hh:mm:ss tt')}
$FolderInfo.FolderName = $Folder
$FolderInfo.Levels = $Levels
$FolderInfo.Owner = $Owner
$FolderInfo.ListUsers = $ListUsers -join $Separator
$FolderInfo.ReadUsers = $ReadUsers -join $Separator
$FolderInfo.ModifyUsers = $ModifyUsers -join $Separator
$FolderInfo.Inherited = $InheritedFrom
$FolderInfo.Size = $Size
$FolderInfo.NumFiles = $NumFiles
$FolderInfo.ScanDate = $ScanDate
Write-Debug $Folder
Write-Output (New-Object –Typename PSObject –Prop $FolderInfo)
} | Select FolderName, Levels, Owner, ListUsers, ReadUsers, ModifyUsers, Inherited, Size, NumFiles, Created, LastModified, LastAccessed, ScanDate |
ConvertTo-csv -NoTypeInformation -Delimiter '|' |
ForEach-Object {$PSItem.Replace('"','')} |
Out-File -FilePath $CSVFile -Force
Write-debug 'Starting import...'
$Query = #"
BULK INSERT $DataBase FROM '$CSVFile' WITH (DATAFILETYPE = 'widechar', FIRSTROW = 2, FIELDTERMINATOR = '|', ROWTERMINATOR = '\n')
"#
sqlcmd -S MyComputer\SQLExpress -E -Q $Query
Arrays can be defined by comma separated list. Each element belongs in quotes.
For $Shares = 'data$,share$'.Split(','), try, $Shares = 'data$','share$'
Similarly for most of your arrays where you used .Split(',')
The filename can be done in many, many ways. Here you use a custom format then change it immediately. Recommend you replace
$ScanDate = Get-Date -Format 'yyyy-MM-dd HH:mm:ss'
$CSVFile = 'C:\FolderInfo\' + $ScanDate.Replace(':','-') + '.csv'
With
$ScanDate = Get-Date -Format 'yyyy-MM-dd-HH-mm-ss'
$CSVFile = "C:\FolderInfo\$ScanDate.csv"
This uses custom date format to set to what you wanted without the extra operation AND leverages PS way of evaluating variables within strings if the string is in double quotes. YMMV, but I also prefer 'yyyyMMdd-HHmmss' for datestamps.
Why are you defining a variable only to use it to define a second variable?
$ErrorPref = 'Continue'
$ErrorActionPreference = $ErrorPref
Why not $ErrorActionPreference = 'Continue'?
I found it later. Could probably use an explanation of what you're doing and how to do it when you define your preference. e.g. # Set default errorAction to 'Continue' during development, to show errors for debugging. Change this to 'silentlycontinue' to ignore errors during run. This will really help when you come back to this script in 18 months and are like WTF does this do?
Also, research Advanced Functions and CmdletBinding() so that you can build your function like a commandlet, including inputting a -debug switch, so you can write with debugging in mind.
What does Function Get-Subs actually do? It looks like some kind of recursion to get the path using the custom commandlet get-childitem2. Do you need the full path? Get-ChildItem $path -Directory -Recurse | select fullname where path is your UNC path or local path or any other provider, really.
Get-NTFSOwner not sure where this comes from, perhaps your custom module. You can use Get-ACL in Powershell 3 (not sure about 2, I don't remember). $owner = (Get-ACL 'path\file.ext').Owner.Replace('mydomain\','')
No idea what you're doing with all the inheritance stuff. Just keep in mind that you can get paths from Get-ChildItem | select Fullname and owner from Get-ACL. These may allow you to skip the custom module.
For:
$FileCount = Get-ChildItem2 -Path $Folder -File -IncludeHidden -IncludeSystem | Measure-Object -property Length -Sum
If ($FileCount.Sum) {$Size = $FileCount.Sum} else {$Size = 0}
If ($FileCount.Count) {$NumFiles = $FileCount.Count} else {$NumFiles = 0}
Use:
$files = Get-ChildItem -Force -File -Path $folder
-Force shows all files. -File limits it to files only. Then the number of files is $files.count. Works with empty folders, folders with one hidden file, and files with hidden and normal files.
For $folderinfo consider using a custom object. If you create it within the loop it should destroy the previous one. Then you can assign values directly to the object instead of storing them in a variable then inserting the variable into the hash table.
Using Get-ChildItem native will help you maintain this script far more easily than your customized module.
For:
| Select FolderName, Levels, Owner, ListUsers, ReadUsers, ModifyUsers, Inherited, Size, NumFiles, Created, LastModified, LastAccessed, ScanDate |
ConvertTo-csv -NoTypeInformation -Delimiter '|' |
ForEach-Object {$PSItem.Replace('"','')} |
Out-File -FilePath $CSVFile -Force
Try:
| Export-CSV -NoTypeInformation $CSVFile # by default this will overwrite, but you're timestamping down to the second so I don't think this will be an issue.
Overall:
Get rid of the custom module, I think everything you're doing here can be done in native PowerShell.
Consider recursion
Use an advanced function, complete with documentation. Ref: https://technet.microsoft.com/en-us/magazine/hh360993.aspx
Definitely use a custom object to store data for each file/folder. Passing a collection of objects to Export-CSV produces excellent output.
pipe to Select -ExpandProperty when an object contains a hashtable or another object, e.g. Get-ACL 'file.txt' | select -ExpandProperty Access gives a list of the access rule objects in the ACL.
Get-Help and Get-Member are amongst the most powerful commands in PowerShell.
Select-Object and Where-Object are up there too.