PowerShell for Windows Admins


May 26, 2014  11:05 AM

File System ACLs – creating an ACL

Richard Siddaway Richard Siddaway Profile: Richard Siddaway
NTFS permissions

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.

 

 

 

 

 

May 25, 2014  5:58 AM

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

Richard Siddaway Richard Siddaway Profile: Richard Siddaway
NTFS 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


May 23, 2014  1:46 AM

Remove entry from trusted hosts list

Richard Siddaway Richard Siddaway Profile: Richard Siddaway

The last variant I want to show is removing a single entry from the list

function remove-trustedhost {
[CmdletBinding()]
param (
[string]$trustedhost,
[string]$computername = $env:COMPUTERNAME
)

if (Test-Connection -ComputerName $computername -Quiet -Count 1) {
$th = Get-WSManInstance -ResourceURI winrm/config/client -ComputerName $computername |
select -ExpandProperty TrustedHosts

if ($th) {
$ths = $th -split “, |,”, 0, “Regex”

$newth = ($ths -ne $trustedhost) -join “, ”

Set-WSManInstance -ResourceURI winrm/config/client -ComputerName $computername -ValueSet @{TrustedHosts = $newth}

}
else {
Write-Warning -Message “No trusted hosts to remove”
}

}
else {
Write-Warning -Message “$computername is unreachable”
}

}

After testing that you can connect to the remote machine use Get-WsManinstance to pull the current trusted hosts list. Split the list into an array. I’ve used a regular expression fo the delimiter so I catch the cases with and without a space after the comma – I tend to leave a space but other people don’t.

Recreate the string without the entry you don’t want.

Its probable you could use the replace operator for this instead but I wanted to show how to remove an entry from an array.

Use set-WSManInstance to reset the trusted hosts list.

Now you have 4 functions for managing your trusted hosts list – enjoy


May 22, 2014  11:39 AM

Clearing the trusted hosts list

Richard Siddaway Richard Siddaway Profile: Richard Siddaway

Sometimes you may just need to clear out all of the current values in the trusted hosts list and start again – especially in a lab environment where you may be experimenting.

function clear-trustedhost {
[CmdletBinding()]
param (
[string]$computername = $env:COMPUTERNAME
)

if (Test-Connection -ComputerName $computername -Quiet -Count 1) {
Set-WSManInstance -ResourceURI winrm/config/client -ComputerName $computername -ValueSet @{TrustedHosts = “”}
}
else {
Write-Warning -Message “$computername is unreachable”
}

}

The trick is to use Set-WSManInstance to set the value of the TrustedHosts list to an empty string.  Don’t try and use $null  – it will fail


May 22, 2014  10:40 AM

Adding to the trusted hosts list

Richard Siddaway Richard Siddaway Profile: Richard Siddaway

Its OK to be able to read the contents of the trusted hosts list but what about adding values to it?

function add-trustedhost {
[CmdletBinding()]
param (
[string]$trustedhost,
[string]$computername = $env:COMPUTERNAME
)

if (Test-Connection -ComputerName $computername -Quiet -Count 1) {
$th = Get-WSManInstance -ResourceURI winrm/config/client -ComputerName $computername |
select -ExpandProperty TrustedHosts

if ($th) {
$newth = $th + “, $trustedhost”
}
else {
$newth = $trustedhost
}

Set-WSManInstance -ResourceURI winrm/config/client -ComputerName $computername -ValueSet @{TrustedHosts = $newth}
}
else {
Write-Warning -Message “$computername is unreachable”
}

}

The function takes a computername paraneter and a string containing one or more machine names to add to the trusted host list.  This is a singe string so

“server02, servero3, server04”

NOT

“serevr02”, “server03”, “server04”

The current value in th trusted hosts list is retrieved and if it has  a current value the extra trusted host( s ) are added. If the trusted hosts list is empty the new list replaces it,  Set-WSManInstance is used to set the value.


May 20, 2014  1:21 PM

Reading the trusted hosts list

Richard Siddaway Richard Siddaway Profile: Richard Siddaway

One of the sessions I did at the recent PowerShell summit was on using the WSMAN cmdlets. In my experience, these cmdlets aren’t used much. This is for a couple of reasons I think – the syntax is a bit difficult and there are often other ways to perform the task.

This short series of posts will concentrate on using the WSMAN cmdlets to work with your trusted hosts list. The trusetd hosts list is used in remoting, especially non-domain remoting or if you need to credssp. to determine which machines your machine trusts.

You can view the trusted hosts list by using the wsman provider:

£> ls WSMan:\localhost\Client\TrustedHosts

 

WSManConfig: Microsoft.WSMan.Management\WSMan::localhost\Client

 

Type            Name             SourceOfValue      Value
----            ----              -------------     -----
System.String   TrustedHosts                        server02

 

if you want just the results

 

£> ls WSMan:\localhost\Client\TrustedHosts | select -ExpandProperty Value

server02

 

You can achieve the same result with Get-WSMANinstance:

Get-WSManInstance -ResourceURI winrm/config/client | select -ExpandProperty TrustedHosts

This is a bit much to type regularly so lets create a function:

 

function get-trustedhost {

[CmdletBinding()]

param (

[string]$computername = $env:COMPUTERNAME

)

 

if (Test-Connection -ComputerName $computername -Quiet -Count 1) {

Get-WSManInstance -ResourceURI winrm/config/client -ComputerName $computername |

select -ExpandProperty TrustedHosts

}

else {

Write-Warning -Message “$computername is unreachable”

}

}

 

The function has a single parameter – the computername that defaults to the local machine.

Run Test-Connection to ensure that you can connect to the machine (-Quiet returns a boolean rather than the ping information). If you can connect use Get-WSMANinstance to fetch the trusted hosts data.

If Test-Connection doesn’t contact the remote machine use Write-Warning to output a message.

 


May 19, 2014  1:00 PM

Share Permissions – setting deny

Richard Siddaway Richard Siddaway Profile: Richard Siddaway
CIM, WMI

The last change to the share permissions functions to modify the Set-SharePermissions functions to enable the application of Deny permissions.

The function becomes:

#requires -Version 3.0

function Set-SharePermission {

[CmdletBinding()]

param (

[Parameter(Mandatory=$true)]

[string]$sharename,

 

[string]$domain = $env:COMPUTERNAME,

 

[Parameter(Mandatory=$true)]

[string]$trusteeName,

 

[Parameter(Mandatory=$true)]

[ValidateSet("Read", "Change", "FullControl")]

[string]$permission = “Read”,

 

[string]$computername = $env:COMPUTERNAME,

 

[parameter(ParameterSetName="AllowPerm")]

[switch]$allow,

 

[parameter(ParameterSetName="DenyPerm")]

[switch]$deny

)

 

switch ($permission) {

‘Read’ {$accessmask = 1179817}

‘Change’ {$accessmask = 1245631}

‘FullControl’ {$accessmask = 2032127}

}

$tclass = [wmiclass]“\\$computername\root\cimv2:Win32_Trustee”

$trustee = $tclass.CreateInstance()

$trustee.Domain = $domain

$trustee.Name = $trusteeName

 

$aclass = [wmiclass]“\\$computername\root\cimv2:Win32_ACE”

$ace = $aclass.CreateInstance()

$ace.AccessMask = $accessmask

 

switch ($psCmdlet.ParameterSetName) {

“AllowPerm” {$ace.AceType = 0}

“DenyPerm” {$ace.AceType = 1}

default {Write-Host “Error!!! Should not be here” }

}

 

$ace.Trustee = $trustee

$shss = Get-WmiObject -Class Win32_LogicalShareSecuritySetting -Filter “Name=’$sharename’” -ComputerName $computername

$sd = Invoke-WmiMethod -InputObject $shss -Name GetSecurityDescriptor |

select -ExpandProperty Descriptor

 

$sclass = [wmiclass]“\\$computername\root\cimv2:Win32_SecurityDescriptor”

$newsd = $sclass.CreateInstance()

$newsd.ControlFlags = $sd.ControlFlags

 

foreach ($oace in $sd.DACL){

if (($oace.Trustee.Name -eq $trusteeName) -AND ($oace.Trustee.Domain -eq $domain) ) {

continue

}

else

{

$newsd.DACL += $oace

}

}

$newsd.DACL += $ace

 

$share = Get-WmiObject -Class Win32_LogicalShareSecuritySetting -Filter “Name=’$sharename’” -ComputerName $computername

$share.SetSecurityDescriptor($newsd)

 

} # end function

 

The changes are to add two switches –allow & –deny. Put them in different parametersets to ensure mutual exclusivity.

