Extract file names from Posh-SSH Get-SFTPChildItem output in PowerShell - powershell

I want to take this part
REGISTRATION_ELI_20221222_071008.csv
from this PowerShell output of Posh-SSH Get-SFTPChildItem
Name REGISTRATION_ELI_20221222_071008.csv, Length 0, User ID 1142,
Group ID 1220, Accessed 12/21/2022 3:00:47 PM, Modified 12/21/2022
3:00:47 PM
How can I get it?
#Date
$time = (Get-Date).ToString("yyyyMMdd")
#Setting credentials for the user account
$password = ConvertTo-SecureString "password" -AsPlainText -Force
$creds = New-Object System.Management.Automation.PSCredential ("gomgom", $password)
$SFTPSession = New-SFTPSession -ComputerName 172.16.xxx.xxx -Credential $Credential -AcceptKey
# Set local file path and SFTP path
$LocalPath = "D:\WORK\Task - Script\20221010 - AJK - ITPRODIS380 - upload file csv ke sql server\csvfile"
$FilePath = Get-SFTPChildItem -SessionId $SFTPSession.SessionId "/Home Credit/Upload/" | Select-String -Pattern "REGISTRATION_ELI_$([datetime]::Now.toString('yyyyMMdd'))"
$FilePath
Remove-SFTPSession $SFTPSession -Verbose

Do not parse the name from the string dump of the file object.
The Posh-SSH Get-SFTPChildItem returns SSH.NET SftpFile, which has Name attribute:
$FilePath.Name
(though then the $FilePath is actually a confusing variable name, as it's an object, not a mere path string)

You can dig into the Regex library and extract it out using Regex
$test = "Name REGISTRATION_ELI_20221222_071008.csv, Length 0, User ID 1142, Group ID 1220, Accessed 12/21/2022 3:00:47 PM, Modified 12/21/2022 3:00:47 PM"
$matches = [regex]::Match($test,"REGISTRATION.+\.csv")
$Matches.Value

$FilePath | Select-String -Pattern 'Name\s(.*?),' -AllMatches| Foreach-Object {$.Matches} | Foreach-Object {$.Groups[1].Value}

Related

Powershell - Get yesterday's file name in real time

I'm noob in PowerShell and trying to use it to send email with attachment.
Today's date is 2021-02-18. I have a folder which have many files created by ERP system everyday, the files name are as follows:
2021-2-17WO_rate.xlsx
2021-2-16WO_rate.xlsx
2021-2-15WO_rate.xlsx
2021-2-14WO_rate.xlsx
2021-2-13WO_rate.xlsx
2021-2-12WO_rate.xlsx
...
I would like the script always choose the newest file as the attachment
(Get-Date).AddDays(-1).ToString('yyyy-MM-dd') can get the yesterday date, but when I intergrate it in to below script, it fails. I have no idea how to solve it after search the many websites.
Anyone can help me? much appreciate.
The code I use is as follows:
$UserName = "XXXX#XXXXX.com"
$Password = ConvertTo-SecureString XXXXXX -AsPlainText –Force
$cred = New-Object System.Management.Automation.PSCredential($UserName,$Password)
$a = (Get-Date).AddDays(-1).ToString('yyyy-MM-dd')
$mailParams = #{
SmtpServer = 'smtp.office365.com'
Port = '587' # or '25' if not using TLS
UseSSL = $true ## or not if using non-TLS
BodyasHtml = $true
Credential = $cred
From = $UserName
To = 'XXXXXX'
Subject = "WORK ORDER STATUS"
Body = 'XXXXXXX'
Attachments = '\\us-fs\us-Groups\Operationsus\Public\erpoutput\date\${a}WO_rate.xlsx'
DeliveryNotificationOption = 'OnFailure', 'OnSuccess'
}
Send-MailMessage #mailParams
The reason why the name is not populated is because you used single quotation mark '. If you want to have the value from the variable, you need to use double quotation string ":
<# Only relevant parts of the script, other lines removed #>
# Specify correct formatting string, based on the information from the question it should be either
$dataFormat = 'yyyy-M-d'
# or
$dataFormat = 'yyyy-M-dd'
$formattedDate = (Get-Date).AddDays(-1).ToString($dataFormat)
$mailParams = #{
<# other params #>
Attachments = "\\us-fs\us-Groups\Operationsus\Public\erpoutput\date\${formattedDate}WO_rate.xlsx"
}
Send-MailMessage #mailParams
NOTE: I also changed variable name so that it's obvious what it is used for.
Edit: corrected date format based on #Theo's comment
If the file is not modified since yesterday as well you can retrieve the path of the last file modified yesterday in the folder and use that as the attachment
$file = Get-ChildItem -Path 'C:\path\to\folder' -Filter '*.xlxs' -File | # get all files with xlxs extension in path
Where-Object { $_.LastWriteTime.Date -eq (Get-Date).AddDays(-1).Date } | # files that were last written to yesterday
Sort-Object LastWriteTime | # sort by lastwrite time in ascending order
Select-Object -Last 1 | # select only the last file on the list
ForEach-Object FullName # output the full file path
$UserName = "XXXX#XXXXX.com"
$Password = ConvertTo-SecureString XXXXXX -AsPlainText –Force
$cred = New-Object System.Management.Automation.PSCredential($UserName,$Password)
$a = (Get-Date).AddDays(-1).ToString('yyyy-MM-dd')
$mailParams = #{
SmtpServer = 'smtp.office365.com'
Port = '587' # or '25' if not using TLS
UseSSL = $true ## or not if using non-TLS
BodyasHtml = $true
Credential = $cred
From = $UserName
To = 'XXXXXX'
Subject = "WORK ORDER STATUS"
Body = 'XXXXXXX'
Attachments = $file # <--------------
DeliveryNotificationOption = 'OnFailure', 'OnSuccess'
}
Send-MailMessage #mailParams

Problem with import variable from text file and loop it

I'm trying to code a script with PowerShell and I'm stuck. Maybe you can send me a link or give a tip?
I write a command in PowerShell and I try to execute it on many servers with different domains:
$Username = 'domain\user'
$Password = 'password'
$pass = ConvertTo-SecureString -AsPlainText $Password -Force
$Cred = New-Object System.Management.Automation.PSCredential -ArgumentList $Username,$pass
invoke-command -filepath \source_script.ps1 -computerName server07 -Credential $cred
i have a long list in serverlist.txt file something like this
city servername credential domain
city servername credential domain
city servername credential domain
and now im trying to do my script on many server in list, but not all server in list. For example, do this script only where city = berlin or london or moscow.
What should i do? How to load variable to $username $ password in loop.
I know how to do it with BATCH with powershell, but i must do it in PowerShell
Look at my batch file, how i do it before:
FOR /F %%a IN (\my_list.txt) DO TYPE \server_list.txt | find.exe "%%a" >> %source_path%\temporary_list.txt
::my_list.txt is the list all off my variables
for /f "tokens=*" %%A in (\temporary_list.txt) do call :run_program %%A
:run_program
SET mycity=%1
SET myserver=%2
SET mycredentials=%3
SET mydomain=%4
psExec.exe -u %mydomain% %mycredentials% \\%myserver% blah blah blah, rest of script
I would do it like this. First of all, import you server list, then iterate over it to decide which servers to work on...
#import your server list
$serverlist = Get-Content -path C:\path\to\serverlist.txt
#iterate over $serverlist, and only act on entries that match Berlin or London or Moscow, etc...
Foreach ($server in $serverlist | Where-Object {$_ -match ("Berlin|London|Moscow")}) {
Invoke-Command -ComputerName $server -Credential $cred -ScriptBlock {
#do work
}
}
This method uses the foreach loop and a regex match alternator to look for Berlin, or London, or Moscow, in each line from your list.
UPDATE
To work with a large array of cities, it would be much more readable if you are working with a csv, with headers. Here is an example...
<#
import your server list as a csv
csv looks like this...
server,city,cred
server1,moscow,cred1
server2,london,cred1
server3,moscow,cred2
server4,berlin,cred1
#>
#import serverlist.csv...
$serverlist = Import-csv -path C:\scripts\lists\serverlist.csv
#import your city list...
$citylist = Get-Content -path C:\scripts\lists\citylist.txt
#iterate $serverlist and check if $citylist contains $_.City...
$serverlist | ForEach-Object {
if ($citylist.Contains($_.City)) {
Invoke-Command -ComputerName $_.Name -Credential $_.cred -ScriptBlock {
#do work
}
}
}
UPDATE 2
If you are working with .txt and not .csv, you can also match on the city.
If your serverlist.txt looks like this...
server city cred
server city cred
server city cred
...you can split each row of $serverlist and match on array index values. Powershell will index from 0, so to match on 'city', you would use index [1], etc...
$serverlist = Get-Content -path C:\scripts\lists\serverlist.txt
#import your city list...
$citylist = Get-Content -path C:\scripts\lists\citylist.txt
#iterate $serverlist
#split each row into a collection
#check if $citylist contains a match on index 1 (city 'column')...
$serverlist | ForEach-Object {
$serverInfo = ($_).split(" ")
if ($citylist.Contains($serverInfo[1])) {
Invoke-Command -ComputerName $serverInfo[0] -Credential $serverInfo[2] -ScriptBlock {
#do work
}
}
}

SID in a Powershell function

I have a PowerShell function that will enable auditing on the Perflogs folder. The function works just fine on a Windows PC with an English installation language. But when I use it on a Danish version it fails because "Everyone" doesnt exit on a Danish installation. On a Danish installation "Everyone" is called "Alle"
So instead of using everyone, then I would like to use the SID "S-1-1-0"
S-1-1-0 = Everyone/World link
But for some reason this also does not work. Does anyone have a clue about this and why I can’t do this?
function AddAuditToFile {
param
(
[Parameter(Mandatory=$true)]
[string]$path
)
Get-Acl $path -Audit | Format-List Path,AuditToString | Out-File -FilePath 'file_before.txt' -Width 200 -Append
$File_ACL = Get-Acl $path
$AccessRule = New-Object System.Security.AccessControl.FileSystemAuditRule("S-1-1-0","CreateFiles,Modify,AppendData”,"none","none",”Success")
$File_ACL.AddAuditRule($AccessRule)
$File_ACL | Set-Acl $path
Get-Acl $path -Audit | Format-List Path,AuditToString | Out-File -FilePath 'file_after.txt' -Width 200 -Append}
I call the function like this:
AddAuditToFile "C:\Perflogs"
Use the SecurityIdentifier class to translate the SID:
$everyoneSid= New-Object System.Security.Principal.SecurityIdentifier "S-1-1-0"
$everyoneSidName= $everyoneSid.Translate([System.Security.Principal.NTAccount])
$everyoneSidName.Value
This will output the actual everyone group name depending on the actual machine.
Gungnir from Spiceworks found the solution.
I had to translate the SID and make a variable and then use the variable
$AccountSID = New-Object -TypeName System.Security.Principal.SecurityIdentifier -ArgumentList 'S-1-1-0'
$AccountName = $AccountSID.Translate([System.Security.Principal.NTAccount]).Value
$AccessRule = New-Object System.Security.AccessControl.FileSystemAuditRule -ArgumentList ($AccountName,'CreateFiles,Modify,AppendData','none','none','Success')

FTPS Upload in Powershell

