Tag Archives: Active Directory

WEM: Notes from the Field 2

whatifitoldyou

Quick Note

If you are using group policy (and why wouldn’t you) to configure your WEM agents, pay close attention to leading and/or trailing spaces. This has happened twice to me with agent versions 4.2 and 4.3.

The Connection Broker Name and Site Name fields in the Workspace Environment management\Agent Host Configuration administrative template must NOT contain any leading or trailing spaces. If you have any, then the agent cannot communicate with your WEM Broker server nor can it load the correct Configuration Site.

Thanks for reading,
Alain

Powershell: The Ultimate User Deletion Script?

akkyu

Intro

I’ve spent the better part of 4 days working on a monster script. I needed this script to perform the following actions:

  1. Take a list of usernames and ensure they exist in Active Directory (AD).
  2. Take a list of usernames and disable their AD accounts
  3. Take a list of usernames and delete their AD accounts
  4. Take a list of usernames and delete any files in their User profile and RDS profile
  5. Take a list of usernames and delete any local profiles present on XenApp Servers
  6. Take a list of usernames and delete any of their assigned XenDesktops
  7. Take a list of usernames and remove their account from any published application
  8. Review this list of usernames and remove any accounts that should not be removed
  9. Provide feedback with write-verbose messages and create a log file of any actions

Needless to say, this script can easily be broken down into different functions for reusability, but I wanted an all-in-one script that would be used by other support team members. This script assumes PowerShell 2.0 and that the following cmdlets are available:

  • Microsoft Active Directory
  • Citrix XenApp Commands (SDK for XenApp 6 or 6.5 so you can run remote commands)
  • Citrix XenDesktop Commands

The user who runs the script should be a AD domain , XenApp and XenDesktop admin.  I do not recommend using this script as is, but you may find parts you can use in your environment.  Also review the URL’s in the .LINK section. I used ideas and code from these web pages to help write this script.

You can edit the #CONSTANTS section for your environment.

The Script

<#
.SYNOPSIS
	Takes ad username or object of usernames and deletes user's resources and account from the domain and Citrix environment.
.DESCRIPTION
	Removes a user's account and resources from the AD domain and Citrix environment.

	It is recommended that this script be run as an admin. In addition, the Microsoft Active Directory, XenDesktop, XenApp Powershell Cmdlets must be available for user and desktop deletion.
.PARAMETER username
      Required parameter.
      User account(s) that will be deleted. User accounts must be disabled before deletion. See disable parameter.
.PARAMETER disable
    Optional switch parameter.
    Defaults to $false.
    If present, user accounts will just be disabled.
.EXAMPLE
	PS C:\PSScript > .\delete-citrixuser.ps1 -username "someuser"

	Will use all default values.
    No feedback messages will be shown.
    All user accounts are expected to be already disabled otherwise, no accounts will be deleted.
.EXAMPLE
	PS C:\PSScript > .\delete-citrixuser.ps1 -username "someuser" -verbose

	Will use all default values.
    Feedback/progress messages will be shown.
    All user accounts are expected to be disabled otherwise, no accounts will be deleted.
.EXAMPLE
    PS C:\PSScript > .\delete-citrixuser.ps1 -username "someuser" -disable -verbose

    Will use all default values.
	Will set AD user accounts to disabled.
    Feedback/progress messages will be shown.
.INPUTS
	Username or object of usernames.
.OUTPUTS
	To see feedback messages use the -verbose common parameter. No objects are output from this script.  This script creates a user deletion log.
.NOTES
	NAME: delete-citrixuser.ps1
	VERSION: 1.00
    CHANGE LOG - Version - When - What - Who
                 1.00 - 07/21/2014 - Initial script - Alain Assaf
	AUTHOR: Alain Assaf
	LASTEDIT: July 21, 2014
