PowerShell for Windows Admins


February 16, 2015  12:23 PM

Testing AD replication

Richard Siddaway Richard Siddaway Profile: Richard Siddaway
Active Directory, Powershell

I thought that using a workflow with its foreach –parallel construct would be a good way to test AD replication. I found that I got double the results – the foreach seemed to go to each machine twice.

Eventually decided to perform the task sequentially

Get-ADDomainController -Filter * |
foreach {
Get-ADReplicationUpToDatenessVectorTable -Target $($psitem.Hostname) -Partition * |
select Server, LastReplicationSuccess, Partition,
@{N=’Partner'; E={(($_.Partner -split “,”)[1]).Remove(0,3)}}, USnFilter
} | ft –a

There a re a number of cmdlets for working with AD replication that are worth investigating

February 15, 2015  3:27 AM

Typing variables

Richard Siddaway Richard Siddaway Profile: Richard Siddaway
Powershell

I was recently asked a question about typing variables after thinking about it came up with this demonstration.

Create a variable with an integer value

£> $a = 2
£> $a.GetType()

IsPublic IsSerial Name
——– ——– —-
True     True     Int32

AS you would expect – you get an integer type.

If you do this

£> $a = ‘123’
£> $a.GetType()

IsPublic IsSerial Name
——– ——– —-
True     True     String

It changes to a string. Which means you can also do this.
£> $a = ‘gdyegf’
£> $a.GetType()

IsPublic IsSerial Name
——– ——– —-
True     True     String

PowerShell variables will adapt their type to the data they contain.

However if you type the variable:

£> [int32]$b = 2
£> $b.GetType()

IsPublic IsSerial Name
——– ——– —-
True     True     Int32

You start with an integer as expected

If you use a string that can be converted to an integer – that will happen and your type is still an integer.
£> $b = ‘123’
£> $b
123
£> $b.GetType()

IsPublic IsSerial Name
——– ——– —-
True     True     Int32

If you try to put a string in the variable

£> $b = ‘effAG’
Cannot convert value “effAG” to type “System.Int32″. Error: “Input string was not in a correct format.”
At line:1 char:1
+ $b = ‘effAG’
+ ~~~~~~~~~~~~
+ CategoryInfo          : MetadataError: (:) [], ArgumentTransformationMetadataException
+ FullyQualifiedErrorId : RuntimeException

It fails because you can’t convert  ‘effAG’ to an integer.

Untyped PowerShell variables can change their type. If you want to ensure the variable always contains a specific type then force that by typing the variable.


February 14, 2015  5:42 AM

Modifying AD attribute – PO Box

Richard Siddaway Richard Siddaway Profile: Richard Siddaway
Active Directory, Powershell

I was left a question on this post – https://richardspowershellblog.wordpress.com/wp-admin/post.php?post=2762&action=edit

Asking how to modify the PO Box value for a user account.  This attribute can be set on the Address tab of AD Users and Computers but isn’t directly visible through AD Administrative Center.

The first trick is to discover the real name of the attribute. One method is to use the Attribute Editor tab in the GUI tools or you can do this:

£> Get-ADUser -Identity richard -Properties * | select *box*

POBox
—–

This is where life gets interesting because the actual name of the attribute is postOfficeBox.

Still to set the value

£> Get-ADUser -Identity fgreen -Properties * | select Name, POBox

Name                                                        POBox
—-                                                        —–
Fred Green
£> Get-ADUser -Identity fgreen -Properties * | Set-ADUser -POBox ‘MX234′
£> Get-ADUser -Identity fgreen -Properties * | select Name, POBox

Name                                                        POBox
—-                                                        —–
Fred Green                                                  MX234

Set-ADuser has a parameter specifically for that attibute

If you need to set the PO Box as part of a bulk change follow the style given in the post at the top of this article and add the –POBox parameter to Set-ADuser


February 10, 2015  1:36 PM

Starting VMs based on WSUS state

Richard Siddaway Richard Siddaway Profile: Richard Siddaway
Hyper-V, Powershell, WSUS

I use WSUS in my lab to update machines – means I only have to download updates once. The issue is that I have to start the VMs so they can communicate with the WSUS server. WSUS will start to flag warnings if machines haven’t contacted the WSUS server for more than that length of time.

Putting that together I can use the UpdateServices (WSUS) module and the Hyper-V module to start machines that have been turn off more than 7 days.

$date = (Get-Date).AddDays(-7)
Get-WsusComputer -NameIncludes ‘W12R2′ |
where LastReportedStatusTime -lt $date |
foreach {
$psitem.FullDomainName
$name = ($psitem.FullDomainName -split “\.”)[0]

$vm = Get-VM -Name $name -ComputerName server02

if ($vm.State -eq ‘Off’) {

Start-VM -Name $name -ComputerName server02

Start-Sleep -Seconds 30
}
}

Set a date 7 days in the past.

Use get-WsusComputer to pull the machine names. I filter on W12R2 (all my machine names contain an abbreviated OS type) and check the last status report time.

Any machines that haven’t reported – the VM data is retrieved and if the machine isn’t running it’s started. Machines take about 25-30 seconds to boot on my lab so a sleep of 30 seconds means I’m not starting them all at once.


February 9, 2015  12:21 PM

Error when AD object not found

Richard Siddaway Richard Siddaway Profile: Richard Siddaway
Active Directory, Powershell

This one popes up quite frequently

If you attempt to access an Active Directory object that doesn’t exist you get an error.

£> Get-ADUser Richardp
Get-ADUser : Cannot find an object with identity: ‘Richardp’ under: ‘DC=Manticore,DC=org’.
At line:1 char:1
+ Get-ADUser Richardp
+ ~~~~~~~~~~~~~~~~~~~
+ CategoryInfo          : ObjectNotFound: (Richardp:ADUser) [Get-ADUser], ADIdentityNotFoundException
+ FullyQualifiedErrorId : ActiveDirectoryCmdlet:Microsoft.ActiveDirectory.Management.ADIdentityNotFoundException,M
icrosoft.ActiveDirectory.Management.Commands.GetADUser

Just using the Erroraction option isn’t going to help

£> Get-ADUser Richardp -ErrorAction SilentlyContinue
Get-ADUser : Cannot find an object with identity: ‘Richardp’ under: ‘DC=Manticore,DC=org’.
At line:1 char:1
+ Get-ADUser Richardp -ErrorAction SilentlyContinue
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo          : ObjectNotFound: (Richardp:ADUser) [Get-ADUser], ADIdentityNotFoundException
+ FullyQualifiedErrorId : ActiveDirectoryCmdlet:Microsoft.ActiveDirectory.Management.ADIdentityNotFoundException,M
icrosoft.ActiveDirectory.Management.Commands.GetADUser

You could do something like this

$ErrorActionPreference = ‘SilentlyContinue’
Get-ADUser Richardp
$ErrorActionPreference = $pref

But a better approach is to use a try-catch block to solve the problem

£> try {
Get-ADUser Richardp -ErrorAction Stop
}
catch {
Write-Warning “User Not Found”
}
WARNING: User Not Found

Instead of issuing the warning perform some other action as appropriate


February 7, 2015  9:39 AM

Stages of panic

Richard Siddaway Richard Siddaway Profile: Richard Siddaway
Powershell

You’re working on a new script and something’s not working properly and you can’t figure it out.

Panic level 1 is when you start using Get-Command and Get-member on everything in sight trying to figure out what’s wrong with the objects

Panic level 2 is when you realise you have to dig through the PowerShell help files. You have used Update-Help recently haven’t you.

Panic level 3 is when you start pulling your PowerShell books off the shelf and scanning the indexes. You have copies of PowerShell in Depth and PowerShell in Action handy don’t you.

Panic level 4 is when you’re resorting to forum searches in the hope someone else has had this problem.

Panic level 5 sets in when the only thing you can find on the Internet is a blog post you wrote 3 years ago which vaguely touches on your problem.

Now you’ve got to start again analysing the code from the top.

Congratulations – if you’ve reached this stage you’re a PowerShell expert!


February 6, 2015  2:17 PM

Workflow for last logon

Richard Siddaway Richard Siddaway Profile: Richard Siddaway
Active Directory, Powershell

PowerShell Workflows have been a bit of an underachiever in terms of their adoption and use.

Something I read prompted the thought that there are a number of activities when working with Active Directory where you have to interrogate a number of machines to get the result.

That’s something that workflows are good at.

A common problem is when did that user last logon. Using the Microsoft cmdlets you get this:

£> Get-ADUser -Identity Richard -Properties * -Server server02  | fl lastlogon* lastLogon          : 130677233108205070
LastLogonDate      : 30/01/2015 14:05:58
lastLogonTimestamp : 130671003582718505

£> Get-ADUser -Identity Richard -Properties * -Server W12R2SCDC01  | fl lastlogon*
lastLogon          : 130671225126188854
LastLogonDate      : 30/01/2015 14:05:58
lastLogonTimestamp : 130671003582718505

Lastlogontimestamp is the identical across the 2 DCs because it’s replicated – however its not always up to date. It can be up to 14 days out.

Now the lastlogon and lastlogontimestamp are not in a readable format – so need a bit of work on that

$dc = “server02″

Get-ADUser -Identity Richard -Properties lastLogon, LastLogonDate, lastLogonTimestamp -Server $dc  |
select Name, LastLogondate,
@{N=’LastLogon'; E={[datetime]::FromFileTime([int64]::Parse($_.lastlogon))}},
@{N=’LastLogonTimestamp'; E={[datetime]::FromFileTime([int64]::Parse($_.lastlogonTimestamp))}},
@{N= ‘Domain Controller'; E={$dc}}
Name               : Richard
LastLogondate      : 30/01/2015 14:05:58
LastLogon          : 06/02/2015 19:08:30
LastLogonTimestamp : 30/01/2015 14:05:58
Domain Controller  : server02

You can see that the lastlogondate (Introduced with Windows 2008 R2) is the lastlogontimestamp in a readable format.  Notoce the difference between the lastlogon and the lastlogontimestamp.

The calculation to get the date is relatively straightforward. Start by converting the property to a int64

[int64]::Parse($_.lastlogonTimestamp)

Then treat that as a filetime and convert to a datetime

[datetime]::FromFileTime([int64]::Parse($_.lastlogonTimestamp))

Now lets look at getting that data from multiple machines in parallel

workflow get-lastlogon {
param (
[string[]]$computername
)

foreach -parallel ($dc in $computername) {
Get-ADUser -Identity Richard -Properties lastLogon, LastLogonDate, lastLogonTimestamp -Server $dc  |
Select-Object -Property Name, LastLogondate,
@{N=’LastLogon'; E={[datetime]::FromFileTime([int64]::Parse($_.lastlogon))}},
@{N=’LastLogonTimestamp'; E={[datetime]::FromFileTime([int64]::Parse($_.lastlogonTimestamp))}} |
Add-Member -MemberType NoteProperty -Name ‘DomainController’ -Value $dc -PassThru
}
}

get-lastlogon -computername (Get-ADDomainController -Filter * | select -ExpandProperty Name)

The workflow accepts an array of computer names. A foreach –parallel  loop is use do run the Get-Aduser command.

One oddity was setting the Domain Controller name into the output object. It wouldn’t accept the value when I used a select calculated field but I could use Add-Member.

if I figure out what’s going on I post the  information

 


February 5, 2015  9:57 AM

Scripting Guy CDXML series finished

Richard Siddaway Richard Siddaway Profile: Richard Siddaway
CIM, Powershell, WMI

My CDXML series on the Scripting Guy blog finished today.  The 4 articles are:

http://blogs.technet.com/b/heyscriptingguy/archive/2015/02/02/registry-cmdlets-manage-the-registry.aspx

http://blogs.technet.com/b/heyscriptingguy/archive/2015/02/03/registry-cmdlets-first-steps-with-cdxml.aspx

http://blogs.technet.com/b/heyscriptingguy/archive/2015/02/04/registry-cmdlets-advanced-cdxml.aspx

http://blogs.technet.com/b/heyscriptingguy/archive/2015/02/05/registry-cmdlets-using-advanced-cdxml.aspx


February 2, 2015  11:41 AM

Scripting Guy CDXML series

Richard Siddaway Richard Siddaway Profile: Richard Siddaway
CIM, Powershell

Today starts a four part series I’ve written for the Scripting Guy blog on using CDXML to create a module to work with the registry.  Don’t know what CDXML is – you will when you’ve read the series

The first post is at http://blogs.technet.com/b/heyscriptingguy/archive/2015/02/02/registry-cmdlets-manage-the-registry.aspx


January 31, 2015  7:54 AM

Modifying text

Richard Siddaway Richard Siddaway Profile: Richard Siddaway
Powershell

I needed to modify some text somewhere in a file. The file looks like this

##  start file
This is some text.

I want to change something.

But not this.
##  end file

I was playing around with various options.  The simplest I found was this:

£> $txt = Get-Content .\test.txt
£> $txt = $txt.Replace(“I want”, “I need”)
£> Set-Content -Value $txt -Path C:\Test\test.txt
£> Get-Content .\test.txt
##  start file
This is some text.

I need to change something.

But not this.
##  end file

You could simplify to

£> $txt = (Get-Content .\test.txt).Replace(“I want”, “I need”)
£> Set-Content -Value $txt -Path C:\Test\test.txt -PassThru

##  start file
This is some text.

I need to change something.

But not this.
##  end file

The passthru parameter displays the file contents you’ve set.

Or if you are a fan of convoluted one liners

£> Set-Content -Value ((Get-Content .\test.txt).Replace(“I want”, “I need”)) -Path C:\Test\test.txt -PassThru
##  start file
This is some text.

I need to change something.

But not this.
##  end file

If you take this approach just make sure your text is uniquely identified otherwise you may change more than you thought.


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: