Monthly Archive

Categories

File System

File System ACLs – creating an ACL

Last time you saw that the permissions assign to a file system object are built from instances of the System.Security.AccessControl.FileSystemAccessRule class.  Run

Get-Acl -Path c:\test | fl *

and look at the Access property.

Drilling into an individual ACL they look like this:

FileSystemRights  : FullControl
AccessControlType : Allow
IdentityReference : BUILTIN\Administrators
IsInherited       : True
InheritanceFlags  : ContainerInherit, ObjectInherit
PropagationFlags  : None

You see the documentation for the class at http://msdn.microsoft.com/en-us/library/system.security.accesscontrol.filesystemaccessrule(v=vs.110).aspx

Creating a new access rule starts by creating a new instance of the class. The documentation shows 4 constructors – ways to build an instance of the class. The simplest requires the name of a user account (or group), the type of operation associated with the rule and whether the operation is allowed or denied.

First off you need to define some data to use during the creation process:

You need to define the user

$user = "$($env:COMPUTERNAME)\Newuser"

The type of access they have

$fsr = [System.Security.AccessControl]::FullControl

See http://msdn.microsoft.com/en-us/library/system.security.accesscontrol(v=vs.110).aspx for the full list

And whether the rule is allowed or denied

$alwdny = [System.Security.AccessControl.AccessControlType]::Allow

You can then create the access rule

$acr = New-Object -TypeName System.Security.AccessControl.FileSystemAccessRule -ArgumentList $user, $fsr, $alwdny

Get the current ACL

$acl = Get-Acl -Path C:\Test

Add the new rule

$acl.AddAccessRule($acr)

And finally set the ACL on the object

Set-Acl -Path c:\test -AclObject $acl

Over the next few posts I’ll show how to simplify this process with some functions – in a similar way to those you saw recently for working with shares.

File System ACLs – Get-Acl #1 – Retrieving permissions

Following on from the recent set of posts about setting security permissions on shares I thought it about time I looked at the file system security permissions. PowerShell supplies 2 cmdlets, in the core engine, Get-Acl and Set-Acl for workign with permissions. These two cmdlets are part of the Microsoft.PowerShell.Security module.

Many Powershell users shy away from these 2 cmdlets – they do have a reputation for being hard to use.  This series of articles is meant to make these very useful cmdlets more accessible and easier to use.

Get-Acl is the obvious starting point – you need to know what ACLs exist on a given object.

£> Get-Acl -Path c:\test | Format-Table -a

    Directory: C:\

Path Owner                 Access
---- -----                 ------
test RSSURFACEPRO2\Richard BUILTIN\Administrators Allow  FullControl...

The Format-Table is only used to condense the width of the output.

The default display shown above isn’t that helpful so lets try a list display.

£> Get-Acl -Path c:\test | Format-List

Path   : Microsoft.PowerShell.Core\FileSystem::C:\test
Owner  : RSSURFACEPRO2\Richard
Group  : RSSURFACEPRO2\Richard
Access : BUILTIN\Administrators Allow  FullControl
         NT AUTHORITY\SYSTEM Allow  FullControl
         BUILTIN\Users Allow  ReadAndExecute, Synchronize
         NT AUTHORITY\Authenticated Users Allow  Modify, Synchronize
         NT AUTHORITY\Authenticated Users Allow  -536805376
Audit  :
Sddl   : O:S-1-5-21-2502823385-1436278615-3517788930-1001G:S-1-5-21-2502823385-1436278615-3517788930-1001D:AI(A;OICIID;
         FA;;;BA)(A;OICIID;FA;;;SY)(A;OICIID;0x1200a9;;;BU)(A;ID;0x1301bf;;;AU)(A;OICIIOID;SDGXGWGR;;;AU)

That starts to look a bit more useful.  The Access and Sddl properties hold what you need.

If you dive straight into retreiving the Access property:

£> Get-Acl -Path c:\test | select -ExpandProperty Access

FileSystemRights  : FullControl
AccessControlType : Allow
IdentityReference : BUILTIN\Administrators
IsInherited       : True
InheritanceFlags  : ContainerInherit, ObjectInherit
PropagationFlags  : None

etc

You will see an entry similar to the above for each security setting on the object.  What would be simpler to work with is the way the Access property is presented when using Format-List. If you examine the complete object produced by Get-Acl:

£> Get-Acl | Format-List *

PSPath                  : Microsoft.PowerShell.Core\FileSystem::C:\MyData\SkyDrive\Data\scripts
PSParentPath            :

Microsoft.PowerShell.Core\FileSystem::C:\MyData\SkyDrive\Data
PSChildName             : scripts
PSDrive                 : C
PSProvider              : Microsoft.PowerShell.Core\FileSystem
CentralAccessPolicyId   :
CentralAccessPolicyName :

AccessToString          : BUILTIN\Administrators Allow  FullControl
                          NT AUTHORITY\SYSTEM Allow  FullControl
                          BUILTIN\Users Allow  ReadAndExecute, Synchronize
                          NT AUTHORITY\Authenticated Users Allow  Modify, Synchronize
                          NT AUTHORITY\Authenticated Users Allow  -536805376

AuditToString           :
Path                    : Microsoft.PowerShell.Core\FileSystem::C:\MyData\SkyDrive\Data\scripts
Owner                   : RSSURFACEPRO2\Richard
Group                   : RSSURFACEPRO2\Richard
Access                  : {System.Security.AccessControl.FileSystemAccessRule,
                          System.Security.AccessControl.FileSystemAccessRule,
                          System.Security.AccessControl.FileSystemAccessRule,
                          System.Security.AccessControl.FileSystemAccessRule...}
Sddl                    : O:S-1-5-21-2502823385-1436278615-3517788930-1001G:S-1-5-21-2502823385-1436278615-3517788930-1
                          001D:AI(A;OICIID;FA;;;BA)(A;OICIID;FA;;;SY)(A;OICIID;0x1200a9;;;BU)(A;ID;0x1301bf;;;AU)(A;OIC
                          IIOID;SDGXGWGR;;;AU)
AccessRightType         : System.Security.AccessControl.FileSystemRights
AccessRuleType          : System.Security.AccessControl.FileSystemAccessRule
AuditRuleType           : System.Security.AccessControl.FileSystemAuditRule
AreAccessRulesProtected : False
AreAuditRulesProtected  : False
AreAccessRulesCanonical : True
AreAuditRulesCanonical  : True

What you need to display is the AccessToString property:

£> Get-Acl | select -ExpandProperty AccessToString
BUILTIN\Administrators Allow  FullControl
NT AUTHORITY\SYSTEM Allow  FullControl
BUILTIN\Users Allow  ReadAndExecute, Synchronize
NT AUTHORITY\Authenticated Users Allow  Modify, Synchronize
NT AUTHORITY\Authenticated Users Allow  -536805376

Which gives a very nice summary of the permissions.

If you want to stick with working with objects then use something like this to duplicate the display

Get-Acl |
select -ExpandProperty Access |
select IdentityReference, AccessControlType,FileSystemRights

CIM snippets–working with file system

The latest instalment from the WMI team on using PowerShell and the CIM cmdlets is available - http://blogs.msdn.com/b/wmi/archive/2014/03/28/performing-management-tasks-using-cim-cmdlets-4-files-and-folders.aspx

This time round the examples are to do with working with the file system – files, folders and shares.

If you’ve worked with WMI you’ll be aware of that very often you get 2 classes one with a prefix of CIM (base class from DMTF definition) and one with Win32 prefix which is the Microsoft implementation. They 2 classes are often identical though the Win32 class may have additions.

The WMI class for working with files is different – it only has a CIM version CIM_DataFile.

The first example in the post is about renaming a file. A much simpler coding of the task would be:

Get-CimInstance -ClassName CIM_Datafile -Filter "Name = 'C:\\Test\\Names.txt'" |
Invoke-CimMethod -MethodName Rename -Arguments @{FileName = 'C:\\Test\\OldNames.txt'}

A couple of points to note:

- when dealing with file paths all \ characters must be doubled. This is because \ is a WMI escape character so you need to escape it to use it literally.

- Invoke-CimMethod uses a hash table for the method arguments with the argument name as the key – this takes away any of the argument order issues you see with Invoke-WmiMethod)

One perennial problem for administrators is users putting their own files on the organization’s file servers.  Want to know if there any files of a specific type in a folder?

Get-CimInstance -ClassName CIM_Datafile -Filter "Extension = 'txt' AND Path = '\\test\\'"

If you leave the path out of the filter then all files on the drive will be searched – could take a while.  Being specific in your filter will save you a lot of time.

Want to find all the mp3 files on a drive?

Get-CimInstance -ClassName CIM_Datafile -Filter "Extension = 'mp3'"

You can’t create files and folders with CIM (or WMI) but you can create shares

$margs = @{
Path = 'C:\Test'
Name = 'Test2April'
Description = 'TestShare'
Type = [uint32]0
}

Invoke-CimMethod -ClassName Win32_Share -MethodName Create -Arguments $margs

Create the hash table of arguments separately  - its easier to read. Bizarrely this time you don’t need to escape the \ in the path

You can see the shares on a system like this:

Get-CimInstance -ClassName Win32_Share

CIM may not be you first port of call when working with the file system but it can be useful – especially on remote systems.

You can find out much more about using CIM to work with the file system in chapetr 8 of PowerShell and WMI – www.manning.com/siddaway2

Automatically create folder for home drive

In this post  http://richardspowershellblog.wordpress.com/2013/10/28/setting-ad-attributes-from-a-csv-file/

I showed how to modify the user’s home folder setting in Active Directory.

A comment was recently left asking about automatically creating the folder on the fileserver and creating the share that is associated with it.

This isn’t a simple exercise – you will need a script to:

You can create the folder using New-Item

New-Item -Path c:\test -Name anyolduser -Type Directory

You can share it

$max = [uint32]5

$type = [uint32]0

Invoke-CimMethod -ClassName Win32_Share -MethodName Create -Arguments @{Name='anyolduser'; Path='c:\test\anyolduser';
Type=$type; MaximumAllowed=$max; Description='anyolduser - homedrive'}

And then you have to set share and NTFS permissions according to your organization’s policies

Deleting a file with WMI

Following on from the last post this is how you can delete a file.

Use the same file structure as previously

This time you need to use the CIM_DataFile class. It’s one of the few classes I’ve found that doesn’t have a Win32_ equivalent.

The class has a Delete method.  Assuming you know the full path to the file

Get-CimInstance -ClassName CIM_DataFile -Filter "Name='C:\\Expendable\\Target1\\proc.txt' " |
Invoke-CimMethod -MethodName Delete

 

You can perform most actions on files and folders using WMI but you can’t create a file or a folder

Deleting Folders with WMI

I recently saw a question about deleting folders with WMI.

Let’s create a file structure we’re happy to delete

New-Item -Path c:\ -Name Expendable -ItemType Directory
New-Item -Path c:\Expendable -Name Target1  -ItemType Directory
New-Item -Path c:\Expendable -Name Target2  -ItemType Directory

Get-Process | Out-File -FilePath c:\Expendable\Target1\proc.txt
Get-Service | Out-File -FilePath c:\Expendable\Target2\serv.txt

 

A look through the WMI classes brings out Win32_Directory

PS> $class = Get-CimClass -ClassName Win32_Directory
PS> $class.CimClassMethods | ft -a

Name                        ReturnType Parameters
----                        ---------- ----------
TakeOwnerShip                   UInt32 {}
ChangeSecurityPermissions       UInt32 {Option, SecurityDescriptor}
Copy                            UInt32 {FileName}
Rename                          UInt32 {FileName}
Delete                          UInt32 {}
Compress                        UInt32 {}
Uncompress                      UInt32 {}
TakeOwnerShipEx                 UInt32 {Recursive, StartFileName, StopFileName}
ChangeSecurityPermissionsEx     UInt32 {Option, Recursive, SecurityDescriptor,...}
CopyEx                          UInt32 {FileName, Recursive, StartFileName, StopFileName}
DeleteEx                        UInt32 {StartFileName, StopFileName}
CompressEx                      UInt32 {Recursive, StartFileName, StopFileName}
UncompressEx                    UInt32 {Recursive, StartFileName, StopFileName}
GetEffectivePermission         Boolean {Permissions}

 

The Delete method looks promising

CIM uses inert objects so we need to get an instance an pipe it into Invoke-CimMethod

Get-CimInstance -ClassName Win32_Directory -Filter "Name='C:\\Expendable'" |
Invoke-CimMethod -MethodName Delete

When you delete a folder like this - the folder, its contents, any subfolders and their contents are deleted. They don’t appear in the recycle bin either so its a one way trip

Touching files

Unix has a command called touch that allows you to set the access time on a file.  PowerShell doesn’t have a direct equivalent but it is very easy to perform the same task:

$date = (Get-Date).AddMonths(-2)
Get-ChildItem -Path C:\Teszzt2 -Filter f*.txt |
Set-ItemProperty -Name LastWriteTime -Value $date -PassThru |
Set-ItemProperty -Name LastAccessTime -Value $date -PassThru |
Set-ItemProperty -Name CreationTime -Value $date

 

Set the date you want.  Get the files and pipe into Set-ItemProperty.  The example shows LastWriteTime, LastAccessTime and CreationTime all being modified. Change the code to just change what you need.

You can see the results

Get-ChildItem -Path C:\Teszzt2 -Filter f*.txt |
select Name, LastAccessTime, LastWriteTime, CreationTime

Cleaning up the temp folder

 

This post

http://powershell.com/cs/blogs/tips/archive/2012/06/05/checking-size-of-downloads-folder.aspx

go me thinking about the temp folder. Its one of those areas tucked away in your profile that just seems to get left to grow.  Time to do something about it.

$testdate = (Get-Date).AddDays(-10)            
$names = "FXSAPIDebugLogFile.txt", "hsperfdata_Richard"            
            
## assume TMP = TEMP            
$path = $env:TEMP            
            
Get-ChildItem -Path $path -File  |            
where LastWriteTime -lt $testdate |            
where Name -NotIn $names |            
Remove-Item -Force #-WhatIf            
            
Get-ChildItem -Path $path -Directory |            
where LastWriteTime -lt $testdate |            
where Name -NotIn $names |            
Remove-Item -Force -Recurse #-WhatIf

I decide to use some of the new functionality in PowerShell v3 and separate the file and folder processing so I could play (er experiment) with some of the new parameters in  Get-ChildItem

Get-ChildItem [-Attributes <FileAttributes]>] [-Directory] [-File] [-Force] [-Hidden] [-ReadOnly] [-System]

[-UseTransaction] [<CommonParameters>]

These are a separate parameter set which means that -exclude and –include don’t work with them.

So start by creating a date to test against. Anything older than this will be deleted. Define a few names of things that have to be left alone (these will probably be different on your system) and set the path. I’ve assumed that the TEMP and TMP environmental variables point to the same path. if your system is different put the rest of the code into a foreach loop to iterate through the two variables.

Starting with files we get all the files in the root of TEMP. The –File parameter means we only look at files – folders are automatically excluded.

I’ve then used two where statements. Now the obvious comment is that I could combine them but if I do that I have to check each file for its age and then if its name is in my exclusion list. This way I test for age and immediately filter out any file that it is too young. Then I test for name. Doing it this way gives a small performance increase if you have a lot of young files.

