I have Windows Server 2016 Datacenter (64 bit) as a File Server (contains several Shared folder & subfolders).
I want to make a list OR export user Folder Structure along with permissions ( Read, Modify, Full .. etc..)
I tried with below PS script but I am getting an error message with I have mentioned after the script.
Powershell
$FolderPath = dir -Directory -Path "E:\Project Folders\#Folder_Name" -Recurse -Force
$Report = #()
Foreach ($Folder in $FolderPath) {
$Acl = Get-Acl -Path $Folder.FullName
foreach ($Access in $acl.Access)
{
$Properties = [ordered]#{'FolderName'=$Folder.FullName;'AD Group or User'=$Access.IdentityReference;'Permissions'=$Access.FileSystemRights;'Inherited'=$Access.IsInherited}
$Report += New-Object -TypeName PSObject -Property $Properties
}
}
$Report | Export-Csv -path "C:\Folder Permissions\Folder Name.csv"
Error:
dir : Access to the path 'E:\Project Folders**Folder Path**\New folder' is denied. At C:\Users\Administrator\Documents\PS Script**File Name**.ps1:1 char:15 + ... olderPath = dir -Directory -Path "E:\Project Folders**Folder Name**" -Re ...+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : PermissionDenied: (E:\Project Fold...ngar\New folder:String) [Get-Child Item], UnauthorizedAccessException + FullyQualifiedErrorId : DirUnauthorizedAccessError,Microsoft.PowerShell.Commands.GetChildItemCommand
Please help me out!
Thanks in Advance
As noted by the other comments.
This is not a PowerShell error/issue, it is a permissions one. The same thing can/will happen if you say you did this use case on the Windows folder tree.
Since you know this will happen, either fix the permissions on the tree you are working on or do this.
Get-ChildItem -Directory -Path 'C:\Windows\System32' -Recurse -ErrorAction SilentlyContinue
or if you want to just stop when a path fails.
# Treat non-terminating erros as terminating
$RootFolderUnc = 'C:\Windows\System32'
Try {Get-ChildItem -Directory -Path $RootFolderUnc -Recurse -ErrorAction Stop}
Catch [System.UnauthorizedAccessException]
{
Write-Warning -Message "$env:USERNAME. You do not have permissions to view this path."
$_.Exception.Message
}
Related
I am trying to get a script working to audit folder permissions on a Windows server, among other data, and export this data to a CSV file for analysis after a ransomware attack.
I ripped the script from a forum, but it did not run correctly as is. Below is a slightly modified version during my troubleshooting.
I am well versed in batch scripting, and have a decent understanding of loops and pipelining, but this Powershell script has me scratching my head.
It seems like the array is not making it to the nested loop.
I am testing in Windows 10 Pro 21H1, using Powershell version 5.1.19041.1320, build 10.0.19041.1320
##The script:
$ErrorActionPreference = "Continue"
$strComputer = $env:ComputerName
$colDrives = Get-PSDrive -PSProvider Filesystem
ForEach ($DriveLetter in $colDrives) {
$StartPath = "$DriveLetter`:\"
Get-ChildItem -LiteralPath $StartPath -Recurse | ?{ $_.PSIsContainer } |
ForEach ($FullPath = Get-Item -LiteralPath{Get-Item -LiteralPath $_.PSPath}{Get-Item -
LiteralPath $FullPath}.Directoryinfo.GetAccessControl())}
Select #{N='Server Name';E={$strComputer}}
#{N='Full Path';E={$FullPath}}
#{N='Type';E={If($FullPath.PSIsContainer -eq $True) {'D'} Else {'F'}}}
#{N='Owner';E={$_.Owner}}
#{N='Trustee';E={$_.IdentityReference}}
#{N='Inherited';E={$_.IsInherited}}
#{N='Inheritance Flags';E={$_.InheritanceFlags}}
#{N='Ace Flags';E={$_.PropagationFlags}}
#{N='Ace Type';E={$_.AccessControlType}}
#{N='Access Masks';E={$_.FileSystemRights}}
Export-CSV -NoTypeInformation -Delimiter "|" -Path "$strComputer`_$DriveLetter.csv"
##The error I am getting:
You cannot call a method on a null-valued expression.
At C:\Users\user\Documents\fileaudit2.ps1:8 char:13
ForEach ($FullPath = Get-Item -LiteralPath{Get-Item -LiteralPath $ ...
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
CategoryInfo : InvalidOperation: (:) [], RuntimeException
FullyQualifiedErrorId : InvokeMethodOnNull
##when I modify the nested loop as follows:
ForEach ($FullPath = Get-Item -LiteralPath{Get-Item -LiteralPath $_.PSPath}{Get-Item -LiteralPath $FullPath}).Directoryinfo.GetAccessControl()}
##I get the error:
Get-Item : Cannot evaluate parameter 'LiteralPath' because its argument is specified as a script block and there is no input. A script block cannot be evaluated without
input.
At C:\Users\user\Documents\fileaudit2.ps1:8 char:46
... Path = Get-Item -LiteralPath{Get-Item -LiteralPath $_.PSPath}{Get-Ite ...
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
CategoryInfo : MetadataError: (:) [Get-Item], ParameterBindingException
FullyQualifiedErrorId : ScriptBlockArgumentNoInput,Microsoft.PowerShell.Commands.GetItemCommand
##I'm just wholly struggling to understand what is not working in this loop.
You are mixing a lot of unneeded Get-Item calls in there.
I also would not use Get-PSDrive for this because I assume you don't want to get results for CD drives, USB devices etc in the report.
Try:
# this returns drives WITH a trailing backslash like C:\
$colDrives = ([System.IO.DriveInfo]::GetDrives() | Where-Object { $_.DriveType -eq 'Fixed' }).Name
# or use:
# this returns drives WITHOUT trailing backslash like C:
# $colDrives = (Get-CimInstance -ClassName win32_logicaldisk | Where-Object { $_.DriveType -eq 3 }).DeviceID
$result = foreach ($drive in $colDrives) {
Get-ChildItem -LiteralPath $drive -Directory -Recurse -ErrorAction SilentlyContinue |
ForEach-Object {
$path = $_.FullName
$acl = Get-Acl -Path $path
foreach ($access in $acl.Access) {
[PsCustomObject]#{
Server = $env:COMPUTERNAME
Drive = $drive[0] # just the first character of the drive
Directory = $path
Owner = $acl.Owner
Trustee = $access.IdentityReference
Inherited = $access.IsInherited
InheritanceFlags = $access.InheritanceFlags -join ', '
'Ace Flags' = $access.PropagationFlags -join ', '
'Ace Type' = $access.AccessControlType
'Access Masks' = $access.FileSystemRights -join ', '
}
}
}
}
# now you can save your result as CSV file for instance you can double-click to open in Excel:
$result | Export-Csv -Path 'X:\WhereEver\audit.csv' -NoTypeInformation -UseCulture
To do this on several remote machines, wrap it inside Invoke-Command
# set the credentials for admin access on the servers
$cred = Get-Credential 'Please enter your admin credentials'
# create an array of the servers you need to probe
$servers = 'Server01', 'Server02'
$result = Invoke-Command -ComputerName $servers -Credential $cred -ScriptBlock {
$colDrives = ([System.IO.DriveInfo]::GetDrives() | Where-Object { $_.DriveType -eq 'Fixed' }).Name
foreach ($drive in $colDrives) {
# code inside this loop unchanged as above
}
}
# remove the extra properties PowerShell added
$result = $result | Select-Object * -ExcludeProperty PS*, RunspaceId
# output to csv file
$result | Export-Csv -Path 'X:\WhereEver\audit.csv' -NoTypeInformation -UseCulture
like in What is the difference between opening application by cmd/ps or by click? already discussed, I have the problem, that firefox does not use my bookmarks, that I copied with powershell, as long as I open/close firefox within a script.
The only chance I have is: open/close firefox manually and then overwrite bookmarks/passwords with powershell.
Also I created the profile with arguments, but as soon as firefox will be opened, firefox is creating a new profile on its own. Obviously it makes a difference, whether I open firefox by click or ps/cmd.
Does anybody has an idea, how to tell firefox which profile it shall use when the user opens firefox?
Here my code:
# Full path of the file
$appdata = $env:APPDATA
$folder = "$appdata\Custom"
$checkfile = "$folder\firstRunRestore"
#If the file does not exist, create it and execute statement.
if (-not(Test-Path -Path $checkfile -PathType Leaf)) {
try {
$null = New-Item -ItemType File -Path $checkfile -Force -ErrorAction Stop
#Create Firefox profile
$p = New-Object System.Diagnostics.Process
$p.StartInfo.UseShellExecute = $true;
$p.StartInfo.WorkingDirectory = "C:\Program Files\Mozilla Firefox";
$p.StartInfo.Filename = "firefox.exe";
$p.StartInfo.Arguments = "-CreateProfile $env:USERNAME";
$null = $p.Start()
Start-Sleep -s 10
Stop-Process -Name "Firefox"
# Wait to close all processes
Start-Sleep -s 7
# Restore files to profile
$firefox = "$env:APPDATA\Mozilla\Firefox\Profiles"
$curProfile = Get-ChildItem -Path $firefox | Sort-Object LastAccessTime -Descending | Select-Object -First 1
$curPath = "$firefox\$curProfile"
$source = "X:\Desktop\FF-Profile"
$keydb = "key4.db"
$logins = "logins.json"
$places = "places.sqlite"
$favicons = "favicons.sqlite"
Copy-Item -Path "$source\$keydb" -Destination "$curPath" -Force
Copy-Item -Path "$source\$logins" -Destination "$curPath" -Force
Copy-Item -Path "$source\$places" -Destination "$curPath" -Force
Copy-Item -Path "$source\$favicons" -Destination "$curPath" -Force
Remove-Item $source -Force -Recurse
}
catch {
throw $_.Exception.Message
}
}
I am currently writing a script that takes a folder of files, moves the first file to a folder with a specific name, then move the rest to another folder with a number for a name.
My script works however it also moves the folder and renames it too. Which section of the code is causing this?
$path = "C:\Users\User1\Desktop\MergeTest\_First\"
$FileCount = Get-ChildItem -Path $path -File | Measure-Object | %{$_.Count}
$FirstFile = Get-ChildItem -Path $path -Force -File | Select-Object -First 1
$FinalReport = "C:\Users\User1\Desktop\MergeTest\___Final\TestOutput.xlsx"
Move-Item "C:\Users\User1\Desktop\MergeTest\_First\$FirstFile" $FinalReport
$Counter = 0;
Write-host $FileCount
for($Counter = 0; $Counter -lt $FileCount; $Counter++)
{
$FileInWork = Get-ChildItem -Path $path -Force -File | Select-Object -First 1
move-item "C:\Users\User1\Desktop\MergeTest\_First\$FileInWork" "C:\Users\User1\Desktop\MergeTest\__Second\$Counter.xlsx"
Write-host "File Moved"
}
What you could do is specify the -Include *.txt condition to your move-item commands so it is only to move just .txt, .log, or whatever file type you're moving and leave the folder how it is.
I believe your code could do with some cleaning up. Now you are executing Get-ChildItem 3 times, where using it once is enough.
Also, you should try and use the Join-Path rather than constructing the path and filenames yourself.
Especially where you do "C:\Users\User1\Desktop\MergeTest\_First\$FileInWork", you should realize that Get-ChildItem returns FileInfo and/or DirectoryInfo objects; not strings.
Anyway, the below code should do what you want:
# define the path where all other paths are in
$rootPath = "C:\Users\User1\Desktop\MergeTest"
# create the working paths using the common root folder path
$filesPath = Join-Path -Path $rootPath -ChildPath '_First'
$firstDestination = Join-Path -Path $rootPath -ChildPath '___Final'
$secondDestination = Join-Path -Path $rootPath -ChildPath '__Second'
# test if the destination folders exist and if not create them
if (!(Test-Path -Path $firstDestination -PathType Container)) {
Write-Host "Creating folder '$firstDestination'"
$null = New-Item -Path $firstDestination -ItemType Directory
}
if (!(Test-Path -Path $secondDestination -PathType Container)) {
Write-Host "Creating folder '$secondDestination'"
$null = New-Item -Path $secondDestination -ItemType Directory
}
# get an array of all FileInfo objects in $filesPath
# you could consider adding -Filter '*.xlsx' here..
$allFiles = Get-ChildItem -Path $filesPath -Force -File
Write-Host 'Total number of files found: {0}' -f $allFiles.Count
# move the files
for ($i = 0; $i -lt $allFiles.Count; $i++) {
if ($i -eq 0) {
# the first file should go in the $firstDestination folder with specified name
$target = Join-Path -Path $firstDestination -ChildPath 'TestOutput.xlsx'
}
else {
# all other files go to the $secondDestination folder
# each file should have the index number as name
$target = Join-Path -Path $secondDestination -ChildPath ('{0}.xlsx' -f ($i + 1))
}
$allFiles[$i] | Move-Item -Destination $target -Force -WhatIf
}
Hope that helps
Remove the -WhatIf if you are satisfied with whatever the output on console shows.
P.S. I really think you should edit your question and change its title, because nothing in the question has to do with Folder deleting after script ends..
i have project assigned to me, first i need to find file in which directory it is in server, code i have written
$drive = get-psdrive |select root |select-string -pattern ':'
Write-Host $drive
foreach ($a in $drive)
{
Get-ChildItem $a -recurse -filter "*DBaEnvProd*" |select directory
}
there shd be one output a there will be only one dbenvprod on server
how to get one value
output iam getting
Get-ChildItem : Cannot find drive. A drive with name '#{Root=C' does not exist.
At D:\temp.ps1:6 char:26
+ Get-ChildItem <<<< $a -recurse -filter "*DBaEnvProd*" |select directory
Get-ChildItem : Cannot find drive. A drive with name '#{Root=D' does not exist.
At D:\temp.ps1:6 char:26
+ Get-ChildItem <<<< $a -recurse -filter "*DBaEnvProd*" |select directory
Get-ChildItem : Cannot find drive. A drive with name '#{Root=E' does not exist.
At D:\temp.ps1:6 char:26
+ Get-ChildItem <<<< $a -recurse -filter "*DBaEnvProd*" |select directory
Get-ChildItem : Cannot find drive. A drive with name '#{Root=F' does not exist.
At D:\temp.ps1:6 char:26
+ Get-ChildItem <<<< $a -recurse -filter "*DBaEnvProd*" |select directory
Get-ChildItem : Cannot find drive. A drive with name '#{Root=Z' does not exist.
At D:\temp.ps1:6 char:26
+ Get-ChildItem <<<< $a -recurse -filter "*DBaEnvProd*" |select directory
Add -InputObject parameter:
Select-String -InputObject {$_.Root} -Pattern ':'
Get-PSDrive | ForEach-Object {Get-ChildItem -LiteralPath $_.Root -Filter "*DBaEnvProd*" -Recurse}
Iam using Powershell script to Move folders from one Drive to another.
Here is what i have tried.
Get-ChildItem -Recurse "C:\Personal" | where-object {$_.lastwritetime -gt '5-25-2015'} | foreach {move-item "$($_.FullName)" "D:\Personal"}
This is working if i am moving files within same drive i.e either from cdrive to c drive or d drive to d drive.
But this is not working when iam trying to move from c drive to d drive,...iam getting error like
Move-Item : The file exists.
At line:1 char:113
+ Get-ChildItem -Recurse "C:\Development" | where-object {($_.lastwritetime -lt (get-date))} | foreach {move-item <<<<
"$($_.FullName)" "D:\Development1"}
+ CategoryInfo : WriteError: (C:\Development\test3.txt:FileInfo) [Move-Item], IOException
+ FullyQualifiedErrorId : MoveFileInfoItemIOError,Microsoft.PowerShell.Commands.MoveItemCommand
Please correct me..
What means "this is not working"?
The recurse flag seems to indicate you want to copy a directory structure. This will only work if your target directory already has the same structure as your source. If not, you have to create it along the way. Something like this would work:
function Move-Directories
{
param (
[parameter(Mandatory = $true)] [string] $source,
[parameter(Mandatory = $true)] [string] $destination
)
try
{
Get-ChildItem -Path $source -Recurse -Force |
Where-Object { $_.psIsContainer } |
ForEach-Object { $_.FullName -replace [regex]::Escape($source), $destination } |
ForEach-Object { $null = New-Item -ItemType Container -Path $_ }
Get-ChildItem -Path $source -Recurse -Force |
Where-Object { (-not $_.psIsContainer) -and ($_.lastwritetime -ge (get-date)) } |
Move-Item -Force -Destination { $_.FullName -replace [regex]::Escape($source), $destination }
}
catch
{
Write-Host "$($MyInvocation.InvocationName): $_"
}
}
With a call like this:
Move-Directories "c:\personal" "d:\personal"