I'm in the process of learning Powershell, and am working on a little script that will upload a group of files to an FTPS server nightly. The files are located on a network share in a sub-directory containing the date in the name. The files themselves will all begin with the same string, let's say "JONES_". I have this script working for FTP, but I don't quite get what I need to do to get it to work for FTPS:
# Set yesterday's date (since uploads will happen at 2am)
$YDate = (Get-Date).AddDays(-1).ToString('MM-dd-yyyy')
#Create Log File
$Logfile = "C:\powershell\$YDate.log"
Function LogWrite
{
Param ([string]$logstring)
Add-Content $Logfile -value $logstring
}
# Find Directory w/ Yesterday's Date in name
$YesterdayFolder = Get-ChildItem -Path "\\network\storage\location" | Where-Object {$_.FullName.contains($YDate)}
If ($YesterdayFolder) {
#we specify the directory where all files that we want to upload are contained
$Dir= $YesterdayFolder
#ftp server
$ftp = "ftp://ftps.site.com"
$user = "USERNAME"
$pass = "PASSWORD"
$webclient = New-Object System.Net.WebClient
$webclient.Credentials = New-Object System.Net.NetworkCredential($user,$pass)
$FilesToUpload = Get-ChildItem -Path (Join-Path $YesterdayFolder.FullName "Report") | Where-Object {$_.Name.StartsWith("JONES","CurrentCultureIgnoreCase")}
foreach($item in ($FilesToUpload))
{
LogWrite "Uploading file: $YesterdayFolder\Report\$item"
$uri = New-Object System.Uri($ftp+$item.Name)
$webclient.UploadFile($uri, $item.FullName)
}
} Else {
LogWrite "No files to upload"
}
I'd rather not have to deal with a 3rd party software solution, if at all possible.
Using psftp didn't work for me. I couldn't get it to connect to the FTP over SSL. I ended up (reluctantly?) using WinSCP with this code:
$PutCommand = '& "C:\Program Files (x86)\WinSCP\winscp.com" /command "open ftp://USER:PASS#ftps.hostname.com:21/directory/ -explicitssl" "put """"' + $Item.FullName + '""""" "exit"'
Invoke-Expression $PutCommand
In the foreach loop.
I'm not sure if you would consider this as "3rd party software" or not, but you can run PSFTP from within Powershell. Here is an example of how you could do that (source):
$outfile=$YesterdayFolder"\Report\"$item.Name
"rm $outfile`nput $outfile`nbye" | out-file batch.psftp -force -Encoding ASCII
$user = "USERNAME"
$pass = "PASSWORD"
&.\psftp.exe -l $user -pw $pass $ftp -b batch.psftp -be

Random string showing up in exported CSV

I have the powershell script built and I'm getting a "Random" bit of output into the CSV file. The string is MailboxExport(and a number). It looks like a value that (Get-MailboxExportRequest).name would return but I can't see where I would pull something like that or how it is being inserted. I think I may have just been staring at it too long and I may just need a fresh pair of eyes to spot my mistake. I would go into what the script is trying to do but I've put quite a few notes in the script that should explain it fairly well.
################################################## PST Extraction Script ##################################################
# Completed October 2013 by Trey Nuckolls
#
# This script is meant to extract PST files from the Site 1 Exchange server at the Site2 site and deliver those PST
# files to a share on the Site2 network. The script will change the input CSV file to keep track of which PSTfiles have been
# extracted and when that occoured. The script will also set security on the PST file so only the user and IT administraion
# can access the PST file.
#
# To run this script, enter the username of the Site 1 domain account that you want to target for extraction of a PST file then
# Run the script. Can be run from any machine on the network as long as it is run by someone with domain admin rights on the
# Site 2 network. Powershell v2 or v3 is required to run the script.
#
#############################################################################################################################
$InPstPath = '\\Site1_Server\PST_Store'
$OutPstPath = '\\Site2_Server\PST_Store'
$AdminPath = '\\Site2_Server\PST_Store\Admin\'
#Container for Site1 username
$User = Get-Content $AdminPath'login.txt'
#Container for encrypted Site1 Password
$PWord = Cat $AdminPath'pass.txt' | ConvertTo-SecureString
#Credential package for accessing Site1 resouces
$Credentials = New-Object –TypeName System.Management.Automation.PSCredential –ArgumentList $User, $PWord
#Creation of Powershell Drives for use during session
New-PSDrive -Name Site1Share -PSProvider FileSystem -Root $InPstPath -Credential $Credentials
New-PSDrive -Name Site2Share -PSProvider FileSystem -Root $OutPstPath
#Container for Powershell session to Exchange server
$PSSession = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri http://Site1_Server/powershell -Credential $Credentials
#Creation of Powershell session to Site1 Exchange server, including import of exchange commandlets
Import-PSSession $PSSession
#Import of the CSV file that lists users to be targeted
$In_List = Invoke-Command {Import-Csv "\\Site1_Server\PST_Store\To_Be_Exported.csv"} -computername Site1_Server -Credential $Credentials
$Processed = foreach ($objUser in $In_List) {
if ($objUser.Completed -ne "Yes") {
$TargetUser = $objUser.name
$ShortDate = (Get-Date).toshortdatestring()
$SourceFile = "Site1Share:\$TargetUser.pst"
$DestinationFile = "Site2Share:\$TargetUser.pst"
#Export Mailbox to PST File
New-MailboxExportRequest -Mailbox $TargetUser -Filepath $InPstPath\$TargetUser.pst
do {Start-Sleep -Seconds 10}
until((Get-MailboxExportRequest -Status InProgress).count -eq 0)
#Copy PST File to PST Share
Copy-Item -Path $SourceFile -Destination $DestinationFile
#Add Security access on PST file (Target_User-Modify). Domain Admin-Full is inherited from parent.
$Acl = Get-Acl $DestinationFile
$Permission = "Site2_Domain\$TargetUser","Modify","Allow"
$AccessRule = New-Object System.Security.AccessControl.FileSystemAccessRule $Permission
$Acl.SetAccessRule($AccessRule)
$Acl | Set-Acl $DestinationFile
#Remove PST file From Temporary area
Remove-Item -Path $SourceFile -Force
#Write back to checklist for new items that have just been processed
[PSCustomObject]#{Name=$TargetUser;Completed="Yes";Date=$ShortDate}
} else { if ($objUser.Completed -eq "Yes") {
#Passthrough of items that have already been completed
[PSCustomObject]#{Name=$objUser.name;Completed=$objUser.Completed;Date=$objUser.Date}}
}}
#Output the new version of the checklist
$Processed | export-csv -Path C:\TEMP\processed.csv
#Overwrite the old version checklist with the new one
Move-Item -Path C:\TEMP\processed.csv -Destination Site1Share:\To_Be_Exported.csv -force
#Cleanup PsDrives and PsSessions
Remove-PSDrive -Name Site1Share
Remove-PSDrive -Name Site2Share
Remove-PSSession -Session (Get-PSSession)
Input CSV is...
"Name","Completed","Date"
"User1","Yes","10/8/2013"
"User2","Yes","10/11/2013"
"User3",,
and output is...
"Name","Completed","Date"
"User1","Yes","10/8/2013"
"User2","Yes","10/11/2013"
"MailboxExport7",,
"User3","Yes","10/11/2013"
It is indeed very likely that the issue is caused by New-MailboxExportRequest, as you already suspected. The cmdlet prints information about the created object, which lumped together with the rest of the output you create in the loop, and then assigned to the variable $Processed.
To avoid this you can suppress the cmdlet output like this:
New-MailboxExportRequest -Mailbox ... | Out-Null
or like this:
New-MailboxExportRequest -Mailbox ... >$null
Assigning the output to a variable should work as well:
$exportRequest = New-MailboxExportRequest -Mailbox ...
On you Export-CSV, try adding the flag: "-NoTypeInformation"
I think this may be some sort of name space crossover issue between the custom object and another existing object (probably the mailboxexportrequest object on the exchange server). After messing around with this for a while I was able to get it to fail in a new way where the resultant csv file was full of details from the mailbox exports and their was a 'name' column that also had listed the usernames. I changed the hashes on the input csv from 'name to 'username' and the resultant MailboxExport entries have ceased. There are now blank row but I'm certainly willing to live with that imperfection as it doesn't break this (short lived) process.
If anyone has any insight into the root cause I'd certainly love to hear what it is but I think I've figured out a solution to the point that I can live with.