Archive for the ‘Powershell’ Category

Powershell: Update Active Directory Permissions

September 1, 2009

If you’re new to manipulating Active Directory in Powershell and you want to answer a question like ‘How do I give domain user prod\bruce write access to AD object LDAP://adserver.prod.com/cn=MegaServer, cn=Computers, cn=prod, cn=net’, then this brilliant article by Richard Siddaway ‘Windows Server 2008 Protection from Accidental Deletion’ is just what you need.

I’m just linking it here because its title makes it a bit difficult to find and it deserves more exposure.

Here’s the guts of it

$ou = [ADSI]"LDAP://ou=Thunderbirds,dc=starking,dc=org"

$sec = $ou.psbase.ObjectSecurity

## set the rights and control type

$act = [System.Security.AccessControl.AccessControlType]::Deny

$adrights = [System.DirectoryServices.ActiveDirectoryRights]::Delete

$adrights2 = [System.DirectoryServices.ActiveDirectoryRights]::DeleteTree

## who does this apply to

$who = New-Object -TypeName System.Security.Principal.NTAccount -ArgumentList "", "prod\bruce"

# stop delete

$newrule1 = New-Object -TypeName System.DirectoryServices.ActiveDirectoryAccessRule -ArgumentList $who, $adrights, $act

$sec.AddAccessRule($newrule1)

$ou.psbase.CommitChanges()

# stop deletetree

$newrule2 = New-Object -TypeName System.DirectoryServices.ActiveDirectoryAccessRule -ArgumentList $who, $adrights2, $act

$sec.AddAccessRule($newrule2)

$ou.psbase.CommitChanges()

Powershell: Updating a MultiValued Active Directory Property

August 30, 2009

This one took me a little big of digging around for because the typical scenario for updating a multi-valued AD property is that a SysAdmin is adding a new email address to a list of email addresses in some property i.e. an Append Operation.

Whereas, what I wanted to do was overwrite one of the values in a multivalued property with an updated value i.e. an Update operation.

First thing you need to know is to use PutEx instead of Put when updating Multi-valued properties. Most simple examples on the Greater Google are about updating single-valued properties – integers or strings or whatever. These are updated using Put. MultiValued properties use PutEx.

Second thing is that you need to use the methods for the correct AD Provider and not to mix them up. Where you are using PutEx, then you commit that change to the actual AD database server using SetInfo() instead of CommitChanges(). I’ll get some more detail on that in here before long.

Next thing you need to realise is that the type of a MultiValued AD property in .NET is PropertyValueCollection, but that the PutEx method takes an Array Of Object (i.e. Object[]) as its parameter. You have to convert from PropertyValueCollection to Array. Fortunately that’s easy.

Finally, and obviously, you need to make sure that the account you are using to bind to Active Directory has Write Privileges to the entry you’re trying to update, otherwise you will get AD’s equivalent of the Blue Screen Of Death, ‘General Access Denied Error’.

To get started, here’s a great page on updating a MuliValued AD property with an Append from The Scripting Guy on TechNet. Digest this and you’ll know most of what you need to know.

Now, here’s how I did my Update operation on the Multivalued property. Probably the code is a bit clunky but it works and we can refine it a bit later. Hopefully the comments in the code provide enough explanation.

The objective is to search for the key ‘Fruit’ in the MultiValued property ‘keywords’ and update its value from ‘Banana’ to ‘Kumquat’

# The account Place\myaccount must have write privs to the LDAP Path specified.
$entry = new-object DirectoryServices.DirectoryEntry (“LDAP://MyLDAPServer/dc=place, dc=room, dc=net”, “place\myaccount”, “mypassword”)
$keywords = $entry.Keywords
# Convert keywords propertyValueCollection to a fixed-length array
# because the DirectoryEntry update function takes an Array, not
# a PropertyValueCollection as a parameter
$length = $keywords.Count
[Array]$keywordsArray = 1..$length
# This CopyTo moves the propertyValueCollection to an array.
$entry.Keywords.CopyTo($keywordsArray, 0)

# Search for ‘Fruit’ entry in keywords
$foundKey = $false
for($i=0; $i -lt $keywordsArray.Length; $i++)
{
$keyword = $keywordsArray[$i]
Write-Debug “$keyword”
if ($keywordsArray[$i].ToLower().Contains(“fruit”))
{
$foundKey = $true
# Retain index of Fruit property so it can be updated below
$fruitIdx = $i
break
}
}

#Check if we found Fruit Entry
if ($foundFruit -eq $false)
{
throw (“Did not find Fruit keyword in Active Directory object”)
}

# Extract Fruit Value from KeywordValue pair
# Fruit entry is “Fruit:
$fruitParts = $keyword.Split(“:”)
Write-Debug “Fruit is $fruitParts[1]”

# Change “Fruit” key. Update the value to ‘Kumquat’
$newFruitEntry = $fruitParts[0] + “Kumquat”
Write-Debug “Updated keyword entry is $newFruitEntry”
$keywordsArray[$FruitIdx] = $newFruitKey

# Update DirectoryEntry
# PutEx, instead of Put required for multivalued AD entries such as keyword
# First parameter denotes type of PutEx i.e. Append, Update or Delete In our case its an Update.
$ADS_PROPERTY_UPDATE = 2
$entry.PutEx($ADS_PROPERTY_UPDATE, “Keywords”, $keywordsArray)
Write-Debug “committing AD Change…”
# Commit to AD database on server
$entry.SetInfo()
Write-Debug “committed”

Powershell: WMI Error. RPC Server Unavailable in Xen AppServer Farm

July 29, 2009

I was getting a very puzzling RPC Server Unavailable error executing a WMI call in Powershell. The call itself was as something as simple as this:

$fruitbat = get-wmiobject -class Win32_OperatingSystem -computer BRUCE

I checked various things:
BRUCE was indeed up (online) had the RPC and WMI Services running, Windows Firewall was off, had a functioning Network Adapter and IP Address. Everything looked good.

Since BRUCE lives in a XenApp Farm I decided to make sure that the Farm was configured correctly and guess what there were some Discovery Errors when I looked inside the Citrix Access Management Console.

I ran ‘Configure Discovery’ from the Access Management Console and it completed but I was still getting RPC errors.

So, out of sheer desperation I recreated my Xen AppServer Farm.
I ran chfarm on BRUCE and recreated MyTestFarm.
Then I did a chfarm on all the other servers and re-joined them to BRUCE and MyTestFarm.

And all was well. Once again the Emergency Sacrificial Ferret was returned to his cage and started munching on those Chinese Peanuts roasted in Coconut Milk he likes so much.

So, if you’re getting RPC Server Unavailable in a Citrix Xen AppServer Farm, you’ve checked everything else and you’re feeling desperate – recreate your Farm. Your Ferret will love you for it.

BUT WAIT
As you can see the success of the WMI call depends on the health of your XenApp Farm. So before recreating the Farm, try some less aggressive fixes than I did. Recreating the entire Farm is like using a Chainsaw to get a crumpet out of your toaster. Try something a little gentler first.

1) Run “Configure Discovery” on ALL your Xen AppServer Farm servers and do that last on your Data Collector so that you know the DC is aware of all the Farm Servers.
2) Check that the Citrix IMA Service is running on all your Farm servers.

You might think of some others.
All the best!

Update: 28-Aug-2009
Here’s a little ripsnorter

Powershell: Cannot run EXE. The term is not recognized as a cmdlet, function, operable program, or script file. Verify the term and try again.

July 29, 2009

What you may not have realised is that you need to dotsource exes as well as .ps1 script files.

In the same way that you must prepend a powershell script with .\ in order to execute it e.g. .\ripper.ps1, you must also prepend exes with .\ if the directory containing the exe is not in your PATH statement.

I was trying to run Microsoft’s devcon utility (devcon.exe) within Powershell from a backwater directory not in my PATH like so:

PS c:\backwater> devcon

Nope. It has to be dotsourced.

This works:

PS c:\backwater> .\devcon

Dotsource your exes or stick the directory in your PATH.

Powershell WMI Access Denied When Retrieving MetaFrame_Server_LoadLevel Object

July 14, 2009

This post is obscure enough for me to think it may never be read.
It was posted July 14, 2009.
Let’s see when the first read happens.

Storming the Bastille of this July 14 problem then, I am using the following escargut-wrenching Powershell WMI command to determine the load on a particular server in my XenApp farm.

$loadlevel = Get-WmiObject -Namespace root\citrix -Class metaframe_server_loadlevel -ComputerName $serverName)

Powershell was denied access. Specifically, I received the following error from the agents of the Sun King Laissez-les manger le gâteau, or in Powershell-ese Access Denied

I will not pretend that I personally debugged this issue, but how you fix it is add the user group ‘Local Administrators’ to the Administrators group of your XenApp Farm in Citrix Access Management Console.

I think what it is, is that the adding of the Access overcomes the Access Denied issue. Or something.

Powershell: Shutdown/Reboot Server using WMI. Privilege Not Held

July 13, 2009

This one only took me a couple of minutes to debug, but it gave me a laugh.

Watcha Doin’ BRUCE ?
I was using the following command from within Powershell to reboot my favourite server, BRUCE

$server = get-wmiobject -computer BRUCE -class Win32_OperatingSystem
$server.reboot()

Powershell told me rack off. Specifically it was chucking an exception with Error: Privilege Not Held. Rude old Powershell.

This error had me briefly puzzled because I KNEW that the Powershell account making the WMI call had admin privileges on BRUCE.

Then I realised that the script itself was running on BRUCE. i.e. I was running a script that was trying to shut down the server on which it was running. This may not be advisable.

So I ran the script on a different server, NARELLE, and tried to reboot BRUCE from there. Surprise, surprise it worked.

In summary, you will get Privilege Not Held from Powershell WMI if you try to reboot a server on which your script/WMI call is executing. Run your script on a different machine and try again.

Other Things To Avoid
– Sawing off a branch while sitting on it.
Smashing yourself over the head with a Fire Extinguisher.

Powershell: Variable Loses Value Outside Of Loop

July 9, 2009

This never happens either.

Here is my code block modified for simplicity. NB In the actual code, “Ampersand” is a real ampersand, not the word “Ampersand”

function LoseIt($fruitBasket, $name)
{
“Ampersand”{
$numKumquats = 0
$fruitList = $fruitBasket.split(“,”)
foreach ($fruit in $fruitList)
{
$fruitname = $fruit + $name
$serv = get-wmiobject Win32_Service | where {$.name -eq $fruitName}
if ($serv.State -eq “Running”)
{
numKumquats += 1
}
write-host “Inside Loop numKumquats is ” $numKumquats
}
}
trap
{
#Exception getting Win32_Service
#scream and cry
}
write-host “Outside Loop numKumquats is ” $numKumquats
if ($numKumquats > 0)
{
MakeKumquatJam $numKumquats
}
}

The thing was, inside the loop $numKumquats was being incremented and printing out the correct values in write-host, but outside the loop, $numKumquats was always null, with the result that the function MakeKumquatJam was never called. Sob.

I “fixed” it by removing the declaration of $numKumquts outside the for loop and moving the call to MakeKumquatJam above the trap. Like so:

function KeepIt($fruitBasket, $name)
{
$numKumquats = 0
“Ampersand”{
$fruitList = $fruitBasket.split(“,”)
foreach ($fruit in $fruitList)
{
$fruitname = $fruit + $name
$serv = get-wmiobject Win32_Service | where {$.name -eq $fruitName}
if ($serv.State -eq “Running”)
{
numKumquats += 1
}
write-host “Inside Loop numKumquats is ” $numKumquats
}
}

write-host “Outside Loop numKumquats is ” $numKumquats
if ($numKumquats > 0)
{
MakeKumquatJam $numKumquats
}

trap
{
#Exception getting Win32_Service
#scream and cry
}
write-host “Outside Loop numKumquats is ” $numKumquats
if ($numKumquats > 0)
{
MakeKumquatJam $numKumquats
}

}

With the above changes the function MakeKumquatJam was called… but it had nothing to do with the for loop.

The real problem was that originally I had actually declared two variables called $numKumquats, each with different scope. The scope of the first $numKumquats was global to the entire function, the scope of the second $numKumquats variable was local to the try/trap block commencing with
“Ampersand”

Originally I thought I had declared ONE instance of the variable $numKumquats which was mysteriously losing its value after the for loop completed. No No No. I had actually declared TWO variables with different scope. What a Powershell noob boner! It would never happen to you, right?

Kumquats. Beware. They make you do stuff.

Powershell: “Failed To Connect To Server SERVERNAME” Remote SQL Server 2005 using SMO

June 18, 2009

That title is a ripper isn’t it ?
It reminds me of this limerick

There once was a young man from Japan
Whose limericks never did scan.
When told this was so,
He said, “Yes, I know.
I think it’s because I try and get as many words into the last line as I possibly can.”

So I was magnificently running the following Powershell fragment:

[System.Reflection.Assembly]::LoadWithPartialName(‘Microsoft.SqlServer.SMO’)
$serverInstance = New-Object(‘Microsoft.SqlServer.Management.SMO.Server’) $MyRemoreDbServer
$MyDatabase = $serverInstance.Databases | where {$_.Name -eq $DbVariableName}

…when one day it doesn’t work, throwing up the luminous red error “Failure To Connect To Server MyDatabase”. I was shocked. Four hours later I was still shocked. It worked on a local connection but not on a remote connection.

Then, another freaking genius of a colleague (different to the last one) came in and said, “Check your Windows Firewall settings”, to which I said “How”, and he showed me.

Which is just as well because Windows Firewall was set to ON and when we set it to OFF my code fragment started working again. Huzzah!

This is the page which had the magic answer on it. I read it but only understood about a quarter of it. I’m sure you will do better. Its called “SQL Server 2005 Remote Connectivity Issue Troubleshoot” and its on a blog called SQL Protocols maintained by the Elders of Zion, Microsoft SQL Server protocols team. Fans of Aztec-Mayan Glyphs will find this page relatively easy to decipher. The repeated references to Windows Firewall didn’t mean anything to me until now.

What you specifically need to do is

1) Enable “Fire and Printer Sharing” in Firewall exception list in Windows Firewall on your remote database server.

2) Add TCP port or sqlservr.exe to Firewall exception list, either add “..\Binn\sqlsevr.exe” or add port.

OR Just turn the Windows Firewall off. If you dare.

I can haz MCSA ?

Powershell: Delete Registry Key On Remote Server

June 16, 2009

Friends Of The Bitten Tadpole:

There wasn’t much in the way of examples on the greater Google for this one. One forum entry even said it was too difficult and suggested using regedit.exe, but in fact it is very easy. Look at the following example:

# Delete the Registry Key given in $deleteKey
Function DeleteRegistryKey($deleteKey)
{
$type = [Microsoft.Win32.RegistryHive]::LocalMachine
$Hive = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey($type, $RemoteMachine)
$Key = $Hive.DeleteSubKey($deleteKey)
}

DeleteRegistryKey “Software\Microsoft\Whatever”

Too easy.

Powershell: Copy File on Remote Computer When Path Has Spaces In It

June 15, 2009

Dear Imaginary Audience,

This one depleted my life force by about 37 mins, so I am figuring it might help some of you others, even though it is simple to the point of trivial.

I have a remote computer called BRUCE:
There is a file called YouLittleRipper.txt contained in the following Path:
C:\Program Files\Microsoft\Mammoth App Folder

To copy YouLittleRipper.txt from another computer NARELLE in Powershell do the following:
– Start Powershell On Narelle
$RipperPath = "\\BRUCE" + "\" + "C$" + "\"Program Files\Microsoft\Mammoth App Folder" + "\"
$Source = $RipperPath + "YouLittleRipper.txt"
$Target = $RipperPath + "OnyaGranny.txt"
copy-item "$Source" "$Target"

The file “OnyaGranny.txt” will now exist on BRUCE in ‘Mammoth App Folder”.

The secret ingredient is to put the quotation marks around the whole path after it has been constructed in the variable $Source.

You owe me 37 mins. Give us it.