Alisdair Craik

Alisdair Craik

11 October 2013

PowerShell 4 Desired State Configuration - Enforce NTFS permissions

By now you've probably heard of PowerShell 4 Desired State Configuration. If not, we'll have some tutorials up for you soon, but in the meantime if you are a DSC convert and you've got access to Server 2012 R2, here's a brand new module to allow you to enforce NTFS permissions on a target computer.

Note: This module requires the release version of PowerShell 4 - not the CTP.

So, this module allows you to enforce NTFS permissions on files and folders on the target computer. Eventually this and all my other desired state modules will be going into the PowerShell.Org DSC modules project - if you haven't had a look yet, check it out.

PSM1 (NTFSPermission.psm1):

Function Get-TargetResource
{
    param
    (
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [String]$Path,

        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [String]$Account,

        [Parameter()]
        [ValidateSet("ReadAndExecute", "Modify", "FullControl")]
        [ValidateNotNullOrEmpty()]
        [String]$Rights,
        
        [Parameter()]
        [ValidateSet("Present", "Absent")]
        [String]$Ensure = "Present",


        [Parameter()]
        [ValidateSet("Allow", "Deny")]
        [String]$Access = "Allow",

        [Parameter()]
        [Bool]$NoInherit = $false
    )

    $InheritFlag = if($NoInherit){ "None" }else{ "ContainerInherit, ObjectInherit" }

    $DesiredRule = New-Object System.Security.AccessControl.FileSystemAccessRule($Account, $Rights, $InheritFlag, "None", $Access)

    $CurrentACL = (Get-Item $Path).GetAccessControl("Access")
    $CurrentRules = $CurrentACL.GetAccessRules($true, $false, [System.Security.Principal.NTAccount])
    $Match = $CurrentRules |?{ ($DesiredRule.IdentityReference -eq $_.IdentityReference) -and 
                                ($DesiredRule.FileSystemRights -eq $_.FileSystemRights) -and 
                                ($DesiredRule.AccessControlType -eq $_.AccessControlType) -and 
                                ($DesiredRule.InheritanceFlags -eq $_.InheritanceFlags )}
    
    $Presence = if($Match){"Present"}else{"Absent"}

    $output = @{
                Ensure = $Presence;
                Path = $Path;
                Account = $Account;
                Rights = $Rights;
                Access = $Access;
                NoInherit = $NoInherit;
                }

    return $output
}

Function Test-TargetResource
{
    param
    (
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [String]$Path,

        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [String]$Account,

        [Parameter()]
        [ValidateSet("ReadAndExecute", "Modify", "FullControl")]
        [ValidateNotNullOrEmpty()]
        [String]$Rights,

        [Parameter()]
        [ValidateSet("Present", "Absent")]
        [String]$Ensure = "Present",


        [Parameter()]
        [ValidateSet("Allow", "Deny")]
        [String]$Access = "Allow",

        [Parameter()]
        [Bool]$NoInherit = $false
    )

    $InheritFlag = if($NoInherit){ "None" }else{ "ContainerInherit, ObjectInherit" }

    $DesiredRule = New-Object System.Security.AccessControl.FileSystemAccessRule($Account, $Rights, $InheritFlag, "None", $Access)

    $CurrentACL = (Get-Item $Path).GetAccessControl("Access")
    $CurrentRules = $CurrentACL.GetAccessRules($true, $false, [System.Security.Principal.NTAccount])
    $Match = $CurrentRules |?{ ($DesiredRule.IdentityReference -eq $_.IdentityReference) -and 
                                ($DesiredRule.FileSystemRights -eq $_.FileSystemRights) -and 
                                ($DesiredRule.AccessControlType -eq $_.AccessControlType) -and  
                                ($DesiredRule.InheritanceFlags -eq $_.InheritanceFlags )}

    $Presence = if($Match){"Present"}else{"Absent"}
    return $Presence -eq $Ensure
}

Function Set-TargetResource
{
    param
    (
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [String]$Path,

        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [String]$Account,

        [Parameter()]
        [ValidateSet("ReadAndExecute", "Modify", "FullControl")]
        [ValidateNotNullOrEmpty()]
        [String]$Rights,

        [Parameter()]
        [ValidateSet("Present", "Absent")]
        [String]$Ensure = "Present",
        
        [Parameter()]
        [ValidateSet("Allow", "Deny")]
        [String]$Access = "Allow",

        [Parameter()]
        [Bool]$NoInherit = $false
    )

    $InheritFlag = if($NoInherit){ "None" }else{ "ContainerInherit, ObjectInherit" }

    $DesiredRule = New-Object System.Security.AccessControl.FileSystemAccessRule($Account, $Rights, $InheritFlag, "None", $Access)
    $CurrentACL = (Get-Item $Path).GetAccessControl("Access")

    if($Ensure -eq "Present")
    {
        $CurrentACL.AddAccessRule($DesiredRule)
        Set-Acl $Path $CurrentACL
    }
    else
    {
        $CurrentRules = $CurrentACL.GetAccessRules($true, $false, [System.Security.Principal.NTAccount])
        $Match = $CurrentRules |?{ ($DesiredRule.IdentityReference -eq $_.IdentityReference) -and 
                                ($DesiredRule.FileSystemRights -eq $_.FileSystemRights) -and 
                                ($DesiredRule.AccessControlType -eq $_.AccessControlType) -and  
                                ($DesiredRule.InheritanceFlags -eq $_.InheritanceFlags )}

        $Match | % {[void]$CurrentACL.RemoveAccessRule($_)}
        Set-Acl $Path $CurrentACL
    }
}

PSD1 (NTFSPermission.psd1):

#
# Module manifest for module 'NTFSPermission'
#
# Generated by: Alisdair Craik
#
# Generated on: 30/09/2013
#

@{

# Script module or binary module file associated with this manifest.
# RootModule = ''

# Version number of this module.
ModuleVersion = '1.0'

# ID used to uniquely identify this module
GUID = '1bec717b-b2a1-4220-b0a6-5cff834f0f76'

# Author of this module
Author = 'Alisdair Craik'

# Company or vendor of this module
CompanyName = 'Vexasoft'

# Copyright statement for this module
Copyright = '(c) 2013 Vexasoft Ltd. All rights reserved.'

# Description of the functionality provided by this module
Description = 'This module is used to add or remove NTFS access rules on the target computer.'

# Minimum version of the Windows PowerShell engine required by this module
PowerShellVersion = '4.0'

# Minimum version of the common language runtime (CLR) required by this module
CLRVersion = '3.0'

# Modules that must be imported into the global environment prior to importing this module
RequiredModules = @()

# Assemblies that must be loaded prior to importing this module
# RequiredAssemblies = @()

# Script files (.ps1) that are run in the caller's environment prior to importing this module.
# ScriptsToProcess = @()

# Type files (.ps1xml) to be loaded when importing this module
# TypesToProcess = @()

# Format files (.ps1xml) to be loaded when importing this module
# FormatsToProcess = @()

# Modules to import as nested modules of the module specified in RootModule/ModuleToProcess
NestedModules = @('NTFSPermission.psm1')

# Functions to export from this module
FunctionsToExport = @("Get-TargetResource", "Set-TargetResource", "Test-TargetResource")

# Cmdlets to export from this module
CmdletsToExport = '*'

# HelpInfo URI of this module
# HelpInfoURI = ''

}

MOF (NTFSPermission.schema.mof):

[ClassVersion("1.0.0"), FriendlyName("NTFSPermission")] 
class NTFSPermission : OMI_BaseResource
{
  [Key] string Path;
  [Key] string Account;
  [write,ValueMap{"ReadAndExecute", "Modify", "FullControl"},Values{"ReadAndExecute", "Modify", "FullControl"}] string Rights;
  [write,ValueMap{"Present", "Absent"},Values{"Present", "Absent"}] string Ensure;
  [write,ValueMap{"Allow", "Deny"},Values{"Allow", "Deny"}] string Access;
  [write] boolean NoInherit;
};

Files should be saved to: '$PSHOME\Modules\PSDesiredStateConfiguration\DSCResources\NTFSPermission\'.

Note: That's with the day one patch to Windows 8.1 / 2012 R2 - this post has been updated to reflect the change in location.

As ever, use notepad to paste the MOF file - the PowerShell ISE will save it with incompatible encoding which will cause the Local Configuration Manager to bug out.

Lastly - usage:

Configuration SampleNTFSConfig
{
    Node MyComputer
    {
        NTFSPermission Permission1
        {
            Ensure = "Present"
            Account = "MyUser"
            Access = "Allow"
            Path = "c:\MyDirectory"
            Rights = "ReadAndExecute"
        } 
    }
}

SampleNTFSConfig
Start-DscConfiguration -Wait -Verbose -Force .\SampleNTFSConfig -ComputerName MyComputer

And that's it. You can now enforce file system rights to the files and folders of any computer running PowerShell 4.

Next up: Enforcing shares and share permissions.

<< Back To The Blog