.LINK
    http://www.linkedin.com/in/alainassaf/
    http://wagthereal.com
    http://stackoverflow.com/questions/11605893/checking-for-the-existence-of-an-ad-object-how-do-i-avoid-an-ugly-error-message
    http://powershell.com/cs/blogs/tips/archive/2009/06/26/using-switch-parameters.aspx
    http://technet.microsoft.com/en-us/library/ee692802.aspx
    http://explorepowershell.com/2012/12/24/checking-setting-remote-desktop-services-profile-settings/
    http://winpowershell.blogspot.com/2006/08/suppressing-output-using-out-null-and.html
    http://blogs.msdn.com/b/powershell/archive/2009/12/29/arguments-for-remote-commands.aspx
    http://techibee.com/powershell/powershell-script-to-delete-windows-user-profiles-on-windows-7windows-2008-r2/1556
    http://stackoverflow.com/questions/12727388/wildcard-with-variable-in-get-aduser
    http://ss64.com/ps/do.html
    http://technet.microsoft.com/en-us/library/ee177002.aspx
    http://technet.microsoft.com/en-us/library/ee176955.aspx
    http://support.citrix.com/static/kc/CTX127254/help/
    http://social.technet.microsoft.com/Forums/windowsserver/en-US/79f9be3c-2945-471d-8a60-a1390f376d6e/removeaduser-has-no-force-option?forum=winserverpowershell
    http://titlerequired.com/2012/10/29/powershell-make-it-do-something-useful/
#>

Param(
    [parameter(Position = 0, Mandatory=$True )]
    [ValidateNotNullOrEmpty()]
	$username,

    [parameter(Position = 1, Mandatory=$False )]
    [ValidateNotNullOrEmpty()]
    [switch]$disable
	)

### FUNCTION: get-mymodule #####################################################
Function Get-MyModule {
    Param([string]$name)
    if(-not(Get-Module -name $name)) {
        if(Get-Module -ListAvailable | Where-Object { $_.name -eq $name }) {
            Import-Module -Name $name
            $true
        } #end if module available then import
        else { $false } #module not available
        } # end if not module
    else { $true } #module already loaded
}
### FUNCTION: get-mymodule #####################################################

### FUNCTION: get-mysnapin #####################################################
Function Get-MySnapin {
    Param([string]$name)
    if(-not(Get-PSSnapin -name $name)) {
        if(Get-PSSnapin -Registered | Where-Object { $_.name -eq $name }) {
            add-PSSnapin -Name $name
            $true
        } #end if module available then import
        else { $false } #snapin not available
        } # end if not snapin
    else { $true } #snapin already loaded
}
### FUNCTION: get-mysnapin #####################################################

### FUNCTION: Check-ADUser #####################################################
#Function to check if user account exists in AD
Function Check-ADUser
{
    Param ($usrname)

    $isuser = $(try {Get-ADUser $usrname} catch {$null})
    if ($isuser -ne $null) {
        return $true
    } else {
        return $false
    }
}
### FUNCTION: Check-ADUser #####################################################

### FUNCTION: Remove-UserProfile################################################
Function Remove-UserProfile
{
    param(
        [parameter(ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)]
        [ValidateNotNullOrEmpty()]
        [string[]]$ComputerName = $env:computername,            

        [parameter(mandatory=$true)]
        [ValidateNotNullOrEmpty()]
        [string]$UserName, 

        [parameter(mandatory=$false)]
        [ValidateNotNullOrEmpty()]
        [string]$localpath 

    )            

    foreach($Computer in $ComputerName) {
        Write-Verbose "Looking for local profiles on $Computer"
        if(Test-Connection -ComputerName $Computer -Count 1 -ea 0) {
            $UserProfile = Get-WmiObject Win32_UserProfile -Computer $Computer -ea 0 -filter “localpath='$localpath'”
            if (!$UserProfile) {
                write-Verbose “$Username not found on $Computer”
            } else {
                $UserProfile | Remove-WmiObject
                Write-Verbose "$UserName profile deleted successfully on $Computer"
            }
        }  else {
            write-verbose "Cannot connect to $Computer"
        }
    }
}
### FUNCTION: Remove-UserProfile################################################