As you are using parametersets you can use a switch based on the parameterset name to set the ACE type.

switch ($psCmdlet.ParameterSetName) {

“AllowPerm” {$ace.AceType = 0}

“DenyPerm” {$ace.AceType = 1}

default {Write-Host “Error!!! Should not be here” }

}

 

Everything else remains the same.

 


May 18, 2014  12:44 PM

Share Permissions – adding a Deny permission

Richard Siddaway Richard Siddaway Profile: Richard Siddaway
CIM, WMI

Modifying the Add-SharePermission function to enable the application of Deny permissions is a simple matter of adding a switch parameter –deny and modifying the way the AcreType is set:

#requires -Version 3.0

function Add-SharePermission {

[CmdletBinding()]

param (

[Parameter(Mandatory=$true)]

[string]$sharename,

 

[string]$domain = $env:COMPUTERNAME,

 

[Parameter(Mandatory=$true)]

[string]$trusteeName,

 

[Parameter(Mandatory=$true)]

[ValidateSet("Read", "Change", "FullControl")]

[string]$permission = “Read”,

 

[string]$computername = $env:COMPUTERNAME,

 

[switch]$deny

)

 

switch ($permission) {

‘Read’ {$accessmask = 1179817}

‘Change’ {$accessmask = 1245631}

‘FullControl’ {$accessmask = 2032127}

}

$tclass = [wmiclass]“\\$computername\root\cimv2:Win32_Trustee”

$trustee = $tclass.CreateInstance()

$trustee.Domain = $domain

$trustee.Name = $trusteeName

 

$aclass = [wmiclass]“\\$computername\root\cimv2:Win32_ACE”

$ace = $aclass.CreateInstance()

$ace.AccessMask = $accessmask

$ace.AceFlags = 0

 

if ($deny)

{

$ace.AceType = 1

}

else

{

$ace.AceType = 0

}

 

$ace.Trustee = $trustee

 

$shss = Get-WmiObject -Class Win32_LogicalShareSecuritySetting -Filter “Name=’$sharename’” -ComputerName $computername

$sd = Invoke-WmiMethod -InputObject $shss -Name GetSecurityDescriptor |

select -ExpandProperty Descriptor

 

$sclass = [wmiclass]“\\$computername\root\cimv2:Win32_SecurityDescriptor”

$newsd = $sclass.CreateInstance()

$newsd.ControlFlags = $sd.ControlFlags

 

foreach ($oace in $sd.DACL){$newsd.DACL += $oace}

$newsd.DACL += $ace

 

$share = Get-WmiObject -Class Win32_LogicalShareSecuritySetting -Filter “Name=’$sharename’” -ComputerName $computername

$share.SetSecurityDescriptor($newsd)

 

} # end function

 

The hard work is done by this part of the code:

 

if ($deny)

{

$ace.AceType = 1

}

else

{

$ace.AceType = 0

}

 

 

where the value of AceType is set to 1 for deny and 0 for allow.

 

 


May 18, 2014  8:40 AM

Share Permissions – working with Deny

Richard Siddaway Richard Siddaway Profile: Richard Siddaway
CIM, WMI

Permissions can be set to either allow access ot to deny access. The functions I’ve presented so far only work with Allow permissions. Using Deny permissions should be avoided if at all possible but sometimes there’s no alternative.

First thing is to modify Get-SharePermission so that it shows if the permission is allowed or denied.

 

#requires -Version 3.0

function Get-SharePermission {

[CmdletBinding()]

param (

[Parameter(Mandatory=$true)]

[string]$sharename,

 

[string]$computername = $env:COMPUTERNAME

)

 

$shss = Get-CimInstance -Class Win32_LogicalShareSecuritySetting -Filter “Name=’$sharename’” -ComputerName $computername

$sd = Invoke-CimMethod -InputObject $shss -MethodName GetSecurityDescriptor |

select -ExpandProperty Descriptor

 

foreach ($ace in $sd.DACL) {

 

switch ($ace.AccessMask) {

1179817 {$permission = ‘Read’}

1245631 {$permission = ‘Change’}

2032127 {$permission = ‘FullControl’}

default {$permission = ‘Special’}

}

 

$trustee = $ace.Trustee

$user = “$($trustee.Domain)\$($trustee.Name)”

 

switch ($ace.AceType) {

0 {$status = ‘Allow’}

1 {$status = ‘Deny’}

2 {$status = ‘Audit’}

}

 

$props = [ordered]@{

User = $user

Permissions = $permission

Status = $status

}

New-Object -TypeName PSObject -Property $props

} # emd foreach

} # end function

 

