UGDSBPSU.psm1

#Region './Public/Convert-AzADObjIDtoSid.ps1' 0
<#
  .DESCRIPTION
  This cmdlet will convert a Azure AD Object ID TO Sid
  .PARAMETER ObjectId
  Azure AD Object ID
  .EXAMPLE
  Convert AzADObject to Sid
    Convert-AzADObjIDtoSid -objectId <ID>
#>

function Convert-AzADObjIDtoSid{
  [CmdletBinding()]
  param(
    [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][String]$ObjectId
  )  
  $bytes = [Guid]::Parse($ObjectId).ToByteArray()
  $array = New-Object 'UInt32[]' 4
  [Buffer]::BlockCopy($bytes, 0, $array, 0, 16)
  $sid = "S-1-12-1-$array".Replace(' ', '-')
  return $sid  
}
#EndRegion './Public/Convert-AzADObjIDtoSid.ps1' 21
#Region './Public/Convert-SidtoAzADObjID.ps1' 0
<#
  .DESCRIPTION
  This cmdlet will convert a SID to an Azure AD Object ID
  .PARAMETER sid
  SID of object
  .EXAMPLE
  Convert Sid to Object ID
    Convert-SidtoAzADObjID -sid <SID>
#>

function Convert-SidtoAzADObjID{
  [CmdletBinding()]
  param(
    [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][String]$sid
  )
  $text = $sid.Replace('S-1-12-1-', '')
  $array = [UInt32[]]$text.Split('-')
  $bytes = New-Object 'Byte[]' 16
  [Buffer]::BlockCopy($array, 0, $bytes, 0, 16)
  [Guid]$guid = $bytes
  return $guid        
}
#EndRegion './Public/Convert-SidtoAzADObjID.ps1' 22
#Region './Public/Disable-Chromebook.ps1' 0
<#
  .DESCRIPTION
  This cmdlet will use GAM to disable a chromebook by SN
  .PARAMETER gamLocation
  The location that GAM files are stored
  .PARAMETER serialNumber
  array of serial numbers that you want to disable
  .EXAMPLE
  Disable a Single SN
    Disable-Chromebook -gamLocation <GAM> -serialNumber <SN>
  Disable multiple SN
    Disable-Chromebook -gamLocation <GAM> -serialNumber @('SN','SN')
#>

function Disable-Chromebook{
  [CmdletBinding()]
  param(
    [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()]$gamLocation,
    [Parameter(Mandatory = $true)][string[]]$serialNumber
  )
  $results = @{}
  try{
    foreach($sn in $serialNumber){
      # Attempt to run GAM script to disable chromebook.
      ($data = Invoke-Expression -Command "$($gamLocation) update cros cros_sn $($sn) action disable 2>&1") | Out-Null
      if($data -match "^.*Got 0 CrOS.*$"){
        $results.Add($sn,"Device Not Found")
      }
      else{
        $results.Add($sn,"Disabled")
      }
    }
  }
  catch{
    throw $Error[0]
  }
  return $results
}
#EndRegion './Public/Disable-Chromebook.ps1' 38
#Region './Public/Enable-Chromebook.ps1' 0
<#
  .DESCRIPTION
  This cmdlet will use GAM to disable a chromebook by SN
  .PARAMETER gamLocation
  The location that GAM files are stored
  .PARAMETER serialNumber
  array of serial numbers that you want to disable
  .EXAMPLE
  Disable a Single SN
    Disable-Chromebook -gamLocation <GAM> -serialNumber <SN>
  Disable multiple SN
    Disable-Chromebook -gamLocation <GAM> -serialNumber @('SN','SN')
#>

function Enable-Chromebook{
  [CmdletBinding()]
  param(
    [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()]$gamLocation,
    [Parameter(Mandatory = $true)][string[]]$serialNumber
  )
  $results = @{}
  try{
    foreach($sn in $serialNumber){
      # Attempt to run GAM script to disable chromebook.
      ($data = Invoke-Expression -Command "$($gamLocation) update cros cros_sn $($sn) action reenable 2>&1") | Out-Null
      if($data -match "^.*Got 0 CrOS.*$"){
        $results.Add($sn,"Device Not Found")
      }
      else{
        $results.Add($sn,"Enabled")
      }
    }
  }
  catch{
    throw $Error[0]
  }
  return $results
}
#EndRegion './Public/Enable-Chromebook.ps1' 38
#Region './Public/Get-Chromebook.ps1' 0
<#
  .DESCRIPTION
  This cmdlet will use GAM installed on your system to query Google for chromebooks based on your query and fields
  .PARAMETER gamLocation
  The location that GAM files are stored
  .PARAMETER query
  The query that you want to use for returning your devices
  .PARAMETER fields
  What fields you want it to return
  .EXAMPLE
  Retrieve a list of devices with a specific query
    Get-Chromebook -gamLocation <GAMPATH> -query $query
#>

function Get-Chromebook{
  [CmdletBinding()]
  [OutputType([System.Collections.Generic.List[PSCustomObject]])]
  param(
    [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()]$gamLocation,
    [Parameter(Mandatory = $false)]$query,
    [Parameter(Mandatory = $false)]$fields
  )
  # Setup the regex that we are using to attempt to properly parse the gam output
  $RegexOptions = [System.Text.RegularExpressions.RegexOptions]
  $csvSplit = '(,)(?=(?:[^"]|"[^"]*")*$)'
  # Create a blank list to store the data
  $devices = [System.Collections.Generic.List[PSCustomObject]]@()
  # Query Gam with the query/fields passed
  ($chromebooks = Invoke-Expression -Command "$($gamLocation) print cros query $($query) nolists fields $($fields) 2>&1") | Out-Null
  # Loop through the lines of the return. Ignore the first line as that should be the headers
  $startHere = $false
  for($x = 1; $x -lt $chromebooks.length; $x++)
  {
    if($startHere -eq $false){
      if($chromebooks[$x] -match "^deviceId,serialNumber.*$"){
        $startHere = $true
      }
      continue
    }
    # Split the line
    $device = [regex]::Split($chromebooks[$x], $csvSplit, $RegexOptions::ExplicitCapture).replace('"','')
    # Set the wanIP to null, if we can set it then we do, otherwise it stays null
    $wanIP = $null
    if($null -ne $device[8]){
      try{$wanIP = ($device[8] | ConvertFrom-JSON).wanIpAddress}
      catch{}
    }
    # Populate custom object for list
    $obj = [PSCustomObject]@{
      "Name" = $device[1]
      "network" = $wanIP
      "mac-address" = $device[6]
      "lastSync" = $device[2]
      "chromebook-model" = $device[4]
      "chromebook-os" = $device[5]
      "location" = $device[7]
    }
    $devices.Add($obj) | Out-Null
  }
  # Return list
  return $devices
}
#EndRegion './Public/Get-Chromebook.ps1' 62
#Region './Public/Get-GoogleOU.ps1' 0
<#
  .DESCRIPTION
  This cmdlet will use GAM installed on your system to query Google for the list of your OUs.
  .PARAMETER gamLocation
  The location that GAM files are stored
  .EXAMPLE
  Retrieve a list of OUs
    Get-GoogleOU -gamLocation <PATH TO GAM>
#>

function Get-GoogleOU{
  [CmdletBinding()]
  [OutputType([System.Collections.Generic.List[PSCustomObject]])]
  param(
    [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()]$gamLocation
  )
  # The command that we want to run with GAM
  $command = "print orgs"
  # Invoke GAM with the path and command
  ($ouRaw = Invoke-Expression -Command "$($gamLocation) $($command) 2>&1") | Out-Null
  # Create a list object to store the results
  $ouList = [System.Collections.Generic.List[PSCustomObject]]@()
  # Go through the results of the gam call
  $skipRows = $true
  for($i = 1; $i -lt $ouRaw.Count; $i++){
    # Skip rows until we hit the header row. This will remove some of the externious GAM output
    if($skipRows -eq $true){
      if($ouRaw[$i] -eq "orgUnitPath,orgUnitId,name,parentOrgUnitId"){
        $skipRows = $false
      }
      continue
    }
    # Split results by comma
    $ou = $ouRaw[$i] -split ","
    # Create a object for each line to store in the list
    $obj = [PSCustomObject]@{
      'orgUnitPath'     = $ou[0]
      'orgUnitId'       = $ou[1]
      'name'            = $ou[2]
      'parentOrgUnitId' = $ou[3]
    }
    $ouList.add($obj) | Out-Null
  }
  # return list of OUs
  return $ouList
}
#EndRegion './Public/Get-GoogleOU.ps1' 46
#Region './Public/Get-GraphAccessToken.ps1' 0
<#
  .DESCRIPTION
  This cmdlet is designed to use MSAL.PS to get an access token through an app registration, and then store in a global access token variable
  .PARAMETER clientID
  The client ID for the app registration used
  .PARAMETER clientSecret
  The secret used for the app registration
  .PARAMETER tenantID
  The tenant id for the app registration
#>

function Get-GraphAccessToken{
  [CmdletBinding()]
  param(
    [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][string]$clientID,
    [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][SecureString]$clientSecret,
    [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][string]$tenantID
  )
  if($(Test-GraphAcessToken $global:accessToken)){
    return $global:accessToken
  }
  $accessTokenVars = @{
    "clientID" = $clientID
    "clientSecret" = $clientSecret
    "tenantID" = $tenantID
    "ForceRefresh" = $true
  }
  $global:accessToken = Get-MsalToken @accessTokenVars
}
#EndRegion './Public/Get-GraphAccessToken.ps1' 29
#Region './Public/Get-GraphHeader.ps1' 0
<#
  .DESCRIPTION
  This cmdlet is designed to format the graph header for the REST api calls
  .PARAMETER ConsistencyLevel
  This field will add the ConsistencyLevel variable to eventual
#>

function Get-GraphHeader{
  [CmdletBinding()]
  param(
    [Parameter()][switch]$ConsistencyLevel
  )
  if(!$(Test-GraphAcessToken $global:accessToken)){
    throw "Please Call Get-GraphAccessToken before calling this cmdlet"
  }
  $headerVars = @{
    Authorization = "Bearer $($global:accessToken.AccessToken)"
    "Content-Type" = "application/json"
  }
  if($ConsistencyLevel){
    $headerVars.Add("ConsistencyLevel","eventual")
  }
  return $headerVars
}
#EndRegion './Public/Get-GraphHeader.ps1' 24
#Region './Public/Get-GraphMail.ps1' 0
<#
  .DESCRIPTION
  This cmdlet is designed to read email from a specific mailbox
  .PARAMETER emailAddress
  The email address of the account that we are reading from
  .PARAMETER folder
  The ID of the folder that we want to read from, if it is not the whole mailbox
  .PARAMETER unread
  If we want to return only unread email
#>

function Get-GraphMail{
  [CmdletBinding()]
  param(
    [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][string]$emailAddress,
    [Parameter()][ValidateNotNullOrEmpty()][string]$folder,
    [Parameter()][switch]$unread
  )
  if(!$(Test-GraphAcessToken $global:accessToken)){
    throw "Please Call Get-GraphAccessToken before calling this cmdlet"
  }
  $headers = Get-GraphHeader
  $uri = "https://graph.microsoft.com/beta/users/$($emailAddress)/messages"
  $FilterList = [System.Collections.Generic.List[PSCustomObject]]@()
  if ($folder) {
    $FilterList.Add("parentFolderId eq '$($folderInID)'") | Out-Null
  }  
  if ($unread) {
    $FilterList.Add("isRead eq false") | Out-Null
  } 
  if($FilterList -ne "")
  {
    $FilterExpression = [scriptblock]::Create(($FilterList -join " and "))
    $uri = "$($uri)?`$filter=$($FilterExpression)"
  }
  try{
    $results = Invoke-RestMethod -Method Get -Uri $uri -Headers $headers -StatusCodeVariable statusCode
    return $results.value
  }
  catch{
    throw "Unable to get emails from mailbox."
  }
}
#EndRegion './Public/Get-GraphMail.ps1' 43
#Region './Public/Get-KeyVaultSecret.ps1' 0
<#
  .DESCRIPTION
  This cmdlet is used to connect to a keyvault as the machine identity of the Azure Machine it is running under
  .PARAMETER vaultName
  The name of the vault that we want to check
  .PARAMETER secretName
  The name of the secret we want to recover
  .EXAMPLE
  Check a secret out of a specific vault
    Get-KeyVaultSecret -vaultName <VAULT> -secretName <SECRET>
#>

function Get-KeyVaultSecret{
  [CmdletBinding()]
  [OutputType([System.String])]
  param(
    [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][string]$vaultName,
    [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][string]$secretName
  )
  # The URI for the vault that we want to access
  $keyVaultURI = "https://$($vaultName).vault.azure.net/secrets/$($secretName)?api-version=2016-10-01"
  # Using the identity of the virtual machine account running the script
  $response = Invoke-RestMethod -Uri 'http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=https%3A%2F%2Fvault.azure.net' -Method GET -Headers @{Metadata="true"}
  # What the vault token is
  $keyVaultToken = $response.access_token
  try{
    # Get the relevant secret and return it
    $secret = Invoke-RestMethod -Uri $keyVaultURI -Method GET -Headers @{Authorization="Bearer $KeyVaultToken"}
    return $secret.Value | ConvertFrom-Json
  }
  # Error handling possible expected errors
  catch{
    if(($Error[0] -match "The remote name could not be resolved")){
      $message = "Error: Attempting to connect to Azure Key vault URI $($keyVaultURI)`n$($_)"
    }
    elseif(($Error[0] -match "Unauthorized")){
      $message = "Error: No authorization to Azure Key Vault URI $($keyVaultURI)`n$($_)"
    }
    elseif(($Error[0] -match "SecretNotFound")){
      $message = "Error: The secret $($secretName) is not found in Azure Key Vault URI $($keyVaultURI)`n$($_)"
    }
    else{
      $message = "Error: Unknown error connection to Azure Key vault URI $($keyVaultURI)`n$($_)"
    }
    Write-EventLog -LogName "Application" -Source "PowerShell Universal Scripts" -EntryType "Warning" -EventId 1001 -Message $message
    return $message
  }
}
#EndRegion './Public/Get-KeyVaultSecret.ps1' 48
#Region './Public/Get-Values.ps1' 0
<#
  .DESCRIPTION
  This cmdlet is designed to help PSU scripts for display of values
  .PARAMETER fields
  What fields to display
#>

function Get-Values(){
  param(
    [Parameter(Mandatory)][string[]]$fields,
    [Parameter(Mandatory)][PSCustomObject]$item
  )  
  $value = ""
  # Loop through the array to take not of what values were selected, and then return that value.
  foreach($i in $fields){
    if($item.$i){
      if($i.Contains('-1')){$value += $i.Substring(0,$i.Length-2) + ","}
      else{$value += $i + ","}
    }
  }
  return $value.Replace("-"," ")
}
#EndRegion './Public/Get-Values.ps1' 22
#Region './Public/Get-YesNo.ps1' 0
<#
  .DESCRIPTION
  This cmdlet is designed to help PSU scripts for display of yes/no
  .PARAMETER fields
  What fields to display
#>

function Get-YesNo(){
  [CmdletBinding()]
  [OutputType([string])]
  param(
    [Parameter(Mandatory = $true)][string[]]$fields,
    [Parameter(Mandatory)][PSCustomObject]$item
  )
  # Loop through values that are passed, and if exist, return yes, otherwise return No
  foreach($i in $fields){
    if($item.$i){return "Yes"}
  }
  return "No"
}  
#EndRegion './Public/Get-YesNo.ps1' 20
#Region './Public/Move-GraphMail.ps1' 0
<#
  .DESCRIPTION
  This cmdlet is designed to move emails between folders in a mailbox
  .PARAMETER id
  The id of the mail message we are acting on
  .PARAMETER emailAddress
  The email address of the account that we are reading from
  .PARAMETER folder
  The id of the folder that we are moving the message to
#>

function Move-GraphMail{
  [CmdletBinding()]
  param(
    [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][string]$id,
    [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][string]$emailAddress,
    [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][string]$folder
  )
  if(!$(Test-GraphAcessToken $global:accessToken)){
    throw "Please Call Get-GraphAccessToken before calling this cmdlet"
  }  
  $headers = Get-GraphHeader
  # Body Content
  $body = @{
    "destinationId" = $folder
  } | ConvertTo-Json 
  $uri = "https://graph.microsoft.com/beta/users/$($emailAddress)/messages/$($id)/move"
  $results = Invoke-RestMethod -Method Post -Uri $uri -Headers $headers -Body $body -StatusCodeVariable statusCode
  # Return Results
  if($statusCode -in (200,201)){
    return $results
  }
  else{
    throw "Unable to move email."
  }      
}
#EndRegion './Public/Move-GraphMail.ps1' 36
#Region './Public/New-RandomString.ps1' 0
<#
  .DESCRIPTION
  This cmdet will generate a random character string based on inputs passed to it.
  .PARAMETER length
  The number of characters you want the random string to contain.
  .PARAMETER characters
  The list of characters that you want it to use to generate the random string
  .EXAMPLE
  New-RandomString -length 10 -characters 'abcdefghiklmnoprstuvwxyzABCDEFGHKLMNOPRSTUVWXYZ1234567890!@#$%^&*'
    Will Generate a random string of 10 characters in length with the characters in abcdefghiklmnoprstuvwxyzABCDEFGHKLMNOPRSTUVWXYZ1234567890!@#$%^&*
