Your basic ITPro blog... What's going on at work, what I'm interested in.

Sunday, September 28, 2008

Ummmm.... I Don't Think so.

At least they asked nicely, though...

image

Thursday, September 18, 2008

A More Elegant Solution...

I have blogged before about our new file server and the folder structure we are using. I wrote about the automation I wrote for creating new shared folders, and for deleting them.

I have since made some changes/updates to these scripts. The biggest issue I had to tackle was a problem that cropped up one evening... I was at home and had to set up some shared folders. So, I VPN'ed in to our network and ran my 'Create Folders' Powershell script.

The script created the AD security groups, created the folder, and then promptly failed while trying to apply the security settings to the folder. After some research, I determined that the security groups were created on one DC and the security settings were trying to be applied via another DC. The new security groups had not yet propagated to the second DC, so it was failing. My solution, check all of my DCs to make sure they knew about the new security groups. My first go-around at this (which works fine), was:

function wait_for_replication {
    param($target = "<server>",
fqdn = "<AD Domain>", $ou = $GroupContainer, $remove = $TRUE, [switch]$table ) Write-Host "Checking replication of security groups..." $context = new-object System.DirectoryServices.ActiveDirectory.DirectoryContext("Domain",$fqdn) $dclist = [System.DirectoryServices.ActiveDirectory.DomainController]::findall($context) if($table) { $DCTable = @() $myobj = "" | select Name,Time $myobj.Name = ("$target [SOURCE]").ToUpper() $myobj.Time = 0.00 $DCTable += $myobj } $dn = "CN=" + $ROgroup + "," + $GroupContainer $start = Get-Date $i = 0 $cont = $TRUE while($cont) { $i++ $oldpos = $Host.UI.RawUI.CursorPosition Write-Host " =========== Check $i ===========" -fore white start-Sleep 1 $replicated = $TRUE foreach($dc in $dclist) { if($target -match $dc.Name){continue} $object = [ADSI]"LDAP://$($dc.Name)/$dn" if($object.name) { Write-Host " - $($dc.Name.ToUpper()) Has Object [$dn]" (" "*5) -fore Green if($table -and !($dctable | ?{$_.Name -match $dc.Name})) { $myobj = "" | Select-Object Name,Time $myobj.Name = ($dc.Name).ToUpper() $myobj.Time = ("{0:n2}" -f ((Get-Date)-$start).TotalSeconds) $dctable += $myobj } } else {Write-Host " ! $($dc.Name.ToUpper()) Missing Object [$dn]" -fore Red;$replicated = $FALSE} } if($replicated){$cont = $FALSE}else{$Host.UI.RawUI.CursorPosition = $oldpos} } $end = Get-Date $duration = "{0:n2}" -f ($end.Subtract($start).TotalSeconds) Write-Host "`n Took $duration Seconds `n" -fore Yellow if($table){$dctable | Sort-Object Time | Format-Table -auto} Write-Host "Moving on!" }

I copied a bulk of this from someone on the web (citation unavailable). It works well and keeps my script from failing. But then, while working with the AWESOME Quest AD CMDlets, I thought I would try to do the same thing with them. After some tinkering, I came up with:

function wait_for_replication 
{
    $DCs = Get-QADComputer -ComputerRole DomainController
    foreach ($DC in $DCs)
    {
        $aa = $null
        $bb = $null
        do
        {
            Write-Host "Checking for Security Group on $($DC.Name)..."
            $null = Connect-QADService -Service $DC.Name
            $aa = Get-QADGroup $MODgroup
            $bb = Get-QADGroup $ROgroup
            Disconnect-QADService 
            Start-Sleep -Seconds 1
        }
        until
        (
            $aa -ne $null -and $bb -ne $null
        )
    }
}

I like this a lot better. I am sure it can be further optimized. And, I look at the rest of this script and see changes I can make to improve things. But, I am making progress. As I continue to learn more about Powershell, I can re-visit my scripts and see what tuning I can do.

Monday, September 15, 2008

Automating Printer Configuration

I have been asked about this numerous times as part of my desire to automate our account/computer set-up and tear-down procedures. Every time we configure a computer, we have to install printers. This involves creating the Standard TCP/IP Port and installing the printer itself, including drivers. We may be doing this for three or more printers. This task get tedious when doing it through the GUI. So, I have finally began working on how to help automate this job...

  1. First things first. Printer port configuration is stored in the registry.  You find it at: [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Print\Monitors\Standard TCP/IP Port\Ports\<PortName>]. Export the key for the port you need. This will be merged on our new install later.
  2. Next, we use the printui.dll to install the printer and its drivers. There are two main commands here... First, install the printer. Second, import the configuration settings that were exported from an already-configured computer.
  3. A couple of restarts to the spooler service, and we are ready to go!

From here, I created a .CDM file that performs the following steps:

  1. Import .reg file with printer port configuration
  2. Stop/Start Spooler service
  3. Install printer, including driver
  4. Import printer configuration
  5. Stop/Start Spooler service

Here's the actual code in one of my CMD files:

regedit /s z:\canonC3200PrinterPort.reg

net stop spooler
net start spooler

start /wait rundll32 printui.dll,PrintUIEntry /if /b "CanonC3200" /f "\\<UNC_Path_To_INF_File>\oemsetup.inf" /r "canon3200" /m "Canon iR C3200-C1 PS Ver1.0" /z

start /wait rundll32 printui.dll,PrintUIEntry /Sr /n "CanonC3200" /a "Z:\canonc3200.dat"

net stop spooler
net start spooler

Hopefully, this will make printer setup a bit easier in the future.

*Some of the resources I used to figure all of this out:

Friday, September 12, 2008

.MemberOf Does Not List the Primary Group

I often use the following command (or something similar) in PowerShell...

(Get-QADUser <name>).MemberOf

NOTE: You must have the Quest AD CMDlets installed. But, of course, if you are using Powershell, you do have them installed! Don't you?!

This simple command will list the groups that a particular AD account is a member of. But, there is one problem... it does not list the Primary Group for the user. At first, I thought this was a problem with the Quest CMDlets (I should have known better!). But, it turns out that this is 'inherent in the system', as it were.

Normally, this isn't a problem, because 'Domain Users' is most often a user's Primary Group. But, there is no guarantee that this is the case.

This problem cropped up when I was working on a script that tested group membership before performing some task. I was getting some odd results. It turned out that, for one user in my test, the group I was testing for was the Primary Group (rather than Domain Users), so it did not show up on his group membership list. Interestingly, the Primary Group setting only has significance for Mac or POSIX-compliant clients. Not sure why it was changed...

My first thought was to modify my group member query to include some additional search for the Primary Group. But then I thought, just change everyone's Primary Group to 'Domain Users'. Easier said that done...

I found this blog post that talked about doing this, but I kept running in to "PrimaryGroupId is ReadOnly" errors. I am guessing that this is some permissions-related issue... But, I didn't want to mess with it. I knew I didn't have too many accounts like this, so I figured if I can identify them easily, then I will make the change manually.

To that end, I came up with this quick little ditty that lists user accounts where "Domain Users" is not the Primary Group:

# PrimaryGroupID for 'Domain Users' = 513
$users = Get-QADUser
foreach ($user in $users) 
{
    if ($user.PrimaryGroupId -ne 513) 
    {
        Write-Host $user.DisplayName " : " $user.DN
    }
}

# As a one-liner
Get-QADUser | %{if($_.PrimaryGroupId -ne 513){$_.DN}}

So, this gives me a list. I then go in to ADUC and make the change. Not ideal, but quick and functional for me.

Wednesday, September 10, 2008

Outlook (Office 12) Crashing

Taking some notes for myself here...

Recently - the past week or two - Outlook has been crashing a few times a day. I was seeing the following in my event logs:

image

It was always Event ID 1000 and 1001. I researching things a bit and came across the following items that seemed promising...

Installed MS Visual C++ Redist Package (didn't help):  http://help.wugnet.com/office/XP-pro-Outlook-07-crash-ftopict1068797.html

Removed Add-Ins (This thread got me thinking about add-ins):  http://www.archivum.info/microsoft.public.outlook.general/2008-07/msg06184.html)

As noted, I had a crash after installing the VC++ Redist. So, I checked the add-ins in Outlook and removed/disabled a couple that I did not want/need in there. No more crashes so far today.

Lesson: Uninstalls don't always uninstall everything. And, remnants can cause problems!

Saturday, September 6, 2008

File Migration is Complete

Just a quick update, we have officially completed our migration off of our old file server and on to our new, virtualized solution. I turned our old file server off this week. We will be re-tasking this box, deploying it to our Worship Arts ministry for them to use as a 'bit bucket' of sorts. It will be theirs to use, and manage, as they see fit.

As for our new file storage solution, I think it is working quite well. I have blogged numerous times in the past about the various aspects of this change. Along the way, I have built a suite of tools for managing our new file server. I wrote a Powershell script to create new shared areas, another script to reset security on existing shared resources, and started a library of tools for reporting purposes.

Over all, I think this project has gone well. While it is, in a very real sense, never-ending, I do feel like we have reached a point where we can call this job "done". The old server is off and the new solution is purring along.

Additional Info

My photo
email: support (AT) mangrumtech (DOT) com
mobile: 480-270-4332