The modification is to add a switch based on the AceType property of the Win32_ACE object (http://msdn.microsoft.com/en-us/library/aa394063(v=vs.85).aspx) to determine the status of the permission. Add a property called Status to the output object and set it equal to the status determined in the switch.

 


May 18, 2014  8:24 AM

Error trapping when getting AD objects

Richard Siddaway Richard Siddaway Profile: Richard Siddaway
Active Directory Administration

How many times have you done this:

£> Get-ADComputer -Identity “bleh”

Get-ADComputer : Cannot find an object with identity: ‘bleh’ under: ‘DC=Manticore,DC=org’.

At line:1 char:1

+ Get-ADComputer -Identity “bleh”

+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

+ CategoryInfo : ObjectNotFound: (bleh:ADComputer) [Get-ADComputer], ADIdentityNotFoundException

+ FullyQualifiedErrorId : ActiveDirectoryCmdlet:Microsoft.ActiveDirectory.Management.ADIdentityNotFoundException,

Microsoft.ActiveDirectory.Management.Commands.GetADComputer

 

Its the same with all of the AD cmdlets – if the object isn’t found you get an error thrown. That’s OK when working interactively but can wreck you script execution – you don’t want to come to work in the morning to find that you script failed on the fifth of fifty (or five hundred) computers.

Your first though might be to use the –ErrorAction parameter:

£> Get-ADComputer -Identity “bleh” -ErrorAction SilentlyContinue

Get-ADComputer : Cannot find an object with identity: ‘bleh’ under: ‘DC=Manticore,DC=org’.

At line:1 char:1

+ Get-ADComputer -Identity “bleh” -ErrorAction SilentlyContinue

+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

+ CategoryInfo : ObjectNotFound: (bleh:ADComputer) [Get-ADComputer], ADIdentityNotFoundException

+ FullyQualifiedErrorId : ActiveDirectoryCmdlet:Microsoft.ActiveDirectory.Management.ADIdentityNotFoundException,

Microsoft.ActiveDirectory.Management.Commands.GetADComputer

 

£> Get-ADComputer -Identity “bleh” -ErrorAction Ignore

Get-ADComputer : Cannot find an object with identity: ‘bleh’ under: ‘DC=Manticore,DC=org’.

At line:1 char:1

+ Get-ADComputer -Identity “bleh” -ErrorAction Ignore

+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

+ CategoryInfo : ObjectNotFound: (bleh:ADComputer) [Get-ADComputer], ADIdentityNotFoundException

+ FullyQualifiedErrorId : ActiveDirectoryCmdlet:Microsoft.ActiveDirectory.Management.ADIdentityNotFoundException,

Microsoft.ActiveDirectory.Management.Commands.GetADComputer

 

Neither SilentlyContinue or Ignore will work – the default is Continue.

 

You can use the $ErrorActionPreference variable:

£> $ErrorActionPreference = ‘SilentlyContinue’

£> Get-ADComputer -Identity “bleh”

£> $ErrorActionPreference = ‘Continue’

 

Set the variable to SilentlyContinue, run your command and then remember to set it back!

Another way is to use a try-catch block:

 

try {

Get-ADComputer -Identity “bleh”

}

catch {}

 

You can put any code needed to handle the object not been found into the catch block.

The catch works because it will pick up ANY exceptions. You can use the specific exception if you need to possibly handle other errors.

try {

Get-ADComputer -Identity “bleh”

}

catch [Microsoft.ActiveDirectory.Management.ADIdentityNotFoundException]

{

Write-Warning “AD computer object not found”

}

catch {}

 

The trick with using multiple catch blocks is to always ensure that the exceptions start with the most specific and work down to the most generic. In this case the Microsoft.ActiveDirectory.Management.ADIdentityNotFoundException exception comes first – its what Get-ADComputer uses when it can’t find the object. If you’re wondering where that information comes from look at the first error message in the post – the exception has been highlighted.

The final catch block will catch any other exceptions that come through.

 


Forgot Password

No problem! Submit your e-mail address below. We'll send you an e-mail containing your password.

Your password has been sent to: