Creating a folder and setting permissions - Powershell - powershell

I am trying to create a folder and set permissions on those folders within one PowerShell script. The script won't change the permissions when run the first time. I have to run the script twice to set the permissions. Not sure what could be causing this odd behavior.
$desired_install_loc = ${env:ProgramFiles}
$base_path = Join-Path $desired_install_loc 'Base_Test';
$install_path = Join-Path $base_path 'Install_Test';
function Create-Directory {
if( !(Test-Path $base_path )){
New-Item -ItemType Directory -Force -Path $base_path;
}
if( !(Test-path $install_path) ){
New-Item -ItemType Directory -Force -Path $install_path;
}
}
function Replace-FolderPerms($folder_path) {
$acl = (Get-Acl -Path $folder_path);
$add_rule = (New-Object System.Security.AccessControl.FileSystemAccessRule("BUILTIN\Users","Read", "Allow"));
$acl.SetAccessRuleProtection($true,$true)
$acl.SetAccessRule($add_rule)
Set-ACL $folder_path $acl;
}
Create-Directory;
Replace-FolderPerms $base_path;
Replace-FolderPerms $install_path;
Creates the folders, but does not set the permissions afterwards.

I was attempting to keep old permissions by setting SetAccessRuleProtection($true, $true). Setting the second argument to $false and fully building out the permissions did the trick.
$desired_install_loc = ${env:ProgramFiles}
$base_path = Join-Path $desired_install_loc 'Base_Test';
$install_path = Join-Path $base_path 'Install_Test';
function Create-Directory {
if( !(Test-Path $base_path )){
New-Item -ItemType Directory -Force -Path $base_path;
}
if( !(Test-path $install_path) ){
New-Item -ItemType Directory -Force -Path $install_path;
}
}
function Replace-FolderPerms($folder_path) {
$acl = (Get-Acl -Path $folder_path);
$add_rule = (New-Object System.Security.AccessControl.FileSystemAccessRule("BUILTIN\Users","Read", "Allow"));
$add_rule_admin = (New-Object System.Security.AccessControl.FileSystemAccessRule("BUILTIN\Administrators", "FullControl", "ContainerInherit,ObjectInherit", "None", "Allow"));
$add_rule_system = (New-Object System.Security.AccessControl.FileSystemAccessRule("SYSTEM", "FullControl", "ContainerInherit,ObjectInherit", "None", "Allow"));
$acl.SetAccessRuleProtection($true,$false);
$acl.AddAccessRule($add_rule);
$acl.AddAccessRule($add_rule_admin);
$acl.AddAccessRule($add_rule_system);
$acl | Set-ACL -Path $folder_path;
}
Create-Directory;
Replace-FolderPerms $base_path;
Replace-FolderPerms $install_path;

Related

NTFS Permission for single user only by using powershell

I am trying to create the user folder for each AD Users. For each folder, I want the folder only accessed by that AD users only.
The finally result I want:
FolderName: "UserAFolder"
Goal: only UserA in "UserAFolder"
But the result is
FolderName: "UserAFolder"
UserA, UserB, UserC ... are all in "UserAFolder"
$folderpath = "\\san\Shares\UserFolders\"
$ulist =import-csv -Path C:\aduserlist.csv
foreach($list in $ulist)
{
$users = $list.username
$newpath = $folderpath+$users
New-Item -ItemType Directory -Path $folderpath -Name $users
$rights = "Modify"
$inheritanceFlag = "ContainerInherit,ObjectInherit"
$propagationFlag = "None"
$type = "Allow"
$objACL = Get-Acl $newpath
$entries = $users, $rights,$inheritanceFlag,$propagationFlag,$type
$rule = New-Object -TypeName System.Security.AccessControl.FileSystemAccessRule -ArgumentList $entries
$objACL.SetAccessRule($rule)
$objACL | Set-Acl -Path $newpath
}
The original Code of ACL is work. Just Parent Folder inheritance issue.
The NTFS Security
$folderpath = "\\san\Shares\UserFolders\"
$ulist =import-csv -Path C:\aduserlist.csv
foreach($list in $ulist){
$users = $list.username
$newpath = $folderpath+$users
New-Item -ItemType Directory -Path $folderpath -Name $users
$users = $list.username
$ADUser = $list.email
$newpath = $folderpath+$users
Add-NTFSAccess $newpath -Account $ADUser -AccessRights Modify -PassThru
}

