I don't know of any existing tools that actually trigger on creation. Though like Nic mentioned, it is hypothetically possible to write something that could do that.
But realistically, how often are users/groups getting created outside of already automated processes? If they're not already, your existing provisioning processes should be augmented to add the relevant RFC2307 attributes also described in this TechNet blog post. For the stragglers that are created manually, you can have a script run at whatever interval you like that looks for objects missing the attributes and populating them as necessary.
In our environment, the script we have runs every 5 min on the DC holding the PDC Emulator role. But we could probably drop it down to once a minute without much additional impact. We also generate our UID/GID values from an algorithm that's based on the object's SID rather than a simple auto-incrementing value. It has the benefit that they're guaranteed* unique between domains/forests and we don't need to do any lookups to find the next value or make sure the value we want to use isn't already in use. I can post that function if you'd like. But it sounds like you guys may already have your own system for that.
*Guaranteed = as much as you can guarantee that two domains won't be created with the same randomly generated domain ID.
Edit: By request, here's the Powershell function we use to generate UIDs/GIDs from a SID.
function Get-UidFromSid()
{
[CmdletBinding()]
param(
[Parameter(Mandatory=$true,Position=0,ValueFromPipeline,ValueFromPipelineByPropertyName)]
[System.Security.Principal.SecurityIdentifier]$sid
)
# convert sid to byte array
$sidBytes = New-Object byte[] $sid.BinaryLength
$sid.GetBinaryForm($sidBytes, 0)
Write-Verbose ("SID bytes: $([System.BitConverter]::ToString($sidBytes))")
# copy the sections we need
$ridDomainBytes = New-Object byte[] 8
[System.Array]::Copy($sidBytes, 16, $ridDomainBytes, 0, 4)
$ridUserBytes = New-Object byte[] 8
[System.Array]::Copy($sidBytes, 24, $ridUserBytes, 0, 4)
Write-Verbose ("Domain portion: $([System.BitConverter]::ToString($ridDomainBytes))")
Write-Verbose ("User portion: $([System.BitConverter]::ToString($ridUserBytes))")
# fix endian'ness if necessary
if (![System.BitConverter]::IsLittleEndian)
{
[System.Array]::Reverse($ridDomainBytes)
[System.Array]::Reverse($ridUserBytes)
}
# convert the byte arrays to longs
$ridDomain = [System.BitConverter]::ToInt64($ridDomainBytes, 0);
$ridUser = [System.BitConverter]::ToInt64($ridUserBytes, 0);
Write-Verbose "Domain(Int64) = $ridDomain, User(Int64) = $ridUser"
# Now we're going to use the first 9 bits of the domain rid followed by the
# first 22 bits of the user rid to make a single 31 bit integer (32-bit signed)
# that will be the new unique UID value for this SID
$ridDomain = ($ridDomain -band 0x1ff) -shl 22
$ridUser = $ridUser -band 0x3fffff
Write-Verbose "Domain(Int64) = $ridDomain, User(Int64) = $ridUser"
return ($ridDomain + $ridUser)
<#
.SYNOPSIS
Calculate the UID value for a Windows SID.
.DESCRIPTION
This function duplicates Centrify's algorithm for generating unique UID
values for Active Directory users and groups. The original algorithm was
provided by Centrify and ported to PowerShell by Ryan Bolger.
.PARAMETER sid
The SecurityIdentifier (SID) object to calculate against.
.OUTPUTS
System.Int32 value of the resulting UID.
.EXAMPLE
Get-UidFromSid ([System.Security.Principal.SecurityIdentifier]'S-1-5-21-3040497277-3966670145-4188292625-1137')
Calculate a UID from a fictional SID.
.EXAMPLE
Get-ADUser myuser | Get-UidFromSid
Calculate a UID from an existing Active Directory user via pipeline input.
#>
}
Here is a GitHub gist with some additional implementations such as C#, Bash, and Python.