Archive for May, 2009

Get Chitika eMiniMalls

I had a pre-release (dare I say “beta”) of this video, and some have shared the links already, but I’ve completed the 10 minute epic that is called “Quest For Password Management.”  The video is rendering and uploading now, and should be up by 11:30 AM GMT.  Its actually a video showing 3 of Quest’s Identity Management products; Quest Password Manager, Quest Defender and Quest Webthority.  Note that Webthority is now part of Defender, and we’re sure to change the name in the future.

Anyway, the recording can be found here:

http://www.idmwizard.com/quest/QuestForPasswordManagement/QuestForPasswordManagement.html

Techie sidenote: The thing that took the longest? Setting up a CA (Certificate Authority).  Now, you’re probably going to ask yourself, “why on earth do I need a CA to do password management?”  And the answer is “you don’t.”  However, if you wish to perform an AD password change, its best to do it over LDAP-S (port 636) rather than plain LDAP (port 389).  And in trying to set this up in Webthority, I swore I found a bug (yes, even Quest products have bugs from time to time).  But it turns out that I just wasn’t configuring the CA properly, and it took our fine support people to point out that I simply didn’t know what I was doing.  In fact, I’m told there will be a support KB article on the topic shortly.  I’ll keep you posted.

Back when I was a dev manager, a site appeared that really intrigued me: Rent A Coder.  I’ve been keeping tabs on for a while now, and finally joined up.  I’m actually surprised its not taken on more prominence, but I do see quite a bit of activity on it.  The most interesting stat is that they have plateaued to having on 5-6% new buyers, and 94%+ of the work is done for repeat customers.

Quick primer on how RAC works: Buyers post up requirements for a project, and developers bid on them.  Once a bid is accepted, the payment for the project is put into an escrow account, and the developer is paid from the account once the project is delivered and tested.  RAC acts as the third party to both buyers and sellers, taking a 15% fee for their services.  Its kind of like ebay for dev projects.

Anyway, I really think this sort of thing is not used enough.  Just the work I do, day to day, mirrors this sort of task-oriented approach.  And if you have a solid enough set of requirements, you should be able to farm out discrete ‘units’ of work and just piece them together.

(note: funny that this post languished in my Drafts for over a month – I really need to check this more often)

Get Chitika eMiniMalls

Still plodding through the GPO scripting through Quest Group Policy Manager given all the other things I have going on but I cranked this out last week quickly to meet a quick checklist item. As the comments in the code say, “In an ideal world, this would be a cmdlet called Delete-QGPO. “  The only change you need to make is to change the $useParams value to $true to start using it like a cmdlet and feed it the params specified.  As with all things PowerShell, the intent is to call this on 1 object (a GPO) and if you want to use it on a set of objects, get that list, and pipe it in, like so:

import-csv deletelist.csv | % {& Delete-QGPO.ps1 $_.GPOName $_.GPMServer $_.ApprovalRequired}

That is it.  Now for all the code . . .

#############################################################################################################
#
# In an ideal world, this would be a cmdlet called:
#    Delete-QGPO GPOName [-GPMServer] [-GPMPort] [-ApprovalRequired]
#
#############################################################################################################
Set-ExecutionPolicy Unrestricted;
#############################################################################################################
# the next section is all hard coded variables which need to be set to script parameters
#############################################################################################################
$useParams = $false;
if ($useParams)
{ 
 # define the GPO name, which is what people will probably know it as – this can be an argument to a script later
 $gpoName = $args[0];
 # Which GPM Server to export from
 $GPMHostname = $args[1];
 $GPMPort = $args[2];
 
 $ApprovalRequired = $args[3];
}
else
{
 # Which GPM Server to export from
 $GPMHostname = "localhost";
 
 $GPMPort = 40200;
 
 # define the GPO name, which is what people will probably know it as – this can be an argument to a script later
 $gpoName = "Test Policy";
 
 $ApprovalRequired = "no";
}
#############################################################################################################
& 'C:\Program Files\Quest Software\Quest Group Policy Manager\QGPMInit.ps1' -computerName $GPMHostname
$foundGPO = $false ;
# loop through all the objects in the data set and find the policy we want
foreach($currentGPO in $VCManager.GetControlledObjects("GPO") |
      Where-Object {$_.Name -eq $gpoName})
{
 $foundGPO = $true;
 
 # count the number of deployed versions
 $counter1 = 0;
 
 $DeleteGPO = $false;
 
 # check out the GPO so we can edit it
 # you can discard the contents returned since we want a previous version
 $VCManager.Delete($currentGPO.VCId, "Deleting GPO - bye bye");
 Write-Output "Requesting approval to delete GPO $gpoName";
 if ($ApprovalRequired.ToUpper().Substring(0,1) -eq "N")
 {
  $VCManager.Approve($currentGPO.VCId, "Requesting Approval");
  $VCManager.Deploy($currentGPO.VCId, "Deploying (actually deleting) GPO")
  Write-Output "GPO $gpoName deleted";
 }
}
if ($foundGPO -eq $false)
{
 Write-Output "GPO $gpoName not found"
}

I also put together a doc this week that was off the beaten path.  And what was different was not the content but the presentation.  It was a 4-5 page doc, but the guy reading it doesn’t tolerate emails that go ‘below the fold.’  So right after the short summary/intro was a link that said ‘Click here to go to the conclusion.’  So our 30 minute chat had him read the intro, conclusion, and then look at the details last.

My Quest GPM/PowerShell/PowerGUI grind continues – I’ve gotten the export working fully, and its pretty well designed.  I’m determined to make each script into a usable set of cmdlets.  When I finish the entire project, I’ll post the final code here.  Right now, everything revolves around Quest’s GPM, but there is some pretty involved code which could be re-purposed for other tasks (perhaps even straight AD GPO manipulation).

But what I haven’t really considered until early this week are the actual requirements.  And then, that same consideration needed to be made by the PM and the Dev guys helping me.  And that’s because the requirements were not being observed in the original pass of what was developed. Ian D (my “customer” inside Quest) actually did something I hadn’t done in quite a while – he made me review the dev requirements before we did anything else on Tuesday.

And those requirements, simply put, was that the user getting the set of imported files should be able to run a single script/executable and all the ‘magic’ happens unattended.  No intervention should be needed, and if there’s a problem, the user is not going to be able to help.  So just dump a log file, and let the user send the log back to ‘the mother ship.’  Even if the user is able to help, he will have little to no rights in AD.

That one rule (that the user will have NO rights in AD) caused me to restructure and rethink some things on Tuesday.  I’m now 80% done, and hope to have something in a deliverable draft on Monday afternoon.  And I’ve taken on board what Ian did to get me focused on Tuesday – I wrote out the requirements and have them in front of me as I’m coding.  I forgot how easy it is to lose focus and ‘go dark’ if you don’t keep the goal in sight.

Lately, I’ve noticed an annoying trend by vendors.  They are starting to put their content onto USB sticks, instead of CDs.  Now, on the surface, this sounds really good.  There’s less waste, since a USB stick is reusable, whereas a CD is usually obsolete within a month or two.  However, vendors are taking a chunk of the stick, and permanently putting their content on.  So on a 512 MB stick, less than 100 MB is taken up by some demo, along with a bunch of PDFs, and a whole partition exists just for that content.  However, the 100 MB is permanent, and you cannot get it back by formatting the entire stick – I’ve tried. 

I’ve got a perfectly good 512 MB stick that has 40 MB of a partition from 2007.  There is nothing interesting on that partition now, and I can get much newer content off the vendor’s website, but it will remain there forever.  I can understand wanting to make sure the client gets the content, but after some time, with stale content, why not give the client the ability to get the entire USB stick back?  And I bet it would be cheaper to produce the sticks, too, instead of paying for the extra partitioning and autorun software.

So I am now getting further into the GPM script, and am writing a script that could ultimately become a cmdlet.  This one exports out a GPO and its associated links.  Next week, I’ll have the complementary import posted.  Everything is hard-coded, but you can see how the script would be parameterised.

———————-

#############################################################################################################
#
# In an ideal world, this would be a cmdlet called:
#    Export-QGPO GPOName [-FilePath] [-DomainName] [-GPMServer] [-GPMPort] [-IncludeLinks] [-PreviousVersion]
#
#############################################################################################################
Set-ExecutionPolicy Unrestricted;
#############################################################################################################
# the next section is all hard coded variables which need to be set to script parameters
#############################################################################################################
# define the GPO name, which is what people will probably know it as – this can be an argument to a script later
$gpoName = "VAS Policy";
# location where to put the exported GPOs
$backupPath = "C:GPMScriptsscratch";
# how far back to go - 1 is the last deployed version
# note: this is changed from previous version which got the live GPO - this is by design
$previousVersionCount = 1;
# Which GPM Server to export from
$GPMHostname = "localhost";
# specify whether links ought to be exported along with the GPO itself
$IncludeLinks = $true;
# the name of the current domain - should be pulled from GPM probably
$CurrentDomain = "quest.local";
#############################################################################################################
& 'C:Program FilesQuest SoftwareQuest Group Policy ManagerQGPMInit.ps1' -computerName $GPMHostname
$foundGPO = $false ;
# loop through all the objects in the data set and find the policy we want
foreach($currentGPO in $VCManager.GetControlledObjects("GPO") |
      Where-Object {$_.Name -eq $gpoName})
{
      $foundGPO = $true;
     
      # count the number of deployed versions
      $counter1 = 0;
      $exportSuccess = $false;
      # now start rolling through history - note: the array brought back by getHistory is unsorted
      # so we need to sort it, and find the first 'Deploy' version
      foreach ($action in $VCManager.GetHistory($currentGPO.VCId) | Sort-Object -Descending Version)
      {
            # pull back only deployed objects, since we need to go 1 back
            # this should probably be deployed or registered GPOs -
            # someone else can put in the additional check
            if ($action.Type -eq "Deploy")
            {
                  $counter1 += 1;
                 
                  # 1 is really the last deployed version - which is what probably ought to be the default
                  if ($counter1 -eq $previousVersionCount)
                  {
       # Retrieve a backup from version control
                    $GPOBackup = $VCManager.GetBackup( $currentGPO.VCId, $action.BackupId);
     # if we got back something valid, start the export
     if( $null -ne $GPOBackup)
        {
      $fileName = $gpoName + ".zip";
      [System.IO.File]::WriteAllBytes( [System.IO.Path]::Combine( $backupPath, $fileName ), $GPOBackup.Bytes );
      # go into this section if you want to export links at the same time as the GPO
      if ($IncludeLinks)
      {
       # get a collection of all GPOLinks
       $currentGPOLinks = $VCManager.GetGpoLinks($CurrentDomain,$currentGPO.Id);
       
       [System.IO.StreamWriter] $LinkFile;
       
       $LinkFile = [System.IO.File]::CreateText([System.IO.Path]::Combine( $backupPath, $gpoName + " Links.xml"));
       foreach ($currentLink in $currentGPOLinks | Sort-Object -Descending LinkOrder )
       {
        $LinkFile.WriteLine("<GPOLink>");
        $LinkFile.WriteLine("  <SOMPath>" + $currentLink.SOMPath + "</SOMPath>");
        $LinkFile.WriteLine("  <LinkOrder>" + $currentLink.LinkOrder + "</LinkOrder>");
        $LinkFile.WriteLine("  <Enabled>" + $currentLink.Enabled + "</Enabled>");
        $LinkFile.WriteLine("  <Enforced>" + $currentLink.Enforced + "</Enforced>");
        $LinkFile.WriteLine("</GPOLink>");
       }
       $LinkFile.Close();
       $LinkFile.Dispose();
      }
     }
     
     $exportSuccess = $true;
                  }
            }
   # should probably break out of the loop here
      }
}