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()
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)
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'
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)
}
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.