#Constants
$datetime = get-date -format "MM-dd-yyyy_HH-mm"
$Domain=(Get-WmiObject Win32_ComputerSystem).Domain
$UserOU="OU=Users,DC=domain,DC=local"
$CloudProfilePath = "\\userprofileserver.domain.local\upm\"
$CloudRDSProfilePath = "\\userprofileserver.domain.local\profiles\"
$ScriptRunner = (get-aduser $env:username | select name).name
$XAZDC = "XenAppZDC.domain.local"
$PSModules = ("activedirectory")
$PSSnapins = (
    "Citrix.ADIdentity.Admin.V1",
    "Citrix.Broker.Admin.V1",
    "Citrix.Common.Commands",
    "Citrix.Common.GroupPolicy",
    "Citrix.Configuration.Admin.V1",
    "Citrix.Host.Admin.V1",
    "Citrix.LicensingConfig.Admin.V1",
    "Citrix.MachineCreation.Admin.V1",
    "Citrix.MachineIdentity.Admin.V1",
    "Citrix.XenApp.Commands")
$accountnames = New-Object System.Collections.ArrayList
#Any account in DONOTDELETE array will always be removed from accountnames array and will not be disabled or deleted.
$DONOTDELETE = New-Object System.Collections.ArrayList
$DONOTDELETE.Add("Guest")>$null
$DONOTDELETE.Add("krbtgt")>$null

#Get List of XenApp Servers
$XAServers = get-xaserver -ComputerName $XAZDC | select servername

#Populate accountnames
foreach ($a in $username) {$accountnames.Add($a)>$null}

#Remove any users in accountnames who are in the DONOTDELETE array
foreach ($b in $DONOTDELETE) {$accountnames.remove($b)>$null}

#initalize output array
$finalout = @()

#Import Module(s) and Snapin(s)
foreach ($module in $PSModules) {
    if (!(get-mymodule $module)) {
        write-verbose "$module PowerShell Cmdlet n ot available."
        write-verbose "Please run this script from a system with the $module PowerShell Cmdlets installed."
        exit
    }
}
foreach ($snapin in $PSSnapins) {
    if (!(get-MySnapin $snapin)) {
        write-verbose "$snapin PowerShell Cmdlet not available."
        write-verbose "Please run this script from a system with the $snapin PowerShell Cmdlets installed."
        exit
    }
}

#Confirm OU is valid
If (!([adsi]::Exists("LDAP://$UserOU"))) {
    write-verbose "$UserOU IS NOT VALID"
    write-verbose "Please use the following format: OU=Users,DC=domain,DC=local"
    Exit
}

