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

Monday, December 29, 2008

Re-Installing WS08 and Hyper-V

UPDATE: This blog post would have been really helpful.


A while back I blogged about our first 64bit server, Windows Server 2008, and Hyper-V. Well, it turns out I didn’t really know what I was doing. But, I had been using this box just to learn on, so it wasn’t a big deal. But, we are now looking at putting this server into production. Soooo… It was time to re-do the server. Turns out… I still don’t know what I’m doing…

We only had one VM on this server, and this VM has a limited user-base. So, I was free to do this work in the middle of the day, which was nice. In the end, I wish I had researched this process more before starting. I made too many assumptions and they cost me.

This server connects to our iSCSI SAN and uses a virtual disk for VM file storage. My original thought was; disconnect the virtual disk, re-install Hyper-V, reconnect the virtual disk, VIOLA! But, it didn’t go quite that smoothly.

The re-install was the easy part. I love how simple it is to install Server 2008, and the way they implement Roles and Features is great. I installed the Hyper-V Role and the Windows Powershell Feature. I was then ready to reconnect my virtual disk and get my VM back up and running.

Except that I could not find a way to get Hyper-V to recognize my VM. Now, truth be told, I didn’t look very hard (big mistake!). But all I saw was an Import feature that required a previous Export (which I didn’t do). My goal was to be able to save the snapshots I had. I was not able to figure out a way to do that. So, I ended up just creating a new VM and using the VHD file I had. As I type this, however, I am realizing a significant problem…

I didn’t commit the snapshots to the base VHD file before I did this. Again, my assumption was that I was going to be able to re-discover my VM and everything would show up intact. Now, I realize, while my VM is back, it is in its pre-snapshot state. All of my post-snapshot changes have been lost. I just confirmed this… Looked at the Event Viewer logs in the VM, prior to today, the most recent log entry is on 9/16/08.

That stinks.

Bad!

Moral of the story: THINK through your process… I totally missed the boat on this one. I didn’t spend enough time thinking through this, following the various options, caveats, and hiccups that could have come up. And, when one did come up, I should have stopped and THOUGHT.

Think think think!

Oh, and, commit any snapshots to the base VHD file. This is done by deleting the snapshot within Hyper-V. Then, when you turn the VM off, any differencing disks are merged in tot he base VHD.

 

Sorry Nick.

 

I am sick to my stomach…

Thursday, December 11, 2008

FolderShare No More

Microsoft warned us FolderShare users that it was going away. Well, tonight it happened. I started getting connection errors on my computers, telling me that I had to upgrade to Windows Live Sync. I have been using FolderShare for quite a while, keeping files in sync between multiple computers and among multiple different people. It is a great tool.

Windows Live Sync looks almost identical and seems to function in much the same way. I haven't peeked into every corner yet, however. It does have a 'remote access' feature, but I currently use TeamViewer (along with KeePass for password management and auto-form-fill) and LOVE IT! I will not be bothering with the remote access features of Windows Live.

In fact, I am not sure I buy into the whole Windows Live thing quite yet. Yes, I use Windows Live Writer for blogging, Messenger for IM (in addition to Google Chat),  and Sync. But, I think of them as individual applications, not as part of a coherent, integrated service. I don't know if that's because I'm 'old school' or because Microsoft hasn't done a good enough job of selling Windows Live.

I just hope that Sync works as well as FolderShare did.

File Transfer Technology

As I've blogged about earlier, we now have a 200Mb QMOE link between our campuses, courtesy of Qwest.

Now, we (and by 'we', I primarily mean my boss) are looking into some file transfer technology to improve the data transfer of 80+GB of data between the campuses at regular intervals. We would like to be able file-transfer our recorded services between campuses for playback.

While we know we can just drag-and-drop while crossing our fingers, we are hoping to find a solution that does a bit more:

  • Improve speed of transfer
  • Easily facilitate transfer between nodes with different OSs (Mac-->Win, for example)
  • Failure recovery/auto-restart/interruption handling
  • Automated? Scriptable? Scheduling?

My boss is currently evaluating technology from Aspera. Apparently, their solution uses UDP rather than TCP. But, while testing, we have seen a pretty high percentage of packet loss on UDP traffic. The issue may be with our Dell PowerConnect 6024, our core switch/router on our Mesa campus. We are looking at testing a replacement for this (a Cisco box) to see if traffic throughout/integrity improves.

If anyone has to transfer large over WAN links, I would love to hear about the technology you use for that!

Wednesday, December 10, 2008

SQL Server Log Files Growing... and GROWING!

So, is it odd to have a 600MB database file with an associated 82GB(!) log file? From what I've read, it seems so. But, I'm no DBA...

We are running SQL Server 2005 and I am trying to learn about Recovery Models (Simple vs. Full), log file re-sizing, how BackupExec 11d fits into the mix, and more.

It is proving to be a steep mountain to climb.

Monday, November 17, 2008

Finding some Solace

This KB article discusses my Event ID 9 error. I have turned all the things OFF, as instructed by this article and my backup/duplicate jobs seems to be running now! Cool!

At the end of this article, it talks about turning these BIOS settings back on, one by one.

Me thinks I'll leave well enough alone for the time being!

Quantum of NO Solace!

Yes, I am sadly reporting that my Quantum Scalar 50 is bringing me no solace! Actually, it looks like it may be my Scalar 50 AND my Adaptec 39320A SCSI card, working in conjunction to frustrate my life.

When I run 'small' tape jobs, things work fine. Catalog tapes, erase tapes, label tapes, etc. But, when I try to actually run a backup/duplicate job...

Event ID 9

I'm not the only one getting these, for sure. I am hoping that some of the 'fixes' I am finding online will help. If not, it will be time to open trouble tickets with Quantum, Adaptec, and maybe Symantec (using BE11d).

In the past few weeks, I was trying to get this hardware setup working with SCDPM with no luck. No one could help... not Microsoft... not Adaptec.

Now, I am seeing the same (or, at least, VERY similar) problems with BE11d. I will be tempted, once I get this thing working, to try out DPM again.

I will follow up with a post, should I actually find a resolution to this.

Monday, November 10, 2008

DPM a BUST

I have to, sadly, announce that DPM will not be used as part of our backup/DR solution. After working with Quantum and Microsoft for a couple of weeks, no one was able to get DPM and the Scalar 50 to play nicely together.

So, it's back to BackupExec 11d. In addition to using the tape library with BE11d, I will be exploring its D2D functions.

BUMMER!

P.S. If anyone has any tips on backing up virtual environments using BE11d, I would love to hear them.

Tuesday, October 28, 2008

Randomness... Is it possible?

I was looking at creating randomness again and came across: RNGCryptoServiceProvider

Specifically, System.Security.Cryptography.RNGCryptoServiceProvider

Most of what I read said that this is the best way to generate random values.

From what I can tell, it is designed to generate a random byte. So, it will fill a byte array with random values between 1 and 255. This is the behavior I saw when testing in PowerShell.

Most of the examples I found online of how to use this to generate a random selection out of an array of options is to do something like:

----------------------------------------------------

# Create character array
$chars = "qwertyuiopasdfghjklzxcvbnm1234567890"

# Create a byte array with one place in it
$byte = New-Object System.Byte[] 1

# Create random number generator
$rng = New-Object System.Security.Cryptography.RNGCryptoServiceProvider

# Fill our byte array with a random value (between 1 and 255), giving us a single random byte value
$rng.GetBytes($byte)

# Get and integer of this value
$rnd = $byte[0] -as [int]

# Generate an integer to be used as an index value in our character array
$int = ($rnd % $($chars.Length))

# Get your random character
$chars[$int]

----------------------------------------------------

While this works fine, I got to wondering if this method will favor some characters in our character array over others. For example, look at this output. I am executing the command "1..255 | % {$_ % $chars.Length} | group" which...

- counts from 1 to 255, sending each number down the pipeline
- for each number, it calcs <number> MODULO <CharArrayLength>
- it then groups my results

*************

§ Derek-D620 {C:\s\u} 1..255 | % {$_ % $chars.Length} | group

Count Name                      Group
----- ----                      -----
    8 1                         {1, 1, 1, 1...}
    8 2                         {2, 2, 2, 2...}
    8 3                         {3, 3, 3, 3...}
    7 4                         {4, 4, 4, 4...}
    7 5                         {5, 5, 5, 5...}
    7 6                         {6, 6, 6, 6...}
    7 7                         {7, 7, 7, 7...}
    7 8                         {8, 8, 8, 8...}
    7 9                         {9, 9, 9, 9...}
    7 10                        {10, 10, 10, 10...}
    7 11                        {11, 11, 11, 11...}
    7 12                        {12, 12, 12, 12...}
    7 13                        {13, 13, 13, 13...}
    7 14                        {14, 14, 14, 14...}
    7 15                        {15, 15, 15, 15...}
    7 16                        {16, 16, 16, 16...}
    7 17                        {17, 17, 17, 17...}
    7 18                        {18, 18, 18, 18...}
    7 19                        {19, 19, 19, 19...}
    7 20                        {20, 20, 20, 20...}
    7 21                        {21, 21, 21, 21...}
    7 22                        {22, 22, 22, 22...}
    7 23                        {23, 23, 23, 23...}
    7 24                        {24, 24, 24, 24...}
    7 25                        {25, 25, 25, 25...}
    7 26                        {26, 26, 26, 26...}
    7 27                        {27, 27, 27, 27...}
    7 28                        {28, 28, 28, 28...}
    7 29                        {29, 29, 29, 29...}
    7 30                        {30, 30, 30, 30...}
    7 31                        {31, 31, 31, 31...}
    7 32                        {32, 32, 32, 32...}
    7 33                        {33, 33, 33, 33...}
    7 34                        {34, 34, 34, 34...}
    7 35                        {35, 35, 35, 35...}
    7 0                         {0, 0, 0, 0...}

*****************

As you can see, some results come up more than others. Specifically (in this case) 1, 2, and 3 will show up more often than the rest of the results. So, looking at my CharArray, the letters w, e, and r will show up more often in my result sets. I am thinking that, to create an environment that does not favor any particular subset of our character array, I need my 'byte' value to be evenly divisible by my charArray length. So, rather than using a max possible value of 255, my max possible value needs to be the largest value where "maxValue MODULO charArrayLength = 0"

Ugh!

And, this doesn’t take into account the possibility that my max value could be larger than 255, in which case this formula just spits out 255.

Ugh! Ugh!

Thursday, October 23, 2008

Generating Passwords with Powershell

UPDATES AT BOTTOM OF POST!

I wanted a way to generate random passwords using Powershell. Research for this was a lot of fun, and I ran across some great resources.

--------------------------------------------

http://dmitrysotnikov.wordpress.com/2007/07/18/generate-random-password-with-powershell/ 
- Love the comment referring to using ”System.Web”

http://www.peterprovost.org/blog/post/Quick-n-Dirty-PowerShell-Password-Generator.aspx

http://www.hanselman.com/blog/DictionaryPasswordGeneratorInPowershell.aspx 
- Interesting variation, while promoting passphrases over passwords (a great discussion to have, no doubt!)

http://www.terminal23.net/2007/06/powershell_random_password_gen.html 
- Cool solution to this

http://www.leadfollowmove.com/archives/powershell/powershell-less-code-same-result

--------------------------------------------

I wanted a couple of specifics regarding my tool's functionality.

  • The option of including/excluding subsets of characters (uppercase, lowercase, numbers, special characters)
  • The option of setting the password length
  • The results had to include at least one character from each subset desired
  • It had to be easily usable in the pipeline

With that all in mind, I came up with the following function. This is the format that I have settled on (more or less) for my functions. I don't know if it's the best way to do things (probably not), but it seems to be working for me at this time.

I am glad to be able to add my $0.02 to the conversation.

#######################################################################
# FUNCTION NAME: New-Password
#   
# See USAGE() function for docs.
#
# WRITTEN BY: Derek Mangrum
#
# REVISION HISTORY:
#     2008-10-23 : Initial version
#######################################################################
function New-Password
{
    param 
    (
        [int]$length,
        [switch]$lowerCase,
        [switch]$upperCase,
        [switch]$numbers,
        [switch]$specialChars
    )

    BEGIN
    {
        # Usage Instructions
        function Usage() 
        {
            Write-Host ''
            Write-Host 'FUNCTION NAME: New-Password' -ForegroundColor White
            Write-Host ''
            Write-Host 'USAGE'
            Write-Host '    New-Password -length 10 -upperCase -lowerCase -numbers'
            Write-Host '    New-Password -length 10 -specialChars'
            Write-Host '    New-Password -le 10 -lo -u -n -s'
            Write-Host '    New-Password'
            Write-Host ''
            Write-Host 'DESCRIPTION:'
            Write-Host ' Generates a random password of a given length (-length parameter)'
            Write-Host ' comprised of at least one character from each subset provided'
            Write-Host ' as a switch parameter.'
            Write-Host ''
            Write-Host 'AVAILABLE SWITCHES:'
            Write-Host ' -lowerCase    : include all lower case letters'
            Write-Host ' -upperCase    : include all upper case letters'
            Write-Host ' -numbers      : include 0-9'
            Write-Host ' -specialChars : include the following- !@#$%^&*()_+-={}[]<>'
            Write-Host ''
            Write-Host 'REQUIREMENTS:'
            Write-Host ' You must provide the -length (four or greater) and at least one character switch'
            Write-Host ''
        }
        
        function generate_password
        {
            if ($lowerCase)    
            { 
                $charsToUse += $lCase
                $regexExp += "(?=.*[$lCase])"
            }
            if ($upperCase)        
            { 
                $charsToUse += $uCase 
                $regexExp += "(?=.*[$uCase])"
            }
            if ($numbers)
            { 
                $charsToUse += $nums 
                $regexExp += "(?=.*[$nums])"
            }
            if ($specialChars)    
            { 
                $charsToUse += $specChars
                $regexExp += "(?=.*[\W])"
            }
            
            $test = [regex]$regexExp
            $rnd = New-Object System.Random
            
            do 
            {
                $pw = $null
                for ($i = 0 ; $i -lt $length ; $i++)
                {
                    $pw += $charsToUse[($rnd.Next(0,$charsToUse.Length))]
                    Start-Sleep -milliseconds 20
                }
            }
            until ($pw -match $test)
            
            return $pw
        }

        # Displays help
        if (($Args[0] -eq "-?") -or ($Args[0] -eq "-help")) 
        {
            Usage
            break
        }
        else
        {
            $lCase = 'abcdefghijklmnopqrstuvwxyz'
            $uCase = $lCase.ToUpper()
            $nums = '1234567890'
            $specChars = '!@#$%^&*()_+-={}[]<>'
        }
    }
    
    PROCESS
    {
        if (($length -ge 4) -and ($lowerCase -or $upperCase -or $numbers -or $specialChars))
        {
            $newPassword = generate_password
        }
        else
        {
            Usage
            break
        }
        
        $newPassword
    }
    
    END
    {
    }
}

UPDATE:
I was talking with friends about this script and something about it I did not like. Namely, the need I had to pause briefly in my password-generation loop (the 'Start-Sleep -m 20'). They said that I needed to seed my System.Random object. Hmmm... OK. Then we talked about how to create a good seed value for this. Long story short, we landed on the following:

$seed = ([system.Guid]::NewGuid().GetHashCode())
$rnd = New-Object System.Random ($seed)

We create a $seed by calling the NewGuid().GetHashCode() method of System.Guid. Then we use this $seed to create our $rnd object. Also, we can then take out the pause. Passwords, as you can imagine, generate much more quickly now.

Wednesday, October 15, 2008

A Quick Quiz...

QUESTION:  What just increased from 1.5Mb to 200Mb?

ANSWER:  The point-to-point link between our two campuses!

We completed the initial installation of our Qwest QMOE line between our campuses. This link is replacing a T1, upping our bandwidth by over 100 times. The T1 is still in place and, using OSPF to handle routing, is automatically used if we lose the QMOE link. We tested the failover/failback and it works great.

As part of this initial setup, we redirected Internet traffic for our Gilbert users across the new link and out the Mesa campus ISP connection. Currently, the Gilbert campus has a T1 connection to our ISP. The Mesa campus has a Cox Business Internet line, 12Mb down and 2Mb up. We will see how this works for us. Failover for this setup, at this time, is a manual process. We are working on automating that as well.

We did not set up any QoS today. But, our plan is to configure QoS to improve transmission for video and voice traffic, mainly video. Our hope is to, at some point in the not-too-distant future, stream services between the campuses in real time. But, that is a future blog post.

I am excited to see the performance of this link moving forward. I am especially interested in getting responses from our Gilbert campus users who regularly use Mesa campus resources.

Wednesday, October 8, 2008

New Backup Solution Going In...

Scalar 50So, after pulling together a bunch of different pieces, we are just about ready to deploy our DPM solution. Pieces have included SAN storage, the tape library (pictures at left), re-tasking a server, etc. Things ended up being much more complicated and time-consuming than I originally thought. But, we are now very close.

The tape library was a huge piece for this, since our backups have been running over one tape for some time now. Our Scalar 50 has one tape drive and supports up to 30 tapes. Our DPM installation will perform D2D2T backups for a variety of our servers and applications. We will be doing straight D2T for other platforms until we get some more SAN disk.

This implementation should greatly enhance our DR and backup solution over our current install... BackupExec with a single tape drive. My plan is to deploy this gear tomorrow and work on configuring Protection Groups over the weekend. Fun stuff! Hopefully, I will be able to sleep better at nights knowing DPM is protecting my data.

Thursday, October 2, 2008

Major Update to my New-SharedFolder script...

More PowerShell geekery ahead!

I've written about this before, here and here. But, I use it so often that I am constantly thinking about how to improve it. Well, today, I made what I feel is a major leap regarding the functionality of this tool.

The main goal of my efforts today was to make this tool more Powershell-y... that is, not so much a script to be run, but a function that now interacts with the pipeline and accepts parameters.

My previous version of this script was normally run dot-sourced and the session looked something like:

image

It required: 1)being run, 2)selecting <1 or 2>; 3)typing of foldername or .txt file path

It was basically a mini-app. This was OK, but not really in the spirit of Powershell. So, I re-wrote things. Actually, I re-organized things more than re-wrote them. Anyway, now the process looks like:

image

It is now a function. So, I can now perform this operation the following ways:

  • 'folderName1','folderName2' | New-SharedFolder
  • 'c:\scripts\folderList.txt' | New-SharedFolder
  • 'folderName', 'c:\scripts\folderList.txt' | New-SharedFolder
  • New-SharedFolder 'folderName'
  • etc...

The function accepts input of a folder name and/or a filename (with path). It runs a Test-Path on the input and, if it finds the file, it reads its contents. If the Test-Path fails, it assumes that a folder name was entered.

Here's the new code, in case you are interested. I am sure there are many things in this code that some PoSH gurus out there might roll their eyes at. But, for me, it is a big leap forward. I know that I am not done working on this function.

function New-SharedFolder
{
    param ( [string]$inputString )

    BEGIN
    {
        # Usage Instructions
        function Usage() 
        {
            <Code to display docs>
        }
        
        # Loads the foldernames in array for processing
        function load_folderlist_array
        {
            param ( [string]$inStr )
            
            if (Test-Path $inStr)
            {
                $fl = Get-Content $inStr
            }
            else
            {
                $fl = $inStr
            }
            return $fl
        }
        
        # Displays help
        if (($Args[0] -eq "-?") -or ($Args[0] -eq "-help")) 
        {
            Usage
            break
        }

        # This Function creates folder and groups
        function process_folders 
        {
            param([array]$allFolderNames)
                        
            foreach ($folder in $allFolderNames) 
            {
                Write-Host "************************************************" -foregroundcolor White
                Write-Host "Now processing folder name: $folder"
        
                # Defining path to folder
                $FullPath = "\\FS01\Groups\" + $folder
            
                #Does this folder already exist?
                if (Test-Path $FullPath) 
                {
                    #Folder already exists. Abort operation.
                    Write-Host -ForegroundColor Red "The folder $FullPath already exists and is being skipped. Please verify the folder name."
                }
                else 
                {
                    # Create variables holding group names and descriptions
                    # If necessary, shorten folder name to 13 characters
                    if ($folder.length -gt 13) 
                    {
                        $MODgroup = "s_$($folder.substring(0,13))_MOD"
                        $MODdesc = "$folder -- MODify security group"
                        $ROgroup  = "s_$($folder.substring(0,13))_RO"
                        $ROdesc = "$folder -- ReadOnly security group"
                    }
                    else 
                    {
                        $MODgroup = "s_$($folder)_MOD"
                        $MODdesc = "$folder -- MODify security group"
                        $ROgroup  = "s_$($folder)_RO"
                        $ROdesc = "$folder -- ReadOnly security group"
                    }
                    # Defines OU location for Security Group Creation
                    $GroupContainer = <Path to OU>
                    # Create Groups
                    Write-Host "Creating MODify security group for this folder..." -NoNewline
                    $null = New-QADGroup -ParentContainer $GroupContainer -name $MODgroup -samAccountName $MODgroup -GroupType 'security' -GroupScope 'GLOBAL' -description $MODdesc
                    Write-Host "  DONE!" -foregroundcolor Green
                    Write-Host "Creating ReadOnly security group for this folder..." -NoNewline
                    $null = New-QADGroup -ParentContainer $GroupContainer -name $ROgroup -samAccountName $ROgroup -GroupType 'security' -GroupScope 'GLOBAL' -description $ROdesc
                    Write-Host "  DONE!" -foregroundcolor Green
        
                    # Create Folder
                    Write-Host "Creating folder under \\FS01\Groups ..." -NoNewline
                    $null = New-Item -path \\FS01\Groups -name $folder -type directory
                    Write-Host "  DONE!" -foregroundcolor Green
                
                    # Check for security groups on all DCs
                    wait_for_replication
                    
                    # Assign rights to the folder for the groups
                    #   MODIFY RIGHTS
                    Write-Host "`nAssigning MODify ACL entries for this folder to the MOD group..."
                    assign_rights "Modify" $MODgroup
                                
                    #   READONLY RIGHTS
                    Write-Host "Assigning ReadOnly ACL entries for this folder to the RO group..."
                    assign_rights "Read" $ROgroup
                }
            }
        }

        # Checks all DCs for security groups, verifying replication
        function wait_for_replication 
        {
            $DCs = Get-QADComputer -ComputerRole DomainController
            
            foreach ($DC in $DCs)
            {
                $aa = $null
                $bb = $null
                Write-Host "`nChecking for Security Group on $($DC.Name)" -noNewLine
                
                do
                {
                    $null = Connect-QADService -Service $DC.Name
                    $aa = Get-QADGroup $MODgroup
                    $bb = Get-QADGroup $ROgroup
                    Disconnect-QADService 
                    Start-Sleep -Seconds 1
                    Write-Host '.' -noNewLine
                }
                until ($aa -ne $null -and $bb -ne $null)
            }
        }
    
        # Modifies ACL on folder, giving MOD and RO groups appropiate rights
        function assign_rights 
        {
            param([string]$Rights, [string]$GroupName)
            
            $acl = get-acl $FullPath
            $Inherit = [Security.AccessControl.InheritanceFlags] "ContainerInherit, ObjectInherit"
            $Prop = [Security.AccessControl.PropagationFlags] "None"
            $NewRule = new-object Security.AccessControl.FileSystemAccessRule $GroupName, $Rights, $Inherit, $Prop, Allow
            $modified = $FALSE
            $modded = $acl.ModifyAccessRule("Add", $NewRule, [ref]$modified)
            set-acl -path $FullPath -AclObject $acl
            
            if ($modded) 
            {
                Write-Host "ACL for $GroupName successfully applied." -foregroundcolor Green
            }
            else 
            {
                Write-Host "WARNING!!! ACL for $GroupName failed to apply!" -ForegroundColor Red 
            }
        }    
    }

    PROCESS
    {
        if ($inputString -and $_)
        {
            Throw 'Please use either pipeline or input parameter'
            break
        }
        elseif ($inputString)
        {
            $folderNames = load_folderlist_array $inputString
        }
        elseif ($_)
        {
            $folderNames = load_folderlist_array $_
        }
        else
        {
            Usage
            break
        }
        
        process_folders $folderNames
    }
    
    END
    {
        Write-Host "`nJob Complete." -foregroundcolor White
    }
}

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.

Additional Info

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