Need guidance on String Manipulation

This topic contains 5 replies, has 3 voices, and was last updated by  Bill Frazier 2 years, 6 months ago.

  • Author
    Posts
  • #24044

    Bill Frazier
    Participant

    I've got the following function snippet that I'm looking for a little guidance on. I need to check that my var isn't already in use as an active account. If the var is in use I need to modify it as you can see in my commented lines below. By the time $SamAcctName makes it to this function I've already "cleaned it up" (removed spaces, apostrophes, dashes, maxlength, etc.). I'd like to either strip the last two characters of the var and replace them with numerals, or append the numerals to the current var. I know the answer is in string processing but what string methods should I be focusing on? Also I need to check if john.smith or ANY john.smithXX exists so I know what my next number will be. What logic construct(s) should I be looking at as well? THANK YOU in advance!

    Function Check-SamAcct {
        $CheckSam = (Get-ADUser $SamAcctName).SamAccountName
        If ($CheckSam -eq $SamAcctName) {
    	
    	# Code to modify $SamAcctName goes here
    	# I need to take the current var and modify/create
    	# a new one if that samAccountName already exists.
    	# I need this function to return a new unique var.
    	# i.e. $SamAcctName = "john.smith" and a john.smith 
    	# already exists.  I need to create john.smith01 and
    	# assign that to $SamAcctName so I can create the new
    	# user account.  Make sense?
    
            }
    
  • #24050

    Rob Simmers
    Participant

    I think this is basically what you are trying to do. There are a lot of ways to do this, but I think Compare-Object is what you should use. Basically, you would generate a object with the possible names and then run a query against AD with a wildcard. Then compare those two object to determine what is available and select the first item. You just need to change the $SamAccountNameFormat to Firstname.LastName, un-remark the AD search line and remove the mock AD data lines:

    function Get-UniqueSamAccountName {
        param[
            [string]$FirstName,
            [string]$LastName
        ]
    
        begin{
            $SamAccountNameFormat = "{0}{1}" -f $FirstName.SubString[0,1], $LastName
            #Generate a list of the possible names, currently up to 50
            $possibleNames = @{}
            $possibleNames += New-Object -TypeName PSObject -Property @{SamAccountName=$SamAccountNameFormat}
            $possibleNames += for[$i=1;$i -le 50;$i++]{$name = "{0}{1:D2}" -f $SamAccountNameFormat, $i; New-Object -TypeName PSObject -Property @{SamAccountName=$name}}
            #Create a search filter for Get-ADUser with a * wildcard
            $searchFilter = "{0}*" -f $SamAccountNameFormat
        }
        process {
            #$currentlyInAD = Get-ADUser -Filter {SamAccountName -Like $searchFilter} -Properties SamAccountName | Select SamAccountName
     
            # Don't have AD handy, but emulate a return from AD
            $currentlyInAD = @{}
            $currentlyInAD += New-Object -TypeName PSObject -Property @{SamAccountName="rsimmers"}
            $currentlyInAD += New-Object -TypeName PSObject -Property @{SamAccountName="rsimmers01"}
            $currentlyInAD += New-Object -TypeName PSObject -Property @{SamAccountName="rsimmers02"}
            $currentlyInAD += New-Object -TypeName PSObject -Property @{SamAccountName="rsimmers04"}
            $currentlyInAD += New-Object -TypeName PSObject -Property @{SamAccountName="rsimmers05"}
            # Compare the list possibilities with the returned AD accounts for the SamAccountName format
            $availableSAM = Compare-Object -ReferenceObject $currentlyInAD -DifferenceObject $possibleNames -Property SamAccountName -PassThru | Where{$_.SideIndicator -eq "=>"} | Sort-Object SamAccountName | Select -ExpandProperty SamAccountName -First 1
        }
        end {
            $availableSAM
            
        }
    }
    
    Get-UniqueSamAccountName -FirstName Rob -LastName Simmers
    

    Return:

    Get-UniqueSamAccountName -FirstName Rob -LastName Simmers
    RSimmers03

    You can also remark out the first line of the mocked AD data for "rsimmers" or "rsimmers01" and see that the function will return the next logical samaccountname.

  • #24053

    Bill Frazier
    Participant

    Rob,
    THANK YOU for the info it definitely gives me a different perspective! I've already passed the var $SamAcctName to the Function and I'd like to use what I already have since $SamAcctName has already been pre-processed/cleaned it up.

    $SamAcctName = 'alexander.richardson'

    The example above is a 20 Character string and the max for a SamAccountName. I need to drop the 2 rightmost characters:
    $SamAcctName.Substring(0,$SamAcctName.Length -2) and somehow append two numerals i.e. 01 02 03

    For a $SamAcctName that is < 18 I need to append two numerals to that. My biggest hurdle at this point is the logic/construct needed to check if john.smith01, john.smith02, john.smith03 exists and iterate from there. I get the gist of your code but was hoping for something a bit more streamlined for my function. I'll definitely digest it and see if I can incorporate some of it. THANK YOU!

  • #24055

    Jack Neff
    Participant

    *Edited to fix butchered code block*

    Script:

    $Names = 'alexander.richardson','alexander.richards02','alexander.richards03','alexander.richards04'
    $SAM = 'alexander.richardson'
    $SuffixArray = '02','03','04','05','06','07','08','09','10'
    $i = 0
    
    while ($Names -contains $SAM){
        "Name:  $SAM"
        "Oh no, $SAM already in use!"
        if ($SuffixArray -contains $SAM.Substring($SAM.Length-2,2)){
            "  It already has a suffix"
            "  Increment the suffix and try again!"
            $SAM = $SAM.Substring(0,$SAM.Length-2) + $SuffixArray[$i]
        } else {
            "  Doesn't contain a suffix yet"
            "  Is the name too long?"
            if ($SAM.Length -gt 18){
                "  Yep, shrink it down and increment"
                $SAM = $SAM.Substring(0,18) + $SuffixArray[$i]
            } else {
                "  Nope it's good, increment it"
                $SAM = $SAM + $SuffixArray[$i]
            }
        }
        $i++
        "==================="
    }
    
    "Boomshockalocka: $SAM"
    

    Output:

    Name:  alexander.richardson
    Oh no, alexander.richardson already in use!
      Doesn't contain a suffix yet
      Is it too long?
      Yep, shrink it down and increment
    ===================
    Name:  alexander.richards02
    Oh no, alexander.richards02 already in use!
      It already has a suffix
      Increment the suffix and try again!
    ===================
    Name:  alexander.richards03
    Oh no, alexander.richards03 already in use!
      It already has a suffix
      Increment the suffix and try again!
    ===================
    Name:  alexander.richards04
    Oh no, alexander.richards04 already in use!
      It already has a suffix
      Increment the suffix and try again!
    ===================
    
    Boomshockalocka: alexander.richards05
    

    Don't have time to test all scenarios there may be bugs but hopefully this gets you stepping in the right direction. Since you want to use "01" or "02" instead of "1" and "2" I used strings instead of integers because I don't know how to append the preceding zero to an int. The downfall of this is that your increments are finite but add as many suffixes to the array as necessary. Also I started the array at "02" because it makes sense that the first person with a matching name be 02 and not 01.

    Obviously you'll use Get-ADUser as the while loop argument.

    Good luck!

  • #24068

    Rob Simmers
    Participant

    I played a little more with this today. Give this a shot:

    function Get-UniqueSamAccountName {
        [CmdletBinding()]
        param(
            [string]$FirstName,
            [string]$LastName,
            [int]$MaxSamNames = 30,
            [int]$MaxSamLength = 20
        )
     
        begin{
            #$SamAccountNameFormat = "{0}.{1}" -f $FirstName, $LastName
            $SamAccountNameFormat = "{0}{1}" -f $FirstName.SubString(0,1), $LastName
            Write-Verbose ("SamAccountName will be truncated to {0} characters" -f $MaxSamLength)
            if ($SamAccountNameFormat.Length -gt $MaxSamLength) {$SamAccountNameFormat = $SamAccountNameFormat.SubString(0,$MaxSamLength)}
            Write-Verbose ("SamAccountName Format: {0}" -f $SamAccountNameFormat)
            Write-Verbose ("Compiling object with {0} SamAccountNames for comparison" -f ($MaxSamNames + 1))
            $possibleNames = @()
            $possibleNames += New-Object -TypeName PSObject -Property @{SamAccountName=$SamAccountNameFormat}
            if ($SamAccountNameFormat.Length -gt ($MaxSamLength -2)) {$SamAccountNameFormat = $SamAccountNameFormat.SubString(0,($MaxSamLength -2))}
            $possibleNames += for($i=1;$i -le $MaxSamNames;$i++){$name = "{0}{1:D2}" -f $SamAccountNameFormat, $i; New-Object -TypeName PSObject -Property @{SamAccountName=$name}}
            #Create a search filter for Get-ADUser with a * wildcard
            $searchFilter = "{0}*" -f $SamAccountNameFormat
        }
        process {
            #Get any user that matches the searchFilter
            Write-Verbose ("Searching Active Directory for user(s): {0}" -f $searchFilter)
            $currentlyInAD = Get-ADUser -Filter {SamAccountName -Like $searchFilter} -Properties SamAccountName | Select SamAccountName
     
            if ($currentlyInAD) {
                Write-Verbose ("Found {0} matches in AD, comparing with possible names list for available SamAccountName" -f @($currentlyInAD).Count)
                # Compare the list possibilities with the returned AD accounts for the SamAccountName format
                $availableSAM = Compare-Object -ReferenceObject $currentlyInAD -DifferenceObject $possibleNames -Property SamAccountName -PassThru | Where{$_.SideIndicator -eq "=>"} | Select -ExpandProperty SamAccountName -First 1
            }
            else {
                Write-Verbose "No matches found in AD, returning first possible SamAccountName"
                #$possibleNames | Sort-Object -Property SamAccountName
                $possibleNames | Select -ExpandProperty SamAccountName -First 1
            }
        }
        end {
            $availableSAM
        }
    }
    

    Some sample output:

    Get-UniqueSamAccountName -FirstName Rob -LastName Simmers -Verbose
    Get-UniqueSamAccountName -FirstName Bill -LastName Frazier -Verbose
    Get-UniqueSamAccountName -FirstName Donald -LastName Rumpelstiltskin  -Verbose
    Get-UniqueSamAccountName -FirstName Alexander -LastName Richardson  -Verbose
    Get-UniqueSamAccountName -FirstName Donald -LastName Rumpelstiltskin -MaxSamLength 8 -Verbose
    Get-UniqueSamAccountName -FirstName Alexander -LastName Richardson  -MaxSamLength 8 -Verbose
    
    VERBOSE: SamAccountName will be truncated to 20 characters
    VERBOSE: SamAccountName Format: RSimmers
    VERBOSE: Compiling object with 31 SamAccountNames for comparison
    VERBOSE: Searching Active Directory for user(s): RSimmers*
    VERBOSE: Found 1 matches in AD, comparing with possible names list for available SamAccountName
    RSimmers01
    VERBOSE: SamAccountName will be truncated to 20 characters
    VERBOSE: SamAccountName Format: BFrazier
    VERBOSE: Compiling object with 31 SamAccountNames for comparison
    VERBOSE: Searching Active Directory for user(s): BFrazier*
    VERBOSE: No matches found in AD, returning first possible SamAccountName
    BFrazier
    VERBOSE: SamAccountName will be truncated to 20 characters
    VERBOSE: SamAccountName Format: DRumpelstiltskin
    VERBOSE: Compiling object with 31 SamAccountNames for comparison
    VERBOSE: Searching Active Directory for user(s): DRumpelstiltskin*
    VERBOSE: No matches found in AD, returning first possible SamAccountName
    DRumpelstiltskin
    VERBOSE: SamAccountName will be truncated to 20 characters
    VERBOSE: SamAccountName Format: ARichardson
    VERBOSE: Compiling object with 31 SamAccountNames for comparison
    VERBOSE: Searching Active Directory for user(s): ARichardson*
    VERBOSE: No matches found in AD, returning first possible SamAccountName
    ARichardson
    VERBOSE: SamAccountName will be truncated to 8 characters
    VERBOSE: SamAccountName Format: DRumpels
    VERBOSE: Compiling object with 31 SamAccountNames for comparison
    VERBOSE: Searching Active Directory for user(s): DRumpe*
    VERBOSE: No matches found in AD, returning first possible SamAccountName
    DRumpels
    VERBOSE: SamAccountName will be truncated to 8 characters
    VERBOSE: SamAccountName Format: ARichard
    VERBOSE: Compiling object with 31 SamAccountNames for comparison
    VERBOSE: Searching Active Directory for user(s): ARicha*
    VERBOSE: No matches found in AD, returning first possible SamAccountName
    ARichard
    

    or you can un-remark the firstname.lastname format and get something like this:

    VERBOSE: SamAccountName will be truncated to 20 characters
    VERBOSE: SamAccountName Format: Rob.Simmers
    VERBOSE: Compiling object with 31 SamAccountNames for comparison
    VERBOSE: Searching Active Directory for user(s): Rob.Simmers*
    VERBOSE: No matches found in AD, returning first possible SamAccountName
    Rob.Simmers
    VERBOSE: SamAccountName will be truncated to 20 characters
    VERBOSE: SamAccountName Format: Bill.Frazier
    VERBOSE: Compiling object with 31 SamAccountNames for comparison
    VERBOSE: Searching Active Directory for user(s): Bill.Frazier*
    VERBOSE: No matches found in AD, returning first possible SamAccountName
    Bill.Frazier
    VERBOSE: SamAccountName will be truncated to 20 characters
    VERBOSE: SamAccountName Format: Donald.Rumpelstiltsk
    VERBOSE: Compiling object with 31 SamAccountNames for comparison
    VERBOSE: Searching Active Directory for user(s): Donald.Rumpelstilt*
    VERBOSE: No matches found in AD, returning first possible SamAccountName
    Donald.Rumpelstiltsk
    VERBOSE: SamAccountName will be truncated to 20 characters
    VERBOSE: SamAccountName Format: Alexander.Richardson
    VERBOSE: Compiling object with 31 SamAccountNames for comparison
    VERBOSE: Searching Active Directory for user(s): Alexander.Richards*
    VERBOSE: No matches found in AD, returning first possible SamAccountName
    Alexander.Richardson
    VERBOSE: SamAccountName will be truncated to 8 characters
    VERBOSE: SamAccountName Format: Donald.R
    VERBOSE: Compiling object with 31 SamAccountNames for comparison
    VERBOSE: Searching Active Directory for user(s): Donald*
    VERBOSE: No matches found in AD, returning first possible SamAccountName
    
  • #24152

    Bill Frazier
    Participant

    Rob/Jack,
    THANK YOU GUYS! You've given me some good ideas. I TRULY appreciate the assistance!

You must be logged in to reply to this topic.