#>

function New-RandomString{
  [CmdletBinding()]
  [OutputType([System.String])]
  param(
    [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][int]$length,
    [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][string]$characters
  )
  # Generate a random string based on the length and characters passed
  $randomString = 1..$length | ForEach-Object { Get-Random -Maximum $characters.length}
  $private:ofs = ""
  return [string]$characters[$randomString]
}
#EndRegion './Public/New-RandomString.ps1' 24
#Region './Public/Set-GraphMailRead.ps1' 0
<#
  .DESCRIPTION
  This cmdlet is designed to mark a specific email as read
  .PARAMETER id
  The id of the mail message we are acting on
  .PARAMETER emailAddress
  The email address of the account that we are reading from
#>

function Set-GraphMailRead{
  [CmdletBinding()]
  param(
    [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][string]$id,
    [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][string]$emailAddress
  )
  if(!$(Test-GraphAcessToken $global:accessToken)){
    throw "Please Call Get-GraphAccessToken before calling this cmdlet"
  }  
  $headers = Get-GraphHeader
  # Body Content
  $body = @{
    "isRead" = $true
  } | ConvertTo-Json
  # Execute Graph Call
  $uri = "https://graph.microsoft.com/beta/users/$($emailAddress)/messages/$($id)"
  $results = Invoke-RestMethod -Method Patch -Uri $uri -Headers $headers -Body $body -StatusCodeVariable statusCode
  # Return Results
  if($statusCode -in (200,201)){
    return $results
  }
  else{
    throw "Unable to mark email as read."
  }  
}
#EndRegion './Public/Set-GraphMailRead.ps1' 34
#Region './Public/Test-AllowedGroupMember.ps1' 0
<#
  .DESCRIPTION
  This cmdlet is used to check to see if a specific user belongs to a group that is passed
  .PARAMETER groupList
  Array of the groups to check
  .PARAMETER domain
  If active directory, what domain to check. If you use this, it ignores any of the Az parameters
  .PARAMETER AzAppRegistration
  The client id of the azure app registration working under
  .PARAMETER AzTenant
  The directory id for the Azure AD tenant
  .PARAMETER AzSecret
  The client secret used to connect to MS Graph
  .EXAMPLE
  Check for a specific user in active directory
    Test-AllowedGroupMember -userPrincipalName <UPN> -groupList @("GROUPNAME") -domain <DOMAIN>
  Check for a specific user in Azure AD group
    Test-AllowedGroupMember -userPrincipalName <UPN> -groupList @("GROUPNAME") -AzTenant $AzTenant -AzAppRegistration $AzAppRegistration -AzSecret $Secret
#>

