Race condition creating directory with New-Item? - windows

I'm seeing a race condition when calling New-Item to create a directory on a foreign machine using a UNC path. The code is below:
New-Item $target -itemType Directory -Force -Verbose |
%{ Write-Host "Creating dir" $_.FullName }
Using Test-Path immediately afterwards returns false. I put a Test-Path -> sleep for 1 second retry loop and after sleeping for 1 second, Test-Path is returning true.
Is New-Item a blocking call? Should I expect to have to wait after calling New-Item?

I cannot reproduce your problem.
PS > New-Item "test" -itemType Directory -Force -Verbose | %{ Test-Path $_.FullName }
VERBOSE: Performing the operation "Create Directory" on target "Destination: C:\Users\Frode\Desktop\test".
True
New-Item creates a new directory by getting a DirectoryInfo-object for the parent directory, and calling it's CreateSubDirectory, like:
DirectoryInfo subdirectory = new DirectoryInfo(parentPath).CreateSubdirectory(childName);
I'm not a developer, but AFAIK that means it's a blocking call, since it waits for an DirectoryInfo-object in return. So mabe the problem is with your storage subsystem.

Try running the New-Item command in another process and wait for it:
Start-Process powershell -Argument "-Command `"New-Item `"$myNewDir`" -ItemType `"directory`"`"" -NoNewWindow -Wait
I was writing a script that would create a folder and then write a 7zip archive to the folder but 7zip would complain that the directory did not exist. This seemed to work around the issue.

Related

How to use Remove-Item -Confirm:$false -Force properly in Windows PowerShell

So, I am writing this script in PowerShell and I am required to delete a few files in APPDATA on Windows. I wrote this line of code and it doesn't remove the item silently. It asks for confirmation even after using $Confirm:false. How do I fix this issue?
My code:
Get-ChildItem -Path $env:APPDATA\"Microsoft\teams\blob_storage" | Remove-Item -Confirm:$false -Force
I get this unwanted confirmation box every time I run the script:
Here is your modified code. I hope it will work for you.
Get-ChildItem -Path $env:APPDATA\"Microsoft\teams\blob_storage" | Remove-Item -Recurse -Force

Check if the same powershell script is already running and run it later if yes

I am calling from my Java EE application through ssh (apache) Powershell script on remote server, this script cannot be run in parallel so I need to check if the script is already running and wait until there is no process and run that waiting script. It might happen that the script will be called several times, eg. 20 times in few seconds and all of these needs to be run one by one.
Any idea how this can be achieved or if this is even possible? Thank you!
The way that I've accomplished this in some of my scripts is to use a lock file
$lockFile = "$PSScriptRoot\LOCK"
if (-not (Test-Path $lockFile)) {
New-Item -ItemType File -Path $lockFile | Out-Null
# Do other stuff...
Remove-Item -Path $lockFile -Force
}
You could maybe modify to something like this?
$lockFile = "$PSScriptRoot\LOCK"
while (Test-Path $lockFile)
{
Start-Sleep -Seconds 2
}
New-Item -ItemType File -Path $lockFile | Out-Null
# Do other stuff...
Remove-Item -Path $lockFile -Force

Automatically creating a folder on share and assigning correct permissions

I have this software that requires every user to have a personal folder only they can look into on a share, just like a home folder. Now I'm working on this script that is being executed every time a user logs in, I want the script to check if there already is a folder with their SAMaccountname on the share, and if there's not, it has to create one with correct permissions.
I'm not good with PowerShell at all and can't come any further at all, I have the part where it checks it and assigns correct permissions for the user. But also the domain admins need full control, and the service account needs R&W permissions. Is there anyone who can help me further with finishing the script? Any effort and help is gladly appreciated.
Current script: the path is weird due to the fact I was testing it on VMWare.
$Path = "\\Win-hnf1r0tnhgh\F\Scans\$($ENV:USERNAME)"
if (!(Test-Path -Path $Path)) {
New-Item -Path $Path -ItemType Directory
}
$Acl = Get-Acl -Path $Path -ErrorAction 'Stop'
$Acl.SetAccessRuleProtection($True, $True) > $null
Set-Acl -Path $Path -AclObject $Acl -ErrorAction 'Stop'
$Acl = Get-Acl -Path $Path
$Acl.Access.Where{$_.IdentityReference -eq "BUILTIN\Users"}.ForEach{$Acl.RemoveAccessRule($_) > $null}
$PermissionChange = [System.Security.AccessControl.FileSystemAccessRule]::new($ENV:USERNAME, 'Modify', "ContainerInherit,ObjectInherit", "None", "Allow")
$Acl.AddAccessRule($PermissionChange) > $null
Set-Acl -Path $Path -AclObject $Acl -ErrorAction 'Stop'
I find ACL modification using native powershell or even .NET to be overly complicated.
You can use this to set full control with a single line:
ICACLS "$($Path)" /grant "DOMAINSHORTNAME\$($ENV:USERNAME):(OI)(CI)F" /T
You can adjust the ICACLS cmd anyway you need, tons of examples online
Simply run the command a few times, one time for each account that needs rights.

