Monthly Archive

Categories

Monthly Archives: July 2015

get-computerDN function

Last time I showed a bare bones function for using [adsisearcher] to retrieve the distinguished name of a domain computer.

This is a somewhat expanded version of that function which adds in the sorts of features you’d want for a production ready function.

function get-computerDN {
[CmdletBinding(SupportsShouldProcess=$true, ConfirmImpact="Low")]

param (
[parameter(Position=0,
   Mandatory=$true,
   ValueFromPipeline=$true,
   ValueFromPipelineByPropertyName=$true,
   HelpMessage="Supply computer name" )]
   [Alias("CN", "Computer")] 
   [ValidateNotNullOrEmpty()]
   [string[]]$computername
)
BEGIN{}#begin
PROCESS{

foreach ($computer in $computername) {
if ($psCmdlet.ShouldProcess("$computer", "Retreiving distinguished name")) {
    $filter = “(&(objectCategory=computer)(objectClass=computer)(cn=$computer))”
    ([adsisearcher]$filter).FindOne().Properties.distinguishedname
}
}

}#process
END{}#end

<#
.SYNOPSIS
Returns distinguished name of a computer

.DESCRIPTION
Returns distinguished name of one or more computers in the domain.
Assumes connectivity to domain controller. Domain independent.

.PARAMETER  computername
Name of computer for which distinguished name will be returned

.EXAMPLE
get-computerDN -computername server02

Returns the distinguished name for server02.

Results are of the form:
CN=SERVER02,OU=Domain Controllers,DC=Manticore,DC=org

.NOTES
[adsisearcher] is a shortcut for System.DirectoryServices.DirectorySearcher

.LINK

#>

}

 

[CmdletBinding()] adds –whatif and other standard parameters. It should be always part of a production script.

 

The param block is expanded to make the parameter mandatory, work with the pipeline and supply a help message if the value isn’t given

 

A couple of aliases for the parameter are defined and the parameter can’t be Null or empty

The parameter also accepts an array of values rather than a single value.

 

The PROCESS block is used for the actual working code. A foreach loop is used to iterate through the computernames and the retrieval of the distinguished name is wrapped in a if statement to allow for –whatif scenarios

 

Finally some help is defined.  I always add help tot he end of function so that its out of the way when I’m working on the code. It spersonal preference. If you want to put your help somewhere else – feel free.

 

Using the function gives you more options:

Simple single computer:£> get-computerDN -computername server02
CN=SERVER02,OU=Domain Controllers,DC=Manticore,DC=org

 

Whatif:

£> get-computerDN -computername server02 -WhatIf
What if: Performing the operation "Retreiving distinguished name" on target "ser
ver02".

 

multiple machines

£> get-computerDN -computername server02, w12r2sus
CN=SERVER02,OU=Domain Controllers,DC=Manticore,DC=org
CN=W12R2SUS,OU=Servers,DC=Manticore,DC=org

 

computername as positional parameter

£> get-computerDN server02, w12r2sus
CN=SERVER02,OU=Domain Controllers,DC=Manticore,DC=org
CN=W12R2SUS,OU=Servers,DC=Manticore,DC=org

 

parameter aliases

£> get-computerDN -computer server02, w12r2sus
CN=SERVER02,OU=Domain Controllers,DC=Manticore,DC=org
CN=W12R2SUS,OU=Servers,DC=Manticore,DC=org

£> get-computerDN -cn server02, w12r2sus
CN=SERVER02,OU=Domain Controllers,DC=Manticore,DC=org
CN=W12R2SUS,OU=Servers,DC=Manticore,DC=org

 

Help

£> Get-Help get-computerDN

NAME
    get-computerDN
   
SYNOPSIS
    Returns distinguished name of a computer
   
   
SYNTAX
    get-computerDN [-computername] <String[]> [-WhatIf] [-Confirm]
    [<CommonParameters>]
   
   
DESCRIPTION
    Returns distinguished name of one or more computers in the domain.
    Assumes connectivity to domain controller. Domain independent.
   

RELATED LINKS
   

REMARKS
    To see the examples, type: "get-help get-computerDN -examples".
    For more information, type: "get-help get-computerDN -detailed".
    For technical information, type: "get-help get-computerDN -full".
    For online help, type: "get-help get-computerDN -online"

 

Full help

£> Get-Help get-computerDN -Full

NAME
    get-computerDN
   
SYNOPSIS
    Returns distinguished name of a computer
   
SYNTAX
    get-computerDN [-computername] <String[]> [-WhatIf] [-Confirm]
    [<CommonParameters>]
   
   
DESCRIPTION
    Returns distinguished name of one or more computers in the domain.
    Assumes connectivity to domain controller. Domain independent.
   

PARAMETERS
    -computername <String[]>
        Name of computer for which distinguished name will be returned
       
        Required?                    true
        Position?                    1
        Default value               
        Accept pipeline input?       true (ByValue, ByPropertyName)
        Accept wildcard characters?  false
       
    -WhatIf [<SwitchParameter>]
       
        Required?                    false
        Position?                    named
        Default value               
        Accept pipeline input?       false
        Accept wildcard characters?  false
       
    -Confirm [<SwitchParameter>]
       
        Required?                    false
        Position?                    named
        Default value               
        Accept pipeline input?       false
        Accept wildcard characters?  false
       
    <CommonParameters>
        This cmdlet supports the common parameters: Verbose, Debug,
        ErrorAction, ErrorVariable, WarningAction, WarningVariable,
        OutBuffer, PipelineVariable, and OutVariable. For more information,
    see
        about_CommonParameters
    (http://go.microsoft.com/fwlink/?LinkID=113216).
   
INPUTS
   
OUTPUTS
   
NOTES
   
   
        [adsisearcher] is a shortcut for
        System.DirectoryServices.DirectorySearcher
   
    -------------------------- EXAMPLE 1 --------------------------
   
    PS C:\>get-computerDN -computername server02
   
   
    Returns the distinguished name for server02.
   
    Results are of the form:
    CN=SERVER02,OU=Domain Controllers,DC=Manticore,DC=org
   
   
   
   
   
   
RELATED LINKS
   

 

mandatory parameter
£> get-computerDN
cmdlet get-computerDN at command pipeline position 1
Supply values for the following parameters:
(Type !? for Help.)
computername[0]:
get-computerDN : Cannot validate argument on parameter 'computername'. The
argument is null, empty, or an element of the argument collection contains a
null value. Supply a collection that does not contain any null values and then
try the command again.
At line:1 char:1
+ get-computerDN
+ ~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidData: (:) [get-computerDN], ParameterBind
   ingValidationException
    + FullyQualifiedErrorId : ParameterArgumentValidationError,get-computerDN
 

Help message on mandatory parameter

£> get-computerDN
cmdlet get-computerDN at command pipeline position 1
Supply values for the following parameters:
(Type !? for Help.)
computername[0]: !?
Supply computer name
computername[0]:

 

Pipeline input

£> 'server02', 'w12r2sus' | get-computerDN
CN=SERVER02,OU=Domain Controllers,DC=Manticore,DC=org
CN=W12R2SUS,OU=Servers,DC=Manticore,DC=org

One thing missing is error handling – I’ll cover that next time

ADSIsearcher returns

I’ve been using the Microsoft AD cmdlets (and before that the Quest cmdlets) for so long that I’d sort of forgotten about [adsisearcher].

It was introduced in PowerShell 2.0 and is a shortcut for System.DirectoryServices.DirectorySearcher

 

A question in the forum about using this remotely made me realise that many people have probably never used it before – and to think that 5 years ago it was the way to go – how things change.

 

The question revolved around using this code

$filter = "(&(objectCategory=computer)(objectClass=computer)(cn=$env:COMPUTERNAME))"
([adsisearcher]$filter).FindOne().Properties.distinguishedname

for other machines.

 

An attempt was made to use Invoke-Command but that won’t work because you are attempting to delegate you credentials to make the call to AD – and that’s not allowed under the default configuration for remoting. You also can’t guarantee that remoting is enabled on older machines.

 

All you have to do is replace $env:COMPUTERNAME with the name of the computer for which you want to get the distinguished name.  Easiest way to do this is with a function

 

function get-computerDN {
param ($computername)
$filter = "(&(objectCategory=computer)(objectClass=computer)(cn=$computername))"
([adsisearcher]$filter).FindOne().Properties.distinguishedname

}

 

This is an absolute bare bones function just to show the way the parameter is used – you should validate the input and add some error handling as basic improvements.

 

£> get-computerDN -computername server02
CN=SERVER02,OU=Domain Controllers,DC=Manticore,DC=org

 

Next time I’ll show how to take the basic functionality and create something a bit more robust

Disk identification

A recent question on the forums regarded using the Win32_LogicalDisk class to retrieve disk size and free space data.  A filter based on the disk letter was being used. The main error with the code was that the filter was being expressed as

 

"DeviceId='E'"

rather than

"DeviceId='E:"

 

The colon is necessary as its part of the DeviceId data – if you are in doubt about the form of the data required by the filter then examine the full output of the class to see an example.

 

There were a couple of other basic issues.

 

Firstly always output objects.

 

Secondly use the size constants MB, GB etc rather than calculating refresh each time.

 

The final modified code looks like this

$computername = $env:COMPUTERNAME
$partition = 'C:'
$description = 'backup_server'

Get-WmiObject -Class Win32_LogicalDisk -Filter "DeviceId='$partition'"     -ComputerName $computername |
select PSComputerName,
@{Name='Partition'; Expression={$_.DeviceId}},
@{Name='Description'; Expression={$description}},
@{Name='Size(GB)';Expression={[math]::Round(($_.Size / 1GB), 2)}},
@{Name='FreeSpace(GB)';Expression={[math]::Round(($_.FreeSpace / 1GB), 2)}}

PowerShell and messaging

This article - http://powershell.org/wp/2015/07/07/rabbitmq-and-powershell/ – reminded me that Windows server comes with a built-in messaging system – MSMQ.

 

There is a PowerShell module for MSMQ - https://technet.microsoft.com/en-us/library/hh405007(v=vs.85).aspx

 

Since reading the RabbitMQ article I think that Rabbit is more useful in a heterogeneous environment but MSMQ would work very nicely in a Windows only environment.

 

I’ve not looked at MSMQ for a long time and not seen much on using it through PowerShell so I think its time to remedy that – I’ll put together a short series on using MSMQ through PowerShell to complement the RabbitMQ information

POwerShell Gallery taking registrations

The PowerShell team have just announced that the PowerShell gallery is now open for YOU to post your module. You need to register to be able to post code to the gallery.

 

http://blogs.msdn.com/b/powershell/archive/2015/07/07/powershell-gallery-registration-is-now-unrestricted.aspx

 

This is a significant step forward in making PowerShell code available to others – if you have a cool module please share

DSC validation

The PowerShell team are asking for validation and feedback on the DSC features in WMF 5.0 - http://blogs.msdn.com/b/powershell/archive/2015/07/06/validate-features-of-powershell-dsc.aspx

 

First up is the PowerShell DSC RunAsCredential - http://blogs.msdn.com/b/powershell/archive/2015/07/06/validate-powershell-dsc-runascredential.aspx

 

Other features will be explained and your validation solicited over the course of this week

Number of working days

Need to know the number of working days left until a specific date?

 

$we = [System.DayOfWeek]::Saturday, [System.DayOfWeek]::Sunday

$date = Get-Date -Year 2015 -Month 8 -Day 28
$now = (Get-Date).AddDays(-1)

$workdays = 0

while ($now -le $date){
 
$now = $now.AddDays(1)

if ($now.DayOfWeek -notin $we ) {
  $workdays++
}

}
$workdays

 

Create a collection of days you don’t want counting – in my case Saturday & Sunday

Set the date you want to count to and current date – the –1 on current day is to set the variable for the loop

 

In the loop increment the date and test against you collection of excluded days. Increment your workday counter and loop

 

Now you can work out how many days until that holiday , course or whatever.  The end date is included in the count by the way

Scripting Guy new series

Ed Wilson, the Scripting Guy, is starting a new series of posts on writing PowerShell scripts – he’ll also cover what to script, when to script and when to work interactively.

 

First post is here http://blogs.technet.com/b/heyscriptingguy/archive/2015/07/05/weekend-scripter-when-to-write-a-powershell-script.aspx

 

Strongly recommend this series to all PowerShell users

Scripting Games July 2015 puzzle

Head over to http://powershell.org/wp/2015/07/04/2015-july-scripting-games-puzzle/ for our inaugural Scripting Games puzzle.

 

I’ll publish a solution at the end of the month

Processing NULL AD values

Back in this post https://richardspowershellblog.wordpress.com/2012/12/09/bulk-modifications-using-set-aduser/

I showed how to perform a bulk change to a number of AD accounts using a CSV file to input the data

I was recently asked what happens if one of the input values is null.

£> Set-ADUser -Identity fgreen  -Division 'Test'
£> Get-ADUser -Identity fgreen  -Properties Division

DistinguishedName : CN=Fred Green,OU=Testing,DC=Manticore,DC=org
Division          : Test
Enabled           : False
GivenName         : Fred
Name              : Fred Green
ObjectClass       : user
ObjectGUID        : 8cf64233-9a87-43dc-8ce1-4f26bf78e12d
SamAccountName    : fgreen
SID               : S-1-5-21-195014076-723736408-1406369008-1112
Surname           : Green
UserPrincipalName : fgreen@Manticore.org

 

£> $div = $null
£> Set-ADUser -Identity fgreen  -Division $div
£> Get-ADUser -Identity fgreen  -Properties Division

DistinguishedName : CN=Fred Green,OU=Testing,DC=Manticore,DC=org
Division          :
Enabled           : False
GivenName         : Fred
Name              : Fred Green
ObjectClass       : user
ObjectGUID        : 8cf64233-9a87-43dc-8ce1-4f26bf78e12d
SamAccountName    : fgreen
SID               : S-1-5-21-195014076-723736408-1406369008-1112
Surname           : Green
UserPrincipalName : fgreen@Manticore.org

 

If the value is already set – a NULL value will effectively clear it

 

If a value isn’t set – nothing happens

£> $div = $null
£> Get-ADUser -Identity dbrown  -Properties Division

DistinguishedName : CN=Dave Brown,OU=Testing,DC=Manticore,DC=org
Division          :
Enabled           : False
GivenName         : Dave
Name              : Dave Brown
ObjectClass       : user
ObjectGUID        : 346649f7-6f69-4f20-985f-b2b08674b942
SamAccountName    : dbrown
SID               : S-1-5-21-195014076-723736408-1406369008-1109
Surname           : Brown
UserPrincipalName : dbrown@Manticore.org

 

£> Set-ADUser -Identity dbrown  -Division $div
£> Get-ADUser -Identity dbrown  -Properties Division

DistinguishedName : CN=Dave Brown,OU=Testing,DC=Manticore,DC=org
Division          :
Enabled           : False
GivenName         : Dave
Name              : Dave Brown
ObjectClass       : user
ObjectGUID        : 346649f7-6f69-4f20-985f-b2b08674b942
SamAccountName    : dbrown
SID               : S-1-5-21-195014076-723736408-1406369008-1109
Surname           : Brown
UserPrincipalName : dbrown@Manticore.org

 

An empty string on the otherhand

$div = ''
Set-ADUser -Identity dbrown  -Division $div

 

Set-ADUser : replace
At line:1 char:1
+ Set-ADUser -Identity dbrown  -Division $div
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (dbrown:ADUser) [Set-ADUser], ADInvalidOperationException
    + FullyQualifiedErrorId : ActiveDirectoryServer:0,Microsoft.ActiveDirectory.Management.Commands.SetADUser

 

Causes an error

 

Bottom line – if you’re passing in a CSV file check that the values in it aren’t NUL or empty – either by validating the parameters in your function or by writing extra code. The first way is much preferred