i need some help.
I need a powershell script with following actions.
The script starts and firts checks whether file 1 is available. If this is available, go to step 2. If file 1 is not available, the script should end.
The script checks whether file 2 exists. If file 2 is available, the script should end; if file 2 is not available, go to step 3.
The script checks whether a program X is running. If YES it should exit the program and copy file 3 into a folder. If program X is not running, it should copy file 3 immediately.
Very important! If in step 2 the file 2 exist, the script must quit.
This what i have but even if file 2 is present, it copies the files.
$Path = "\\xyz\trigger1.txt"
$Path2 = "$env:userprofile\xyz\trigger2.txt"
if ((Test-Path $Path) -and !(Test-Path $Path2)) {
"trigger1.txt exist"
"trigger2.txt not exist"
if((get-process "XXX" -ea SilentlyContinue) -eq $Null)
{
"not Running"
copy-item "\\xyz\xyz\*" "$env:userprofile\def\" -recurse -ErrorAction SilentlyContinue
start-sleep -s 5
Start-Process -filepath "C:\Program Files (x86)\mno\XXX.exe"
}
else
{
"running"
stop-process -name "XXX" -force
start-sleep -s 5
copy-item "\\xyz\xyz\*" "$env:userprofile\def\" -recurse -ErrorAction SilentlyContinue
start-sleep -s 5
Start-Process -filepath "C:\Program Files (x86)\mno\XXX.exe"
}
}
else
{
"trigger1.txt not exist"
start-sleep -s 5
}
Make sure you're running the script under right user.
Make sure you use -PathType Leaf in Test-Path if you are only looking for files.
Make sure logical operators are working with parentheses to avoid mistakes.
if ((Test-Path $Path -PathType Leaf) -and (-not (Test-Path $Path2 -PathType Leaf))) {
}
Related
I am currently strugling on a simple powershell script to archive files.
I have thousand of old file in a folder and i want to archive them depending on the month/year of their creation date in archives named "YYYYMM".
I use the code below
Get-ChildItem -Path $sourcePath -filter $filter |
Where-Object {(($_.CreationTime) -le $dateCriteria) -and ($_.psIsContainer -eq $false)}|
ForEach {
$archive = "{0:yyyy}{0:MM}.7z" -f $_.CreationTime
$archivePath= Join-Path -Path $destinationFolder -ChildPath $archive
& "C:\Program Files\7-Zip\7z.exe" a -mx9 -t7z -m0=lzma2 -sdel $archivePath$_.FullName |Out-Null
}
The logic seems fine as it creates files like
201809.7z
201810.7z
...
In my destination folder.
The problem is i see errors in the console:
System ERROR:
The file exists
or
System ERROR:
Access denied
or
System ERROR:
The file exists
ERROR: ********\202011.7z
Can not open the file as archive
As a result, in my destination folder, in addition to the expected archive files i have file like "201810.7z.tmp1"
I changes the working directory to isolate those files by adding -w"{WORK_PATH}"
to the command line.
I also added Start-Sleep -Milliseconds 1
as it looked like concurrent access even if my script is mono threaded (maybe 7zip doesn't end properly) but it didn't work.
With Start-Sleep -Milliseconds 500it seems to work but for obvious reasons i dont want to use that. What would the proper way to do that be ?
EDIT 1
Following MisterSmith's answer i changed my code for
Get-ChildItem -Path $emplacementSource -filter $filtreNomFichiers |
Where-Object {(($_.CreationTime) -le $dernierJour) -and ($_.psIsContainer -eq $false)}|
ForEach {
$archive = "{0:yyyy}{0:MM}.7z" -f $_.CreationTime
$cheminArchive= Join-Path -Path $dossierCible -ChildPath $archive
[Array]$arguments = "a" ,"-w$workDir", "-mx9" ,"-t7z" ,"-m0=lzma2" ,"-sdel" ,$cheminArchive, $_.FullName
$pinfo = New-Object System.Diagnostics.ProcessStartInfo
$pinfo.FileName = $sevenZip
$pinfo.RedirectStandardError = $true
$pinfo.CreateNoWindow= $true
$pinfo.UseShellExecute = $false
$pinfo.Arguments = "$arguments"
$process = New-Object System.Diagnostics.Process
$process.StartInfo = $pinfo
$process.Start()
$output = $process.StandardError.ReadToEnd()
$process.WaitForExit()
if(0 -ne $process.ExitCode){
Write-Output "$(Get-TimeStamp) Erreur: $output" | Out-file $fichierLogs -append
}
}
I still have File exists errors and .tmpX archives in my temp folder.
Instead of using & use Start-Process and pass the -wait switch or use -PassThru switch and the returned System.Diagnostics.Process to check if the process has finished yourself. Either will get the same result as your Start-Sleep -Milliseconds 500 test, but it will only wait for the time taken by 7z.exe to complete its execution.
Side note - you can append multiple files at once. That would probably work out quicker overall than adding each file separately.
If I run the script below it will perform all the tasks that I need it to. I need the task to run every 15 seconds in a loop. When I add a loop of any type, the loop gets stuck on only a portion of the script an no longer functions as expected. I'm looking to loop the entire script top to bottom. The current script does not have any loops at this time. Any help would be greatly appreciated.
# Syslog Management. Automate has a max size of 977KB before it dumps everything into a file called Syslogold.txt
# If Syslogold.txt already exists it will be overwriten and logs will be gone.
# Script below will move syslogold.txt into syslog folder where it will be renamed to Syslog-(CurrentDate).TxT
# When ever a new Syslogold.txt is generated by Automate, this script will append the output to a daily file in a monthly folder.
# On the first day of every month, the previous month will be zipped and archived.
#
$LastMonth = (Get-Date).AddMonths(-1).ToString('MMM-yyyy')
$CurrentSyslogFolder = "C:\Windows\LTsvc\Syslogs\Syslogs-$(Get-Date -Format "MMM-yyyy")"
$OldDirectory = "C:\Windows\LTsvc\Syslogs\Syslogs-$LastMonth"
$CurrentLog = "Syslog-$(Get-Date -Format "dd-MMM-yyyy").txt"
$OldLog = "C:\Windows\LTSvc\syslogold.txt"
$SyslogArchive = "C:\Windows\LTSvc\Syslogs\Archive\Syslog-$LastMonth.zip"
do{
$TestPath01 = Test-Path -Path $CurrentSyslogFolder
$TestPath02 = Test-Path -Path $CurrentSyslogFolder\$CurrentLog
$TestPath03 = Test-Path -Path $OldLog
$TestPath04 = Test-Path -Path $OldDirectory
if($TestPath01)
{
write-host "Syslog directory current."
}
else
{
write-host "Current syslog directory is for last month. Creating new folder."
New-Item -ItemType Directory -Force -Path $CurrentSyslogFolder
}
if(($TestPath02) -and ($TestPath03))
{
write-host "Automate syslog archive found"
write-host "Daily syslog Found."
write-host "Appending Automate archive with daily syslog"
Add-Content -Path $CurrentSyslogFolder\$CurrentLog -Value ""
Get-Content -Path $OldLog | Add-Content -Path $CurrentSyslogFolder\$CurrentLog
Remove-Item -Path $OldLog
start-sleep -Seconds 15
}
else
{
write-host "Automate syslogs active."
write-host "Waiting for Automate to archive syslogs."
if ($TestPath03)
{
Write-Host "Automate has completed archiving Syslogs."
Write-Host "Moving archive to daily syslog."
Write-host "Daily syslog missing. Creating daily syslog now"
Move-Item $OldLog -Destination $CurrentSyslogFolder\$CurrentLog
}
}
if($TestPath04)
{
write-host "Last months directory found."
Write-Host "Compressing last months directory"
Compress-Archive -Path "$OldDirectory" -DestinationPath "$SyslogArchive"
Write-host "Moving compressed directory to archive"
Write-host "Cleaning up files"
Remove-Item -Path "$SyslogDirectory\Syslogs-$LastMonth" -Recurse
Write-host "Cleanup completed"
start-sleep -Seconds 15
}
else
{
Write-host "Will try again in 15 seconds"
start-sleep -Seconds 15
}
}until($infinity)
To be honest the best way would be Scheduled Task ScheduledTask
But you can also use do or while
do{ YOURSCRIPT start-sleep -Seconds 15 }until($infinity)
I need help on a specific issue with Powershell.
What I am trying to do is that starting multiple services one bye one and after successfully start process, I need to copy some files from one location to another. These files are created only after the service/app is up. I need to check it from a string in a text file (like "Service successfully started").
I tried to make a for-each loop but because of copying and text check locations are different, I couldn't manage to do it. And honestly, I don't have much information about nested loops. Maybe you can give me some ideas to make this work.
For examples, for 1 service;
Source folder file locations;
C:\sourcepath\location1\folder\abc.dat
C:\sourcepath\location1\folder\cde.dat
txt file which needs to be checked if there is a string line called "Service successfully started" (to understand the service-app successfully started)
C:\sourcepath\folder1\logs\logfile.txt
Destination folder file locations
D:\destinationpath\location1\ (abc.dat and cde.dat files should be in same folder)
--- The flow should be like that;
Start a service
Make sure it's up as checking the txt file string
After controls, make copying process from source folder to destination for the specified files (as creating destination folder based on source folder)
Stop the service
After checking it's status as stopped, again start another service and do the same processes until the last service but for different locations
For example, location1 should be location2 and then location3 but the file names are the same. Also destination folder should be created according to source folder.
Even any directions will be helpful.
Edit1:
So far, I could write code.
[array]$serviceNames = "lfsvc", "iphlpsvc"
[array]$app = "app1", "app2"
$sourceStart = "C:\Source\"
$destinationStart = "C:\Target\"
$logs = "\logs"
$sourceFull = $sourceStart+$app.Get(0)+"\data"
$destinationFull = $destinationStart+$app.Get(0)
ForEach ($serviceNames in $serviceNames)
{
Start-Service $serviceNames -ErrorAction SilentlyContinue;
$text = Select-String -Path $sourceStart+$app.Get(0)+$logs\log.txt -Pattern "Service successfully started"
if ($text -ne $null)
{
md $destination;
Copy-Item -Path $sourceFull\123.txt -Destination $destinationFull\123.txt
Copy-Item -Path $sourceFull\456.txt -Destination $destinationFull\456.txt
}
}
I need to point other $app values in a row as pointing other $serviceNames values accordingly.
I need to take control the if values if wait till it shows the service successfully started line
Thanks
Edit2:
If I want to write it in long way, that should be something like that. (Ofc, if I can check the string from a specified text file, it would be gr8)
I need to shorten the codes
[array]$serviceNames = "aService", "bService"
Start-Service $serviceNames[0] -ErrorAction SilentlyContinue;
Start-Sleep -Seconds 75;
md "C:\Dest\aService\fld";
Copy-Item -Path "C:\Source\aService\fld\123.txt" -Destination "C:\Dest\aService\fld\123.txt";
Copy-Item -Path "C:\Source\aService\fld\456.txt" -Destination "C:\Dest\aService\fld\456.txt";
Copy-Item -Path "C:\Source\aService\fld\789.txt" -Destination "C:\Dest\aService\fld\789.txt";
Stop-Service $serviceNames[0] -ErrorAction SilentlyContinue;
Start-Sleep -Seconds 15;
Start-Service $serviceNames[1] -ErrorAction SilentlyContinue;
Start-Sleep -Seconds 75;
md "C:\Dest\bService\fld";
Copy-Item -Path "C:\Source\bService\fld\123.txt" -Destination "C:\Dest\bService\fld\123.txt";
Copy-Item -Path "C:\Source\bService\fld\456.txt" -Destination "C:\Dest\bService\fld\456.txt";
Copy-Item -Path "C:\Source\bService\fld\789.txt" -Destination "C:\Dest\bService\fld\789.txt";
Stop-Service $serviceNames[1] -ErrorAction SilentlyContinue;
Start-Sleep -Seconds 15;
I think I have an idea of what you want.
[array]$serviceNames = "lfsvc", "iphlpsvc"
[array]$apps = "app1", "app2"
$sourceStart = "C:\Source\"
$destinationStart = "C:\Target\"
$logs = "\logs"
# main loop, which loops over the apps
foreach($app in $apps)
{
$sourceFull = $sourceStart + $app + "\data"
$destinationFull = $destinationStart + $app
# each app will iterate over all of the services
ForEach ($name in $serviceNames)
{
# uses -passthru to get the service object, and pulls its status from that. Will cause any errors to terminate the script
$status = (Start-Service $name -ErrorAction Stop -PassThru).Status
# this while loop will cause it to pause until the service is in "running" state
while($status -ne "Running") {Start-Sleep -Seconds 5; Get-Service $name}
$text = Select-String -Path $($sourceStart + $app + $logs + "\log.txt") -Pattern "Service successfully started"
# check to see if the $text variable is null or empty, if not, do the thing
if (![string]::IsNullOrEmpty($text))
{
if(!(Test-Path -Path $destinationFull)){New-Item -ItemType Directory -Path $destinationFull}
Get-Content -Path "$sourceFull\123.txt" | Add-Content -Path "$destinationFull\123.txt"
Get-Content -Path "$sourceFull\456.txt" | Add-Content -Path "$destinationFull\456.txt"
}
# stops the service
$status = Stop-Service $name -PassThru
# pauses until the service is stopped
while($status -ne "Stopped") {Start-Sleep -Seconds 5; Get-Service $name}
}
}
something like this?
I need a little help putting this script into ONE big loop of somekind
What i need it to do is, the words in bold is where i am stuggling
1.Open each network share, only the first level (I have called this $Dirs)
2.Create a text file in each network share, if it already exists then to write "error and where it errored" but to contiune script
3.To delete the file created, if it does not exists then to write "error where it errored" but to contiune
4.if the scripts completes (creates the file then deletes the file from each share to put standard output as "healthy" or "error" if it did not.
So idealy the script needs to open each share, create a text file, check the text file is created, then delete the text file, once deleted and if there was no errors say "healthy"
$Dirs = "\\Share_abc\", "\\Share_def\", "\\Share_ghi", "\\Share_jkl\", "\\Share_mno"
$FileName = "DAVETEST1234.txt"
$FileExists = (Test-Path -path $WantedfileTest1)
$WantedfileTest1 = Join-Path $dir -ChildPath $FileName
#create file in share path, if file is not created write error
foreach ($dir in $dirs) {
New-Item -itemType file -Path $Dirs -Name ($FileName) -Value "Healthy" -ErrorAction SilentlyContinue
Start-Sleep -s 5
if ($FileExists -eq $false) {Write-Host "Error"} }
#remove item created
foreach ($Dir in $Dirs) {
Remove-Item -Path $WantedfileTest1 -ErrorAction SilentlyContinue
Start-Sleep -s 5
if ($FileExists -eq $true) {Write-Host "Error"} }
This identical code has been used in 3 servers, and only one of them does it silently fail to move the items (it still REMOVES them, but they do not appear in the share).
Azure-MapShare.ps1
param (
[string]$DriveLetter,
[string]$StorageLocation,
[string]$StorageKey,
[string]$StorageUser
)
if (!(Test-Path "${DriveLetter}:"))
{
cmd.exe /c "net use ${DriveLetter}: ${StorageLocation} /u:${StorageUser} ""${StorageKey}"""
}
Get-Exclusion-Days.ps1
param (
[datetime]$startDate,
[int]$daysBack
)
$date = $startDate
$endDate = (Get-Date).AddDays(-$daysBack)
$allDays =
do {
"*"+$date.ToString("yyyyMMdd")+"*"
$date = $date.AddDays(-1)
} until ($date -lt $endDate)
return $allDays
Migrate-Files.ps1
param(
[string]$Source,
[string]$Filter,
[string]$Destination,
[switch]$Remove=$False
)
#Test if source path exist
if((Test-Path -Path $Source.trim()) -ne $True) {
throw 'Source did not exist'
}
#Test if destination path exist
if ((Test-Path -Path $Destination.trim()) -ne $True) {
throw 'Destination did not exist'
}
#Test if no files in source
if((Get-ChildItem -Path $Source).Length -eq 0) {
throw 'No files at source'
}
if($Remove)
{
#Move-Item removes the source files
Move-Item -Path $Source -Filter $Filter -Destination $Destination -Force
} else {
#Copy-Item keeps a local copy
Copy-Item -Path $Source -Filter $Filter -Destination $Destination -Force
}
return $True
The job step is type "PowerShell" on all 3 servers and contains this identical code:
#Create mapping if missing
D:\Scripts\Azure-MapShare.ps1 -DriveLetter 'M' -StorageKey "[AzureStorageKey]" -StorageLocation "[AzureStorageAccountLocation]\backup" -StorageUser "[AzureStorageUser]"
#Copy files to Archive
D:\Scripts\Migrate-Files.ps1 -Source "D:\Databases\Backup\*.bak" -Destination "D:\Databases\BackupArchive"
#Get date range to exclude
$exclusion = D:\Scripts\Get-Exclusion-Days.ps1 -startDate Get-Date -DaysBack 7
#Remove items that are not included in exclusion range
Remove-Item -Path "D:\Databases\BackupArchive\*.bak" -exclude $exclusion
#Move files to storage account. They will be destroyed
D:\Scripts\Migrate-Files.ps1 -Source "D:\Databases\Backup\*.bak" -Destination "M:\" -Remove
#Remove remote backups that are not from todays backup
Remove-Item -Path "M:\*.bak" -exclude $exclusion
If I run the job step using SQL then the files get removed but do not appear in the storage account. If I run this code block manually, they get moved.
When I start up PowerShell on the server, I get an error message: "Attempting to perform the InitializeDefaultDrives operation on the 'FileSystem' provider failed." However, this does not really impact the rest of the operations (copying the backup files to BackupArchive folder, for instance).
I should mention that copy-item also fails to copy across to the share, but succeeds in copying to the /BackupArchive folder
Note sure if this will help you but you could try to use the New-PSDrive cmdlet instead of net use to map your shares:
param (
[string]$DriveLetter,
[string]$StorageLocation,
[string]$StorageKey,
[string]$StorageUser
)
if (!(Test-Path $DriveLetter))
{
$securedKey = $StorageKey | ConvertTo-SecureString -AsPlainText -Force
$credentials = New-Object System.Management.Automation.PSCredential ($StorageUser, $securedKey)
New-PSDrive -Name $DriveLetter -PSProvider FileSystem -Root $StorageLocation -Credential $credentials -Persist
}
Apparently I tricked myself on this one. During testing I must have run the net use command in an elevated command prompt. This apparently hid the mapped drive from non-elevated OS features such as the Windows Explorer and attempts to view its existence via non-elevated command prompt sessions. I suppose it also was automatically reconnecting during reboots because that did not fix it.
The solution was as easy as running the net use m: /delete command from an elevated command prompt.