PowerShell for Windows Admins


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.

 


May 18, 2014  5:16 AM

Share Permissions – changing

Richard Siddaway Richard Siddaway Profile: Richard Siddaway
CIM, PowerShell v3, WMI

So far you’ve seen how to read, remove and add permissions to a share. The final scenario to be covered is modifying a permission.

The functions I’ve presented to date only enable you to set Allow permissions on a share. I’ll be covering Deny permissions in later posts. This mimics the way that I tend to develop functionality – get part working then add more in increments until you have what you need.

For now this is function will enable you to modify permissions on a share:

#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

)

 

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

$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){

 

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

 

If you compare this to the Add-SharePermission and Remove-SharePermission you’ll see that the Set-SharePermission is really the Add-SharePermission function with the code to add the old ACE to the new DACL modified to be similar to Remove-SharePermission.

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

continue

}

else

{

$newsd.DACL += $oace

}

The code loops through the old ACEs and as long as the name AND domain don’t match the trustee for whom you’re changing permissions they’re added to the new DACL. In other worlds the old permissions for the trustee are removed from the DACL.

 


May 17, 2014  7:02 AM

Share Permissions – Removing

Richard Siddaway Richard Siddaway Profile: Richard Siddaway
CIM, PowerShell v3, WMI

You’ve seen how to read share permissions and how to add share permissions – now its time to remove share permissions. Most of the code we need is in the Add-Sharepermission function – it just needs a bit of a tweak.

#requires -Version 3.0

function Remove-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 ($permission) {

‘Read’ {$accessmask = 1179817}

‘Change’ {$accessmask = 1245631}

‘FullControl’ {$accessmask = 2032127}

}

 

$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) -AND ($oace.Accessmask -eq $accessmask)) {

continue

}

else

{

$newsd.DACL += $oace

}

}

 

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

$share.SetSecurityDescriptor($newsd)

 

} # end function

 

The function uses the same parameters as Add-Permission i.e. mandatory share name, trustee name and permission with optional computer and domain names. The switch statement converts the permission into an access mask.

Use Get-WmiObject to get the current security descriptor and use [wmiclass] to create a new one.

Copy the control flags and the ACE except for the any that correspond to the trustee name, domain and the permission you want to remove.

Use SetSecurityDescriptor to apply the new permissions

 

 


May 16, 2014  1:23 PM

Share Permissions – adding

Richard Siddaway Richard Siddaway Profile: Richard Siddaway
CIM, PowerShell v3, WMI

Having seen how to read the permissions on a share its time to turn to one of the other common tasks associated with shares – adding permissions. This is usually done when the share is created but there are scenarios where you need to add extra permissions.

I’ve written the function Add-SharePermission to accomplish this task:

#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 ($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

$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'”

$share.SetSecurityDescriptor($newsd)

 

} # end function

 

The function starts with a requires restricting the function to version 3 and above. The function would run on version 2 so this is for consistency with the Get-SharePermission function I showed last time.

The parameter block includes a mandatory sharename and trusteename (user or group). Optional parameters allow a domain and computername to be set – both default to the local machine. A final mandatory parameter allows the specification of the permissions to be applied – read, change or full control – these values are validated at input using the ValidateSet option.

The next step in the function is to take the permission and turn it into an access mask using a switch – its the inverse of the one you saw in Get-SharePermission.

I’ve had to use the [wmiclass] type accelerator to to create the Win32_Trustee and Win32_ACE instances I need. The CIM cmdlets can’t be used because these classes don’t have keys defined.

The properties are set on the 2 objects.

Get-WmiObject is used to get the SecurityDescriptor of the share.

A new security descriptor is created with the relevant properties from the existing one copied across – the current ACEs have to be copied individually as shown. The new ACE is added to the DACL

Finally Get-WmiObject is used to retrieve the share object and the SetSEcurityDescriptor method is used to apply the new security descriptor.

Some interesting uses the wmiclass accelerator to make this work but the function shows it is doable without too much effort.

 

 


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: