i wonder if there is a way to get the file extension from a public folder attachment?
Backgroud:
We are using a software (AttachmentsProcessor) which extracts all attachments from the e-mails in the public folder structure and save it to the filesystem. The software puts a .lnk in the e-mail, which points to the location in the filesystem. So we can open the attachment by double-click.
Lately we moved our public folder structure from internal Exchange to Office365 / Exchange Online. During this process we tried to put all extracted attachments back into the e-mails. After we done some tests, we noticed the this didn't work for some of the e-mails. We have still the .lnk as an attachment.
So what am I looking for?
I would like to write a script in powershell which shows me a list of all e-mails and the corresponding folders (Identites), which have a .lnk file attached.
On my search I just found something that works for mailboxes but nothing for public folders.
-> Get-Mailbox | Export-Mailbox -AttachmentFilenames "*.PDF"
-> Get-Mailbox | New-MailboxExportRequest -ContentFilter {Attachment -like "*.PDF"}
Any help would be very nice. ;-)
Thanks for your attention
Peter
I can't overtly write all the code for you. But I have something that can get you started. This script will recursively iterate through your public folders and find items that have attachments on them. The last bit of code inside the loop currently saves the files to disk, but that's where you can replace that with some logic to do what you need it to do (i.e. filtering by attachment, pulling the link information, etc.).
$TargetDirectory = "C:\temp\PublicFolders"
function process-folders-recursive($folder, $path) {
if($folder.DefaultMessageClass -ne "IPM.Appointment" -and $folder.DefaultMessageClass -ne "IPM.Contact")
{
$path = (Join-Path $path $folder.name)
write-host $folder.class $path
if($folder.Items.Count -gt 0 ){
foreach($item in $folder.Items){
if($item.MessageClass -ne "IPM.Appointment")
{
#Write-Host $item.name
if($item.Attachments.Count -gt 0 ) {
if(!(Test-Path -path $path)){
New-Item -ItemType directory -Path $path
}
foreach($attch in $item.Attachments){
try
{
Write-Host $attch.FileName
$fileName = $attch.FileName
$fileNameAndPath = (Join-Path $path $fileName)
$attch.saveasfile($fileNameAndPath)
}
catch [System.Exception]
{
Write-Host $path $fileName # $_.Exception.ToString()
}
}
}
}
}
}
$folder.Folders | ForEach { process-folders-recursive $_ $path}
}
}
$objOutlook = New-Object -comobject outlook.application
$objNamespace = $objOutlook.GetNamespace(“MAPI”)
#Get Outlook folder with name Public Folders
$publicFolders = $objNamespace.Folders | Where { $_.name.StartsWith("Public Folders") } | Select -f 1
#Go into the "All Public Folders" folder
$AllPublicFolders = $publicFolders.Folders | Where { $_.name -eq "All Public Folders" } | Select -f 1
#Recurse through the Public Folders
process-folders-recursive $AllPublicFolders $TargetDirectory
Related
This is the last section of my script that I use to transfer student leavers areas to an archive file server. The script runs and does everything as expected but still throws an error out saying
Move-Item : Cannot find path '\\domain\students$\E-J$\MH201507' because it does not exist. The script does find this path and moves the home area so I'm not sure why I get this error. Or is there any way to fix this or is it easier to hide the error somehow?
The CSV document contains a list of the sam account name and contains their home area location on the domain so it knows where to copy the path over from.
Any help would be massively appreicaited! Kind Regards
#Declaring the .csv file that contains a list of all leavers home directories.
$HomeDirectoryList = Import-CSV "C:\Scripts\Leavers\HomeDirectoryExport.csv"
$Username = $HomeDirectoryList.samAccountName
$HomeDirectory = $HomeDirectoryList.HomeDirectory
$Archive = "\\myfileserver.ac.uk\D$\21-22 Leavers"
ForEach ($Username in $HomeDirectoryList)
{
Move-Item -Path $HomeDirectory -Destination $Archive
}
Sample data with which the error occurs:
samAccountName HomeDirectory
WB214589 \\domain\students$\A-D$\WB214589
MH201507 \\domain\students$\E-J$\MH201507
You are setting the variable $HomeDirectory outside the loop, so that will contain an array of home directory paths.
Then you use variable $Username to iterate the data from the CSV file, but inside that loop you never use it.
Try:
$HomeDirectoryList = Import-CSV 'C:\Scripts\Leavers\HomeDirectoryExport.csv'
$Archive = '\\myfileserver.ac.uk\D$\21-22 Leavers'
foreach ($student in $HomeDirectoryList) {
Write-Host "Moving HomeDirectory folder for student '$($student.samAccountName)'"
Move-Item -Path $student.HomeDirectory -Destination $Archive -Force
}
If you need to catch errors happening, change the loop to:
foreach ($student in $HomeDirectoryList) {
Write-Host "Moving HomeDirectory folder for student '$($student.samAccountName)'"
try {
Move-Item -Path $student.HomeDirectory -Destination $Archive -Force -ErrorAction Stop
}
catch {
Write-Warning "Error moving homedirectory '$($student.HomeDirectory)':`r`n$($_.Exception.Message)"
}
}
Every morning I have an email that automatically generates, and withing the email is a CSV attachment. This is what I have so far:
$outlook = New-Object -ComObject Outlook.Application
$namespace = $outlook.GetNamespace("MAPI")
# Below: 6 is the default for inbox, so this saves the user from having to
# select the inbox folder. Change if emails w/ attatchements are going to a
# different folder
$folder = $namespace.GetDefaultFolder(6)
$filepath = "I:\PowerShell"
$folder.Items | foreach {
$_.attachments | foreach {
$filename = $_.filename
if ($filename.Contains("example.csv")) {
$_.SaveAsFile((Join-Path $filepath $filename))
Rename-Item -LiteralPath '.\example.csv' -NewName "Server.csv" -Force
}
}
}
When I run this the first time, it successfully grabs the attachment from the email, saves the attachment to the designated folder, and renames it to "Server.csv". However, when I run this a second time, it will grab the attachment and save it, however it will not rename/overwrite it as "Server.csv", so it will only save it as example.csv. It will sometimes throw an error saying
Rename-Item : Cannot create a file when that file already exists
However, I have -Force there, so I'm not sure why that is happening.
Any advice?
-Force allows you to rename a file that is hidden or read-only. It does not allow you to rename a file to replace an existing file. Check if the destination file already exists, and remove it if it does. Then save the attachment directly with the desired filename.
$outfile = Join-Path $filepath 'Server.csv'
if (Test-Path -LiteralPath $outfile) {
Remove-Item $outfile -Force
}
$_.SaveAsFile($outfile)
I know I could use System.IO.FileSystemWatcher in a Powershell script to detect when a file is created in Azure files (in my storage account). Is there a better way to detect a file being created than this method ?
I can't seem to find any logs that I could perhaps parse ?
Thanks
This could do you if you really didn't want to use the FileSystemWatch
$Files = Get-ChildItem -file -Recurse "C:\Temp"
$IfOlderThan = 60
$NewlyCreated = #()
$OldCreated = #()
$Count = 0
Foreach ($file in $files){
if($file.CreationTime -gt (Get-Date).AddMinutes(-$IfOlderThan)){
$NewlyCreated += $file
} Else {
$OldCreated += $file
}
}
Write-Host "Found " -NoNewline;Write-host $($NewlyCreated.Count) -NoNewline -ForegroundColor Green; Write-Host " file\s that have been created in the last $IfOlderThan minutes"
$NewlyCreated | select Name, Fullname, CreationTime
My aim is to compare two directories exactly - including the structure of the directories and sub-directories.
I need this, because I want to monitor if something in the folder E:\path2 was changed. For this case a copy of the full folder is in C:\path1. If someone changes something it has to be done in two directories.
It is important for us, because if something is changed in the directory (accidentally or not) it could break down other functions in our infrastructure.
This is the script I've already written:
# Compare files for "copy default folder"
# This Script compares the files and folders which are synced to every client.
# Source: https://mcpmag.com/articles/2016/04/14/contents-of-two-folders-with-powershell.aspx
# 1. Compare content and Name of every file recursively
$SourceDocsHash = Get-ChildItem -recurse –Path C:\path1 | foreach {Get-FileHash –Path $_.FullName}
$DestDocsHash = Get-ChildItem -recurse –Path E:\path2 | foreach {Get-FileHash –Path $_.FullName}
$ResultDocsHash = (Compare-Object -ReferenceObject $SourceDocsHash -DifferenceObject $DestDocsHash -Property hash -PassThru).Path
# 2. Compare name of every folder recursively
$SourceFolders = Get-ChildItem -recurse –Path C:\path1 #| where {!$_.PSIsContainer}
$DestFolders = Get-ChildItem -recurse –Path E:\path2 #| where {!$_.PSIsContainer}
$CompareFolders = Compare-Object -ReferenceObject $SourceFolders -DifferenceObject $DestFolders -PassThru -Property Name
$ResultFolders = $CompareFolders | Select-Object FullName
# 3. Check if UNC-Path is reachable
# Source: https://stackoverflow.com/questions/8095638/how-do-i-negate-a-condition-in-powershell
# Printout, if UNC-Path is not available.
if(-Not (Test-Path \\bb-srv-025.ftscu.be\DIP$\Settings\ftsCube\default-folder-on-client\00_ftsCube)){
$UNCpathReachable = "UNC-Path not reachable and maybe"
}
# 4. Count files for statistics
# Source: https://stackoverflow.com/questions/14714284/count-items-in-a-folder-with-powershell
$count = (Get-ChildItem -recurse –Path E:\path2 | Measure-Object ).Count;
# FINAL: Print out result for check_mk
if($ResultDocsHash -Or $ResultFolders -Or $UNCpathReachable){
echo "2 copy-default-folders-C-00_ftsCube files-and-folders-count=$count CRITIAL - $UNCpathReachable the following files or folders has been changed: $ResultDocs $ResultFolders (none if empty after ':')"
}
else{
echo "0 copy-default-folders-C-00_ftsCube files-and-folders-count=$count OK - no files has changed"
}
I know the output is not perfect formatted, but it's OK. :-)
This script spots the following changes successfully:
create new folder or new file
rename folder or file -> it is shown as error, but the output is empty. I can live with that. But maybe someone sees the reason. :-)
delete folder or file
change file content
This script does NOT spot the following changes:
move folder or file to other sub-folder. The script still says "everything OK"
I've been trying a lot of things, but could not solve this.
Does anyone can help me how the script can be extended to spot a moved folder or file?
I think your best bet is to use the .NET FileSystemWatcher class. It's not trivial to implement an advanced function that uses it, but I think it will simplify things for you.
I used the article Tracking Changes to a Folder Using PowerShell when I was learning this class. The author's code is below. I cleaned it up as little as I could stand. (That publishing platform's code formatting hurts my eyes.)
I think you want to run it like this.
New-FileSystemWatcher -Path E:\path2 -Recurse
I could be wrong.
Function New-FileSystemWatcher {
[cmdletbinding()]
Param (
[parameter()]
[string]$Path,
[parameter()]
[ValidateSet('Changed', 'Created', 'Deleted', 'Renamed')]
[string[]]$EventName,
[parameter()]
[string]$Filter,
[parameter()]
[System.IO.NotifyFilters]$NotifyFilter,
[parameter()]
[switch]$Recurse,
[parameter()]
[scriptblock]$Action
)
$FileSystemWatcher = New-Object System.IO.FileSystemWatcher
If (-NOT $PSBoundParameters.ContainsKey('Path')){
$Path = $PWD
}
$FileSystemWatcher.Path = $Path
If ($PSBoundParameters.ContainsKey('Filter')) {
$FileSystemWatcher.Filter = $Filter
}
If ($PSBoundParameters.ContainsKey('NotifyFilter')) {
$FileSystemWatcher.NotifyFilter = $NotifyFilter
}
If ($PSBoundParameters.ContainsKey('Recurse')) {
$FileSystemWatcher.IncludeSubdirectories = $True
}
If (-NOT $PSBoundParameters.ContainsKey('EventName')){
$EventName = 'Changed','Created','Deleted','Renamed'
}
If (-NOT $PSBoundParameters.ContainsKey('Action')){
$Action = {
Switch ($Event.SourceEventArgs.ChangeType) {
'Renamed' {
$Object = "{0} was {1} to {2} at {3}" -f $Event.SourceArgs[-1].OldFullPath,
$Event.SourceEventArgs.ChangeType,
$Event.SourceArgs[-1].FullPath,
$Event.TimeGenerated
}
Default {
$Object = "{0} was {1} at {2}" -f $Event.SourceEventArgs.FullPath,
$Event.SourceEventArgs.ChangeType,
$Event.TimeGenerated
}
}
$WriteHostParams = #{
ForegroundColor = 'Green'
BackgroundColor = 'Black'
Object = $Object
}
Write-Host #WriteHostParams
}
}
$ObjectEventParams = #{
InputObject = $FileSystemWatcher
Action = $Action
}
ForEach ($Item in $EventName) {
$ObjectEventParams.EventName = $Item
$ObjectEventParams.SourceIdentifier = "File.$($Item)"
Write-Verbose "Starting watcher for Event: $($Item)"
$Null = Register-ObjectEvent #ObjectEventParams
}
}
I don't think any example I've found online tells you how to stop watching the filesystem. The simplest way is to just close your PowerShell window. But I always seem to have 15 tabs open in each of five PowerShell windows, and closing one of them is a nuisance.
Instead, you can use Get-Job to get the Id of registered events. Then use Unregister-Event -SubscriptionId n to, well, unregister the event, where 'n' represents the number(s) you find in the Id property of Get-Job..
So basically you want to synchronize the two folders and note all the changes made on that:
I would suggest you to use
Sync-Folder Script
Or
FreeFile Sync.
All,
I am using Power Shell Community Extensions for PSv1 and the ZIP file is being created correctly. However, I only want the images in the ZIP file and I want to remove the folder from the ZIP file.
I have the folder called newpictures that is zipped up. I then use the -flattenpaths option in the Power Shell Community Extensions to put all the images in the base path, but the folder remains.
I have been searching online for a solution. I am not sure if I have this right, so can someone look over this code and let me know if this is correct before I proceed?
if (test-path $PSCXInstallDir) {
write-zip -path "Microsoft.PowerShell.Core\FileSystem::$TestSite" -outputpath "Microsoft.PowerShell.Core\FileSystem::$ZipFileCreationDir\$ZipFileName" -noclobber -quiet -flattenpaths -level 9
start-sleep -seconds 30
if (test-path $ZipFileCreationDir\$ZipFileName) {
$ShellApp = new-object -com shell.application
$TheZipFile = $ShellApp.namespace("$ZipFileCreationDir\$ZipFileName")
$TheZipFile.items() | where-object {$_.name -eq $FolderToCompress} | remove-item $FolderToCompress
}
}
The variables are:
$PSCXInstallDir = "C:\Program Files\PowerShell Community Extensions"
$TestSite = "\\10.0.100.3\www2.varietydistributors.com\catalog\newpictures"
$ZipFileCreationDir = "\\10.0.100.3\www2.varietydistributors.com\catalog"
$ZipFileName = "vdi-test.zip"
$FolderToCompress = "newpictures"
Thanks in advance for any feedback. In short, I just want to remove the single folder within the ZIP file.
Remove-Item won't work on items inside the zip file. You need to move the objects you want to delete outside the zip file before you can delete them:
$ShellApp = New-Object -COM 'Shell.Application'
$TheZipFile = $ShellApp.NameSpace("$ZipFileCreationDir\$ZipFileName")
$TheZipFile.Items() | ? { $_.Name -eq $FolderToCompress } | % {
$ShellApp.NameSpace($env:TEMP).MoveHere($_)
Remove-Item (Join-Path $env:TEMP $_.Name)
}
Note that Items() doesn't recurse into nested folders, it only enumerates the files and folders of the current namespace. If you need to process contents of nested folders you need to specify the nested path:
$NestedFolder = $ShellApp.NameSpace('C:\path\to\your.zip\nested\folder')
or recurse with something like this:
function RecurseIntoZip($fldr) {
$fldr.Items() | ? { $_.Name -eq $FolderToCompress } | % {
$_.Name
}
$fldr.Items() | ? { $_.Type -eq 'File folder' } | % {
RecurseIntoZip $_.GetFolder
}
}
RecurseIntoZip $TheZipFile