function Test-AllowedGroupMember{
  [CmdletBinding()]
  [OutputType([System.Boolean])]
  param(
    [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][string]$userPrincipalName,
    [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][Object[]]$groupList,
    [Parameter()][string]$domain,
    [Parameter()][string]$AzAppRegistration,
    [Parameter()][string]$AzTenant,
    [Parameter()][pscredential]$AzSecret
    
  )
  # Nested function to be able to recurse through groups in Azure AD since Get-MGGroupMembers does not have this function currently
  function getNestedMembers{
    param(
      [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][string]$groupId,
      [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][string]$userPrincipalName
    )
    # Set found to false
    $results = $false
    # Get memberes of the group that was passed
    $members = Get-MgGroupMember -All -GroupId $groupId
    # If the username is found return true
    if($userPrincipalName -in $members.AdditionalProperties.userPrincipalName){
      return $true
    }
    # If not found, check the list for other nested groups
    else{
      $groups = $members | where-object {$_.AdditionalProperties.'@odata.type' -eq "#microsoft.graph.group"}
      # Loop through those groups those nested function
      foreach($group in $groups){
        $results = getNestedMembers -groupId $groupId -userPrincipalName $userPrincipalName
        if($results -eq $true){
          # if the results returned are true, return true.
          return $true
        }
      }
    }
  }
  # If set to query Azure AD Groups connect to MS Graph
  if($AzAppRegistration){
    # Connect to MS Graph
    $msalToken = Get-MsalToken -clientID $AzAppRegistration -clientSecret $AzSecret.Password -tenantID $AzTenant
    Connect-MgGraph -AccessToken $msalToken.AccessToken | Out-Null
  }
  foreach($group in $groupList){
    try{
      if($domain){
        # Get all the members and nested members of the group in Active Directory
        $members = Get-ADGroupMember -Recursive -Server $domain -Identity $group -ErrorAction SilentlyContinue  | where-object {$_.objectClass -eq 'User'} | Get-ADUser | select-object UserPrincipalName
        # Check to see if the list contains the expected UPN and if return true
        if($members.UserPrincipalName -contains $userPrincipalName){
          return $true
        }        
      }
      else{
        # Get the group from Azure AD
        $groups = Get-MGgroup -Filter "DisplayName eq '$($group)'"
        # Loop through if there are multiple groups with the same name
        foreach($group in $groups){
          # Get the results of the function to recurse through the groups
          $results = getNestedMembers -groupId $group.id -userPrincipalName $userPrincipalName
          # Return true if correct
          if($results -eq $true){
            return $true
          }
        }
      }
    }
    catch{
      throw "An error occured while processing group $($group) : $($Error[0])"
    }
  }
  return $false
}
#EndRegion './Public/Test-AllowedGroupMember.ps1' 95
#Region './Public/Test-GraphAcessToken.ps1' 0
<#
  .DESCRIPTION
  This cmdlet is tests to see if the passed variable is not null, and expires in less than 10 minutes
  .PARAMETER token
  The current access tokenb variable
#>

function Test-GraphAcessToken{
  [CmdletBinding()]
  param(
    [Parameter()][System.Object]$token
  )
  if(!$token){
    return $false
  }
  $expiryTime = $token.ExpiresOn - (Get-Date)
  if($expiryTime.Minutes -lt 10){
    return $false
  }
  else{
    return $true
  }
}
#EndRegion './Public/Test-GraphAcessToken.ps1' 23
#Region './Public/Test-SamAccountName.ps1' 0
<#
  .DESCRIPTION
  This cmdlet is designed to check Active Directory for a valid samAccountName when creating/changing user.
  .PARAMETER samAccountName
  The account name we want to test for
  .PARAMETER server
  The server that we want to test against
  .EXAMPLE
  Check for a specific samAccountName
    Test-SamAccountName -samAccountName <NAME> -server <SERVER>
#>

function Test-SamAccountName{
  [CmdletBinding()]
  [OutputType([System.String],[System.Boolean])]
  param(
    [Parameter(Mandatory = $true)]$samAccountName,
    [Parameter(Mandatory = $true)]$server    
  )
  # Default Addition at the end of the name if it exists.
  $postFix = 2
  # Loop through to try to find a valid samAccountName or fail if loops too many times
  do{
    try{
      # Check to see if the user already exists.
      Get-ADUser -Identity $samAccountName -Server $server | Out-Null
      # If it does exist, then add the postfix
      if($postFix -eq 2){
        $samAccountName = "$($samAccountName)$($postFix)"
      }
      # If postfix is greater than default, then remove it (as we max at 9) to add the new postfix
      else{
        $samAccountName = "$($samAccountName.substring(0,$samAccountName.length -1))$($postFix)"
      } 
    }
    # If the account doesn't exist, return the samAccountName as good
    catch [Microsoft.ActiveDirectory.Management.ADIdentityResolutionException] {
      return $samAccountName
    }
    catch {
      throw $Error[0]
    }
    $postFix++
  }while($postFix -lt 10)  
  # Return false if we couldn't find a valid samAccountName we could use
  return $false  
}
#EndRegion './Public/Test-SamAccountName.ps1' 47