Problem
Enablement of Home Directory Creation post MS AD account creation of the user through SailPoint ISC as it’s not happening just by inserting the required attributes of “homeDrive” and “homeDirectory” into Create Account Profile of AD.
Diagnosis
The troubleshooting steps are as follows.
1. We Integrated the ISC with MS AD through OOTB connector.
2. In the OOTB connector, we had 2 attributes namely “homeDrive” and “homeDirectory” which is supported.
3. Hence, we added the above attributes in account schema and create account profile
4. The mentioned 2 attribute values were getting aggregated into ISC and getting provisioned and populated inside the AD profile of user.
5. But the physical drive creation does not happen.
6. This needs to be done separately using powershell scripts.
Solution
1. We create the AD After Create Connector Rule in ISC.
2. Through this After Create AD connector rule, we are calling another PowerShell script to maintain the recommendations of SailPoint of “Asynchronous approach” in connector rule.
3. The details of the AD After Create Connector rule are as follows.
Add-Type -Path "C:\SailPoint\IQService\Utils.dll";
$logDate = Get-Date -UFormat "%Y%m%d"
$logFile = "C:\Test\ConnectorAfterCreate_$logDate.log"
$enableDebug = $true
function LogToFile([String] $info) {
if($enableDebug){
$info | Out-File $logFile -Append -Force
}
}
LogToFile("Entering SailPoint TestOrg - ActiveDirectory-ConnectorAfterCreate Rule")
$createdOnServer = "";
$sReader = New-Object System.IO.StringReader([System.String]$env:Request);
$sResult = New-Object System.IO.StringReader([System.String]$env:Result);
$xmlReader = [System.xml.XmlTextReader]([sailpoint.utils.xml.XmlUtil]::getReader($sReader));
LogToFile($xmlReader)
$xmlReader_Result = [System.xml.XmlTextReader]([sailpoint.utils.xml.XmlUtil]::getReader($sResult));
LogToFile($xmlReader_Result)
$requestObject = New-Object Sailpoint.Utils.objects.AccountRequest($xmlReader);
LogToFile($requestObject)
$resultObject = New-Object Sailpoint.Utils.objects.ServiceResult($xmlReader_Result);
LogToFile($resultObject)
$requestAsString = $requestObject.nativeIdentity;
$sAMAccountName = $null
$flagToCreateHomeDirectoryScript = $false;
$flagToCreateHomeDirectoryPathScript = $false;
$flagToEnableMailBoxScript = $false;
$homeDrive = $null
$homeDirectory = $null
$Environment = $null
LogToFile("The Native Identity Details are ==> $requestAsString")
LogToFile("Operation ==> $requestObject.operation")
LogToFile("Entering SailPoint TestOrg - ActiveDirectory-ConnectorAfterCreate Rule")
foreach ($attrib in $requestObject.AttributeRequests) {
if (($attrib.Name -eq "sAMAccountName") -and ($attrib.Value -ne $null) ) {
LogToFile("TestOrg ==> sAMAccountName is not EMPTY for the User. Hence, set the Flag to true")
$sAMAccountName = $attrib.Value;
$flagToCreateHomeDirectoryScript = $true;
LogToFile("The value of sAMAccountName is : $sAMAccountName")
LogToFile("AccountRequest Operation: $requestObject.operation")
}
if (($attrib.Name -eq "homeDirectory") -and ($attrib.Value -ne $null) ) {
LogToFile("TestOrg ==> homeDirectory is not EMPTY for the User. Hence, set the Flag to true")
$homeDirectory = $attrib.Value;
$flagToCreateHomeDirectoryPathScript = $true;
LogToFile("The value of homeDirectory is : $homeDirectory")
LogToFile("AccountRequest Operation: $requestObject.operation")
}
if (($attrib.Name -eq "Environment") -and ($attrib.Value -ne $null) ) {
LogToFile("TestOrg ==> Environment is not EMPTY for the User. Hence, set the Flag to true")
$Environment = $attrib.Value;
$flagToEnableMailBoxScript = $true;
LogToFile("The value of Environment is : $Environment")
LogToFile("AccountRequest Operation: $requestObject.operation")
}
}
if($resultObject.Errors.count -eq 0) {
LogToFile("Entering into Joiner after Script")
foreach ($resultEntry in $resultObject.Attributes.GetEnumerator()){
if($resultEntry.Name -eq "createdOnServer"){
$createdOnServer = $resultEntry.Value
}
}
if($flagToCreateHomeDirectoryScript -and $flagToCreateHomeDirectoryPathScript){
LogToFile($requestObject.toxml())
LogToFile("createdOnServer: $createdOnServer")
LogToFile("Triggering the PowerShell script to trigger script after user account has been created for Create Home Directory for the User")
$homeDrive = "W:";
$command = "C:\SailPoint\PSScripts\CreateHomeDir_v6.ps1"
LogToFile("The SamAccountName is :: $SamAccountName and homeDirectory Path is :: $homeDirectory")
$command = -join ($command, " -SamAccountName '$sAMAccountName' -HomeRootPath '$homeDirectory'")
LogToFile("Invoking the Command as :: $command")
Start-Sleep -Seconds 20
LogToFile("Sleep of 20 seconds is COMPLETED!")
Invoke-Expression $command
LogToFile("The Command is Invoked. Hence, sleep of 30 seconds is initiated")
Start-Sleep -Seconds 30
LogToFile("The Command is Invoked. Hence, sleep of 30 seconds is Completed")
LogToFile("Invoking the Command as :: $command is SUCCESSFUL!")
LogToFile("Request as XML object is: $requestAsString")
}else{
LogToFile("The Flag of flagToCreateHomeDirectoryScript is FALSE..!!")
}
}else{
LogToFile("Error while create account request")
LogToFile($resultObject.toxml())
}
LogToFile("resultObject.toxml")
LogToFile($resultObject.toxml())
LogToFile("Exiting SailPoint TestOrg - ActiveDirectory-ConnectorAfterCreate Rule")
-
The actual powershell script which creates the home directory is as follows.
param(
# samAccountName of the user being created in AD
[Parameter(Mandatory = $true)]
[string]$SamAccountName,
# Root path where all home folders will be created
[Parameter(Mandatory = $true)]
[string]$HomeRootPath,
# Home Drive Letter
[string]$HomeDriveLetter = "W:",
# Log folder path (optional, default as requested)
[Parameter(Mandatory = $false)]
[string]$LogPath = "C:\SailPoint\PSLogs\HomeDirectoryCreate"
)
# Stop on errors
$ErrorActionPreference = "Stop"
function Initialize-Logging {
# Ensure log directory exists
if (-not (Test-Path $LogPath)) {
New-Item -ItemType Directory -Path $LogPath -Force | Out-Null
}
# Correlation ID for this run
$script:CorrelationId = Get-Date -Format "yyyyMMddHHmmssfff"
# Sanitize SamAccountName for filename safety
if ($SamAccountName) {
$safeSam = $SamAccountName -replace '[^a-zA-Z0-9]', '_'
}
else {
$safeSam = 'UnknownUser'
}
# Unique log file per user + process
$timestamp = Get-Date -Format 'yyyyMMdd_HHmmssfff'
$script:LogFile = Join-Path $LogPath ("CreateHomeDir_{0}_{1}_{2}.log" -f $safeSam, $PID, $timestamp)
}
function Write-Log {
param(
[Parameter(Mandatory = $true)]
[string]$Message,
[Parameter(Mandatory = $false)]
[ValidateSet('Information', 'Warning', 'Error', 'Debug')]
[string]$Level = 'Information'
)
$timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
$logMessage = "[$timestamp] [$Level] $script:CorrelationId - $Message"
if ($script:LogFile) {
Add-Content -Path $script:LogFile -Value $logMessage
}
switch ($Level) {
'Warning' { Write-Warning $Message }
'Error' { Write-Error $Message }
'Debug' { Write-Debug $Message }
default { Write-Verbose $Message }
}
}
# Initialize logging as early as possible
Initialize-Logging
Write-Log "Starting HomeDir for SamAccountName: $SamAccountName" -Level Information
Write-Log "Parameters -> HomeRootPath: $HomeRootPath, HomeDriveLetter: $HomeDriveLetter, LogPath: $LogPath" -Level Debug
# Import AD module
try {
Import-Module ActiveDirectory -ErrorAction Stop
Write-Log "Successfully imported ActiveDirectory module" -Level Debug
}
catch {
Write-Log "Failed to import ActiveDirectory module for the user :: $SamAccountName. Error: $($_.Exception.Message)" -Level Error
exit 1
}
try {
# 1) Get the AD user (SID needed for ACL)
Write-Log "Attempting to get AD user: $SamAccountName" -Level Debug
$user = Get-ADUser -Identity $SamAccountName -ErrorAction Stop
Write-Log "Successfully retrieved AD user $SamAccountName (SID: $($user.SID))" -Level Debug
# 2) Build full home directory path
# Ensure HomeRootPath does NOT already contain SamAccountName
if ($HomeRootPath.TrimEnd('\') -like "*\$SamAccountName") {
$HomeRootPath = Split-Path $HomeRootPath -Parent
}
$homePath = Join-Path -Path $HomeRootPath -ChildPath $SamAccountName
Write-Log "Resolved home directory path as: $homePath" -Level Debug
# 3) Create folder if missing
if (-not (Test-Path -Path $homePath)) {
Write-Log "Home directory does not exist. Creating: $homePath" -Level Information
New-Item -Path $homePath -ItemType Directory -Force | Out-Null
}
else {
Write-Log "Home directory already exists: $homePath" -Level Debug
}
$sam = $user.SamAccountName
Write-Log "The SamAccountName Extracted from AD is :: $sam" -Level Information
$str = ":(OI)(CI)M"
Write-Log "The Details of permissions are :: $str" -Level Information
$accessrule = $sam + $str
Write-Log "The Details of Access Rules are :: $accessrule" -Level Information
Write-Log "Calling the new approach of ICACLS command" -Level Information
icacls $homepath /inheritance:r /grant:r "Administrators:(OI)(CI)F" $accessrule
Write-Log "Call to ICACLS Command is a SUCCESS!!" -Level Information
Write-Log "Successfully applied MODIFY ACL for $SamAccountName on $homePath" -Level Information
# 5) Set AD attributes homeDirectory + homeDrive
Write-Log "Updating AD user $SamAccountName with HomeDirectory: $homePath and HomeDrive: $HomeDriveLetter" -Level Information
Set-ADUser -Identity $SamAccountName `
-HomeDirectory $homePath `
-HomeDrive $HomeDriveLetter
Write-Log "Successfully updated AD attributes for $SamAccountName" -Level Information
Write-Log "SUCCESS|$SamAccountName|$homePath" -Level Information
Write-Log "HomeDir completed successfully for $SamAccountName" -Level Information
exit 0
}
catch {
Write-Log ("Error while creating home directory or updating AD for {0}: {1}" -f $SamAccountName, $_.Exception.Message) -Level Error
exit 1
}