PowerShell for Windows Admins


May 28, 2014  12:51 PM

PowerShell Scripting Best Practices

Richard Siddaway Richard Siddaway Profile: Richard Siddaway
PowerShell scripting

Ed Wilson is running a series on Best Practices on his Hey, Scripting Guy blog at the moment.  I especially like today’s which is on simple scripts.  I do a lot of quick and dirty scripts that end up being thrown away at the  end of the project or incorporated into more formal modules for reuse.

Simple scripts are a very powerful admin tool and one that a lot of people don’t think about as they are conditioned into the idea that everything should be a module and other big code concepts. Don’t get me wrong – modules and everything else around enterprise level scripting are very important and a lot of my work is done that way but the quick, utility style script still has its place.

Check out Ed’s article at

http://blogs.technet.com/b/heyscriptingguy/archive/2014/05/28/powershell-best-practices-simple-scripting.aspx

May 27, 2014  1:21 PM

File system ACLs – copying ACLs

Richard Siddaway Richard Siddaway Profile: Richard Siddaway
NTFS permissions, PowerShell

A comment was left on the first post in the series asking if I could show how to copy ACLs from one object to another.  For the sake of this post we’ll assume that the ACLs from c:\test will be copied to c:\test2.

If this is one shot deal you can just use the PowerShell pipeline:

Get-Acl -Path C:\Test | Set-Acl -Path C:\Test2

If you need something that will be used more frequently – how about a copy-acl function:

function copy-acl {
[CmdletBinding()]
param (
[ValidateScript({Test-Path -Path $_ })]
[string]$path,

[ValidateScript({Test-Path -Path $_ })]
[string]$destination
)

try
{
Get-Acl -Path $path | Set-Acl -Path $destination -Passthru -ErrorAction Stop
}
catch
{
Throw “Error setting ACL on $destination”
}
}

Get the source and target paths as parameters – validation scripts are used to determine if the paths exist.

Use the Get—Acl  | Set-Acl combination from earlier to set the ACL on the destination.  wrap it in a try/catch and use –Passthru on Set-Acl to see some output


May 26, 2014  11:05 AM

File System ACLs – creating an ACL

Richard Siddaway Richard Siddaway Profile: Richard Siddaway
NTFS permissions, PowerShell

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, PowerShell

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
PowerShell

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
PowerShell

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
PowerShell

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
PowerShell

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, PowerShell, 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, PowerShell, 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.

 

 


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: