
- Post History
- Subscribe to RSS Feed
- Mark as New
- Mark as Read
- Bookmark
- Subscribe
- Printer Friendly Page
- Report Inappropriate Content
on ‎03-10-2022 07:32 AM
Introduction
There are many businesses out there that rely on multiple domains to operate their IT infrastructure. In these cases, it can make it difficult to process integration hub activities for the Microsoft AD Spoke to add or remove users in a group that belongs to a different domain. In this article I will describe the process I took to solve this problem I was facing when creating an automated solution for group memberships in AD via ServiceNow's Integration Hub using the Microsoft AD Spoke actions.
*I will be adding more to this article, so if there is somethings missing, I will try to add them as soon as I can.
Pre-Requisites
You would need minimum two plugins in order to use PowerShell and Microsoft AD Spoke actions in flow designer.
1. IntegrationHub - This enables you to use the PowerShell step in flow designer Action.
2. ServiceNow IntegrationHub Professional Pack Installer - This lets you use dynamic inputs in flow designer.
Refer to this link for more details about integration plugins for flow designer.
Connection & Credential Setup
Navigate to Connections & Credentials > Credentials and create a new windows credential that works with the AD Domain Controller(DC) that would be used as the primary DC.
Note: The best option would be to used the parent/root DC for the best outcome. However, during the process I was faced with, a child domain controller was used as the primary DC.
After confirming that the credential is successfully tested against the DC, navigate to Connection & Credentials > Connection & Credential Alias. Select the AD record that has the sn_ad_spoke.AD ID.
Note: AD will only exist when the Microsoft AD Spoke plugin is activated
Create a new connection shown in the related list of the AD Connection & Credential Alias.
Note: You can also just navigate to Connection & Credentials > Connections to create a new credential record, however you will have to select the type of Basic Connection for PowerShell & SSH and other details that would be automatically populated if done directly from the AD Connection & Credential Alias related list.
Configure MID Server Script Files
Navigate to MID Server > Script Files
Open the ActiveDirectoryMain.psm1 record and add the Add AD user account to Group Custom code block under the original code block name Add AD user account to Group seen in the code snippet below.
I replace $domainController for the userObject with $userdomaincustom. This allows for the creation of a custom input to place the users domain controller for the PowerShell to execute against.
######################
# Add AD user account to Group
#
######################>
function addADUserAccountToGroup {
param([string]$domainController, [string]$username, [string]$groupname, [boolean]$useCred, [System.Management.Automation.PSCredential]$credential)
SNCLog-ParameterInfo @("Running addADUserAccountToGroup", $domainController, $username, $groupname)
$userObject = getADObject -domainController $domainController -type "User" -objectName $username -useCred $useCred -credential $credential
$groupObject = getADObject -domainController $domainController -type "Group" -objectName $groupname -useCred $useCred -credential $credential
$groupObject.add("LDAP://"+$userObject.distinguishedName);
if (-not $?) {
SNCLog-DebugInfo "`tFailed to add $username account to $groupname group, $error"
}
}
######################
# Add AD user account to Group Custom
# This is the custom function added inside the ActiveDirectory script module
######################>
function addADUserAccountToGroupCrossDomain {
param([string]$userdomaincustom , [string]$domainController , [string]$username, [string]$groupname, [boolean]$useCred, [System.Management.Automation.PSCredential]$credential)
SNCLog-ParameterInfo @("Running addADUserAccountToGroup", $userdomaincustom, $domainController, $username, $groupname)
$userObject = getADObject -domainController $userdomaincustom -type "User" -objectName $username -useCred $useCred -credential $credential
$groupObject = getADObject -domainController $domainController -type "Group" -objectName $groupname -useCred $useCred -credential $credential
$groupObject.add("LDAP://"+$userObject.distinguishedName);
if (-not $?) {
SNCLog-DebugInfo "`tCould not get required info, $error"
}
}
Perform the same action for the Remove AD user account from Group code block by adding Remove AD user account from Group Custom
###################################
# Remove AD user account from Group
###################################>
function removeADUserAccountFromGroup {
param([string]$domainController, [string]$username, [string]$groupname, [boolean]$useCred, [System.Management.Automation.PSCredential]$credential)
SNCLog-ParameterInfo @("Running removeADUserAccountFromGroup", $domainController, $username, $groupname)
$userObject = getADObject -domainController $domainController -type "User" -objectName $username -useCred $useCred -credential $credential
$groupObject = getADObject -domainController $domainController -type "Group" -objectName $groupname -useCred $useCred -credential $credential
$groupObject.remove("LDAP://"+$userObject.distinguishedName);
if (-not $?) {
SNCLog-DebugInfo "`tFailed to remove $username account from $groupname group, $error"
}
}
######################
# Remove AD user account from Group Custom
# This is the custom function added inside the ActiveDirectory script module
######################>
function removeADUserAccountFromGroupCrossDomain {
param([string]$userdomaincustom , [string]$domainController , [string]$username, [string]$groupname, [boolean]$useCred, [System.Management.Automation.PSCredential]$credential)
SNCLog-ParameterInfo @("Running removeADUserAccountFromGroup", $userdomaincustom, $domainController, $username, $groupname)
$userObject = getADObject -domainController $userdomaincustom -type "User" -objectName $username -useCred $useCred -credential $credential
$groupObject = getADObject -domainController $domainController -type "Group" -objectName $groupname -useCred $useCred -credential $credential
$groupObject.remove("LDAP://"+$userObject.distinguishedName);
if (-not $?) {
SNCLog-DebugInfo "`tCould not get required info, $error"
}
}
Create a copy of ActionAddUserToADGroup.ps1 and named it ActionRemoveUserFromGroupCrossDomain.ps1
import-module "$executingScriptDirectory\ADSpoke\ActiveDirectoryMain"
if (test-path env:\SNC_groupname) {
$groupname = $env:SNC_groupname;
$username = $env:SNC_username;
};
$groupname = $groupname -replace "%27","'";
$username = $username -replace "%27","'";
SNCLog-ParameterInfo @("Running AddUserToADGroup", $groupname, $username)
addADUserAccountToGroupCrossDomain -userdomaincustom $userdomaincustom -domainController $computer -username $username -groupname $groupname -useCred $useCred -credential $cred
Create a copy of ActionRemoveUserFromGroup.ps1 and named it ActionRemoveUserFromGroupCrossDomain.ps1
import-module "$executingScriptDirectory\ADSpoke\ActiveDirectoryMain"
if (test-path env:\SNC_groupname) {
$groupname = $env:SNC_groupname;
$username = $env:SNC_username;
};
$groupname = $groupname -replace "%27","'";
$username = $username -replace "%27","'";
removeADUserAccountFromGroupCrossDomain -userdomaincustom $userdomaincustom -domainController $computer -username $username -groupname $groupname -useCred $useCred -credential $cred
Create a copy of ActionIsUserFromGroup.ps1 and named it ActionIsUserFromGroupCrossDomain.ps1
import-module "$executingScriptDirectory\ADSpoke\ActiveDirectoryMain"
if (test-path env:\SNC_groupname) {
$groupname = $env:SNC_groupname;
$username = $env:SNC_username;
};
$groupname = $groupname -replace "%27", "'";
$username = $username -replace "%27", "'";
$userObject = getADObject -domainController $userdomaincustom -type "User" -objectName $username -useCred $useCred -credential $cred
$groupObject = getADObject -domainController $computer -type "Group" -objectName $groupname -useCred $useCred -credential $cred
#check user exists
if ($userObject -eq $null) {
Write-Host "Invalid UserName"
} else {
$userGroups = $userObject.memberOf
$groupname = $groupname.Replace("(","\(")
$groupname = $groupname.Replace(")","\)")
if ($userGroups -Match "CN=$groupname,") {
write-host "User is Member of group"
}
else {
write-host "User is not in group"
}
}
Create a copy of ActionLookupGroup.ps1 and named it ActionLookupGroupCrossDomain.ps1
import-module activedirectory -warningaction "SilentlyContinue";
$groupName = $groupName -replace "%27","'";
$propertiesList = $propertiesList -replace "%27","'";
if ($propertiesList) {
$arr = $propertiesList -split ","
$arrpropertiesList = @($arr)
}
try{
$group = Get-ADGroup -Identity $groupName -Credential $cred -Server $groupdomaincustom -Properties $arrpropertiesList -ErrorAction Stop;
}catch{
$ErrorMessage = $_.Exception.Message;
}
if($ErrorMessage){
$result = "Error:"+$ErrorMessage;
}
else{
foreach($property in $group.PsObject.Properties){
$propName = $($property.Name);
if($propName -ne "PropertyNames" -and $propName -ne "nTSecurityDescriptor"){
$value = $($property.Value) | ConvertTo-Json -Compress;
if ($value -is [String]){
$value = $value.Trim();
}
if($value) {
$result = $result+ """$propName""" + ":" + $value + "," ;
}
else {
$result = $result+ """$propName""" + ":" + """""" + "," ;
}
}
}
}
Write-Host $result;
Create a copy of ActionLookupUser.ps1 and named it ActionLookupUserCrossDomain.ps1
import-module activedirectory -warningaction "SilentlyContinue";
$userName = $userName -replace "%27","'";
$propertiesList = $propertiesList -replace "%27","'";
if ($propertiesList) {
$arr = $propertiesList -split ","
$arrpropertiesList = @($arr)
}
try{
$user = Get-ADUser -Identity $userName -Credential $cred -Server $userdomaincustom -Properties $arrpropertiesList -ErrorAction Stop;;
}catch{
$ErrorMessage = $_.Exception.Message;
}
if($ErrorMessage){
$result = "Error:"+$ErrorMessage;
}else {
foreach($property in $user.PsObject.Properties){
$propName = $($property.Name);
if($propName -ne "PropertyNames" -and $propName -ne "nTSecurityDescriptor"){
$value = $($property.Value) | ConvertTo-Json -Compress;
if ($value -is [String]){
$value = $value.Trim();
}
if($value) {
$result = $result+ """$propName""" + ":" + $value + "," ;
}
else {
$result = $result+ """$propName""" + ":" + """""" + "," ;
}
}
}
}
Write-Host $result;
Once you have created the MID Server Script Files and altered the main.ps1 for the add and remove code then you should RESTART THE MID SERVER(s) in order for the new script files to be added to the MID Server for execution.
Configure Microsoft AD Spoke Actions
Navigate to Flow Designer and go to the Actions section.
Create copies of the following Microsoft AD Spoke actions:
Name | Internal name | Application |
Add User To Group | add_user_to_group | Microsoft AD Spoke |
Is User In Group | is_user_in_group | Microsoft AD Spoke |
Lookup Group | lookup_group | Microsoft AD Spoke |
Remove User From Group | remove_user_from_group | Microsoft AD Spoke |
After copy of the action, navigate to the action and to the Inputs step of the Action Outline. Adding an input that will be used to consume the domain variable as mentioned in the MID Server Script Files.
In the next step named Create Payload (Script), add a new Input Variables that will have a data pill from the input from the first section as seen below.
Then, add the same variable in the Output variables section of the same step as seen below.
In the next step named Execute Action (PowerShell), update the MID Server Script field with the newly created (copied) MID Server Script File created earlier for execution of the modified code. Make sure it lines up with the action that you copied.
For example:
If the action is: | Then update the MID Server Script field to: |
Add User To Group | ActionAddUserToADGroupCrossDomain.ps1 |
Is User In Group | ActionIsUserFromGroupCrossDomain.ps1 |
Lookup Group | ActionLookupGroupCrossDomain.ps1 |
Remove User From Group | ActionRemoveUserFromGroupCrossDomain.ps1 |
In the input variables section, continue to create the input like the other steps and then add the data pill that is for the create payload step that is associated to the output you created name step > Create Payload > userdomaincustom.
Once completed, save the action and test. Once tested successfully, publish the action in order to be used within the flow.
Configuring Flow/SubFlow Action Steps
After the newly created actions are ready to be used, enter in the userID for the user name and for the userdomaincustom enter the domain controller servers host name.
I would like to give a shout out and credit to Deepak's blog which helped guide me to this solution:
I also took some direction from this support document:
- 5,777 Views
- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Report Inappropriate Content
Hi Stephen,
I'm currently doing an implementation of this AD Spoke for a customer of ours and their AD infrastructure is fairly complex as described in this article.
I managed to figure out a lot of this article by playing around other then editing the .psm1 module which is quite useful and cool. I've made sure that I've followed this setup step-by-step and I'm still facing the issue of not being able to find the Group or the User if I change the domain I query from.
My setup at the moment I query from the top level DC which in this case should have access to the individual objects, which it does when all I'm doing is getting the AD Object through a query and a specify the domain in the command.
The issue is persisting when I'm trying to add an AD object in Domain A which is under a sub DC into group in Domain D under a different DC.
Any help and guidance if you have any extra details would be fantastic :).
Thanks,
Daniel

- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Report Inappropriate Content
@DanielD1 Are you able to run the same query directly in the DC outside of ServiceNow? That is something we also ran into, is that even though your ServiceNow configuration may be correct, there may be configuration or permission on the AD side preventing the query.
Thank you for your response to my article. I am happy to help.
- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Report Inappropriate Content
Hi Stephen
Thank you for your great article. I followed all the steps you mentioned. I got stuck in the copying action and create new action "Add User To Group Cross Domains". I am not able to edit step 2 execute action. It shows me read only. Can you please help me what could be problem? We use San diego version of ServiceNow.
Thanks,
Alpesh
- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Report Inappropriate Content
Hi Stephen,
Also my thanks for this article. In my company we also have a situation where the users are registered in another domain than the resources.
I have built de powershell scripts according to the examples in your article. I am using a connection with credentials that should give me sufficient rights to manage the groups in my starting domain and also is allowed to access the AD in all other domains.
Despite the fact that I am able to lookup the users situated in the other domain (using your lookup user cross domain) , I am unable to add the users from another domain to a group residing in the starting domain.
I receive the message:
IPaaSActionProbe | There is no such object on the server. (Exception from HRESULT: 0x80072030) HRESULT: [-2147016656]
Failed while executing ActionAddUserToGroupCrossDomain.ps1
Any suggestions would be greatly appreciated!
Jan

- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Report Inappropriate Content
@Alpesh Suthar1can you provide a screenshot of what you are seeing, cause I just tried to replicate the copy step and was able to edit. Just curious and it may be silly, but you have the admin role correct?
@Jan Vissers That appears to be a error for the server side where the AD lives. Could be a replication issue or the object you are trying to call from the PowerShell via ServiceNow iHub action doesn't exist. Try running the PowerShell command on the server itself first to see if you get the same error. If you do then its something with the object not available due to it not existing on the domain controller or permissions are not set correctly. Hope that helps.
- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Report Inappropriate Content
Hi Stephen
It was my old post but we were able to figure it out. Integration on our instance is working great. Thank you for your article. Keep doing this great work.
- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Report Inappropriate Content
Hi @Stephen Skaggs ,
Thank you for the great article! I’m currently stuck on an issue where I'm trying to add a user from Domain A to a group that exists in Domain B. I’ve followed all the steps and I'm passing the customUserDomain, but the script is returning groups from the user’s current domain instead of the target group domain.I am trying to update "Is User in Group" Action.
Any suggestions would be greatly appreciated!
- Pinku
- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Report Inappropriate Content
@Jan Vissers -Were you able to resolve the issue mentioned above? I'm encountering the same problem while trying to set up cross-domain access.
-Pinku.