#Confirm user(s) exists
foreach  ($usr in $accountnames) {
    $UserStatus = Check-ADUser $usr
    If ($UserStatus) { #User Exists - delete or disable user
        write-verbose "USER EXISTS: $usr"
        If ($disable) { #Disable is true - just disable the user
            write-verbose "DISABLING: $usr"
            disable-ADAccount -Identity $usr
            get-aduser $usr -properties info | foreach { Set-ADUser -Identity $_.samaccountname -Replace @{info="$($_.info)`r`nAccount disabled on: $datetime by $ScriptRunner"} }
            $finalout += "$usr disabled by $ScriptRunner on $datetime"
        } Else {        #Disable flag is false - delete user (if already disabled)
            #Confirm user account is disabled
            $citrixuser = Get-ADUser $usr -properties profilepath | select -ExcludeProperty disting*
            if ($citrixuser.Enabled) { #User account is not disabled - write error and go to next user
                write-verbose "$usr is not disabled. Please disable prior to deletion."
                $finalout += "$usr is not disabled. Please disable prior to deletion."
            } else { #user account is disabled clean up and delete user account.
                #Get User's profile and RDS profile path
                $userprofilepath = $citrixuser.profilepath
                $citrixuser = [ADSI]“LDAP://$citrixuser”
                $rdsprofilepath = $citrixuser.psbase.InvokeGet(“terminalservicesprofilepath”)
                #Delete user's profile and RDS profile
                if (test-path $userprofilepath) {
                    remove-item -Recurse -Force $userprofilepath
                    write-verbose "FOLDER DELETED: $userprofilepath"
                    $finalout += "FOLDER DELETED: $userprofilepath"
                } elseif (test-path ($userprofilepath + ".V2")) {
                    remove-item -Recurse -Force ($userprofilepath + ".V2")
                    write-verbose ("FOLDER DELETED: $userprofilepath" + ".V2")
                    $finalout += ("FOLDER DELETED: $userprofilepath" + ".V2")
                } else {
                    write-verbose ("FOLDER DOES NOT EXIST: $userprofilepath or $userprofilepath" + ".V2")
                }
                if (test-path $rdsprofilepath) {
                    remove-item -Recurse -Force $rdsprofilepath
                    write-verbose "FOLDER DELETED: $rdsprofilepath"
                    $finalout += "FOLDER DELETED: $rdsprofilepath"
                } elseif (test-path ($rdsprofilepath + ".V2")) {
                    remove-item -Recurse -Force ($rdsprofilepath + ".V2")
                    write-verbose ("FOLDER DELETED: $rdsprofilepath" + ".V2")
                    $finalout += ("FOLDER DELETED: $rdsprofilepath" + ".V2")
                } else {
                    write-verbose ("FOLDER DOES NOT EXIST: $rdsprofilepath or $rdsprofilepath" + ".V2")
                }
                #Delete user's local profile on XenApp servers
                foreach ($srv in $XAServers) {
                    $tmpdir = invoke-command -computername $srv.servername {"$env:homedrive\users"}
                    $usrdir = $tmpdir + "\" + $usr
                    remove-userprofile -computername $srv.servername -username $usr -localpath $usrdir.Replace("\","\\") -verbose
                }
                #Delete any assigned XenDesktops
                $user = $Domain.split(".")[0] + "\" + $usr
                $UserDesktops = Get-BrokerDesktop -AssociatedUserName $user -DesktopKind "Private"
                if ($UserDesktops -ne $null) {
                    foreach ($d in $UserDesktops) {
                        set-brokerprivatedesktop $d.machinename -InMaintenanceMode $true
                        do {
                            start-sleep -s 5
                            $result1 = Get-BrokerPrivateDesktop -MachineName $d.Machinename
                        } until ($result1.InMaintenanceMode -eq $true)
                        write-verbose "$d.machinename - maintenance mode enabled"
                        New-BrokerHostingPowerAction -Action 'Shutdown' -MachineName $d.machinename
                        do {
                            start-sleep -s 5
                            $result2 = Get-BrokerHostingPowerAction -MachineName $d.MachineName | select -last 1
                        } until ($result2.State -eq "Completed")
                        write-verbose "$d.machinename - powered down"
                    }
                    #Unassign user from desktop
                    Remove-BrokerUser -Name $user -Machine $d.machinename
                    #Remove desktop from DesktopGroup
                    remove-Brokermachine -Machine $d.machinename -desktopgroup $d.desktopgroupuid
                    #Delete desktop and AD Computer Account
                    Remove-BrokerMachine -MachineName $d.machinename
                    Remove-AcctADAccount -IdentityPoolName $d.Catalogname -ADAccountName $d.machinename -RemovalOption 'Delete'
                    write-verbose "$d.machinename deleted from Desktop Studio and Active Directory"
                    $finalout += "$d.machinename deleted from Desktop Studio and Active Directory"
                } else {
                    write-verbose "No desktops associated with $user"
                    $finalout += "No desktops associated with $user"
                }
                #Remove user from any Citrix published resources
                $apps = (Get-XAApplicationReport -ComputerName $XAZDC -BrowserName * | where {$_.Accounts -contains $user})
                if ([bool]($apps -ne $null)) { #Found useraccount assigned to some published resources
                    foreach ($app in $apps) {
                        Remove-XAApplicationAccount -ComputerName $xazdc -BrowserName $app.Browsername -Accounts $user
                    }
                } else {
                    write-verbose "$usr is not assigned to any published applications"
                }
                #Delete user account from AD
                remove-aduser -identity $usr -confirm:$false
                write-verbose "$user removed from $domain"
                $finalout += "$user removed from $domain"
            }
        }
    } Else { #User does not exist - stop and go to next user
        write-verbose "$usr does not exist."
    }
}

#CreateReport
if ($finalout -ne $null) {
    $LogFileFolder = "c:\_scripts"
#    $datetime = get-date -format "MM-dd-yyyy_HH-mm"
    $LogFileName = "Citrix_DeleteUser_Report" + $datetime + ".txt"
    $LogFile = $LogFileFolder + "\" + $LogFilename
    $finalout | ft -auto | out-file $Logfile -append
}

I look forward to any and all comments. I’m sure there are better ways to write some of the above. There’s always more than one way to skin a cat in PowerShell.

Thanks,
Alain

Powershell: Automating User Creation

Intro

I recently began a new job and it is unique for me. The organization I work for actually helps to generate revenue, and we are a customer of  corporate IT. This is a first for me where I’m not working directly for IT. I had just left a job where corporate viewed IT as too complex, too costly, and not responsive to the business. As a result, 80%-90% of the staff was fired (or re-badged) and all IT service and infrastructure was outsourced. As a manager, I had to preside over the knowledge transfer of my teams to the outsource company and then let  them go. At the same time, I had to maintain some productivity in a morale sapping environment. Needless it say is was rough, but many of my former team members got great jobs afterward as did I.

This outsourcing process had already occurred with my current employer. So, the business engages IT to implement projects, but in my case, they were not able to provide this particular service. So I find that I’m running an independent cloud implementation of XenDesktop. This includes AD (users, groups, OU’s, GPO’s – no trust with corporate AD), DHCP, DNS, File servers, XenApp, vDisk creation, Remote Access, Microsoft updates, and software testing/installs/upgrades. Basically everything from soup to nuts.

This project needs to grow to include many more users, but there is no automation present. So I’ve begun examining the biggest “pain” points in this environment to scale it out. First, we will look at user creation.

AD DC

It has been years since I’ve been saddled with user account management (except for service accounts and the like). Our cloud users have to remember a separate set of credentials to use our cloud environment; this is already a barrier to adoption. To relieve this, we have made our usernames match the corporate domain usernames, and left it up to the user to make the passwords the same. User account creation begins with the user filling out a SharePoint form. Then we start a manual process to create the user’s account. To relieve this, I exported a request to a CSV file and wrote a PowerShell script that runs in the cloud to create the user, set the password, set the user profile locations, and assign groups. I reviewed several websites for ideas and I noted them in my script’s help area.

Assumptions

For the purposes of this script, the CSV file has the following header fields:

Status Records status of user request in SharePoint site.
Name In the format of Lastname, Firstname
Account Corporate AD username
Password Corporate AD Password
Cloud Version Used to assign groups
Email Address Corporate email
Position/Title Title – used to assign groups
Office Number Work Phone
Mobile Number Mobile Phone
Department Cost Center Added to notes in user account
Manager Added to notes in user account (we do not assume that the manager has a user account in the Cloud AD domain)
Manager Office Number Added to notes in user account
Location or Region Used for contact info
Modality Used for Organization Info
Group Used for Organization Info

It is assumed that you are running this script as a domain admin and the server you are running it from has the Microsoft AD PowerShell cmdlets installed.

If you wish to get feedback from the script while it is running use the -verbose parameter – i.e. >create-clouduser.ps1 -verbose

TO DO’s

I don’t have an SMTP server setup in my cloud environment. When I do, I will add routines to the script to check a “New User” mailbox for new emails and then send confirmation to these users that their account was created. This will allow me to bridge the 2 domains (until corporate IT agrees to set up a domain trust).

The Script


<#
.SYNOPSIS
    Reads CSV file and creates a user in the YOURCOMPANY domain.
.DESCRIPTION
    Reads CSV file and creates a user in the YOURCOMPANY domain.  

    It is recommended that this script be run as an admin. In addition, the Microsoft Active Directory Powershell Cmdlets must be available for user creation.
.PARAMETER UserOU
    Defaults to OU=Users,DC=YOURCOMPANY,DC=local
    OU location where user account will be created. Input must match above format.
.PARAMETER UserFolder
    Defaults to any CSV file in C:\scripts\NewUserRequests Folder
    Enter a path to folder where CSV files exist. CSV files must have the following fields:
    Status
    Name
    Account
    Password
    Cloud Version
    Email Address
    Position/Title
    Office Number
    Mobile Number
    Department Cost Center
    Manager
    Manager Office Number
    Location or Region
    Modality
    Group
.EXAMPLE
    PS C:\PSScript > .\create-cloudixuser.ps1
    Will use all default values.
    User OU=Users,DC=YOURCOMPANY,DC=local
    UserFolder=c:\scripts\NewUserRequests
.EXAMPLE
    PS C:\PSScript > .\create-cloudixuser.ps1 -verbose
    Will use all default values.
    User OU=Users,DC=YOURCOMPANY,DC=local
    UserFolder=c:\scripts\NewUserRequests
    Will write feedback/progress messages.
.INPUTS
    None.  You cannot pipe objects to this script.
.OUTPUTS
    To see feedback messages use the -verbose common parameter. No objects are output from this script.  This script creates a user creation log.
.NOTES
    NAME: create-clouduser.ps1
    VERSION: 1.00
    CHANGE LOG - Version - When - What - Who
                 1.00 - 06/06/2014 - Initial script - Alain Assaf
    AUTHOR: Alain Assaf
    LASTEDIT: June 6, 2014
.LINK
<a href="http://www.linkedin.com/in/alainassaf/">http://www.linkedin.com/in/alainassaf/</a>
<a href="http://wagthereal.com">http://wagthereal.com</a>
<a href="http://windowsitpro.com/blog/what-do-not-do-powershell-part-1">http://windowsitpro.com/blog/what-do-not-do-powershell-part-1</a>
<a href="http://stackoverflow.com/questions/6828055/powershell-checking-if-ou-exist">http://stackoverflow.com/questions/6828055/powershell-checking-if-ou-exist</a>
<a href="http://blogs.metcorpconsulting.com/tech/?p=1723">http://blogs.metcorpconsulting.com/tech/?p=1723</a>
<a href="http://stackoverflow.com/questions/14902501/powershell-script-with-params-and-functions">http://stackoverflow.com/questions/14902501/powershell-script-with-params-and-functions</a>
<a href="http://stackoverflow.com/questions/11526285/how-to-count-objects-in-powershell">http://stackoverflow.com/questions/11526285/how-to-count-objects-in-powershell</a>
<a href="http://stackoverflow.com/questions/5203730/cut-off-text-in-string-after-before-seperator-in-powershell">http://stackoverflow.com/questions/5203730/cut-off-text-in-string-after-before-seperator-in-powershell</a>
<a href="http://dxpetti.com/blog/?p=442">http://dxpetti.com/blog/?p=442</a>
<a href="http://technet.microsoft.com/en-us/library/dd378958%28v=ws.10%29.aspx">http://technet.microsoft.com/en-us/library/dd378958%28v=ws.10%29.aspx</a>
<a href="http://powershell.org/wp/forums/topic/set-aduser-append-to-ad-notes-field/">http://powershell.org/wp/forums/topic/set-aduser-append-to-ad-notes-field/</a>
<a href="http://www.out-web.net/?p=1233">http://www.out-web.net/?p=1233</a>
<a href="http://blogs.technet.com/b/heyscriptingguy/archive/2010/07/11/hey-scripting-guy-weekend-scripter-checking-for-module-dependencies-in-windows-powershell.aspx">http://blogs.technet.com/b/heyscriptingguy/archive/2010/07/11/hey-scripting-guy-weekend-scripter-checking-for-module-dependencies-in-windows-powershell.aspx</a>
<a href="http://technet.microsoft.com/en-us/library/ff730937.aspx">http://technet.microsoft.com/en-us/library/ff730937.aspx</a>
#>  

Param(
    [parameter(Position = 0, Mandatory=$False )]    
    [ValidateNotNullOrEmpty()]
    [string]$UserOU="OU=Users,DC=YOURCOMPANY,DC=local",  

    [parameter(Position = 1, Mandatory=$False )]
    [ValidateNotNullOrEmpty()]   
    [string]$UserFolder="C:\scripts\NewUserRequests"
    )  

### FUNCTION: get-mymodule #####################################################
Function Get-MyModule {
    Param([string]$name)
    if(-not(Get-Module -name $name)) {
        if(Get-Module -ListAvailable | Where-Object { $_.name -eq $name }) {
            Import-Module -Name $name
            $true
        } #end if module available then import
        else { $false } #module not available
        } # end if not module
    else { $true } #module already loaded
} 
### FUNCTION: get-mymodule #####################################################  

### FUNCTION: get-csvdata #####################################################
function get-csvdata($CSVFolder) {
    $DataFiles = (Get-ChildItem $CSVFolder -recurse -force | Where { $_.Name -like "*.csv" } | Foreach-Object -process { $_.FullName })
    $DataFilesCount = ($DataFiles | measure).count
    Write-Verbose "Discovered $DataFilesCount CSV Data files in $CSVFolder"  

    ForEach ($DataFilesItem in $DataFiles) {
        $FileInfo = Get-Item $DataFilesItem
        $LogDate = $FileInfo.LastWriteTime
        Write-Verbose "Reading data from $DataFilesItem ($LogDate ) "
        [array]$CSVData += Import-CSV $DataFilesItem -header status,name,Account,Password,CloudVer,Email,Title,WorkNumber,MobileNumber,CostCenter,Manager,ManagerWorkPhone,Location,Modality,Group
        ## Only use the header if you want to rename the attributes for the imported objects. If the column headers in the CSV are fine, don't use -header
      }
    [int] $CSVDataCount = $CSVData.Count
    Write-Verbose "Imported $CSVDataCount records"
    Return ($CSVData)
}
### FUNCTION: get-csvdata #####################################################  

#Import Module(s)
if (!(get-mymodule activedirectory)) {
    write-verbose "Microsoft Active Directory PowerShell Cmdlet not available."
    write-verbose "Please run this script from a system with the Microsoft Active Directory PowerShell Cmdlets installed."
    exit
}  

#Constants
$datetime = get-date -format "MM-dd-yyyy_HH-mm"
$Domain="@YOURCOMPANY.local"
$CloudProfilePath = "\\PROFILESERVER.YOURCOMPANY.local\upm\"
$CloudRDSProfilePath = "\\PROFILESERVER.YOURCOMPANY.local\profiles\"
$ScriptRunner = (get-aduser $env:username | select name).name  

#Confirm OU is valid
If (!([adsi]::Exists("LDAP://$UserOU"))) {
    write-verbose "$UserOU IS NOT VALID"
    write-verbose "Please use the following format: OU=Users,DC=YOURCOMPANY,DC=local"
    Exit
}  

#Confirm CSV Folder exists and has CSV files. If True get the files, otherweise exit
if (test-path $UserFolder) {
    if ((Get-ChildItem $UserFolder | where {$_.name -like "*.csv"}) -ne $null) {
        $UserList = get-csvdata($UserFolder)
    } else {
        write-verbose "No CSV files in $UserFolder."
        Exit
    }
} else {
    write-verbose "$UserFolder is not valid"
    Exit
}  

#Create Users
foreach ($user in $UserList) {
    if (!([bool]([adsisearcher]"samaccountname=$user.Account").FindOne()) -and ($user.status -ne 'Status')) {
        #Get first and last name
        $pos = ($user.name).IndexOf(",")
        $SurName = ($user.name).Substring(0, $pos)
        $GivenName = ($user.name).Substring($pos+2)
        #Create other account name data
        $DisplayName = $GivenName+ " " + $SurName
        $Initials = ($GivenName).Substring(0,1) + ($SurName).Substring(0,1)
        $Username = $user.Account
        $UPN=$Username+$Domain
        #Get Cloud password to the Philips CODE password
        $Password = $user.Password
        #Create Description
        $Description = $User.Modality + " " + $user.Title
        #Create Contact Info
        $MobilePhone = $User.MobileNumber
        $OfficePhone = $User.WorkNumber
        $Email = $User.Email
        #Create Organization Info
        $Company = "YOUR COMPANY"
        $Department = $User.Modality + " " + $user.Group
        $Divsion = $User.Modality
        $EmployeeNumber = $User.Account
        $Manager = $user.Manager
        $Office = $User.Location
        $Title = $user.Title
        #Create User Profile Location
        $ProfilePath = $CloudProfilePath + $Username
        #Create User in AD
        new-aduser -name $Displayname -DisplayName $DisplayName -GivenName $GivenName -Surname $SurName -SamAccountName $UserName -UserPrincipalName $UPN -Description $Description -MobilePhone $MobilePhone -OfficePhone $OfficePhone -EmailAddress $Email -Company $Company -Department $Department -Division $Division -EmployeeNumber $EmployeeNumber -Office $Office -Title  $Title -ProfilePath $ProfilePath -Path $UserOU
        write-verbose "New AD User Account created for $DisplayName"
        #Set Password
        set-adaccountpassword -Identity $Username -NewPassword (ConvertTo-SecureString -AsPlainText $Password -Force)
        #Set Remote Desktop Services User Profile
        $RDSProfilePath = $CloudRDSProfilePath + $username
        Get-ADUser $username | ForEach-object {
            $ADSI = [ADSI]('LDAP://{0}' -f $_.DistinguishedName)
            try {
                $ADSI.InvokeSet('TerminalServicesProfilePath',$RDSProfilePath)
                $ADSI.SetInfo()
            } catch { Write-Verbose $Error[0] }
        }
        #Add User to groups
        #Based on Cloud Version - modify as needed to match AD groups
        $GrpArray = @()
        switch ($user.cloudver) {
            "Field Service" {}
            "Work from Home" {$GrpArray += "WFH"}
            "Pilot" {}
            "QA" {}
            "Sales" {}
            "Inventory" {}
        }
        #Based on Title - modify as needed to match AD groups
        switch ($user.title) {
            "Admin Assistant" {}
            "Director" {$GrpArray += "Leadership"}
            "FEP" {$GrpArray += ""}
            "Helpdesk" {$GrpArray += "Helpdesk"}
            "Helpdesk LEAD" {$GrpArray += "Domain Admins"; $GrpArray += "Helpdesk"}
            "FSE" {$GrpArray += "FSE"}
            "Pre-Sales" {$GrpArray += "Sales"}
            "RF Engineer" {$GrpArray += "Some-Application-Users" ; $GrpArray += "RF"}
            "RLM" {}
            "Sales" {$GrpArray += "Sales"}
            "SDC" {$GrpArray += "SDC"}
            "TC" {$GrpArray += "TC"}
            "Other" {}
        }
        Add-ADPrincipalGroupMembership $username -MemberOf $GrpArray
        write-verbose "The following groups were added to $DisplayName:"
        write-verbose "$GrpArray"
        #Add Notes to user
        get-aduser $username -properties info | foreach { Set-ADUser -Identity $_.samaccountname -Replace @{info="$($_.info)Manager - $Manager"} }
        get-aduser $username -properties info | foreach { Set-ADUser -Identity $_.samaccountname -Replace @{info="$($_.info)`r`nManager Phone - $($user.ManagerWorkPhone)"} }
        get-aduser $username -properties info | foreach { Set-ADUser -Identity $_.samaccountname -Replace @{info="$($_.info)`r`nEmployee Cost Center - $($user.costcenter)"} }
        get-aduser $username -properties info | foreach { Set-ADUser -Identity $_.samaccountname -Replace @{info="$($_.info)`r`nAccount created on: $datetime by $ScriptRunner"} }
        #Enable Account
        Enable-ADAccount -Identity $Username
    }
}

Thanks,
Alain