Public/Invoke-AdSync.ps1

# .ExternalHelp PSADSync-Help.xml
function Invoke-AdSync {
    [OutputType([void])]
    [CmdletBinding(SupportsShouldProcess, DefaultParameterSetName = 'Default')]
    param
    (
        [Parameter(Mandatory)]
        [ValidateNotNullOrEmpty()]
        [string]$CsvFilePath,

        [Parameter(Mandatory)]
        [ValidateNotNullOrEmpty()]
        [hashtable]$FieldSyncMap,

        [Parameter(Mandatory)]
        [ValidateNotNullOrEmpty()]
        [hashtable]$FieldMatchMap,

        [Parameter()]
        [ValidateNotNullOrEmpty()]
        [hashtable]$FieldValueMap,

        [Parameter(Mandatory, ParameterSetName = 'CreateNewUsers')]
        [ValidateNotNullOrEmpty()]
        [hashtable]$UserMatchMap,

        [Parameter(Mandatory, ParameterSetName = 'CreateNewUsers')]
        [ValidateNotNullOrEmpty()]
        [ValidateSet('FirstInitialLastName', 'FirstNameLastName', 'FirstNameDotLastName', 'LastNameFirstTwoFirstNameChars')]
        [string]$UsernamePattern,

        [Parameter()]
        [ValidateNotNullOrEmpty()]
        [scriptblock]$UserTerminationAction,

        [Parameter()]
        [ValidateNotNullOrEmpty()]
        [switch]$ReportOnly,

        [Parameter()]
        [ValidateNotNullOrEmpty()]
        [hashtable]$Exclude
    )
    begin {
        $ErrorActionPreference = 'Stop'
    }
    process {
        try {
            $getCsvParams = @{
                CsvFilePath = $CsvFilePath
            }

            if ($PSBoundParameters.ContainsKey('Exclude')) {
                if (-not (TestCsvHeaderExists -CsvFilePath $CsvFilePath -Header ([array]$Exclude.Keys))) {
                    throw 'One or more CSV headers excluded with -Exclude do not exist in the CSV file.'
                }
                $getCsvParams.Exclude = $Exclude
            }

            if (-not (TestFieldMapIsValid -FieldSyncMap $FieldSyncMap -CsvFilePath $CsvFilePath)) {
                throw 'Invalid attribute found in FieldSyncMap.'
            }
            if (-not (TestFieldMapIsValid -FieldMatchMap $FieldMatchMap -CsvFilePath $CsvFilePath)) {
                throw 'Invalid attribute found in FieldMatchMap.'
            }

            if ($PSBoundParameters.ContainsKey('FieldValueMap')) {
                if (-not (TestFieldMapIsValid -FieldValueMap $FieldValueMap -CsvFilePath $CsvFilePath)) {
                    throw 'Invalid attribute found in FieldValueMap.'
                }    
            }

            $FieldSyncMap.GetEnumerator().where({ $_.Value -is 'string' }).foreach({
                    if (-not (TestIsValidAdAttribute -Name $_.Value)) {
                        throw 'One or more AD attributes in FieldSyncMap do not exist. Use Get-AvailableAdUserAttribute for a list of available attributes.'
                    }
                })

            Write-Output 'Enumerating all Active Directory users. This may take a few minutes depending on the number of users...'
            if (-not ($script:adUsers = Get-CompanyAdUser -FieldMatchMap $FieldMatchMap -FieldSyncMap $FieldSyncMap)) {
                throw 'No AD users found'
            }

            Write-Output 'Enumerating all CSV users...'
            if (-not ($csvusers = Get-CompanyCsvUser @getCsvParams)) {
                throw 'No CSV users found'
            }

            $script:totalSteps = @($csvusers).Count
            $stepCounter = 0
            $rowsProcessed = 1
            @($csvUsers).foreach({
                    try {
                        ## account for the CSV header row
                        $csvRow = $rowsProcessed + 1
                        $logEntry = $true
                        if ($ReportOnly.IsPresent) {
                            $prgMsg = "Attempting to find attribute mismatch for user in CSV row [$($stepCounter + 1)]"
                        } else {
                            $prgMsg = "Attempting to find and sync AD any attribute mismatches for user in CSV row [$($stepCounter + 1)]"
                        }
                        WriteProgressHelper -Message $prgMsg -StepNumber ($stepCounter++)
                        $csvUser = $_
                        if ($adUserMatch = FindUserMatch -CsvUser $csvUser -FieldMatchMap $FieldMatchMap) {
                            $CSVAttemptedMatchIds = $aduserMatch.CSVAttemptedMatchIds
                            $csvIdValue = ($CSVAttemptedMatchIds | % { $csvUser.$_ }) -join ','
                            $csvIdField = $CSVAttemptedMatchIds -join ','

                            #region FieldValueMap check
                            if ($PSBoundParameters.ContainsKey('FieldValueMap')) {
                                $selectParams = @{ 
                                    Property = @('*') 
                                    Exclude  = [array]($FieldValueMap.Keys)
                                }
                                @($FieldValueMap.GetEnumerator()).foreach({
                                        $selectParams.Property += @{ 
                                            Name       = $_.Key
                                            Expression = $_.Value
                                        }
                                    })
                                $csvUser = $csvUser | Select-Object @selectParams
                            }
                            #endregion
                        
                            ## User termination check
                            if ((TestIsUserTerminationEnabled) -and (TestUserTerminated -CsvUser $csvUser)) {
                                if (-not $ReportOnly.IsPresent) {
                                    $termParams = @{
                                        AdUser = $adUserMatch.MatchedAduser
                                    }
                                    if ($PSBoundParameters.ContainsKey('UserTerminationAction')) {
                                        $termParams.UserTerminationAction = $UserTerminationAction
                                    }
                                    InvokeUserTermination @termParams
                                }

                                $logAttribs = @{
                                    CSVAttributeName  = 'UserTermination'
                                    CSVAttributeValue = 'UserTermination'
                                    ADAttributeName   = 'UserTermination'
                                    ADAttributeValue  = 'UserTermination'
                                    Message           = $_.Exception.Message
                                }
                            } else {
                                $findParams = @{
                                    AdUser       = $adUserMatch.MatchedAdUser
                                    CsvUser      = $csvUser
                                    FieldSyncMap = $FieldSyncMap
                                }
                                $attribMismatches = FindAttributeMismatch @findParams
                                if ($attribMismatches) {
                                    $logEntry = $false
                                    $attribMismatches | foreach {
                                        $logAttribs = @{
                                            CSVAttributeName  = 'AttributeChange - {0}' -f [string]($_.CSVField.Keys)
                                            CSVAttributeValue = [string]($_.CSVField.Values)
                                            ADAttributeName   = 'AttributeChange - {0}' -f [string]($_.ActiveDirectoryAttribute.Keys)
                                            ADAttributeValue  = [string]($_.ActiveDirectoryAttribute.Values)
                                            Message           = $null
                                        }
                                        WriteLog -CsvIdentifierField $csvIdField -CsvIdentifierValue $csvIdValue -Attributes $logAttribs    
                                    }
                                    
                                    if (-not $ReportOnly.IsPresent) {
                                        $syncParams = @{
                                            CsvUser                   = $csvUser
                                            ActiveDirectoryAttributes = $attribMismatches.ADShouldBe
                                            Identity                  = $adUserMatch.MatchedAduser.samAccountName
                                        }
                                        Write-Verbose -Message "Running SyncCompanyUser with params: [$($syncParams | Out-String)]"
                                        SyncCompanyUser @syncParams
                                    }
                                } elseif ($attribMismatches -eq $false) {
                                    throw 'Error occurred in FindAttributeMismatch'
                                } else {
                                    Write-Verbose -Message "No attributes found to be mismatched between CSV and AD user account for user [$csvIdValue]"
                                    $logAttribs = @{
                                        CSVAttributeName  = 'AlreadyInSync'
                                        CSVAttributeValue = 'AlreadyInSync'
                                        ADAttributeName   = 'AlreadyInSync'
                                        ADAttributeValue  = 'AlreadyInSync'
                                        Message           = $null
                                    }
                                }
                            }
                        } else {
                            ## No user match was found
                            if (-not ($csvIds = @(GetCsvIdField -CsvUser $csvUser -FieldMatchMap $FieldMatchMap).where({ $_.Field }))) {
                                Write-Warning -Message  'No CSV ID fields were found.'
                                $csvIdField = "CSV Row: $csvRow"
                                $csvIdValue = "CSV Row: $csvRow"

                                $logAttribs = @{
                                    CSVAttributeName  = "CSV Row: $csvRow"
                                    CSVAttributeValue = "CSV Row: $csvRow"
                                    ADAttributeName   = 'NoMatch'
                                    ADAttributeValue  = 'NoMatch'
                                    Message           = $null
                                }
                            } elseif ($PSBoundParameters.ContainsKey('UserMatchMap') -and (TestShouldCreateNewUser -CsvUser $csvUser)) {
                                $csvIdField = $csvIds.Field -join ','
                                if (-not $ReportOnly.IsPresent) {
                                    $newUserParams = @{
                                        CsvUser         = $csvUser
                                        UsernamePattern = $UsernamePattern
                                        UserMatchMap    = $UserMatchMap
                                        RandomPassword  = $true
                                        FieldSyncMap    = $FieldSyncMap
                                        FieldMatchMap   = $FieldMatchMap
                                    }
                                    if ($PSBoundParameters.ContainsKey('FieldValueMap')) {
                                        $newUserParams.FieldValueMap = $FieldValueMap
                                    }
                                    $newAdUser = New-CompanyAdUser @newUserParams
                                }

                                $logAttribs = @{
                                    CSVAttributeName  = 'NewUserCreated'
                                    CSVAttributeValue = 'NewUserCreated'
                                    ADAttributeName   = 'NewUserCreated'
                                    ADAttributeValue  = 'NewUserCreated'
                                    Message           = "UserName: [$($newAdUser.Name)] - Password: [$($newAdUser.Password)]"
                                }
                                $csvIdValue = ($csvIds | foreach { $csvUser.($_.Field) })
                            } else {
                                $csvIdField = $csvIds.Field -join ','
                                $csvIdValue = "CSV Row: $csvRow"

                                $logAttribs = @{
                                    CSVAttributeName  = "CSV Row: $csvRow"
                                    CSVAttributeValue = "CSV Row: $csvRow"
                                    ADAttributeName   = 'NoMatch'
                                    ADAttributeValue  = 'NoMatch'
                                    Message           = $null
                                }
                            }
                        }
                    
                    } catch {
                        $csvIdField = "CSV Row: $csvRow"
                        $csvIdValue = "CSV Row: $csvRow"
                        $logAttribs = @{
                            CSVAttributeName  = 'Error'
                            CSVAttributeValue = 'Error'
                            ADAttributeName   = 'Error'
                            ADAttributeValue  = 'Error'
                            Message           = $_.Exception.Message
                        }
                    } finally {
                        if ($logEntry) {
                            WriteLog -CsvIdentifierField $csvIdField -CsvIdentifierValue $csvIdValue -Attributes $logAttribs
                        }
                        $rowsProcessed++
                    }
                })
        } catch {
            Write-Error -Message "Function: $($MyInvocation.MyCommand.Name) Error: $($_.Exception.Message)"
        } finally {
            Remove-Variable -Scope Script -Name adUsers -ErrorAction Ignore
        }
    }
}