Copy files, and skip the ones in use

I am attempting to make a PowerShell script to run every night, to copy over PDF files from C:\share1 to C:\share2 and write to the event log.
My current file copy script bit looks like this:
Try
{
get-childitem -Path C:\Share1 | ForEach-Object { Copy-Item $_.FullName C:\share2 -verbose }
#Writes to event log as success.
}
Catch
#Logs the event as failed
}
The issue I run into here is that the files beeing copied/replaced are in use.
When a file is in use the script stops copying on that said file with a error:
PS>TerminatingError(Copy-Item): "The process cannot access the file 'C:/share2/1.pdf' because it is being used by another process."
I would like to at least modify my script so it continues to copy the remaining files.
If i for example had 100 files and the 3rd one was in use, the entire transfer stops.
How can I modify my script to continue on remaining items?
You are looking for the common -ErrorAction paramter with set to SilentlyContinue:
get-childitem -Path C:\Share1 | ForEach-Object { Copy-Item $_.FullName C:\share2 -verbose -ErrorAction SilentlyContinue }
Note: You don't need the Foreach-Object cmdlet here:
Get-ChildItem -Path C:\Share1 | Copy-Item C:\share2 -verbose -ErrorAction SilentlyContinue
Note 2: As gvee mentioned, this will ignore all errors. Another option would be to use handle.exe from the sysinternals suite to check whether there is an open handle.

Powershell's Start-Process command doesn't start exe anywere outside Powershell ise

I'm writing simple script to unarchive (rar) a project from Teamcenter to temp directory, then run specific program (Mentor), then archive again.
I've read a lot of examples about starting exe from PS, but they mostly relate to small exes like notepad, without dlls and other resources.
In Powershell Ise the script works perfectly. But when I call the script from teamcenter, Mentor is missing dlls.
Before I run Mentor, in the script, I do:
Get-ChildItem Env:
to check environment variables and all variables exist. I tried to set environments manually, like this:
$wf_classpath = Get-ChildItem Env:WF_CLASSPATH
[System.Environment]::SetEnvironmentVariable("WF_CLASSPATH", $wf_classpath.Value, "Process")
Does not work.
I tried to set homefolder:
$mentor = Start-Process $file.FullName -Wait -WorkingDirectory $workdir
Does not work.
Then I tried to call a batch file from the script with environments, does not work.
Try call cmd.exe /c ... does not work.
Full script here, works perfect only in Powershell Ise, if I call the script from other programs, exe does not start.
$shell = new-object -com shell.application
$invocation = $MyInvocation.MyCommand.Definition
$rootpath = $PSScriptRoot
$outpath = "$($PSScriptRoot)\out"
$pathtorar = "c:\Siemens\menutils\Rar.exe"
Remove-Item -Recurse -Force $outpath
New-Item $outpath -ItemType directory
$archive = get-childitem $rootpath | where { $_.extension -eq ".rar" } | Select-Object -First 1
$arglist = "x $($archive.FullName) $($outpath)"
Start-Process -FilePath $pathtorar -ArgumentList $arglist -Wait
Remove-Item -Recurse -Force $archive.FullName
$file = get-childitem $outpath -Recurse | where { $_.extension -eq ".prj" } | Select-Object -First 1
Write-Host "$(get-date -Format yyyy-MM-dd-hh-ss)
Start process: $($file.FullName)"
$mentor = Start-Process $file.FullName -Wait
$arglist = "a -m0 -r -ep1 $($archive.FullName) $($outpath)"
Start-Process -FilePath $pathtorar -ArgumentList $arglist -Wait
Remove-Item -Recurse -Force $outpath
Read-Host -Prompt "Press Enter to exit"
What's the difference between running the script from Powershell Ise and other programs?
How should I set environment variables to run the script from other scripts/programs?
Its probably that your Current directory is not correct and WorkingDirectory in my experience is buggy. The dll's will be obtained from the current directory if they are not at the regular system paths.
Use this function before Start-Process
[IO.Directory]::SetCurrentDirectory($Dir)

Resources