Monthly Archives: July 2012

Modifying data folder permissions with Powershell

If you are doing a migration from one domain to another, you may need to change groups (old groups for new groups!). We did trying playing around with SID History, but with limited success.

With this script, you need to give it a CSV file containing a list of the groups that you want swapped (two columns, oldgroup & newgroup, in a file called groups.csv), and you will also need to edit the variables for in the script for your domain names.

There is a $removeoldgroup variable. If this is set to true, the old group is replaced. If it set to false, it is left. The script also checks to see if a particaular ACL entry is inherited. This is important because otherwise, you can get duplicate entries on the ACL (although no necessarily visible from the GUI, or having any effect in normal user use, it isn’t very clean).

Finally, as we had several areas where “Administrators” had been removed, the script will also add them back in if they are missing.

There are three files that are created by the script.
“Before Permission Changes.txt” – This will contain a list of folders and ACL’s before any processing is done.
“After Permission Changes.txt” – This will contain a list of folders and ACL’s once the processing is complete.
“folders done.txt” – This will contain a list of all folders that have been modified, as well as the date/time.

A few things to note.
1) It will fail to handle long paths (260 or more characters). You will need to create a share further down the tree in order to successfully process those. I looked at other ways around this, but didn’t find one which appealed.
2) I ran this from my Windows7 workstation against the server using UNC paths. I did test this on the server itself (Windows Server 2008, not R2), and it didn’t work as I expected with the subfolders. I don’t know if this is to do with an older version of Powershell, or an older version of .NET…
2) Test, test, test. I spent the best part of a week testing this in different scenarios, including reversing things to put the old groups back. Still, we have 140,000 folders and and problems would be a potential nightmare to solve. If you have more folders, do more testing 🙂

$destinationpath = "\\server1\share1"
$oldDomain = "EMEA"
$newDomain = "IPRUK"

$removeoldgroup = $false

"Job started at " + (Get-Date) `
	| Out-File "folders Done.txt" -Append
	
#Build a list of folders
$folders = Get-childitem $destinationpath -recurse `
	| where {$_.mode -like "d----"} 

#Get the ACL's and store in a txt file for future review.
$folders | Get-Acl `
	| Format-List @{l='Directory';e={($_.PSpath).substring(38)}},`
			accessToString `
			| Out-File -FilePath "Before permission changes.txt"

#Read the groups from a CSV with two columns, oldgroup & newgroup.
$groups = import-csv groups.csv

foreach ($folder in $folders){
	
	$aclaccess = Get-Acl $folder.FullName | select Access -ExpandProperty Access

	Foreach ($group in $groups) {
		$oldgroup = $olddomain + "\" + $group.oldgroup
		$newgroup = $newdomain + "\" + $group.newgroup

		Foreach ($accessright in $aclaccess) {
			[string]$aclIdentity = $accessright.IdentityReference
			If ($oldgroup -eq $aclIdentity) {
				
				#We don't need to change if it is inherited
				If (!$accessright.IsInherited) {
					#We have a match, so grab the full ACL from folder.
					$acl = Get-Acl $folder.fullname
					
					#Build new access rule to add to ACL.
					$FileSystemRights = $accessright.FileSystemRights
					$InheritanceFlags = $accessright.InheritanceFlags
					$PropogationFlags = $accessright.PropagationFlags
					$ACEtype = $accessright.AccessControlType
					$permission = ($newgroup, `
						$Filesystemrights, `
						$InheritanceFlags, `
						$PropogationFlags, `
						$ACEtype)
					$Rule = New-Object `
						System.Security.AccessControl.FileSystemAccessRule $permission
						Try {
							$acl.AddAccessRule($rule)
							}
						Catch {
						$FolderFullName = $folder.FullName
						$Datenow = (Get-Date)
						"$Datenow - $folderfullname, $permission : " + `
							$_ | Out-File "Group Errors.txt" -Append
							}
					#Build rule to remove from ACL.
					If ($removeoldgroup){
						$permission = ($oldgroup, `
							$Filesystemrights, `
							$InheritanceFlags, `
							$PropogationFlags, `
							$ACEtype)
						$Rule = New-Object `
							System.Security.AccessControl.FileSystemAccessRule $permission
						$acl.RemoveAccessRuleAll($Rule)
						}
					
					#Apply new access rule to folder.
					Set-Acl $folder.FullName $acl
					
					#Check for builtin\administrators, add if missing
					$adminright = $acl.access `
						| Where {$_.IdentityReference -eq "BUILTIN\Administrators"}
					If (!$adminright){
						#Build new access rule to add to ACL.
						$FileSystemRights = "FullControl"
						$InheritanceFlags = "ContainerInherit,ObjectInherit"
						$PropogationFlags = "None"
						$ACEtype = "Allow"
						$permission = ("BUILTIN\Administrators", `
							$Filesystemrights, `
							$InheritanceFlags, `
							$PropogationFlags, `
							$ACEtype)
						$Rule = New-Object `
							System.Security.AccessControl.FileSystemAccessRule $permission
						$acl.AddAccessRule($rule)	
						#Apply new access rule to folder.
						Set-Acl $folder.FullName $acl
						}
					}
				}		
			}
		}
	$FolderFullName = $folder.FullName
	"$folderFullName completed at " + (Get-Date) `
		| Out-File "folders Done.txt" -Append
	}
#Final report on permissions.
$folders | Get-Acl `
	| Format-List @{l='Directory';e={($_.PSpath).substring(38)}},`
			accessToString `
			| Out-File -FilePath "After permission changes.txt"
			
"Job completed at " + (Get-Date) `
	| Out-File "folders Done.txt" -Append

So, this, combined with Finding Long Paths, and the script for modifying USER permissions, means that I have the scripts I wanted to proceed with our domain migration!

Leave a comment

Filed under Microsoft, Powershell, Windows 2008

Finding long paths with Powershell

If you are working with “get-childitem”, it won’t deal with long paths.

Expect to see the following:

Get-ChildItem : The specified path, file name, or both are too long. The fully qualified file name must be less than 260 chara
cters, and the directory name must be less than 248 characters.

At C:\Dropbox\Powershell\Scripts\get-longpaths.ps1:7 char:26
+ $folders = Get-childitem <<<< $destinationpath -recurse `
+ CategoryInfo : ReadError: (\\server\d$…akistan Branch):String) [Get-ChildItem], PathTooLongException
+ FullyQualifiedErrorId : DirIOError,Microsoft.PowerShell.Commands.GetChildItemCommand

To deal with this, you probably want to get a list of problem folders (obviously you can only get the level where the problem first occurs, not a list of all the items in the problem folder). You can do this using a try{} catch{} structure.

The following code will build a list of folders using get-childitem (which will return pathtoolongexpection errors), but then pass that list to a foreach, which will go through each folder in turn running a get-childitem. This however is in a try{} catch{} structure so the failure will be captured, and the the foreach will move on to the next folder.

Why two runs with get-childitem? Because if we tried using a trap{} on the first one, the script would stop before continuing so we would only get the first long path.

$destinationpath = "\\server\d$\GROUPDATATEST"

Write-Host "Start check"
	
#Build a list of folders
$folders = Get-childitem $destinationpath -recurse `
	| where {$_.mode -like "d----"} 
	
Foreach ($folder in $folders){
	Try{
		$dump = Get-ChildItem $folder.FullName -ErrorAction Stop
		}
	Catch{
	$FolderFullName = $folder.fullname
		Add-Content -Value "$folderFullname" -Path "LongPaths.txt"
		}
}
	
Write-Host "Check Done"

3 Comments

Filed under Microsoft, Powershell