Powershell 2.0 extract certain files from zip (include subdirectories)

Apologies, this question is scattered on the internet but I have yet to find a satisfactory answer that uses only Powershell 2.0 (with .NET v3.5) - no external libraries or programs
I'm using the following code to extract log.txt from ZipFile.zip (no matter log.txt's location)
$Destination = (new-object -com shell.application).NameSpace('C:\ZipExtractDir')
$ZipFile = (new-object -com shell.application).NameSpace('C:\ZipFile.zip')
$Destination.CopyHere(($Zipfile.Items() | where-object {$_.Name -like '*log.txt'}), 1044)
Works if log.txt is in directory root \log.txt
Fails if log.txt is in a subdirectory \Subfolder\log.txt
Fails if referencing the literal (.zip) path
{$_.Name -Like '*Subfolder\log.txt'} (both double & single quotes fail)
Have tried using -eq -like -contains '' "" $_.FullName
I'm quite certain that I'm filtering incorrectly - can anyone help with this code so that it will parse subdirectories as well?
Similar to what you have already done, you can set up the Shell.Application namespaces like this. Then you can copy the extracted directory to the destination path.
$zipFilePath = "Zipfile.zip"
$destinationPath = "C:\Users\Public\Downloads"
$zipfile = (New-Object -Com Shell.Application).NameSpace($zipFilePath)
$destination = (New-Object -Com Shell.Application).NameSpace($destinationPath)
$destination.CopyHere($zipfile.Items())
Then to list the log.txt files, we can contruct the full extracted path with Join-Path. This basically just appends the zip file name from System.IO.Path.GetFileNameWithoutExtension() to the destination path. Then just use Get-ChildItem to list the files recursively with the -Recurse and -Filter switches.
$extractedPath = Join-Path -Path $destinationPath -ChildPath ([System.IO.Path]::GetFileNameWithoutExtension($zipFilePath))
Get-ChildItem -Path $extractedPath -Filter log.txt -Recurse
And to test this for PowerShell 2.0 we can use -version 2 with powershell.exe:
powershell.exe -version 2 .\test.ps1
UPDATE
If you want to inspect files before extracting, you'll need to recurse the directories yourself. Below is a demo of how this can be done.
function New-ZipChildRootFolder
{
param
(
[string]$DestinationPath,
[string]$ZipFileName
)
$folderPath = Split-Path -Path $ZipFileName -Leaf
$destination = (New-Object -ComObject Shell.Application).NameSpace($DestinationPath)
$destination.NewFolder($folderPath)
}
function Get-ZipChildItems
{
param
(
[string]$ZipFilePath,
[string]$DestinationPath
)
$zipfile = (New-Object -ComObject Shell.Application).NameSpace($ZipFilePath)
$zipFileName = [System.IO.Path]::GetFileNameWithoutExtension($ZipFilePath)
Write-Output "Create root zip folder : $zipFileName"
New-ZipChildRootFolder -DestinationPath $DestinationPath -ZipFileName $zipFileName
foreach ($item in $zipFile.items())
{
Get-ZipChildItemsRecurse -Items $item -DestinationPath $DestinationPath -ZipFileName $zipFileName
}
}
function Get-ZipChildItemsRecurse
{
param
(
[object]$Items,
[string]$DestinationPath,
[string]$ZipFileName
)
foreach ($file in $Items.getFolder.Items())
{
if ($file.IsFolder -eq $true)
{
Write-Output "Creating folder : $($file.Path)"
New-ZipChildFolder -Folder $file -DestinationPath $DestinationPath -ZipFileName $ZipFileName
Get-ZipChildItemsRecurse -Items $file -DestinationPath $DestinationPath -ZipFileName $ZipFileName
}
else
{
$filename = Split-Path -Path $file.Path -Leaf
if ($filename -eq "log.txt")
{
Write-Output "Copying file : $($file.Path)"
New-ZipChildFile -File $file -DestinationPath $DestinationPath -ZipFileName $ZipFileName
}
}
}
}
function New-ZipChildFile
{
param
(
[object]$File,
[string]$DestinationPath,
[string]$ZipFileName
)
$destination = New-Object -ComObject Shell.Application
$items = $File.Path.Split("\")
$zipRootIndex = [array]::IndexOf($items, $ZipFileName)
$path = $items[$zipRootIndex..($items.Length - 2)] -join "\"
$fullPath = Join-path -Path $DestinationPath -ChildPath $path
$destination.NameSpace($fullPath).CopyHere($File)
}
function New-ZipChildFolder
{
param
(
[object]$Folder,
[string]$DestinationPath,
[string]$ZipFileName
)
$destination = New-Object -ComObject Shell.Application
$items = $Folder.Path.Split("\")
$zipRootIndex = [array]::IndexOf($items, $ZipFileName)
$folders = $items[$zipRootIndex..($items.Length - 1)]
$currentFolder = $DestinationPath
foreach ($folder in $folders)
{
$destination.NameSpace($currentFolder).NewFolder($folder)
$currentFolder = Join-Path -Path $currentFolder -ChildPath $folder
}
}
Usage:
$zipFilePath = "C:\Zipfile.zip"
$destinationPath = "C:\Users\Public\Downloads"
Get-ZipChildItems -ZipFile $zipFilePath -DestinationPath $destinationPath

Embed ForEach statement inside different ForEach on remote computer

As always, new to powershell and trying to self-teach myself. Thank you all in advance:
We have a logon script that auto-sets the registry to OUR homepage. When we build computers, we put the logon script inside the C:\Users\Default\%Appdata%\roaming....\startup\ folder. That way any new user that logs on gets the bat file put into their %AppData% folder, and their homepage is auto set.
We recently built a new server and due to some issues, we need to change our homepage URL, therefore needing to change the logon.bat file on all the computers for all user profiles.
This script I found on here works perfectly, but only on the local computer it is running on:
$source = '\\ITE00463866\Applications\_Layer1_Installs\TS Sector\firstlogon.txt'
$profilesfolder = 'c:\users\'
$excluded_profiles = #( 'All Users', 'Default User', 'Default.migrated', 'Public', 'DefaultAppPool', 'cdwuser', '.NET v4.5 Classic', '.NET v4.5')
$profiles = get-childitem $profilesfolder -Directory -force | Where-Object { $_.BaseName -notin $excluded_profiles }
$targetfolder = "\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup"
foreach ($profile in $profiles) {
$destination = $profilesfolder + $profile + $targetfolder
If ( $(Try { Test-Path $destination.trim() } Catch { $false }) ) {
copy-item -path $source -destination $destination -Force -Verbose
}
Else {
New-Item -Path $destination -ItemType Directory
copy-item -path $source -destination $destination -Force -Verbose
}
}
I have been trying to add the above ForEach statement INSIDE of a Get-Content | FOREACH($PC in $Computers){....} statement but get all these ODD issues and it only effects the local machine running the script on. For instance, taking every folder in my System32 folder and creating a user named whatever the System32 folder was named, then putting the logon.bat inside of all those %AppData% folders...
$source = '\\ITE00463866\Applications\_Layer1_Installs\TS Sector\firstlogon.txt'
$list = "\\ITE00463866\Applications\_Layer1_Installs\TS Sector\test.txt"
$computers = gc $list
foreach($pc in $computers){
$targetfolder = "\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup"
$excluded_profiles = #( 'Administrator', 'All Users', 'Default User', 'Default.migrated', 'Public', 'DefaultAppPool', 'cdwuser', '.NET v4.5 Classic', '.NET v4.5')
$profiles = get-childitem $profilesfolder -Directory -force | Where-Object { $_.BaseName -notin $excluded_profiles }
$profilesfolder = 'c:\users\'
foreach ($profile in $profiles) {
$destination = $profilesfolder + $profile + $targetfolder
if ( $(Try { Test-Path $destination.trim() } Catch { $false }) ) {
#If folder Startup folder is found for profile, add file to destionation, force overwrite
copy-item -path $source -destination $destination -Force -Verbose
}
Else {
#If folder is NOT found, create folder and move file to destination
New-Item -Path $destination -ItemType Directory
copy-item -path $source -destination $destination -Force -Verbose
}
}
}
How do I combine the two scripts to:
For each computer in my list, look in all the user profiles and for each profile (excluding the ones mentioned) add the new logon.bat
If the script u have posted at the top works for you and as long as winrm is configured in ur environment, you can enclose it inside a scriptblock like below:
$scriptblock = {
$source = '\\ITE00463866\Applications\_Layer1_Installs\TS Sector\firstlogon.txt'
$profilesfolder = 'c:\users\'
$excluded_profiles = #( 'All Users', 'Default User', 'Default.migrated', 'Public', 'DefaultAppPool', 'cdwuser', '.NET v4.5 Classic', '.NET v4.5')
$profiles = get-childitem $profilesfolder -Directory -force | Where-Object { $_.BaseName -notin $excluded_profiles }
$targetfolder = "\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup"
foreach ($profile in $profiles) {
$destination = $profilesfolder + $profile + $targetfolder
If ( $(Try { Test-Path $destination.trim() } Catch { $false }) ) {
copy-item -path $source -destination $destination -Force -Verbose
}
Else {
New-Item -Path $destination -ItemType Directory
copy-item -path $source -destination $destination -Force -Verbose
}
}
}
And then use ur foreach loop like so:
$list = "\\ITE00463866\Applications\_Layer1_Installs\TS Sector\test.txt"
$computers = gc $list
foreach($pc in $computers){
Invoke-Command -ComputerName $pc -ScriptBlock $scriptblock
}
Asked one of our principal programmers and he corrected my original script without needing to add a scipt block to it. Thank you for 1st suggestion!
Here is the final script that worked!
$source = '\\ITE00463866\Applications\_Layer1_Installs\TS Sector\firstlogon.bat'
$list = "\\ITE00463866\Applications\_Layer1_Installs\TS Sector\computers.txt"
$computers = gc $list
foreach($pc in $computers){
$targetfolder = "\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup"
$excluded_profiles = #( 'Administrator', 'All Users', 'Default User', 'Default.migrated', 'Public', 'DefaultAppPool', 'cdwuser', '.NET v4.5 Classic', '.NET v4.5')
$profilesfolder = '\\' + $pc + '\c$\users\'
$profiles = get-childitem $profilesfolder -Directory -force | Where-Object { $_.BaseName -notin $excluded_profiles }
foreach ($profile in $profiles) {
$destination = $profilesfolder + $profile + $targetfolder
if ( $(Try { Test-Path $destination.trim() } Catch { $false }) ) {
#If folder Startup folder is found for profile, add file to destionation, force overwrite
copy-item -path $source -destination $destination -Force -Verbose
}
Else {
#If folder is NOT found, create folder and move file to destination
New-Item -Path $destination -ItemType Directory
copy-item -path $source -destination $destination -Force -Verbose
}
}
}

Skip Permissions if Directory exists

Here is code I have to create a directory for my users. I was wondering how to skip the ACL part if the directory already exists.
#Search AD for Elem Students
$elem = Get-ADUser -Filter 'company -eq "1479"'
$path = "\\hs-ss\students\elem"
foreach ($user in $elem)
{
$Username = $User.SamAccountName
#Create user directory of Storage Server
New-Item -Path $path -Name $Username -ItemType "directory" -Force | Out-Null
$ACL = Get-Acl "$path\$Username"
$ACL.SetAccessRuleProtection($true, $false)
$ACL.Access | ForEach { [Void]$ACL.RemoveAccessRule($_) }
$ACL.AddAccessRule((New-Object System.Security.AccessControl.FileSystemAccessRule("$NTDomain\Domain Admins", "FullControl", "ContainerInherit, ObjectInherit", "None", "Allow")))
$ACL.AddAccessRule((New-Object System.Security.AccessControl.FileSystemAccessRule("$NTDomain\$username", "Modify", "ContainerInherit, ObjectInherit", "None", "Allow")))
Set-Acl "$Path\$username" $ACL
}
Check if the directory exists:
if (-not (Test-Path -LiteralPath "$path\$Username" -Type Container)) {
...
}

automated folder structure creation with conditions

any idea / suggestion how to simplify this code? I would like to find different approach to create folders as they need to be created.
function TargetDir { # create folder structure for patches by OS type
if ($col6 -like "*WindowsXP*" -or $col4 -like "*Windows XP*"
) {$TargetDir = "$Path\$col1\XP"
if (!(Test-Path -path $TargetDir
)) {New-Item $Path\$col1\XP -type directory
New-Item -type file -Name $Col6 -Path $TargetDir}
else {New-Item -type file -Name $Col6 -Path $TargetDir}
}
elseif ($col6 -like "*Windows6.0*" -or $col4 -like "*Vista*"
) {$TargetDir = "$Path\$col1\Vista"
if (!(Test-Path -path $TargetDir
) ) {New-Item $Path\$col1\Vista -type directory
New-Item -type file -Name $Col6 -Path $TargetDir}
else {New-Item -type file -Name $Col6 -Path $TargetDir}
}
elseif ($col6 -like "*Windows6.1*" -or $col4 -like "*Windows 7*"
) {$TargetDir = "$Path\$col1\Win7"
if (!(Test-Path -path $TargetDir
) ) {New-Item $Path\$col1\Win7 -type directory
New-Item -type file -Name $Col6 -Path $TargetDir}
else {New-Item -type file -Name $Col6 -Path $TargetDir}
}
elseif ($col6 -like "*Windows8-RT*" -or $col4 -like "*Windows 8 *"
) {$TargetDir = "$Path\$col1\Win8"
if (!(Test-Path -path $TargetDir)
) {New-Item $Path\$col1\Win8 -type directory
New-Item -type file -Name $Col6 -Path $TargetDir}
else {New-Item -type file -Name $Col6 -Path $TargetDir}
}
elseif ($col6 -like "*Windows8.1*" -or $col4 -like "*Windows 8.1*"
) {$TargetDir = "$Path\$col1\Win81"
if (! (Test-Path -path $TargetDir)
) {New-Item $Path\$col1\Win81 -type directory
New-Item -type file -Name $Col6 -Path $TargetDir}
else {New-Item -type file -Name $Col6 -Path $TargetDir}
}
} # end if
Thank you for any suggestion how this could be simplified.
When you find yourself using elseif a lot switch might be there to save you.
You are checking if $col4 or $col6 has a match correct? Since that appears the case you only really need to check one of those values as long as it is not blank (Just bear with me). Also to make your function more portable (if not for you then for others) then we are going to use function parameters.
function Get-TargetDirectory{
param(
[string]$value1,
[string]$value2,
[string]$path,
[string]$directory
)
If($value1){$toMatch = $value1} #The value of $value1 is tested to contain a non empty string / null
If($value2){$toMatch = $value2} #The value of $value2 is tested to contain a non empty string / null
switch -Regex ($toMatch){
"Windows ?XP"{$TargetDir = "$Path\$directory\XP"}
"(Windows6\.0|Vista)"{$TargetDir = "$Path\$directory\Vista"}
"(Windows6\.1|Windows 7)"{$TargetDir = "$Path\$directory\Win7"}
"Windows ?8"{$TargetDir = "$Path\$directory\Win8"}
"Windows ?8\.1"{$TargetDir = "$Path\$directory\Win81"}
default{$TargetDir = ""}
}
If($TargetDir){ #The value of $TargetDir is tested to contain a non empty string / null
# Create the directory if it does not already exist
If(!(Test-Path -path $TargetDir)){[void](New-Item $TargetDir -type Directory)}
# Create the empty file in $TargetDir
New-Item -type file -Name $value1 -Path $TargetDir
}
}
I changed the names in my function to be more friendly. The following is how they map to your previous names.
Yours Get-TargetDirectory
$col1 $directory
$col4 $value2
$col6 $value1
$path $path
Not perfect as there are some caveats that you dont already account for like the presence of the folder I refer to as $directory. Using the -Regex keyword we can simplify the match operations. Also, there is no need to have the code to create items exist multiple times since it will be called regardless.
Hopefully this, untested code, works for you and shows you some good coding practices.
Matt thank you for your replay. I admit that I shared almost non detail but I had on my mind something much more simpler... Your suggestion to use switch was the key to get it done that way.
switch -wildcard ($FileName)
{
"*6.0*" { $TargetDir = "$Path\Vista\" }
"*6.1*" { $TargetDir = "$Path\Windows 7\" }
"*8-R*" { $TargetDir = "$Path\Windows 8\" }
"*8.1*" { $TargetDir = "$Path\Windows 8.1\" }
default { write-host "Define target directory for $FileName" -foreground "red"}
}
if (Test-Path -path $TargetDir) {
New-Item $TargetDir -type directory
New-Item -type file -Name $FileName -Path $TargetDir
} else {
New-Item -type file -Name $FileName -Path $TargetDir -Force
}