PowerShell for Windows Admins


June 6, 2014  7:36 AM

PowerShell Summit NA 2014 sessions

Richard Siddaway Richard Siddaway Profile: Richard Siddaway
PowerShell

The slides and code from my three sessions

WSMAN cmdlets

Networking administration

Cmdletize the Registry

are available from

http://powershell.org/wp/2014/05/22/na-2014-powershell-summit-wrap-up-and-speaker-slides/

You can find videos of some of the sessions – including my three here

http://powershell.org/wp/2014/05/16/powershell-summit-n-a-2014-session-videos/

June 4, 2014  1:18 PM

Function parameter validation–ValidateScript

Richard Siddaway Richard Siddaway Profile: Richard Siddaway
PowerShell

Sometimes you need a little bit more than the standard parameter validation options can provide – in that case use ValidateScript – you can write your own validation routine. For example:

function test {
[CmdletBinding()]
param (
[parameter(Mandatory=$true,ValueFromPipeLine=$true,ValueFromPipeLineByPropertyName=$true)]
[ValidateNotNullOrEmpty()]
[ValidateScript({Test-Path -Path $_})]
[string]$Path
)

$path

}

All I’ve done to the function used last time is that I’ve added

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

This will test the path you input to determine if it exists.

Supply a path

£> test -Path C:\Test
C:\Test

and the function works as expected.

£> test -Path
test : Missing an argument for parameter ‘Path’. Specify a parameter of type ‘System.String’ and try again.
At line:1 char:6
+ test -Path
+      ~~~~~
+ CategoryInfo          : InvalidArgument: (:) [test], ParameterBindingException
+ FullyQualifiedErrorId : MissingArgument,test
Miss off the value of –path and you get the error telling you that the argument is missing. Being able to have multiple validation tests does give you the most flexibility.

£> test -Path C:\Test22
test : Cannot validate argument on parameter ‘Path’. The “Test-Path -Path $_” validation script for the argument with
value “C:\Test22″ did not return a result of True. Determine why the validation script failed, and then try the command
again.
At line:1 char:12
+ test -Path C:\Test22
+            ~~~~~~~~~
+ CategoryInfo          : InvalidData: (:) [test], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationError,test
In this case an invalid path is supplied and the validation script runs and determines that the path is invalid.

In case you were wondering about the order of validation – in this case it doesn’t matter if you change the order

function test {
[CmdletBinding()]
param (
[parameter(Mandatory=$true,ValueFromPipeLine=$true,ValueFromPipeLineByPropertyName=$true)]
[ValidateScript({Test-Path -Path $_})]
[ValidateNotNullOrEmpty()]
[string]$Path
)

$path

}

£> test -Path C:\Test
C:\Test

£> test -Path
test : Missing an argument for parameter ‘Path’. Specify a parameter of type ‘System.String’ and try again.
At line:1 char:6
+ test -Path
+      ~~~~~
+ CategoryInfo          : InvalidArgument: (:) [test], ParameterBindingException
+ FullyQualifiedErrorId : MissingArgument,test
£> test -Path C:\Test22
test : Cannot validate argument on parameter ‘Path’. The “Test-Path -Path $_” validation script for the argument with
value “C:\Test22″ did not return a result of True. Determine why the validation script failed, and then try the command
again.
At line:1 char:12
+ test -Path C:\Test22
+            ~~~~~~~~~
+ CategoryInfo          : InvalidData: (:) [test], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationError,test
The validation tests fail as expected.


June 3, 2014  11:57 AM

Function parameter validation–NotNullorEmpty

Richard Siddaway Richard Siddaway Profile: Richard Siddaway
PowerShell

A question on the forum made me realise that there is still  a lot of confusion around the advanced function parameter validation options so I thought I’d spend  a few posts clearing some of the confusion.

The question was about ValidateNotNullorEmpty so I’ll start with that one.

function test {
[CmdletBinding()]
param (
[parameter(Mandatory=$true,ValueFromPipeLine=$true,ValueFromPipeLineByPropertyName=$true)]
[ValidateNotNullOrEmpty()]
[string]$Path
)

$path

}

If you supply a path – it’s echoed back to you:

£> test -Path C:\Test
C:\Test

If you don’t supply the path parameter you’ll be prompted for it:

£> test
cmdlet test at command pipeline position 1
Supply values for the following parameters:
Path: c:\test
c:\test

if you use the function and don’t supply a value:

£> test -Path
test : Missing an argument for parameter ‘Path’. Specify a parameter of type ‘System.String’ and try again.
At line:1 char:6
+ test -Path
+      ~~~~~
+ CategoryInfo          : InvalidArgument: (:) [test], ParameterBindingException
+ FullyQualifiedErrorId : MissingArgument,test

The validation routine kicks in.

The pipeline works as well:

£> Get-ChildItem -Path c:\ -Filter test | test
Test

Though notice that its returning Test instead of c:\test – that’s because Get-ChildItem doesn’t return a property called path but Name is the first string property accessible in the object which is why its used.

Now if you try to access a non-existent path in this way

£> Get-ChildItem -Path c:\ -Filter test27 | test
£>

You’ll get nothing returned  – because

Get-ChildItem -Path c:\ -Filter test27

doesn’t put anything on the pipeline so test is never called.

If you want to see the function work with a non-existent folder try this:

£> test -Path (Get-ChildItem -Path c:\ -Filter test27)
test : Cannot validate argument on parameter ‘Path’. The argument is null or empty. Provide an argument that is not null
or empty, and then try the command again.
At line:1 char:12
+ test -Path (Get-ChildItem -Path c:\ -Filter test27)
+            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo          : InvalidData: (:) [test], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationError,test

ValidateNotNullEmpty is a good test for determining if a value is present and not Null or an empty string. It should be paired where possible with mandatory=$true to ensure that the parameter is used.

This and other validation options are discussed in about_Functions_Advanced_Parameters


June 2, 2014  10:53 AM

Unavailable modules

Richard Siddaway Richard Siddaway Profile: Richard Siddaway
PowerShell, Windows 2012

Another question on the powershell.org forum centred on modules not available on down level versions of Windows – in this case the SmbShare module.

A large number of modules were introduced with Windows server 2012 (many are also available on Windows 8). Over 60% of this new functionality is created using CDXML. A WMI class is created to do the work. Calls to the class are wrapped in XML and the subsequent file can be published as a PowerShell module.

These are easy to spot as they have a .CDXML extension.

If you are having problems with a module that doesn’t appear to be on Windows Server 2008 R2 or earlier – check the file extension in the module folder on a Windows Server 2012 (R2) system– if its CDXML the WMI class almost certainly won’t be available on the earlier versions of Windows.


May 28, 2014  1:39 PM

File system ACLs–function to add ACL

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

I thought that today I’d start putting together a function to add an ACL to a file system object. The starting point is the code that stepped through the process in an earlier post:

http://msmvps.com/blogs/richardsiddaway/archive/2014/05/26/file-system-acls-creating-an-acl.aspx

function add-acl {
[CmdletBinding()]
param (
[Parameter(Mandatory=$true)]
[ValidateScript({Test-Path -Path $_ })]
[string]$path,

[Parameter(Mandatory=$true)]
[string]$trusteeName,

[Parameter(Mandatory=$true)]
[ValidateSet("Read", "Write", "ListDirectory", "ReadandExecute", "Modify", "FullControl")]
[string]$permission = “Read”,

[switch]$deny

)

$fsr = [System.Security.AccessControl.FileSystemRights]::$permission
if ($deny) {
$alwdny = [System.Security.AccessControl.AccessControlType]::Deny
}
else {
$alwdny = [System.Security.AccessControl.AccessControlType]::Allow
}
$acr = New-Object -TypeName System.Security.AccessControl.FileSystemAccessRule -ArgumentList $trusteeName, $fsr, $alwdny

$acl = Get-Acl -Path $path
$acl.AddAccessRule($acr)
Set-Acl -Path $path -AclObject $acl -Passthru
}

The parameters supply the path to the object, the trustee receiving the permissions, the permission and if its being denied.

The function creates the appropriate objects for the file system rights and access control type and then creates an access rule.

Get-Acl is used to fetch the current acl to which the new access rule is added. Set-Acl is used to overwrite the ACL.

One thing that hasn’t been covered is the Inheritance flags – they will be added in the next iteration of the function.


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


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: