# Email Creation and Validation via PowerShell

**URL:** <https://developer.sailpoint.com/discuss/t/email-creation-and-validation-via-powershell/185066>\
**Category:** SHF Discussion and Questions\
**Tags:** provisioning, identity-security-cloud\
**Created:** [October 10, 2025, 2:09pm UTC](https://developer.sailpoint.com/discuss/t/email-creation-and-validation-via-powershell/185066 "2025-10-10T14:09:47Z")\
**Posts on this page:** 4\
**Page:** 1

<div class="post-metadata">

**Author:** ![thiagogosantanasi](https://avatars.discourse-cdn.com/v4/letter/t/9de0a6/32.png) [@thiagogosantanasi](https://developer.sailpoint.com/discuss/u/thiagogosantanasi)\
**Post date:** [October 10, 2025, 2:09pm UTC](https://developer.sailpoint.com/discuss/t/email-creation-and-validation-via-powershell/185066/1 "2025-10-10T14:09:48Z")

</div>

Hi everyone,

We are currently implementing a process where, to create an email in the **local Active Directory** , it is necessary to validate — through a **PowerShell script executed locally in the AD** — whether the email already exists in **Microsoft Entra ID (Office)**.

- If the email **does not exist** , it will be created locally in the AD.

- If it **already exists** , the script automatically generates a new variation according to the defined naming rule.

Normally, this type of process would be handled using an **AttributeGenerator rule** and a **connected source to Microsoft Entra ID**.  
However, in this case, the validation will be performed **via PowerShell** , directly from the local AD environment.

👉 **Questions:**

- Is this PowerShell-based validation approach supported by SailPoint?

- Are there any recommended best practices for this type of hybrid integration?

Thank you in advance for your help and guidance.

---

<div class="post-metadata">

**Author:** ![sukanta\_biswas](https://sea1.discourse-cdn.com/sailpoint/discuss/user_avatar/developer.sailpoint.com/sukanta_biswas/32/24596_2.png) [@sukanta\_biswas](https://developer.sailpoint.com/discuss/u/sukanta_biswas)\
**Post date:** [October 10, 2025, 3:02pm UTC](https://developer.sailpoint.com/discuss/t/email-creation-and-validation-via-powershell/185066/2 "2025-10-10T15:02:28Z")

</div>

@thiagogosantanasi -

Yes—you can do this with IdentityNow (ISC) using the AD connector’s **IQService PowerShell hooks**. It’s a common pattern when you need logic that can’t run in a cloud-executed rule (e.g., calling Microsoft Graph). Below are the do’s, don’ts, and a clean blueprint you can adopt.

# Is it supported?

- **Supported approach:** Use the **Before Script**  
**PowerShell script** on the AD source (IQService). The script runs **locally on the IQService Windows host** , can call Microsoft Graph/Entra ID, and can **set/adjust attributes** (like `mail` and `proxyAddresses`) before the create hits AD.

- **What it’s not:** Cloud-executed rules in ISC can’t make outbound HTTP calls; so your Graph validation cannot live there. Doing it in IQService PowerShell is the right “supported” spot.

- **Supportability note:** SailPoint Support will help with connector behavior; for issues inside your custom script they’ll expect you to own the script. That’s standard.

# Recommended best practices (hybrid AD + Entra validation)

## Where to put the logic

1. **Before Create PowerShell (AD Source)**

2. **(Optional) Before Modify PowerShell**

> If you need ISC to know the final value immediately, also set the same value in the outgoing payload so it lands in AD and is visible to ISC on the **provisioning response/next aggregation**. Most teams rely on the next account aggregation to reflect it back to the identity **authoritatively**.

## Microsoft Graph specifics

- **API:** Prefer Graph over legacy MSOnline/ExchangeOnline modules.

- **Auth:** Use an **app registration** with client credentials; store secrets securely:

- **Resilience:** Implement retries with **`Retry-After`** handling for 429/503, short timeouts, and a **max attempts** loop on name variants.

## Naming policy & collision handling

- Centralize the **pattern** (`first.last`, length caps, diacritics folding) and **variant rule** (`first.last1`, `first.last2`, …).

- **Normalize** (lowercase, ASCII) and trim to enforce Exchange/AD constraints (e.g., 64 local-part soft guard).

- Validate against **both** directories:

- Stop after a sane max (e.g., 50 variants) and fail gracefully with a clear error if no slot is free.

## AD attributes to set

- `mail`: the decided primary SMTP address

- `proxyAddresses`: include `SMTP:primary@corp.com` and any policy-required aliases as `smtp:` entries

- (If UPN follows email) set `userPrincipalName` consistently

## Operational hygiene

- **Logging:** Write clear events to Windows Event Log (source: `IQService-EmailPolicy`), include the SailPoint requestId if available. Avoid logging secrets.

- **Idempotency:** If a create is retried, the function should return the **same** final email decision for the same person (e.g., seed with a stable key like employee ID + timestamp floor).

- **Performance:** Token cache (client-credentials token) and keep checks sub-second.

- **Security:** App perms should be **least-privilege** (read-only users).

- **Testing:** Dry-run mode and unit tests for your variant generator; test with “evil” names (accents, hyphens, long names).

- **Monitoring:** Alert on repeated collisions \>N or throttling spikes.

# Minimal PowerShell blueprint (IQService “Before Script/After Script”)

> Pseudocode outline—adapt to your IQService script skeleton and payload names.

```auto
# Input: $operation, $accountData (hashtable/dictionary of attributes destined to AD)
# Output: mutate $accountData to set mail/proxyAddresses before create

function Get-GraphToken {
  # Retrieve client credentials from Windows Credential Manager
  # Call token endpoint; cache in a global variable with expiry
}

function EmailExists-InEntra($email) {
  $token = Get-GraphToken
  $encoded = [System.Web.HttpUtility]::UrlEncode("mail eq '$email' or userPrincipalName eq '$email'")
  $uri = "https://graph.microsoft.com/v1.0/users`?\$filter=$encoded&\$select=id"
  # Invoke-RestMethod with retry for 429/503; return $true if any user
}

function AliasExists-InEntra($email) {
  $token = Get-GraphToken
  $probe1 = "proxyAddresses/any(p:p eq 'SMTP:$email') or proxyAddresses/any(p:p eq 'smtp:$email')"
  $encoded = [System.Web.HttpUtility]::UrlEncode($probe1)
  $uri = "https://graph.microsoft.com/v1.0/users`?\$filter=$encoded&\$select=id"
  # Invoke-RestMethod; return $true if any user
}

function EmailExists-InAD($email) {
  # Use ADSI/RSAT to query local AD for (mail=$email) OR (proxyAddresses=SMTP:$email) OR (proxyAddresses=smtp:$email)
}

function New-UniqueEmail($baseLocalPart, $domain) {
  for ($i=0; $i -le 50; $i++) {
    $local = ($i -eq 0) ? $baseLocalPart : "$baseLocalPart$i"
    $candidate = "$local@$domain"
    if (-not (EmailExists-InAD $candidate) -and -not (EmailExists-InEntra $candidate) -and -not (AliasExists-InEntra $candidate)) {
      return $candidate
    }
  }
  throw "Unable to find unique email after 50 attempts."
}

if ($operation -eq "Create") {
  # 1) Derive base from givenName/surname; normalize (lowercase, strip diacritics)
  $first = $accountData['givenName']
  $last = $accountData['sn']
  $base = (Normalize-ToAscii "$first.$last").ToLower()
  $domain = "corp.example.com" # Or read from config

  # 2) Find unique
  $finalEmail = New-UniqueEmail -baseLocalPart $base -domain $domain

  # 3) Set attributes for AD create
  $accountData['mail'] = $finalEmail
  $proxy = @("SMTP:$finalEmail") # add policy aliases if needed
  $accountData['proxyAddresses'] = $proxy

  # 4) (Optional) keep UPN aligned
  if ($accountData.ContainsKey('userPrincipalName')) {
    $accountData['userPrincipalName'] = $finalEmail
  }
}

```

# FAQ quick hits

- **Why not an AttributeGenerator rule + connected Entra source?**  
That’s the cleanest “all-inside-SailPoint” route, but cloud rules can’t call Graph and you’ve chosen to validate from AD. The IQService PowerShell hook is the right supported escape hatch.

- **Will SailPoint “see” the final email immediately?**  
The value is written to AD during create; ISC will reflect it either from the provisioning response or on the **next aggregation**. If you need it on the identity profile instantly, ensure the attribute is in the provisioning response or run a targeted account refresh.

- **Any sync gotchas?**  
If you run AAD Connect (or cloud sync), make sure your **authoritative system** and flows don’t overwrite each other. Keep a single source of truth for the email policy and ensure mappings are consistent.

Cheers!!!

---

<div class="post-metadata">

**Author:** ![thiagogosantanasi](https://avatars.discourse-cdn.com/v4/letter/t/9de0a6/32.png) [@thiagogosantanasi](https://developer.sailpoint.com/discuss/u/thiagogosantanasi)\
**Post date:** [October 13, 2025, 12:39pm UTC](https://developer.sailpoint.com/discuss/t/email-creation-and-validation-via-powershell/185066/3 "2025-10-13T12:39:04Z")

</div>

> [@sukanta\_biswas](#):
>
> ` # Use ADSI/RSAT to query local AD for (mail=$email) OR (proxyAddresses=SMTP:$`

Hi, @sukanta_biswas  
Thank you very much for your reply. I will do all the tests as you recommend.

---

<div class="post-metadata">

**Author:** ![system](https://global.discourse-cdn.com/sailpoint/original/2X/f/f2136700ed5e3703e0b85e02f6be799dacca7735.png) [@system](https://developer.sailpoint.com/discuss/u/system)\
**Post date:** [December 12, 2025, 12:40pm UTC](https://developer.sailpoint.com/discuss/t/email-creation-and-validation-via-powershell/185066/5 "2025-12-12T12:40:53Z")

</div>

This topic was automatically closed 60 days after the last reply. New replies are no longer allowed.