I then pass the files to Remove-Item to delete.

The directories are processed in a similar manner except that we use –Directory on Get-ChildItem and add –Recurse to the Remove-Item call so that non-empty folders are removed.

Next move is to make this a scheduled task using the new PowerShell scheduled task cmdlets.

Unblocking Files with PowerShell v3

There are a number of new features in PowerShell v3 that while not huge like CIM or workflow are os significant help to the hard pressed administrator.  One of these is the Unblock-File cmdlet.

If you haven’t updated your help the online version is available at http://technet.microsoft.com/en-us/library/hh849924.aspx

To test it I downloaded the PowerShell v2 release notes from

http://www.microsoft.com/en-us/download/details.aspx?id=11539

 

The gives me a file

Windows Mangement Framework Release Notes en-US.rtf

 

When you download a file – internet explorer safety mechanisms kick in and the file is blocked. This prevents a number of things happening including running scripts

You can test if a file is blocked by right clicking the file and looking at its properties – it will have an Unblock button at the bottom right of the dialog.

You can also use PowerShell to do this.

First we need to identify the files that are blocked.  These are files with an Alternative Data Stream of “Zone.Identifier”

 

PS> Get-Item -Path "c:\test\Windows Mangement Framework Release Notes en-US.rtf" -Stream "Zone.Identifier"

   FileName: C:\test\Windows Mangement Framework Release Notes en-US.rtf

Stream                   Length
------                   ------
Zone.Identifier              26

 

If you try to test multiple files

Get-Item -Path c:\test\*.* -Stream "Zone.Identifier"

be prepared for lots of error messages when the system doesn’t find an alternative data stream.  Better still control the error messages

Get-Item -Path c:\test\*.* -Stream "Zone.Identifier" -ErrorAction SilentlyContinue

  FileName: C:\test\indice_analitico.pdf

Stream                   Length
------                   ------
Zone.Identifier              26

   FileName: C:\test\Windows Mangement Framework Release Notes en-US.rtf

Stream                   Length
------                   ------
Zone.Identifier              26

Now I don’t recognise that pdf file so I’ll leave that for later

Either of these will filter on the extension

Get-Item -Path c:\test\*.rtf -Stream "Zone.Identifier" -ErrorAction SilentlyContinue
Get-Item -Path c:\test\*.* -Filter *.rtf -Stream "Zone.Identifier" -ErrorAction SilentlyContinue

Once you have the file

Get-Item -Path c:\test\*.* -Filter *.rtf -Stream "Zone.Identifier" -ErrorAction SilentlyContinue | Unblock-File

won’t work because only the –LiteralPath parameter of Unblock-File takes pipeline input and that’s by property name

 

These two options will work

Get-Item -Path c:\test\*.* -Filter *.rtf -Stream "Zone.Identifier" -ErrorAction SilentlyContinue |
foreach {Unblock-File -Path $_.FileName }

 

Get-ChildItem -Path c:\test\*.* -Filter *.rtf | Unblock-File

 

I prefer the first because it allows me to test and then modify to perform the unblock.

Folder sizes

Question on the forums related to folder sizes and last write time

Get-ChildItem -Path "C:\PersonalData\MyBooks\PowerShell and WMI" -Recurse |             
where { $_.PSIsContainer} |            
foreach {            
 $size = Get-ChildItem -Path $_.FullName | measure -Sum Length | select -ExpandProperty Sum            
             
 Add-Member -InputObject $($_) -MemberType NoteProperty -Name Size -Value $size            
            
 $_ | select Fullname, LastWriteTime, @{N="Size(MB)"; E={[math]::Round(($_.Size/1mb), 2)}}            
} | Format-Table -AutoSize -Wrap

Unfortunately the object returned by get-ChildItem doesn’t include folder size. So we loop through each folder & get the sum of its contents. The size value is added to the folder object and Fullname, LastwriteTime and size displayed.  The size is recalculated to megabytes. Substitute your favourite size