I am having share with write access. I am writing a powershell script to write a log file in that share.
I would like to check the condition whether i am having write access to this share before writing in it.
How to check for write access/Full control using powershell?
I have tried with Get-ACL cmdlet.
$Sharing= GEt-ACL "\\Myshare\foldername
If ($Sharing.IsReadOnly) { "REadonly access" , you can't write" }
It has Isreadonly property, But is there any way to ensure that the user has Fullcontrol access?
This does the same thing as #Christian's C# just without compiling C#.
function Test-Write {
[CmdletBinding()]
param (
[parameter()] [ValidateScript({[IO.Directory]::Exists($_.FullName)})]
[IO.DirectoryInfo] $Path
)
try {
$testPath = Join-Path $Path ([IO.Path]::GetRandomFileName())
[IO.File]::Create($testPath, 1, 'DeleteOnClose') > $null
# Or...
<# New-Item -Path $testPath -ItemType File -ErrorAction Stop > $null #>
return $true
} catch {
return $false
} finally {
Remove-Item $testPath -ErrorAction SilentlyContinue
}
}
Test-Write '\\server\share'
I'd like to look into implementing GetEffectiveRightsFromAcl in PowerShell because that will better answer the question....
I use this way to check if current user has write access to a path:
# add this type in powershell
add-type #"
using System;
using System.IO;
public class CheckFolderAccess {
public static string HasAccessToWrite(string path)
{
try
{
using (FileStream fs = File.Create(Path.Combine(path, "Testing.txt"), 1, FileOptions.DeleteOnClose))
{ }
return "Allowed";
}
catch (Exception e)
{
return e.Message;
}
}
}
"#
# use it in this way:
if ([checkfolderaccess]::HasAccessToWrite( "\\server\share" ) -eq "Allowed") { ..do this stuff } else { ..do this other stuff.. }
Code doesn't check ACL but just if is possible to write a file in the path, if it is possible returns string 'allowed' else return the exception's message error.
Here's a pretty simple function I built. It returns "Read", "Write", "ReadWrite", and "" (for no access):
function Test-Access()
{
param([String]$Path)
$guid = [System.Guid]::NewGuid()
$d = dir $Path -ea SilentlyContinue -ev result
if ($result.Count -eq 0){
$access += "Read"
}
Set-Content $Path\$guid -Value $null -ea SilentlyContinue -ev result
if ($result.Count -eq 0){
$access += "Write";
Remove-Item -Force $Path\$guid
}
$access
}
Related
I am trying to encrypt multiple files in the folder, however it will not working if more than one files the filepath folder. If it only one file exist in the folder, it will encrypt without any issue. The following if the code that I have so far. Any advise what did I missed. Thanks
#region Encrypt
function Encrypt()
{
$pgp_program = "${env:ProgramFiles(x86)}" + "\GnuPG\bin\gpg.exe"
$recipient = "PROD_20"
$filepath = "C:\filesToEncypt\"
$files = Get-ChildItem -Path $filepath -recurse
try
{
foreach ($item in $files)
{
try
{
write-host $item
Start-Process $pgp_program -ArgumentList "--always-trust -r $recipient -e $item"
}
finally
{
}
}
}
catch [Exception]
{
Write-Host $_.Exception.Message
exit 1
}
}
Encrypt
I have the following PowerShell script:
param([switch]$Elevated)
function Test-Admin
{
$currentUser = New-Object Security.Principal.WindowsPrincipal $([Security.Principal.WindowsIdentity]::GetCurrent())
$currentUser.IsInRole([Security.Principal.WindowsBuiltinRole]::Administrator)
}
if ((Test-Admin) -eq $false) {
if ($elevated) {
# tried to elevate, did not work, aborting
} else {
Start-Process powershell.exe -Verb RunAs -ArgumentList ('-noprofile -noexit -file "{0}" -elevated ' -f ($myinvocation.MyCommand.Definition))
}
exit
}
function UpdateHosts {
param ($hostName)
Write-Host $hostName
try {
$strHosts = (Get-Content C:\WINDOWS\system32\drivers\etc\hosts -Raw)
if([string]::IsNullOrEmpty($strHosts)) {
Write-Error "Get-Content hosts empty"
exit
}
} catch {
Write-Error "Unable to read hosts file"
Write-Error $_
exit
}
try {
$strHosts -replace "[\d]+\.[\d]+\.[\d]+\.[\d]+ $hostName","$ipAddress $hostName" | Set-Content -Path C:\WINDOWS\system32\drivers\etc\hosts
} catch {
Write-Error "Unable to write hosts file"
Write-Error $_
exit
}
}
$ipAddress = "127.0.0.1"
UpdateHosts -hostName local.pap360.com
Sometimes, when I run it, I get the following error:
Set-Content : The process cannot access the file 'C:\WINDOWS\system32\drivers\etc\hosts' because it is being used by another process.
When I open up C:\WINDOWS\system32\drivers\etc\hosts in Notepad it's then blank. ie. all the data I had in it is wiped.
My question is... how can I prevent this from happening?
Like if Set-Content can't access the hosts file to write to it then how is it able to wipe it's contents? And why isn't the catch block working?
Here's the full error:
Set-Content : The process cannot access the file 'C:\WINDOWS\system32\drivers\etc\hosts' because it is being used by
another process.
At C:\path\to\test.ps1:36 char:92
+ ... $hostName" | Set-Content -Path C:\WINDOWS\system32\drivers\etc\hosts
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : WriteError: (C:\WINDOWS\system32\drivers\etc\hosts:String) [Set-Content], IOException
+ FullyQualifiedErrorId : GetContentWriterIOError,Microsoft.PowerShell.Commands.SetContentCommand
I also don't understand why it's so intermittent. Is there some Windows process that opens the hosts file up for 1s once a minute or some such?
First of all, check if your Firewall or AV software isn't restricting access to the file.
If that is not the case and 'some' other process is currently locking the hosts file, perhaps add a test before reading or writing the file can help:
function Test-LockedFile {
param (
[parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
[Alias('FullName', 'FilePath')]
[ValidateScript({Test-Path $_ -PathType Leaf})]
[string]$Path
)
$file = [System.IO.FileInfo]::new($Path)
# old PowerShell versions use:
# $file = New-Object System.IO.FileInfo $Path
try {
$stream = $file.Open([System.IO.FileMode]::Open,
[System.IO.FileAccess]::ReadWrite,
[System.IO.FileShare]::None)
if ($stream) { $stream.Close() }
return $false # file is not locked
}
catch {
return $true # file is locked
}
}
Then use like this:
function UpdateHosts {
param ($hostName)
Write-Host $hostName
$path = 'C:\WINDOWS\system32\drivers\etc\hosts'
# test if the file is readable/writable
# you can of course also put this in a loop to keep trying for X times
# until Test-LockedFile -Path $path returns $false.
if (Test-LockedFile -Path $path) {
Write-Error "The hosts file is currently locked"
}
else {
try {
$strHosts = (Get-Content $path -Raw -ErrorAction Stop)
if([string]::IsNullOrEmpty($strHosts)) {
Write-Error "Get-Content hosts empty"
exit
}
}
catch {
Write-Error "Unable to read hosts file:`r`n$($_.Exception.Message)"
exit
}
try {
$strHosts -replace "[\d]+\.[\d]+\.[\d]+\.[\d]+\s+$hostName", "$ipAddress $hostName" |
Set-Content -Path $path -Force -ErrorAction Stop
}
catch {
Write-Error "Unable to write hosts file:`r`n$($_.Exception.Message)"
exit
}
}
}
I've written a function to check if an excel file is being used/locked by another process/user in a shared network drive, and if used, to pause the script and keep checking till it's available as the next action is to move it out of it's folder. However when I'm using System.IO to read the file, it does not open the file. I've tested on my local drive and this does open the file, but does this not work in Network Drives?
$IsLocked = $True
Function Test-IsFileLocked {
[cmdletbinding()]
Param (
[parameter(Mandatory=$True,ValueFromPipeline=$True,ValueFromPipelineByPropertyName=$True)]
[Alias('FullName','PSPath')]
[string[]]$Path
)
Process {
while($isLocked -eq $True){
If ([System.IO.File]::Exists($Path)) {
Try {
$FileStream = [System.IO.File]::Open($Path,'Open','Write')
$FileStream.Close()
$FileStream.Dispose()
$IsLocked = $False
} Catch {
$IsLocked = $True
echo "file in use, trying again in 10 secs.."
Start-Sleep -s 10
}
}
}
}
}
This is where the code does not pick up/open the excel file in my function
$FileStream = [System.IO.File]::Open($Path,'Open','Write')
This is where the program calls the function.Loop through a folder of items in the network drive and if the item matches the pattern, then the function will be called to check if the file is in use:
$DirectoryWithExcelFile = Get-ChildItem -Path "Z:\NetworkDriveFolder\"
$DestinationFolder = "Z:\DestinationFolder\"
$pattern = "abc"
foreach($file in $DirectoryWithExcelFile){
if($file.Name -match $pattern){
Test-IsFileLocked -Path $file
$destFolder = $DestinationFolder+$file.Name
Move-item $file.FullName -destination $destFolder
break
}
}
You have to place the close and dispose in the finally part of the try so if it throws an exception it disposes of the lock. There's no guarantee it's a file lock exception either so you are better to throw the exception, catch the exception you're expecting or write it out
$IsLocked = $True
Function Test-IsFileLocked {
[cmdletbinding()]
Param (
[parameter(Mandatory=$True,ValueFromPipeline=$True,ValueFromPipelineByPropertyName=$True)]
[Alias('FullName','PSPath')]
[string]$Path
)
Process {
while($isLocked -eq $True){
If ([System.IO.File]::Exists($Path)) {
Try {
$FileStream = [System.IO.File]::Open($Path,'Open','Write')
$IsLocked = $False
} Catch [System.IO.IOException] {
$IsLocked = $True
echo "file in use, trying again in 10 secs.."
Start-Sleep -s 10
} Catch {
# source https://stackoverflow.com/questions/38419325/catching-full-exception-message
$formatstring = "{0} : {1}`n{2}`n" +
" + CategoryInfo : {3}`n" +
" + FullyQualifiedErrorId : {4}`n"
$fields = $_.InvocationInfo.MyCommand.Name,
$_.ErrorDetails.Message,
$_.InvocationInfo.PositionMessage,
$_.CategoryInfo.ToString(),
$_.FullyQualifiedErrorId
$formatstring -f $fields
write-output $formatstring
} finally {
if($FileStream) {
$FileStream.Close()
$FileStream.Dispose()
}
}
}
}
}
}
Edit:
Path should be a string, not a string array unless you have multiple paths in which case rename to $Paths and loop through them $Paths| % { $Path = $_; #do stuff here }
$Launcher_PackageFile = "Launcher.exe"
$Launcher_PackageDestinationDirectory = "$Env:ProgramData\Launcher";
#$Launcher_StartupDirectory = (New-Object -ComObject Shell.Application).NameSpace(0x07)
$Launcher_Source = "$Env:PackageRoot\Launcher\$Launcher_PackageFile"
Script UpdateConfigurationVersion
{
GetScript = {
# For cmdlet. Must always return a hash table.
return #{
"Name"="LauncherPackage"
"Version"="Latest"
};
}
TestScript = {
# Check if the file is in the folder
$TestPath = Join-Path $Launcher_PackageDestinationDirectory $Launcher_PackageFile
if (Test-Path $TestPath) {
return $true;
}
return $false;
}
SetScript = {
# Logic
#move the exe
if (!Test-Path $Launcher_PackageDestinationDirectory) {
New-Item -Type Directory -Path $Launcher_PackageDestinationDirectory
}
Copy-Item -Path $Launcher_Source -Destination $Launcher_PackageDestinationDirectory
#TODO Create a shortcut to the startup directory
#or create a task in the scheduler
}
}
Incorrect syntax
(!Test-Path
if (!(Test-Path $Launcher_PackageDestinationDirectory)) {....
or
if (-not(Test-Path $Launcher_PackageDestinationDirectory) {....
Also add -ErrorAction SilentlyContinue to your Test-Path command to suppress error.
With that info is difficult to tell.
Add messages to TestScript and SetScript to see what's going on. For example:
Write-Verbose -Message 'Testing if already exists'
TestScript gets executed before SetScript, check whether is returning true or false (by adding messages). TestScript may be returning true and thus the SetScript is not running. Output $TestPath to see if the path is correct.
Then check the logs on C:\Windows\System32\Configuration\ConfigurationStatus
I'm trying to write a script that needs to be detect what the ArgumentList of a PowerShell module is. Is there any way of finding this out?
The end game is to be able to use this to create a simple DI container for loading modules.
You can use the AST parser to show you the param() block of the module file. Maybe use Get-Module to find out information about where the module files are located, then parse those and walk the AST to get the information you're after. Does this seem like something that would be useful?
function Get-ModuleParameterList {
[CmdletBinding()]
param(
[string] $ModuleName
)
$GetModParams = #{
Name = $ModuleName
}
# Files need -ListAvailable
if (Test-Path $ModuleName -ErrorAction SilentlyContinue) {
$GetModParams.ListAvailable = $true
}
$ModuleInfo = Get-Module #GetModParams | select -First 1 # You'll have to work out what to do if more than one module is found
if ($null -eq $ModuleInfo) {
Write-Error "Unable to find information for '${ModuleName}' module"
return
}
$ParseErrors = $null
$Ast = if ($ModuleInfo.RootModule) {
$RootModule = '{0}\{1}' -f $ModuleInfo.ModuleBase, (Split-Path $ModuleInfo.RootModule -Leaf)
if (-not (Test-Path $RootModule)) {
Write-Error "Unable to determine RootModule for '${ModuleName}' module"
return
}
[System.Management.Automation.Language.Parser]::ParseFile($RootModule, [ref] $null, [ref] $ParseErrors)
}
elseif ($ModuleInfo.Definition) {
[System.Management.Automation.Language.Parser]::ParseInput($ModuleInfo.Definition, [ref] $null, [ref] $ParseErrors)
}
else {
Write-Error "Unable to figure out module source for '${ModuleName}' module"
return
}
if ($ParseErrors.Count -ne 0) {
Write-Error "Parsing errors detected when reading RootModule: ${RootModule}"
return
}
$ParamBlockAst = $Ast.Find({ $args[0] -is [System.Management.Automation.Language.ParamBlockAst] }, $false)
$ParamDictionary = [ordered] #{}
if ($ParamBlockAst) {
foreach ($CurrentParam in $ParamBlockAst.Parameters) {
$CurrentParamName = $CurrentParam.Name.VariablePath.UserPath
$ParamDictionary[$CurrentParamName] = New-Object System.Management.Automation.ParameterMetadata (
$CurrentParamName,
$CurrentParam.StaticType
)
# At this point, you can add attributes to the ParameterMetaData instance based on the Attribute
}
}
$ParamDictionary
}
You should be able to give that a module name or the path to a module. It's barely been tested, so there are probably some instances where it won't work. Right now, it returns a dictionary like viewing the 'Parameters' property returned from Get-Command. If you want the attribute information, you'd need to do a little work to build each one.