mirror of
https://github.com/Gerenios/AADInternals
synced 2026-06-08 11:09:38 +00:00
2434 lines
93 KiB
PowerShell
2434 lines
93 KiB
PowerShell
## Directory Sync API functions
|
|
|
|
# NOTE: Azure AD Sync API gets redirected quite often 2-3 times per request.
|
|
# Therefore the functions need to be called recursively and use $Recursion parameter.
|
|
|
|
# Get synchronization configuration using Provisioning and Azure AD Sync API
|
|
# May 6th 2020
|
|
function Get-SyncConfiguration
|
|
{
|
|
<#
|
|
.SYNOPSIS
|
|
Gets tenant's synchronization configuration
|
|
|
|
.DESCRIPTION
|
|
Gets tenant's synchronization configuration using Provisioning and Azure AD Sync API.
|
|
If the user doesn't have admin rights, only a subset of information is returned.
|
|
|
|
.Parameter AccessToken
|
|
Access Token
|
|
|
|
.Example
|
|
Get-AADIntSyncConfiguration
|
|
|
|
AllowedFeatures : {ObjectWriteback, , PasswordWriteback}
|
|
AnchorAttribute : mS-DS-ConsistencyGuid
|
|
ApplicationVersion : 1651564e-7ce4-4d99-88be-0a65050d8dc3
|
|
ClientVersion : 1.4.38.0
|
|
DirSyncClientMachine : SERVER1
|
|
DirSyncFeatures : 41016
|
|
DisplayName : Company Ltd
|
|
IsDirSyncing : true
|
|
IsPasswordSyncing : false
|
|
IsTrackingChanges : false
|
|
MaxLinksSupportedAcrossBatchInProvision : 15000
|
|
PreventAccidentalDeletion : EnabledForCount
|
|
SynchronizationInterval : PT30M
|
|
TenantId : 57cf9f28-1ad7-40f4-bee8-d3ab9877f0a8
|
|
TotalConnectorSpaceObjects : 1
|
|
TresholdCount : 500
|
|
TresholdPercentage : 0
|
|
UnifiedGroupContainer :
|
|
UserContainer :
|
|
DirSyncAnchorAttribute : mS-DS-ConsistencyGuid
|
|
DirSyncServiceAccount : Sync_SERVER1_xxxxxxxxxxx@company.onmicrosoft.com
|
|
DirectorySynchronizationStatus : Enabled
|
|
InitialDomain : company.onmicrosoft.com
|
|
LastDirSyncTime : 2020-03-03T10:23:09Z
|
|
LastPasswordSyncTime : 2020-03-04T10:23:43Z
|
|
ADSyncBlackListEnabled : false
|
|
ADSyncBlackList : {1.0}
|
|
ADSyncLatestVersion : 3.2
|
|
ADSyncMinimumVersion : 1.0
|
|
|
|
.Example
|
|
Get-AADIntSyncConfiguration
|
|
|
|
ApplicationVersion : 1651564e-7ce4-4d99-88be-0a65050d8dc3
|
|
ClientVersion : 1.4.38.0
|
|
DirSyncAnchorAttribute : mS-DS-ConsistencyGuid
|
|
DirSyncClientMachine : SERVER1
|
|
DirSyncServiceAccount : Sync_SERVER1_xxxxxxxxxxx@company.onmicrosoft.com
|
|
DirectorySynchronizationStatus : Enabled
|
|
DisplayName : Company Ltd
|
|
InitialDomain : company.onmicrosoft.com
|
|
IsDirSyncing : true
|
|
LastDirSyncTime : 2020-03-03T10:23:09Z
|
|
LastPasswordSyncTime : 2020-03-04T10:23:43Z
|
|
|
|
#>
|
|
[cmdletbinding()]
|
|
Param(
|
|
[Parameter(Mandatory=$False)]
|
|
[String]$AccessToken
|
|
)
|
|
Process
|
|
{
|
|
# Get from cache if not provided
|
|
$AccessToken = Get-AccessTokenFromCache -AccessToken $AccessToken -ClientID "1b730954-1685-4b74-9bfd-dac224a7b894" -Resource "https://graph.windows.net"
|
|
|
|
# First get configuration from Provisioning API (no admin rights needed)
|
|
$config = Get-CompanyInformation -AccessToken $AccessToken
|
|
|
|
# Show the warning of the pending state
|
|
if($config.DirectorySynchronizationStatus.StartsWith("Pending"))
|
|
{
|
|
Write-Warning "Synchronization status is $($config.DirectorySynchronizationStatus) and it may be stuck to this state for up to 72h!"
|
|
}
|
|
|
|
# Return value
|
|
$attributes=[ordered]@{
|
|
ApplicationVersion = $config.DirSyncApplicationType
|
|
ClientVersion = $config.DirSyncClientVersion
|
|
DirSyncAnchorAttribute = $config.DirSyncAnchorAttribute
|
|
DirSyncClientMachine = $config.DirSyncClientMachineName
|
|
DirSyncServiceAccount = $config.DirSyncServiceAccount
|
|
DirectorySynchronizationStatus = $config.DirectorySynchronizationStatus
|
|
DisplayName = $config.DisplayName
|
|
InitialDomain = $config.InitialDomain
|
|
IsDirSyncing = $config.DirectorySynchronizationEnabled
|
|
LastDirSyncTime = $config.LastDirSyncTime
|
|
LastPasswordSyncTime = $config.LastPasswordSyncTime
|
|
PasswordSynchronizationEnabled = $config.PasswordSynchronizationEnabled
|
|
}
|
|
|
|
# Try to get synchronization information using Azure AD Sync
|
|
try
|
|
{
|
|
$config2=Get-SyncConfiguration2 -AccessToken $AccessToken
|
|
|
|
# Merge the configs
|
|
foreach($key in $attributes.Keys)
|
|
{
|
|
$config2[$key] = $attributes[$key]
|
|
}
|
|
|
|
$capabilities=Get-SyncCapabilities -AccessToken $AccessToken
|
|
|
|
# Merge the configs
|
|
foreach($key in $capabilities.Keys)
|
|
{
|
|
$config2[$key] = $capabilities[$key]
|
|
}
|
|
|
|
return New-Object PSObject -Property $config2
|
|
}
|
|
catch
|
|
{
|
|
return New-Object PSObject -Property $attributes
|
|
}
|
|
|
|
|
|
|
|
}
|
|
}
|
|
|
|
# Get synchronization configuration using Sync API
|
|
# Oct 11th 2018
|
|
function Get-SyncConfiguration2
|
|
{
|
|
<#
|
|
.SYNOPSIS
|
|
Gets tenant's synchronization configuration
|
|
|
|
.DESCRIPTION
|
|
Gets tenant's synchronization configuration using Provisioning and Azure AD Sync API.
|
|
|
|
.Parameter AccessToken
|
|
Access Token
|
|
|
|
.Example
|
|
Get-AADIntSyncConfiguration
|
|
|
|
AllowedFeatures : {ObjectWriteback, , PasswordWriteback}
|
|
AnchorAttribute : objectGUID
|
|
ApplicationVersion : 1651564e-7ce4-4d99-88be-0a65050d8dc3
|
|
ClientVersion : 1.1.819.0
|
|
DirSyncClientMachine : AAD-SYNC-01
|
|
DirSyncFeatures : 41016
|
|
DisplayName : Company Ltd
|
|
IsDirSyncing : true
|
|
IsPasswordSyncing : false
|
|
IsTrackingChanges : false
|
|
MaxLinksSupportedAcrossBatchInProvision : 15000
|
|
PreventAccidentalDeletion : EnabledForCount
|
|
SynchronizationInterval : PT30M
|
|
TenantId : 57cf9f28-1ad7-40f4-bee8-d3ab9877f0a8
|
|
TotalConnectorSpaceObjects : 24
|
|
TresholdCount : 500
|
|
TresholdPercentage : 0
|
|
UnifiedGroupContainer :
|
|
UserContainer :
|
|
#>
|
|
[cmdletbinding()]
|
|
Param(
|
|
[Parameter(Mandatory=$False)]
|
|
[String]$AccessToken,
|
|
[Parameter(Mandatory=$False)]
|
|
[int]$Recursion=1
|
|
)
|
|
Process
|
|
{
|
|
# Accept only three loops
|
|
if($Recursion -gt 3)
|
|
{
|
|
throw "Too many recursions"
|
|
}
|
|
|
|
# Get from cache if not provided
|
|
$AccessToken = Get-AccessTokenFromCache -AccessToken $AccessToken -ClientID "1b730954-1685-4b74-9bfd-dac224a7b894" -Resource "https://graph.windows.net"
|
|
|
|
# Create the body block
|
|
$body=@"
|
|
<GetCompanyConfiguration xmlns="http://schemas.microsoft.com/online/aws/change/2010/01">
|
|
<includeLicenseInformation>false</includeLicenseInformation>
|
|
</GetCompanyConfiguration>
|
|
"@
|
|
$Message_id=(New-Guid).ToString()
|
|
$Command="GetCompanyConfiguration"
|
|
|
|
$serverName=$Script:aadsync_server
|
|
|
|
$envelope = Create-SyncEnvelope -AccessToken $AccessToken -Command $Command -Message_id $Message_id -Body $body -Binary -Server $serverName
|
|
|
|
# Call the API
|
|
$response=Call-ADSyncAPI $envelope -Command "$Command" -Tenant_id (Read-AccessToken($AccessToken)).tid -Message_id $Message_id -Server $serverName
|
|
|
|
# Convert binary response to XML
|
|
$xml_doc=BinaryToXml -xml_bytes $response -Dictionary (Get-XmlDictionary -Type WCF)
|
|
|
|
if(IsRedirectResponse($xml_doc))
|
|
{
|
|
return Get-SyncConfiguration -AccessToken $AccessToken -Recursion ($Recursion+1)
|
|
}
|
|
else
|
|
{
|
|
# Create a return object
|
|
$res=$xml_doc.Envelope.Body.GetCompanyConfigurationResponse.GetCompanyConfigurationResult
|
|
|
|
$AllowedFeatures = @()
|
|
foreach($feature in $res.AllowedFeatures.'#text')
|
|
{
|
|
$AllowedFeatures += $feature
|
|
}
|
|
|
|
$config=[ordered]@{
|
|
|
|
AllowedFeatures = $AllowedFeatures
|
|
AnchorAttribute = $res.DirSyncConfiguration.AnchorAttribute
|
|
ApplicationVersion = $res.DirSyncConfiguration.ApplicationVersion
|
|
ClientVersion = $res.DirSyncConfiguration.ClientVersion
|
|
DirSyncClientMachine = $res.DirSyncConfiguration.CurrentExport.DirSyncClientMachineName
|
|
DirSyncFeatures = $res.DirSyncFeatures
|
|
DisplayName = $res.DisplayName
|
|
IsDirSyncing = $res.IsDirSyncing
|
|
IsPasswordSyncing = $res.IsPasswordSyncing
|
|
IsTrackingChanges = $res.DirSyncConfiguration.IsTrackingChanges
|
|
MaxLinksSupportedAcrossBatchInProvision = $res.MaxLinksSupportedAcrossBatchInProvision2
|
|
PreventAccidentalDeletion = $res.DirSyncConfiguration.PreventAccidentalDeletion.DeletionPrevention
|
|
SynchronizationInterval = $res.SynchronizationInterval
|
|
TenantId = $res.TenantId
|
|
TotalConnectorSpaceObjects = $res.DirSyncConfiguration.CurrentExport.TotalConnectorSpaceObjects
|
|
TresholdCount = $res.DirSyncConfiguration.PreventAccidentalDeletion.ThresholdCount
|
|
TresholdPercentage = $res.DirSyncConfiguration.PreventAccidentalDeletion.ThresholdPercentage
|
|
UnifiedGroupContainer = $res.WriteBack.UnifiedGroupContainer
|
|
UserContainer = $res.WriteBack.UserContainer
|
|
}
|
|
|
|
return $config
|
|
}
|
|
}
|
|
}
|
|
|
|
# Enables or disables Password Hash Sync (PHS)
|
|
function Set-PasswordHashSyncEnabled
|
|
{
|
|
<#
|
|
.SYNOPSIS
|
|
Enables or disables password hash sync (PHS)
|
|
|
|
.DESCRIPTION
|
|
Enables or disables password hash sync (PHS) using Azure AD Sync API.
|
|
If dirsync is disabled, it's first enabled using Provisioning API.
|
|
|
|
Enabling / disabling the PHS usually takes less than 10 seconds. Check the status using Get-AADIntCompanyInformation.
|
|
|
|
.Parameter AccessToken
|
|
Access Token
|
|
|
|
.Parameter Enabled
|
|
True or False
|
|
|
|
.Example
|
|
Set-AADIntPasswordHashSyncEnabled -Enabled $true
|
|
|
|
#>
|
|
[cmdletbinding()]
|
|
Param(
|
|
[Parameter(Mandatory=$False)]
|
|
[String]$AccessToken,
|
|
[Parameter(Mandatory=$True)]
|
|
[Boolean]$Enabled
|
|
)
|
|
Process
|
|
{
|
|
Write-Warning "Set-AADIntPasswordHashSyncEnabled is deprecated."
|
|
Write-Warning "Use 'Set-AADIntSyncFeatures -EnableFeatures PasswordHashSync' instead."
|
|
|
|
# Get from cache if not provided
|
|
$AccessToken = Get-AccessTokenFromCache -AccessToken $AccessToken -ClientID "1b730954-1685-4b74-9bfd-dac224a7b894" -Resource "https://graph.windows.net"
|
|
|
|
# Get the current feature status
|
|
$features = Get-SyncFeatures -AccessToken $AccessToken
|
|
|
|
# Check whether the PHS sync is already enabled
|
|
if($Enabled -and $features.PasswordHashSync)
|
|
{
|
|
Write-Host "Password Hash Synchronization already enabled"
|
|
}
|
|
elseif(!$Enabled -and !$features.PasswordHashSync)
|
|
{
|
|
Write-Host "Password Hash Synchronization already disabled"
|
|
}
|
|
else
|
|
{
|
|
# Enable or disable PHS
|
|
if($Enabled)
|
|
{
|
|
$features = Set-SyncFeatures -AccessToken $AccessToken -EnableFeatures PasswordHashSync
|
|
if(!$features.PasswordHashSync)
|
|
{
|
|
Write-Error "Could not enable Password Hash Sync"
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
$features = Set-SyncFeatures -AccessToken $AccessToken -DisableFeatures PasswordHashSync | Out-Null
|
|
if($features.PasswordHashSync)
|
|
{
|
|
Write-Error "Could not disable Password Hash Sync"
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
# Set sync features
|
|
# Nov 3rd 2021
|
|
function Set-SyncFeatures
|
|
{
|
|
<#
|
|
.SYNOPSIS
|
|
Enables or disables synchronisation features.
|
|
|
|
.DESCRIPTION
|
|
Enables or disables synchronisation features using Azure AD Sync API.
|
|
As such, doesn't require "Global Administrator" credentials, "Directory Synchronization Accounts" credentials will do.
|
|
|
|
.Parameter AccessToken
|
|
Access Token
|
|
|
|
.Parameter EnableFeatures
|
|
List of features to be enabled
|
|
|
|
.Parameter DisableFeatures
|
|
List of features to be disabled
|
|
|
|
.Example
|
|
Get-AADIntAccessTokenForAADGraph -SaveToCache
|
|
PS C:\>Set-AADIntSyncFeature -EnableFeatures PasswordHashSync -DisableFeatures BlockCloudObjectTakeoverThroughHardMatch
|
|
|
|
BlockCloudObjectTakeoverThroughHardMatch : False
|
|
BlockSoftMatch : False
|
|
DeviceWriteback : False
|
|
DirectoryExtensions : False
|
|
DuplicateProxyAddressResiliency : True
|
|
DuplicateUPNResiliency : True
|
|
EnableSoftMatchOnUpn : True
|
|
EnableUserForcePasswordChangeOnLogon : False
|
|
EnforceCloudPasswordPolicyForPasswordSyncedUsers : False
|
|
PassThroughAuthentication : False
|
|
PasswordHashSync : True
|
|
PasswordWriteBack : False
|
|
SynchronizeUpnForManagedUsers : True
|
|
UnifiedGroupWriteback : False
|
|
UserWriteback : False
|
|
#>
|
|
[cmdletbinding()]
|
|
Param(
|
|
[Parameter(Mandatory=$False)]
|
|
[String]$AccessToken,
|
|
[Parameter(Mandatory=$False)]
|
|
[ValidateSet('PasswordHashSync','PasswordWriteBack','DirectoryExtensions','DuplicateUPNResiliency','EnableSoftMatchOnUpn','DuplicateProxyAddressResiliency','EnforceCloudPasswordPolicyForPasswordSyncedUsers','UnifiedGroupWriteback','UserWriteback','DeviceWriteback','SynchronizeUpnForManagedUsers','EnableUserForcePasswordChangeOnLogon','PassThroughAuthentication','BlockSoftMatch','BlockCloudObjectTakeoverThroughHardMatch')]
|
|
[String[]]$EnableFeatures,
|
|
[Parameter(Mandatory=$False)]
|
|
[ValidateSet('PasswordHashSync','PasswordWriteBack','DirectoryExtensions','DuplicateUPNResiliency','EnableSoftMatchOnUpn','DuplicateProxyAddressResiliency','EnforceCloudPasswordPolicyForPasswordSyncedUsers','UnifiedGroupWriteback','UserWriteback','DeviceWriteback','SynchronizeUpnForManagedUsers','EnableUserForcePasswordChangeOnLogon','PassThroughAuthentication','BlockSoftMatch','BlockCloudObjectTakeoverThroughHardMatch')]
|
|
[String[]]$DisableFeatures
|
|
)
|
|
Begin
|
|
{
|
|
$feature_values = [ordered]@{
|
|
"PasswordHashSync" = 1
|
|
"PasswordWriteBack" = 2
|
|
"DirectoryExtensions" = 4
|
|
"DuplicateUPNResiliency" = 8
|
|
"EnableSoftMatchOnUpn" = 16
|
|
"DuplicateProxyAddressResiliency" = 32
|
|
# 64
|
|
# 128
|
|
# 256
|
|
"EnforceCloudPasswordPolicyForPasswordSyncedUsers" = 512
|
|
"UnifiedGroupWriteback" = 1024
|
|
"UserWriteback" = 2048
|
|
"DeviceWriteback" = 4096
|
|
"SynchronizeUpnForManagedUsers" = 8192
|
|
"EnableUserForcePasswordChangeOnLogon" = 16384
|
|
# 32768
|
|
# 65536
|
|
"PassThroughAuthentication" = 131072
|
|
# 262144
|
|
"BlockSoftMatch" = 524288
|
|
"BlockCloudObjectTakeoverThroughHardMatch" = 1048576
|
|
}
|
|
}
|
|
Process
|
|
{
|
|
# Get from cache if not provided
|
|
$AccessToken = Get-AccessTokenFromCache -AccessToken $AccessToken -ClientID "1b730954-1685-4b74-9bfd-dac224a7b894" -Resource "https://graph.windows.net"
|
|
|
|
# Get the current features
|
|
$features = (Get-SyncConfiguration2 -AccessToken $AccessToken).DirSyncFeatures
|
|
|
|
# Enable features
|
|
foreach($feature in $EnableFeatures)
|
|
{
|
|
$features = $features -bor $feature_values[$feature]
|
|
}
|
|
|
|
# Disable features
|
|
foreach($feature in $DisableFeatures)
|
|
{
|
|
$features = $features -band (0x7FFFFFFF -bxor $feature_values[$feature])
|
|
}
|
|
|
|
Update-SyncFeatures -AccessToken $AccessToken -Features $features
|
|
|
|
Get-SyncFeatures -AccessToken $AccessToken
|
|
}
|
|
}
|
|
|
|
# Get sync features
|
|
# Nov 3rd 2021
|
|
function Get-SyncFeatures
|
|
{
|
|
<#
|
|
.SYNOPSIS
|
|
Show the status of synchronisation features.
|
|
|
|
.DESCRIPTION
|
|
Show the status of synchronisation features using Azure AD Sync API.
|
|
As such, doesn't require "Global Administrator" credentials, "Directory Synchronization Accounts" credentials will do.
|
|
|
|
.Parameter AccessToken
|
|
Access Token
|
|
|
|
.Example
|
|
Get-AADIntAccessTokenForAADGraph -SaveToCache
|
|
PS C:\>Get-AADIntSyncFeatures
|
|
|
|
BlockCloudObjectTakeoverThroughHardMatch : True
|
|
BlockSoftMatch : False
|
|
DeviceWriteback : False
|
|
DirectoryExtensions : False
|
|
DuplicateProxyAddressResiliency : True
|
|
DuplicateUPNResiliency : True
|
|
EnableSoftMatchOnUpn : True
|
|
EnableUserForcePasswordChangeOnLogon : False
|
|
EnforceCloudPasswordPolicyForPasswordSyncedUsers : False
|
|
PassThroughAuthentication : False
|
|
PasswordHashSync : True
|
|
PasswordWriteBack : False
|
|
SynchronizeUpnForManagedUsers : True
|
|
UnifiedGroupWriteback : False
|
|
UserWriteback : False
|
|
#>
|
|
[cmdletbinding()]
|
|
Param(
|
|
[Parameter(Mandatory=$False)]
|
|
[String]$AccessToken
|
|
)
|
|
Begin
|
|
{
|
|
$feature_values = [ordered]@{
|
|
"BlockCloudObjectTakeoverThroughHardMatch" = 1048576
|
|
"BlockSoftMatch" = 524288
|
|
"DeviceWriteback" = 4096
|
|
"DirectoryExtensions" = 4
|
|
"DuplicateProxyAddressResiliency" = 32
|
|
"DuplicateUPNResiliency" = 8
|
|
"EnableSoftMatchOnUpn" = 16
|
|
"EnableUserForcePasswordChangeOnLogon" = 16384
|
|
"EnforceCloudPasswordPolicyForPasswordSyncedUsers" = 512
|
|
"PassThroughAuthentication" = 131072
|
|
"PasswordHashSync" = 1
|
|
"PasswordWriteBack" = 2
|
|
"SynchronizeUpnForManagedUsers" = 8192
|
|
"UnifiedGroupWriteback" = 1024
|
|
"UserWriteback" = 2048
|
|
}
|
|
}
|
|
Process
|
|
{
|
|
# Get from cache if not provided
|
|
$AccessToken = Get-AccessTokenFromCache -AccessToken $AccessToken -ClientID "1b730954-1685-4b74-9bfd-dac224a7b894" -Resource "https://graph.windows.net"
|
|
|
|
# Get the current features
|
|
$features = (Get-SyncConfiguration2 -AccessToken $AccessToken).DirSyncFeatures
|
|
|
|
$attributes = [ordered]@{}
|
|
|
|
# Enable features
|
|
foreach($key in $feature_values.Keys)
|
|
{
|
|
$attributes[$key] = ($features -band $feature_values[$key]) -gt 0
|
|
}
|
|
|
|
New-Object psobject -Property $attributes
|
|
}
|
|
}
|
|
|
|
|
|
# Update dirsync features
|
|
# Nov 3rd 2021
|
|
function Update-SyncFeatures
|
|
{
|
|
[cmdletbinding()]
|
|
Param(
|
|
[Parameter(Mandatory=$False)]
|
|
[String]$AccessToken,
|
|
[Parameter(Mandatory=$True)]
|
|
[int]$Features,
|
|
[Parameter(Mandatory=$False)]
|
|
[int]$Recursion=1
|
|
)
|
|
Process
|
|
{
|
|
# Accept only three loops
|
|
if($Recursion -gt 3)
|
|
{
|
|
throw "Too many recursions"
|
|
}
|
|
|
|
# Get from cache if not provided
|
|
$AccessToken = Get-AccessTokenFromCache -AccessToken $AccessToken -ClientID "1b730954-1685-4b74-9bfd-dac224a7b894" -Resource "https://graph.windows.net"
|
|
|
|
# Create the body block
|
|
$body=@"
|
|
<SetCompanyDirsyncFeatures xmlns="http://schemas.microsoft.com/online/aws/change/2010/01">
|
|
<dirsyncFeatures>$Features</dirsyncFeatures>
|
|
</SetCompanyDirsyncFeatures>
|
|
"@
|
|
$Message_id=(New-Guid).ToString()
|
|
$Command="SetCompanyDirsyncFeatures"
|
|
|
|
$serverName=$Script:aadsync_server
|
|
|
|
$envelope = Create-SyncEnvelope -AccessToken $AccessToken -Command $Command -Message_id $Message_id -Body $body -Binary -Server $serverName
|
|
|
|
# Call the API
|
|
$response=Call-ADSyncAPI $envelope -Command "$Command" -Tenant_id (Read-AccessToken($AccessToken)).tid -Message_id $Message_id -Server $serverName
|
|
|
|
# Convert binary response to XML
|
|
$xml_doc=BinaryToXml -xml_bytes $response -Dictionary (Get-XmlDictionary -Type WCF)
|
|
|
|
if(IsRedirectResponse($xml_doc))
|
|
{
|
|
return Set-SyncFeatures -AccessToken $AccessToken -Features $Features -Recursion ($Recursion+1)
|
|
}
|
|
else
|
|
{
|
|
# Create a return object
|
|
$res=$xml_doc.Envelope.Body.GetCompanyConfigurationResponse.GetCompanyConfigurationResult
|
|
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
# Provision Azure AD Sync Object
|
|
function Set-AzureADObject
|
|
{
|
|
<#
|
|
.SYNOPSIS
|
|
Creates or updates Azure AD object using Azure AD Sync API
|
|
|
|
.DESCRIPTION
|
|
Creates or updates Azure AD object using Azure AD Sync API. Can also set cloud-only user's sourceAnchor (ImmutableId) and onPremisesSAMAccountName. SourceAnchor can only be set once!
|
|
|
|
.Parameter AccessToken
|
|
Access Token
|
|
|
|
.Parameter sourceAnchor
|
|
The source anchor for the Azure AD object. Typically Base 64 encoded GUID of on-prem AD object.
|
|
|
|
.Parameter cloudAnchor
|
|
The cloud anchor for the Azure AD object in the form "<type>_<objectid>". For example "User_a98368aa-f0cb-41b5-a7c6-10f18c6c837d"
|
|
|
|
.Parameter userPrincipalName
|
|
User Principal Name of the Azure AD object
|
|
|
|
.Parameter surname
|
|
The last name of the Azure AD object
|
|
|
|
.Parameter onPremisesSamAccountName
|
|
The on-prem AD samaccountname of the Azure AD object
|
|
|
|
.Parameter onPremisesDistinguishedName
|
|
The on-prem AD DN of the Azure AD object
|
|
|
|
.Parameter onPremisesSecurityIdentifier
|
|
The on-prem AD security identifier of the Azure AD object
|
|
|
|
.Parameter netBiosName
|
|
The on-prem netbiosname of the Azure AD object
|
|
|
|
.Parameter lastPasswordChangeTimeStamp
|
|
Timestamp when the on-prem AD object's password was changed
|
|
|
|
.Parameter givenName
|
|
The first name of the Azure AD object
|
|
|
|
.Parameter dnsDomainName
|
|
The dns domain name of the Azure AD object
|
|
|
|
.Parameter displayName
|
|
The display name of the Azure AD object
|
|
|
|
.Parameter countryCode
|
|
The country code of the Azure AD object.
|
|
|
|
.Parameter commonName
|
|
The common name of the Azure AD object
|
|
|
|
.Parameter accountEnabled
|
|
Is the Azure AD object enabled. Default is $True.
|
|
|
|
.Parameter cloudMastered
|
|
Is the Azure AD object editable in Azure AD. Default is $true
|
|
|
|
.Parameter usageLocation
|
|
Two letter country code for usage location of Azure AD object.
|
|
|
|
#>
|
|
[cmdletbinding()]
|
|
Param(
|
|
[Parameter(Mandatory=$False)]
|
|
[String]$AccessToken,
|
|
[Parameter(Mandatory=$False)]
|
|
[String]$CloudAnchor,
|
|
[Parameter(Mandatory=$False)]
|
|
[String]$SourceAnchor,
|
|
[Parameter(Mandatory=$False)]
|
|
[String]$userPrincipalName,
|
|
[Parameter(Mandatory=$False)]
|
|
[String]$surname,
|
|
[Parameter(Mandatory=$False)]
|
|
[String]$onPremisesSamAccountName,
|
|
[Parameter(Mandatory=$False)]
|
|
[String]$onPremisesDistinguishedName,
|
|
[Parameter(Mandatory=$False)]
|
|
[String]$onPremiseSecurityIdentifier,
|
|
[Parameter(Mandatory=$False)]
|
|
[String]$netBiosName,
|
|
[Parameter(Mandatory=$False)]
|
|
[String]$lastPasswordChangeTimestamp,
|
|
[Parameter(Mandatory=$False)]
|
|
[String]$givenName,
|
|
[Parameter(Mandatory=$False)]
|
|
[String]$dnsDomainName,
|
|
[Parameter(Mandatory=$False)]
|
|
[String]$displayName,
|
|
[Parameter(Mandatory=$False)]
|
|
$countryCode,
|
|
[Parameter(Mandatory=$False)]
|
|
[String]$commonName,
|
|
[Parameter(Mandatory=$False)]
|
|
$accountEnabled,
|
|
[Parameter(Mandatory=$False)]
|
|
$cloudMastered,
|
|
[Parameter(Mandatory=$False)]
|
|
[ValidateSet('AF','AX','AL','DZ','AS','AD','AO','AI','AQ','AG','AR','AM','AW','AU','AT','AZ','BS','BH','BD','BB','BY','BE','BZ','BJ','BM','BT','BO','BQ','BA','BW','BV','BR','IO','BN','BG','BF','BI','KH','CM','CA','CV','KY','CF','TD','CL','CN','CX','CC','CO','KM','CG','CD','CK','CR','CI','HR','CU','CW','CY','CZ','DK','DJ','DM','DO','EC','EG','SV','GQ','ER','EE','ET','FK','FO','FJ','FI','FR','GF','PF','TF','GA','GM','GE','DE','GH','GI','GR','GL','GD','GP','GU','GT','GG','GN','GW','GY','HT','HM','VA','HN','HK','HU','IS','IN','ID','IQ','IE','IR','IM','IL','IT','JM','JP','JE','JO','KZ','KE','KI','KP','KR','KW','KG','LA','LV','LB','LS','LR','LY','LI','LT','LU','MO','MK','MG','MW','MY','MV','ML','MT','MH','MQ','MR','MU','YT','MX','FM','MD','MC','MN','ME','MS','MA','MZ','MM','NA','NR','NP','NL','NC','NZ','NI','NE','NG','NU','NF','MP','NO','OM','PK','PW','PS','PA','PG','PY','PE','PH','PN','PL','PT','PR','QA','RE','RO','RU','RW','BL','SH','KN','LC','MF','PM','VC','WS','SM','ST','SA','SN','RS','SC','SL','SG','SX','SK','SI','SB','SO','ZA','GS','SS','ES','LK','SD','SR','SJ','SZ','SE','CH','SY','TW','TJ','TZ','TH','TL','TG','TK','TO','TT','TN','TR','TM','TC','TV','UG','UA','AE','GB','US','UM','UY','UZ','VU','VE','VN','VG','VI','WF','EH','YE','ZM','ZW')][String]$usageLocation,
|
|
[Parameter(Mandatory=$False)]
|
|
[ValidateSet('User','Group','Contact','Device')]
|
|
[String]$ObjectType="User",
|
|
[Parameter(Mandatory=$False)]
|
|
[String[]]$proxyAddresses,
|
|
[Parameter(Mandatory=$False)]
|
|
[String]$thumbnailPhoto,
|
|
[Parameter(Mandatory=$False)]
|
|
[String[]]$groupMembers,
|
|
|
|
[Parameter(Mandatory=$False)]
|
|
[String]$deviceId,
|
|
[Parameter(Mandatory=$False)]
|
|
[String]$deviceOSType,
|
|
[Parameter(Mandatory=$False)]
|
|
[String]$deviceTrustType,
|
|
[Parameter(Mandatory=$False)]
|
|
[String[]]$userCertificate,
|
|
[Parameter(Mandatory=$False)]
|
|
[ValidateSet('Set','Add')]
|
|
[String]$Operation="Set",
|
|
[Parameter(Mandatory=$False)]
|
|
[int]$Recursion=1
|
|
)
|
|
Process
|
|
{
|
|
# Accept only three loops
|
|
if($Recursion -gt 3)
|
|
{
|
|
throw "Too many recursions"
|
|
}
|
|
# Get from cache if not provided
|
|
$AccessToken = Get-AccessTokenFromCache -AccessToken $AccessToken -ClientID "1b730954-1685-4b74-9bfd-dac224a7b894" -Resource "https://graph.windows.net"
|
|
|
|
# Create the body block
|
|
$body=@"
|
|
<ProvisionAzureADSyncObjects xmlns="http://schemas.microsoft.com/online/aws/change/2010/01">
|
|
<syncRequest xmlns:b="http://schemas.microsoft.com/online/aws/change/2014/06" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
|
|
<b:SyncObjects>
|
|
<b:AzureADSyncObject>
|
|
<b:PropertyValues xmlns:c="http://schemas.microsoft.com/2003/10/Serialization/Arrays">
|
|
|
|
$(Add-PropertyValue "SourceAnchor" $sourceAnchor)
|
|
$(Add-PropertyValue "accountEnabled" $accountEnabled -Type bool)
|
|
$(Add-PropertyValue "commonName" $commonName)
|
|
$(Add-PropertyValue "countryCode" $countryCode -Type long)
|
|
$(Add-PropertyValue "displayName" $displayName)
|
|
$(Add-PropertyValue "dnsDomainName" $dnsDomainName)
|
|
$(Add-PropertyValue "givenName" $givenName)
|
|
$(Add-PropertyValue "lastPasswordChangeTimestamp" $lastPasswordChangeTimestamp)
|
|
$(Add-PropertyValue "netBiosName" $netBiosName)
|
|
$(Add-PropertyValue "onPremiseSecurityIdentifier" $onPremiseSecurityIdentifier -Type base64)
|
|
$(Add-PropertyValue "onPremisesDistinguishedName" $onPremisesDistinguishedName)
|
|
$(Add-PropertyValue "onPremisesSamAccountName" $onPremisesSamAccountName)
|
|
|
|
$(Add-PropertyValue "surname" $surname)
|
|
$(Add-PropertyValue "userPrincipalName" $userPrincipalName)
|
|
$(Add-PropertyValue "cloudMastered" $cloudMastered -Type bool)
|
|
$(Add-PropertyValue "usageLocation" $usageLocation)
|
|
$(Add-PropertyValue "CloudAnchor" $CloudAnchor)
|
|
$(Add-PropertyValue "ThumbnailPhoto" $thumbnailPhoto)
|
|
$(Add-PropertyValue "proxyAddresses" $proxyAddresses -Type ArrayOfstring)
|
|
$(Add-PropertyValue "member" $groupMembers -Type ArrayOfstring)
|
|
|
|
$(Add-PropertyValue "deviceId" $deviceId -Type base64)
|
|
$(Add-PropertyValue "deviceTrustType" $deviceTrustType)
|
|
$(Add-PropertyValue "deviceOSType" $deviceOSType)
|
|
$(Add-PropertyValue "userCertificate" $userCertificate -Type ArrayOfbase64)
|
|
|
|
$(if($ObjectType -eq "User"){Add-PropertyValue "userType" $userType})
|
|
$(if($ObjectType -eq "Group"){Add-PropertyValue "securityEnabled" $true -Type bool})
|
|
</b:PropertyValues>
|
|
<b:SyncObjectType>$ObjectType</b:SyncObjectType>
|
|
<b:SyncOperation>$Operation</b:SyncOperation>
|
|
</b:AzureADSyncObject>
|
|
</b:SyncObjects>
|
|
</syncRequest>
|
|
</ProvisionAzureADSyncObjects>
|
|
"@
|
|
|
|
$Message_id=(New-Guid).ToString()
|
|
$Command="ProvisionAzureADSyncObjects"
|
|
|
|
$serverName=$aadsync_server
|
|
|
|
$envelope = Create-SyncEnvelope -AccessToken $AccessToken -Command $Command -Message_id $Message_id -Body $body -Binary -Server $serverName
|
|
|
|
# Call the API
|
|
$response=Call-ADSyncAPI $envelope -Command "$Command" -Tenant_id (Read-AccessToken($AccessToken)).tid -Message_id $Message_id -Server $serverName
|
|
|
|
# Convert binary response to XML
|
|
$xml_doc=BinaryToXml -xml_bytes $response -Dictionary (Get-XmlDictionary -Type WCF)
|
|
|
|
|
|
if(IsRedirectResponse($xml_doc))
|
|
{
|
|
return Set-AzureADObject -AccessToken $AccessToken -Recursion ($Recursion+1) -sourceAnchor $sourceAnchor -ObjectType $ObjectType -userPrincipalName $userPrincipalName -surname $surname -onPremisesSamAccountName $onPremisesSamAccountName -onPremisesDistinguishedName $onPremisesDistinguishedName -onPremiseSecurityIdentifier $onPremisesDistinguishedName -netBiosName $netBiosName -lastPasswordChangeTimestamp $lastPasswordChangeTimestamp -givenName $givenName -dnsDomainName $dnsDomainName -displayName $displayName -countryCode $countryCode -commonName $commonName -accountEnabled $accountEnabled -cloudMastered $cloudMastered -usageLocation $usageLocation -CloudAnchor $CloudAnchor
|
|
}
|
|
|
|
# Check whether this is an error message
|
|
if($xml_doc.Envelope.Body.Fault)
|
|
{
|
|
Throw $xml_doc.Envelope.Body.Fault.Reason.Text.'#text'
|
|
}
|
|
|
|
# Return
|
|
$xml_doc.Envelope.Body.ProvisionAzureADSyncObjectsResponse.ProvisionAzureADSyncObjectsResult.SyncObjectResults.AzureADSyncObjectResult
|
|
}
|
|
}
|
|
|
|
|
|
# Removes the given Azure AD Object
|
|
function Remove-AzureADObject
|
|
{
|
|
<#
|
|
.SYNOPSIS
|
|
Removes Azure AD object using Azure AD Sync API
|
|
|
|
.DESCRIPTION
|
|
Removes Azure AD object using Azure AD Sync API
|
|
|
|
.Parameter AccessToken
|
|
Access Token
|
|
|
|
.Parameter sourceAnchor
|
|
The source anchor for the Azure AD object. Typically Base 64 encoded GUID of on-prem AD object.
|
|
|
|
.Parameter cloudAnchor
|
|
The cloud anchor for the Azure AD object in the form "<type>_<objectid>". For example "User_a98368aa-f0cb-41b5-a7c6-10f18c6c837d"
|
|
|
|
|
|
#>
|
|
[cmdletbinding()]
|
|
Param(
|
|
[Parameter(Mandatory=$False)]
|
|
[String]$AccessToken,
|
|
[Parameter(ParameterSetName='sourceAnchor', Mandatory=$True)]
|
|
[String]$sourceAnchor,
|
|
[Parameter(ParameterSetName='cloudAnchor', Mandatory=$True)]
|
|
[String]$cloudAnchor,
|
|
[Parameter(Mandatory=$False)]
|
|
[ValidateSet('User','Group','Contact','Device')]
|
|
[String]$ObjectType="User",
|
|
[Parameter(Mandatory=$False)]
|
|
[int]$Recursion=1
|
|
)
|
|
Process
|
|
{
|
|
# Accept only three loops
|
|
if($Recursion -gt 3)
|
|
{
|
|
throw "Too many recursions"
|
|
}
|
|
# Get from cache if not provided
|
|
$AccessToken = Get-AccessTokenFromCache -AccessToken $AccessToken -ClientID "1b730954-1685-4b74-9bfd-dac224a7b894" -Resource "https://graph.windows.net"
|
|
|
|
# Create the body block
|
|
$body=@"
|
|
<ProvisionAzureADSyncObjects xmlns="http://schemas.microsoft.com/online/aws/change/2010/01">
|
|
<syncRequest xmlns:b="http://schemas.microsoft.com/online/aws/change/2014/06" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
|
|
<b:SyncObjects>
|
|
<b:AzureADSyncObject>
|
|
<b:PropertyValues xmlns:c="http://schemas.microsoft.com/2003/10/Serialization/Arrays">
|
|
$(Add-PropertyValue "SourceAnchor" $sourceAnchor)
|
|
$(Add-PropertyValue "CloudAnchor" $cloudAnchor)
|
|
</b:PropertyValues>
|
|
<b:SyncObjectType>$ObjectType</b:SyncObjectType>
|
|
<b:SyncOperation>Delete</b:SyncOperation>
|
|
</b:AzureADSyncObject>
|
|
</b:SyncObjects>
|
|
</syncRequest>
|
|
</ProvisionAzureADSyncObjects>
|
|
"@
|
|
|
|
$Message_id=(New-Guid).ToString()
|
|
$Command="ProvisionAzureADSyncObjects"
|
|
|
|
$serverName=$aadsync_server
|
|
|
|
$envelope = Create-SyncEnvelope -AccessToken $AccessToken -Command $Command -Message_id $Message_id -Body $body -Binary -Server $serverName
|
|
|
|
# Call the API
|
|
$response=Call-ADSyncAPI $envelope -Command "$Command" -Tenant_id (Read-AccessToken($AccessToken)).tid -Message_id $Message_id -Server $serverName
|
|
|
|
# Convert binary response to XML
|
|
$xml_doc=BinaryToXml -xml_bytes $response -Dictionary (Get-XmlDictionary -Type WCF)
|
|
|
|
|
|
if(IsRedirectResponse($xml_doc))
|
|
{
|
|
return Remove-AzureADObject -AccessToken $AccessToken -Recursion ($Recursion+1) -sourceAnchor $sourceAnchor -ObjectType $ObjectType
|
|
}
|
|
|
|
# Return
|
|
$xml_doc.Envelope.Body.ProvisionAzureADSyncObjectsResponse.ProvisionAzureADSyncObjectsResult.SyncObjectResults.AzureADSyncObjectResult
|
|
}
|
|
}
|
|
|
|
|
|
# Finalize Azure AD Sync
|
|
function Finalize-Export
|
|
{
|
|
[cmdletbinding()]
|
|
Param(
|
|
[Parameter(Mandatory=$False)]
|
|
[String]$AccessToken,
|
|
[Parameter(Mandatory=$False)]
|
|
[int]$Count=1,
|
|
[Parameter(Mandatory=$False)]
|
|
[int]$Recursion=1
|
|
)
|
|
Process
|
|
{
|
|
# Accept only three loops
|
|
if($Recursion -gt 3)
|
|
{
|
|
throw "Too many recursions"
|
|
}
|
|
|
|
# Get from cache if not provided
|
|
$AccessToken = Get-AccessTokenFromCache -AccessToken $AccessToken -ClientID "1b730954-1685-4b74-9bfd-dac224a7b894" -Resource "https://graph.windows.net"
|
|
|
|
# Create the body block
|
|
|
|
|
|
$body=@"
|
|
<FinalizeExport xmlns="http://schemas.microsoft.com/online/aws/change/2010/01">
|
|
<totalExported>$count</totalExported>
|
|
<successfulExportCount>$count</successfulExportCount>
|
|
</FinalizeExport>
|
|
"@
|
|
$Message_id=(New-Guid).ToString()
|
|
$Command="FinalizeExport"
|
|
|
|
$serverName=$aadsync_server
|
|
|
|
$envelope = Create-SyncEnvelope -AccessToken $AccessToken -Command $Command -Message_id $Message_id -Body $body -Binary -Server $serverName
|
|
|
|
# Call the API
|
|
$response=Call-ADSyncAPI $envelope -Command "$Command" -Tenant_id (Parse-AccessToken($AccessToken)).tid -Message_id $Message_id -Server $serverName
|
|
|
|
# Convert binary response to XML
|
|
$xml_doc=BinaryToXml -xml_bytes $response -Dictionary (Get-XmlDictionary -Type WCF)
|
|
|
|
if(IsRedirectResponse($xml_doc))
|
|
{
|
|
return Finalize-Export -Count $Count -AccessToken $AccessToken -Recursion ($Recursion+1)
|
|
}
|
|
else
|
|
{
|
|
return $xml_doc
|
|
}
|
|
}
|
|
}
|
|
|
|
# Get sync objects from Azure AD
|
|
function Get-SyncObjects
|
|
{
|
|
<#
|
|
.SYNOPSIS
|
|
Gets tenant's synchronized objects
|
|
|
|
.DESCRIPTION
|
|
Gets tenant's synchronized objects using Azure AD Sync API
|
|
|
|
.Parameter AccessToken
|
|
Access Token
|
|
|
|
.Parameter Version
|
|
Version number of AD Sync, defaults to 2. Version 2 returns only non-empty attributes and is thus much more efficient.
|
|
|
|
.Example
|
|
Get-AADIntSyncObjects -AccessToken $at -Version 1
|
|
|
|
AccountEnabled : true
|
|
Alias :
|
|
City :
|
|
CloudAnchor : User_64c6616b-f961-4882-a03e-9209d01711aa
|
|
CloudLegacyExchangeDN : /o=ExchangeLabs/ou=Exchange Administrative Group (FYDIBOHF23SPDLT)/cn=Recipients/cn=7e07ff8b-5d1c-4319-b608-c371914fbd99-Megan Bowen
|
|
CloudMSExchArchiveStatus :
|
|
CloudMSExchBlockedSendersHash :
|
|
CloudMSExchRecipientDisplayType : 1073741824
|
|
CloudMSExchSafeRecipientsHash :
|
|
CloudMSExchSafeSendersHash :
|
|
CloudMSExchTeamMailboxExpiration :
|
|
CloudMSExchTeamMailboxSharePointUrl :
|
|
CloudMSExchUCVoiceMailSettings :
|
|
CloudMSExchUserHoldPolicies :
|
|
CloudMastered : false
|
|
CommonName : Megan Bowen
|
|
Company :
|
|
Country :
|
|
CountryCode : 0
|
|
CountryLetterCode :
|
|
Department :
|
|
Description :
|
|
DisplayName : Megan Bowen
|
|
DnsDomainName : company.com
|
|
...
|
|
|
|
.Example
|
|
Get-AADIntSyncObjects -AccessToken $at
|
|
|
|
AccountEnabled : true
|
|
CloudAnchor : User_64c6616b-f961-4882-a03e-9209d01711aa
|
|
CloudLegacyExchangeDN : /o=ExchangeLabs/ou=Exchange Administrative Group (FYDIBOHF23SPDLT)/cn=Recipients/cn=7e07ff8b-5d1c-4319-b608-c371914fbd99-Megan Bowen
|
|
CloudMSExchRecipientDisplayType : 1073741824
|
|
CloudMastered : false
|
|
CommonName : Megan Bowen
|
|
CountryCode : 0
|
|
DisplayName : Megan Bowen
|
|
DnsDomainName : company.com
|
|
GivenName : Megan
|
|
LastPasswordChangeTimestamp : 20190801164342.0Z
|
|
NetBiosName : COMPANY
|
|
OnPremiseSecurityIdentifier :
|
|
OnPremisesDistinguishedName : CN=Megan Bowen,OU=Domain Users,DC=company,DC=com
|
|
OnPremisesSamAccountName : MeganB
|
|
SourceAnchor :
|
|
Surname : Bowen
|
|
SyncObjectType : User
|
|
SyncOperation : Set
|
|
UsageLocation : US
|
|
UserPrincipalName : MeganB@company.com
|
|
UserType : Member
|
|
#>
|
|
[cmdletbinding()]
|
|
Param(
|
|
[Parameter(Mandatory=$False)]
|
|
[String]$AccessToken,
|
|
[Parameter(Mandatory=$False)]
|
|
[int]$Recursion=1,
|
|
[Parameter(Mandatory=$False)]
|
|
[ValidateSet(1,2)]
|
|
[Int]$Version=2,
|
|
[Parameter(Mandatory=$False)]
|
|
[bool]$FullSync=$true
|
|
)
|
|
Process
|
|
{
|
|
# Accept only three loops
|
|
if($Recursion -gt 3)
|
|
{
|
|
throw "Too many recursions"
|
|
}
|
|
|
|
# Check the version
|
|
if($Version -eq 2)
|
|
{
|
|
$txtVer = $Version.ToString()
|
|
}
|
|
|
|
# Get from cache if not provided
|
|
$AccessToken = Get-AccessTokenFromCache -AccessToken $AccessToken -ClientID "1b730954-1685-4b74-9bfd-dac224a7b894" -Resource "https://graph.windows.net"
|
|
|
|
# Create the body block
|
|
$body=@"
|
|
<ReadBackAzureADSyncObjects$txtVer xmlns="http://schemas.microsoft.com/online/aws/change/2010/01">
|
|
<includeLicenseInformation>true</includeLicenseInformation>
|
|
<inputCookie i:nil="true" xmlns:i="http://www.w3.org/2001/XMLSchema-instance"></inputCookie>
|
|
<isFullSync>$($FullSync.toString().toLower())</isFullSync>
|
|
</ReadBackAzureADSyncObjects$txtVer>
|
|
"@
|
|
$Message_id=(New-Guid).ToString()
|
|
$Command="ReadBackAzureADSyncObjects$txtVer"
|
|
|
|
$serverName=$Script:aadsync_server
|
|
|
|
$envelope = Create-SyncEnvelope -AccessToken $AccessToken -Command $Command -Message_id $Message_id -Body $body -Binary -Server $serverName
|
|
|
|
# Call the API
|
|
$response=Call-ADSyncAPI $envelope -Command "$Command" -Tenant_id (Read-AccessToken($AccessToken)).tid -Message_id $Message_id -Server $serverName
|
|
|
|
# Convert binary response to XML
|
|
$xml_doc=BinaryToXml -xml_bytes $response -Dictionary (Get-XmlDictionary -Type WCF)
|
|
|
|
if(IsRedirectResponse($xml_doc))
|
|
{
|
|
return Get-SyncObjects -AccessToken $AccessToken -Recursion ($Recursion+1) -Version $Version
|
|
}
|
|
else
|
|
{
|
|
# Create a return object
|
|
if($Version -eq 2)
|
|
{
|
|
$res=$xml_doc.Envelope.Body.ReadBackAzureADSyncObjects2Response.ReadBackAzureADSyncObjects2Result
|
|
}
|
|
else
|
|
{
|
|
$res=$xml_doc.Envelope.Body.ReadBackAzureADSyncObjectsResponse.ReadBackAzureADSyncObjectsResult
|
|
}
|
|
|
|
# Loop through objects
|
|
foreach($obj in $res.ResultObjects.AzureADSyncObject)
|
|
{
|
|
$details=@{}
|
|
$details.SyncObjectType=$obj.SyncObjectType
|
|
$details.SyncOperation=$obj.SyncOperation
|
|
|
|
# Loop through all key=value pairs
|
|
foreach($kv in $obj.PropertyValues.KeyValueOfstringanyType)
|
|
{
|
|
$details[$kv.Key]=$kv.value.'#text'
|
|
}
|
|
|
|
# Return
|
|
New-Object -TypeName PSObject -Property $details
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
# Set's user's password
|
|
function Set-UserPassword
|
|
{
|
|
<#
|
|
.SYNOPSIS
|
|
Sets the password of the given user
|
|
|
|
.DESCRIPTION
|
|
Sets the password of the given user using Azure AD Sync API. If the Result is 0, the change was successful.
|
|
Requires that Directory Synchronization is enabled for the tenant!
|
|
|
|
.Parameter AccessToken
|
|
Access Token
|
|
|
|
.Parameter SourceAnchor
|
|
User's source anchor (ImmutableId)
|
|
|
|
.Parameter CloudAnchor
|
|
User's cloud anchor "<Type>_<objectid>". For example "User_60f87269-f258-4473-8cca-267b50110e7a"
|
|
|
|
.Parameter Password
|
|
User's new password
|
|
|
|
.Parameter ChangeDate
|
|
Time of the password change. Can be now or in the past.
|
|
|
|
.Parameter Iterations
|
|
The number of iterations of pbkdf2. Defaults to 1000.
|
|
|
|
.Parameter IncludeLegacy
|
|
Include windowsLegacyCredentials, i.e., NTHash. If certificate not provided, tries to get one from Azure AD
|
|
|
|
.Parameter PfxFileName
|
|
Name of windowsLegacyCredentials encryption certificate
|
|
|
|
.Parameter PfxPassword
|
|
Password of windowsLegacyCredentials encryption certificate
|
|
|
|
.Example
|
|
Set-AADIntUserPassword -SourceAnchor "Vvl6blILG0/Cr/8TWOe9pg==" -Password "MyPassword" -ChangeDate ((Get-Date).AddYears(-1))
|
|
|
|
CloudAnchor Result SourceAnchor
|
|
----------- ------ ------------
|
|
CloudAnchor 0 Vvl6blILG0/Cr/8TWOe9pg==
|
|
|
|
.Example
|
|
Set-AADIntUserPassword -CloudAnchor "User_60f87269-f258-4473-8cca-267b50110e7a" -Password "MyPassword" -ChangeDate ((Get-Date).AddYears(-1))
|
|
|
|
CloudAnchor Result SourceAnchor
|
|
----------- ------ ------------
|
|
User_60f87269-f258-4473-8cca-267b50110e7a 0 SourceAnchor
|
|
#>
|
|
[cmdletbinding()]
|
|
Param(
|
|
[Parameter(Mandatory=$False)]
|
|
[String]$AccessToken,
|
|
[Parameter(ParameterSetName='Cloud', Mandatory=$True)]
|
|
[String]$CloudAnchor,
|
|
[Parameter(ParameterSetName='Source', Mandatory=$True)]
|
|
[String]$SourceAnchor,
|
|
[Parameter(ParameterSetName='UPN', Mandatory=$True)]
|
|
[String]$UserPrincipalName,
|
|
[Parameter(Mandatory=$False)]
|
|
[String]$Password,
|
|
[Parameter(Mandatory=$False)]
|
|
[String]$Hash,
|
|
[switch]$IncludeLegacy,
|
|
[Parameter(Mandatory=$False)]
|
|
[DateTime]$ChangeDate=(Get-Date),
|
|
[Parameter(Mandatory=$False)]
|
|
[int]$Recursion=1,
|
|
[parameter(Mandatory=$false)]
|
|
[int]$Iterations=1000,
|
|
|
|
# Legacy credentials encryption certificate
|
|
[Parameter(Mandatory=$False)]
|
|
[string]$PfxFileName,
|
|
[Parameter(Mandatory=$False)]
|
|
[string]$PfxPassword
|
|
)
|
|
Process
|
|
{
|
|
# Password or Hash must be given
|
|
if([string]::IsNullOrEmpty($Password) -and [string]::IsNullOrEmpty($Hash))
|
|
{
|
|
throw "Password or Hash must be given!"
|
|
}
|
|
# Accept only three loops
|
|
if($Recursion -gt 3)
|
|
{
|
|
throw "Too many recursions"
|
|
}
|
|
|
|
# Warn once about iterations over 1000
|
|
if($Recursion -eq 1 -and $Iterations -gt 1000)
|
|
{
|
|
Write-Warning "Iterations more than 1000, login may not work correctly!"
|
|
}
|
|
|
|
# Get from cache if not provided
|
|
$AccessToken = Get-AccessTokenFromCache -AccessToken $AccessToken -ClientID "1b730954-1685-4b74-9bfd-dac224a7b894" -Resource "https://graph.windows.net"
|
|
|
|
# If the UserPrincipalName is given, get the user's cloudAnchor
|
|
if($UserPrincipalName)
|
|
{
|
|
$user = Get-User -AccessToken $AccessToken -UserPrincipalName $UserPrincipalName
|
|
$CloudAnchor="User_$($user.ObjectId)"
|
|
}
|
|
|
|
if($Password)
|
|
{
|
|
$Hash = (Get-MD4 -bArray ([System.Text.UnicodeEncoding]::Unicode.GetBytes($password))).ToUpper()
|
|
}
|
|
|
|
# Create AAD hash
|
|
$CredentialData = Create-AADHash -Hash $Hash -Iterations $Iterations
|
|
|
|
# Create Windows Legacy Credentials blob
|
|
if($IncludeLegacy)
|
|
{
|
|
# Load the certificate
|
|
if(![string]::IsNullOrEmpty($PfxFileName))
|
|
{
|
|
$certificate = Load-Certificate -FileName $PfxFileName -Password $PfxPassword
|
|
}
|
|
# Get the encryption certificate if not provided
|
|
else
|
|
{
|
|
$DCaaSConfig = Get-WindowsCredentialsSyncConfig -AccessToken $AccessToken
|
|
if(-not $DCaaSConfig.Certificate)
|
|
{
|
|
Write-Warning "Could not get encryption certificate. Is AADDS enabled for the tenant?"
|
|
}
|
|
else
|
|
{
|
|
$certificate = $DCaaSConfig.Certificate
|
|
}
|
|
}
|
|
|
|
Write-Verbose "Encrypting with certificate: $($certificate.Thumbprint) $($certificate.Subject)"
|
|
|
|
# Create the NTHash blob
|
|
$NTHashBlob = @(0x5A,0x00,0x09,0x00, # Ref: https://github.com/mubix/ntds_decode/blob/master/attributes.h#L3048
|
|
# 0x09005A = 589914 = ATT_UNICODE_PWD / unicodePwd
|
|
|
|
0x10,0x00,0x00,0x00 # Size of the hash in bytes? 0x10 = 16 bytes
|
|
)+ (Convert-HexToByteArray -HexString $Hash)
|
|
|
|
# Encrypt the blob
|
|
$ADAuthInfo = Protect-ADAuthInfo -Data $NTHashBlob -Certificate $certificate
|
|
$windowsLegacyCredentials = Convert-ByteArrayToB64 -Bytes $ADAuthInfo
|
|
|
|
}
|
|
|
|
# Create the body block
|
|
$body=@"
|
|
<ProvisionCredentials xmlns="http://schemas.microsoft.com/online/aws/change/2010/01">
|
|
<request xmlns:b="http://schemas.datacontract.org/2004/07/Microsoft.Online.Coexistence.Schema" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
|
|
<b:RequestItems>
|
|
<b:SyncCredentialsChangeItem>
|
|
<b:ChangeDate>$($ChangeDate.ToUniversalTime().ToString("o"))</b:ChangeDate>
|
|
$(if($CloudAnchor){"<b:CloudAnchor>$CloudAnchor</b:CloudAnchor>"}else{"<b:CloudAnchor i:nil=""true""/>"})
|
|
<b:CredentialData>$CredentialData</b:CredentialData>
|
|
<b:ForcePasswordChangeOnLogon>false</b:ForcePasswordChangeOnLogon>
|
|
$(if($SourceAnchor){"<b:SourceAnchor>$SourceAnchor</b:SourceAnchor>"}else{"<b:SourceAnchor i:nil=""true""/>"})
|
|
$(if($windowsLegacyCredentials){"<b:WindowsLegacyCredentials>$windowsLegacyCredentials</b:WindowsLegacyCredentials>"}else{"<b:WindowsLegacyCredentials i:nil=""true""/>"})
|
|
<b:WindowsSupplementalCredentials i:nil="true"/>
|
|
</b:SyncCredentialsChangeItem>
|
|
</b:RequestItems>
|
|
</request>
|
|
</ProvisionCredentials>
|
|
"@
|
|
$Message_id=(New-Guid).ToString()
|
|
$Command="ProvisionCredentials"
|
|
|
|
$serverName=$Script:aadsync_server
|
|
|
|
$envelope = Create-SyncEnvelope -AccessToken $AccessToken -Command $Command -Message_id $Message_id -Body $body -Binary -Server $serverName
|
|
|
|
# Call the API
|
|
$response=Call-ADSyncAPI $envelope -Command "$Command" -Tenant_id (Read-AccessToken($AccessToken)).tid -Message_id $Message_id -Server $serverName
|
|
|
|
# Convert binary response to XML
|
|
$xml_doc=BinaryToXml -xml_bytes $response -Dictionary (Get-XmlDictionary -Type WCF)
|
|
|
|
if(IsRedirectResponse($xml_doc))
|
|
{
|
|
return Set-UserPassword -AccessToken $AccessToken -Recursion ($Recursion+1) -SourceAnchor $SourceAnchor -Password $Password -ChangeDate $ChangeDate -Iterations $Iterations
|
|
}
|
|
else
|
|
{
|
|
# Return
|
|
|
|
return $xml_doc.Envelope.Body.ProvisionCredentialsResponse.ProvisionCredentialsResult.Results.SyncCredentialsChangeResult
|
|
|
|
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
# Creates or reset service account
|
|
function Reset-ServiceAccount
|
|
{
|
|
<#
|
|
.SYNOPSIS
|
|
Create or reset Azure AD Connect sync service account.
|
|
|
|
.DESCRIPTION
|
|
Creates a new user account for Azure AD Connect sync service OR resets existing user's password.
|
|
The created user will have DirecotrySynchronizationAccount role.
|
|
|
|
.Parameter AccessToken
|
|
Access Token
|
|
|
|
.Parameter ServiceAccount
|
|
Name of the service account to be created.
|
|
|
|
.Example
|
|
Reset-AADIntServiceAccount -AccessToken $at -ServiceAccount myserviceaccount
|
|
|
|
Password UserName
|
|
-------- --------
|
|
s@S)uv_?*!IBsu%- myserviceaccount@company.onmicrosoft.com
|
|
#>
|
|
[cmdletbinding()]
|
|
Param(
|
|
[Parameter(Mandatory=$False)]
|
|
[String]$AccessToken,
|
|
[Parameter(Mandatory=$True)]
|
|
[String]$ServiceAccount,
|
|
[Parameter(Mandatory=$False)]
|
|
[int]$Recursion=1
|
|
)
|
|
Process
|
|
{
|
|
# Accept only three loops
|
|
if($Recursion -gt 3)
|
|
{
|
|
throw "Too many recursions"
|
|
}
|
|
|
|
# Get from cache if not provided
|
|
$AccessToken = Get-AccessTokenFromCache -AccessToken $AccessToken -ClientID "1b730954-1685-4b74-9bfd-dac224a7b894" -Resource "https://graph.windows.net"
|
|
|
|
# Create the body block
|
|
$body=@"
|
|
<GetServiceAccount xmlns="http://schemas.microsoft.com/online/aws/change/2010/01">
|
|
<identifier>$ServiceAccount</identifier>
|
|
</GetServiceAccount>
|
|
"@
|
|
$Message_id=(New-Guid).ToString()
|
|
$Command="GetServiceAccount"
|
|
|
|
$serverName=$Script:aadsync_server
|
|
|
|
$envelope = Create-SyncEnvelope -AccessToken $AccessToken -Command $Command -Message_id $Message_id -Body $body -Binary -Server $serverName
|
|
|
|
# Call the API
|
|
$response=Call-ADSyncAPI $envelope -Command "$Command" -Tenant_id (Read-AccessToken($AccessToken)).tid -Message_id $Message_id -Server $serverName
|
|
|
|
# Convert binary response to XML
|
|
$xml_doc=BinaryToXml -xml_bytes $response -Dictionary (Get-XmlDictionary -Type WCF)
|
|
|
|
if(IsRedirectResponse($xml_doc))
|
|
{
|
|
return Get-ServiceAccount -AccessToken $AccessToken -Recursion ($Recursion+1) -ServiceAccount $ServiceAccount
|
|
}
|
|
else
|
|
{
|
|
# Return
|
|
|
|
$retval = $xml_doc.Envelope.Body.GetServiceAccountResponse.GetServiceAccountResult
|
|
if($retval -eq $null)
|
|
{
|
|
return $xml_doc.Envelope.Body.Fault.Reason.Text.'#text'
|
|
}
|
|
else
|
|
{
|
|
# Create and return response object
|
|
$Attributes = @{
|
|
UserName = $retval.UserName
|
|
Password = $retval.Password
|
|
}
|
|
return New-Object -TypeName psobject -Property $Attributes
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
# Enable or disable pass-through authentication
|
|
function Set-PassThroughAuthenticationEnabled
|
|
{
|
|
<#
|
|
.SYNOPSIS
|
|
Enables or disables passthrough authentication (PTA).
|
|
|
|
.DESCRIPTION
|
|
Enables or disables passthrough authentication (PTA) using msapproxy.net api.
|
|
|
|
.Parameter AccessToken
|
|
Access Token.
|
|
|
|
.Parameter Enabled
|
|
Whether to enable or disable PTA.
|
|
|
|
.Example
|
|
PS C:\>$cred=Get-Credential
|
|
PS C:\>$pt=Get-AADIntAccessTokenForPTA -Credentials $cred
|
|
PS C:\>Set-AADIntPassThroughAuthentication -AccessToken $pt -Enable $true
|
|
|
|
IsSuccesful Enable Exists
|
|
----------- ------ ------
|
|
true true true
|
|
#>
|
|
[cmdletbinding()]
|
|
Param(
|
|
[Parameter(Mandatory=$False)]
|
|
[String]$AccessToken,
|
|
[Parameter(Mandatory=$True)]
|
|
[bool]$Enable
|
|
)
|
|
Process
|
|
{
|
|
# Get from cache if not provided
|
|
$AccessToken = Get-AccessTokenFromCache -AccessToken $AccessToken -ClientID "cb1056e2-e479-49de-ae31-7812af012ed8" -Resource "https://proxy.cloudwebappproxy.net/registerapp"
|
|
|
|
# Create the body block
|
|
$body=@"
|
|
<PassthroughAuthenticationEnablementRequest xmlns="http://schemas.datacontract.org/2004/07/Microsoft.ApplicationProxy.Common.RegistrationCommons.Registration" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
|
|
<AuthenticationToken xmlns="http://schemas.datacontract.org/2004/07/Microsoft.ApplicationProxy.Common.Security.AadSecurity">$AccessToken</AuthenticationToken>
|
|
<Enable>$($Enable.ToString().ToLower())</Enable>
|
|
<SkipExoEnablement>false</SkipExoEnablement>
|
|
<UserAgent>AADConnect/1.1.882.0 PassthroughAuthenticationConnector/1.5.405.0</UserAgent>
|
|
</PassthroughAuthenticationEnablementRequest>
|
|
"@
|
|
$tenant_id = Get-TenantId -AccessToken $AccessToken
|
|
|
|
# Call the api
|
|
$response=Invoke-RestMethod -UseBasicParsing -Uri "https://$tenant_id.registration.msappproxy.net/register/EnablePassthroughAuthentication" -Method Post -ContentType "application/xml; charset=utf-8" -Body $body
|
|
|
|
if($response.PassthroughAuthenticationRequestResult.ErrorMessage)
|
|
{
|
|
Write-Error $response.PassthroughAuthenticationRequestResult.ErrorMessage
|
|
}
|
|
|
|
# Create and return the response object
|
|
$attributes=@{
|
|
IsSuccesful = $response.PassthroughAuthenticationRequestResult.IsSuccessful
|
|
Enable = $response.PassthroughAuthenticationRequestResult.Enable
|
|
Exists = $response.PassthroughAuthenticationRequestResult.Exists
|
|
}
|
|
return New-Object -TypeName psobject -Property $Attributes
|
|
}
|
|
}
|
|
|
|
# Aug 21st 2019
|
|
function Get-DesktopSSO
|
|
{
|
|
<#
|
|
.SYNOPSIS
|
|
Returns the status of Seamless SSO status
|
|
|
|
.DESCRIPTION
|
|
Returns the status of Seamless SSO status
|
|
|
|
.Parameter AccessToken
|
|
Access Token.
|
|
|
|
.Example
|
|
PS C:\>$cred=Get-Credential
|
|
PS C:\>$pt=Get-AADIntAccessTokenForPTA -Credentials $cred
|
|
PS C:\>Get-AADIntSeamlessSSO -AccessToken $pt
|
|
|
|
Domains : company.com
|
|
Enable : True
|
|
ErrorMessage :
|
|
Exists : True
|
|
IsSuccessful : True
|
|
#>
|
|
[cmdletbinding()]
|
|
Param(
|
|
[Parameter(Mandatory=$False)]
|
|
[String]$AccessToken
|
|
)
|
|
Process
|
|
{
|
|
# Get from cache if not provided
|
|
$AccessToken = Get-AccessTokenFromCache -AccessToken $AccessToken -ClientID "cb1056e2-e479-49de-ae31-7812af012ed8" -Resource "https://proxy.cloudwebappproxy.net/registerapp"
|
|
|
|
$tenantId = (Read-Accesstoken $AccessToken).tid
|
|
$url="https://$tenantId.registration.msappproxy.net/register/GetDesktopSsoStatus"
|
|
|
|
$body=@"
|
|
<TokenAuthenticationRequest xmlns="http://schemas.datacontract.org/2004/07/Microsoft.ApplicationProxy.Common.Security.AadSecurity" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
|
|
<AuthenticationToken>$AccessToken</AuthenticationToken>
|
|
</TokenAuthenticationRequest>
|
|
"@
|
|
$results=Invoke-RestMethod -UseBasicParsing -Uri $url -Body $body -Method Post -ContentType "application/xml; charset=utf-8"
|
|
|
|
$attributes=@{
|
|
"ErrorMessage" = $results.DesktopSsoStatusResult.ErrorMessage
|
|
"IsSuccessful" = $($results.DesktopSsoStatusResult.IsSuccessful -eq "true")
|
|
"Enabled" = $($results.DesktopSsoStatusResult.Enable -eq "true")
|
|
"Exists" = $($results.DesktopSsoStatusResult.Exists -eq "true")
|
|
"Domains" = $results.DesktopSsoStatusResult.domains.string
|
|
}
|
|
|
|
return New-Object -TypeName PSObject -Property $attributes
|
|
}
|
|
}
|
|
|
|
# Aug 21st 2019
|
|
function Set-DesktopSSO
|
|
{
|
|
<#
|
|
.SYNOPSIS
|
|
Enables or disables Seamless SSO for the given domain
|
|
|
|
.DESCRIPTION
|
|
Enables or disables Seamless SSO for the given domain
|
|
|
|
.Parameter AccessToken
|
|
Access Token.
|
|
|
|
.Example
|
|
PS C:\>$cred=Get-Credential
|
|
PS C:\>$pt=Get-AADIntAccessTokenForPTA -Credentials $cred
|
|
PS C:\>Set-AADIntDesktopSSO -AccessToken $pt -DomainName "company.net" -Password "MySecretPassWord"
|
|
|
|
IsSuccessful ErrorMessage
|
|
------------ ------------
|
|
True
|
|
#>
|
|
[cmdletbinding()]
|
|
Param(
|
|
[Parameter(Mandatory=$False)]
|
|
[String]$AccessToken,
|
|
[Parameter(Mandatory=$False)]
|
|
[String]$ComputerName="AZUREADSSOACC",
|
|
[Parameter(Mandatory=$True)]
|
|
[String]$DomainName,
|
|
[Parameter(Mandatory=$False)]
|
|
[Bool]$Enable=$True,
|
|
[Parameter(Mandatory=$True)]
|
|
[String]$Password
|
|
)
|
|
Process
|
|
{
|
|
# Get from cache if not provided
|
|
$AccessToken = Get-AccessTokenFromCache -AccessToken $AccessToken -ClientID "cb1056e2-e479-49de-ae31-7812af012ed8" -Resource "https://proxy.cloudwebappproxy.net/registerapp"
|
|
|
|
$tenantId = (Read-Accesstoken $AccessToken).tid
|
|
$url="https://$tenantId.registration.msappproxy.net/register/EnableDesktopSso"
|
|
|
|
$body=@"
|
|
<DesktopSsoRequest xmlns="http://schemas.datacontract.org/2004/07/Microsoft.ApplicationProxy.Common.RegistrationCommons.Registration" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
|
|
<AuthenticationToken xmlns="http://schemas.datacontract.org/2004/07/Microsoft.ApplicationProxy.Common.Security.AadSecurity">$AccessToken</AuthenticationToken>
|
|
<ComputerName>$ComputerName</ComputerName>
|
|
<DomainName>$DomainName</DomainName>
|
|
<Enable>$($Enable.ToString().ToLower())</Enable>
|
|
<Secret>$([System.Security.SecurityElement]::Escape($Password))</Secret>
|
|
</DesktopSsoRequest>
|
|
"@
|
|
$results=Invoke-RestMethod -UseBasicParsing -Uri $url -Body $body -Method Post -ContentType "application/xml; charset=utf-8"
|
|
|
|
|
|
$attributes=@{
|
|
"ErrorMessage" = $results.DesktopSsoEnablementResult.ErrorMessage
|
|
"IsSuccessful" = $($results.DesktopSsoEnablementResult.IsSuccessful -eq "true")
|
|
}
|
|
|
|
$setPwd=Read-Host -Prompt "Would you like to set the password of computer account $ComputerName to `"$Password`" also in your ON-PREM ACTIVE DIRECTORY (yes/no)?"
|
|
if($setPwd -eq "yes")
|
|
{
|
|
|
|
try
|
|
{
|
|
$computer = Get-ADComputer $ComputerName
|
|
Set-ADAccountPassword -Identity $computer.DistinguishedName -NewPassword (ConvertTo-SecureString -AsPlainText $Password -Force)
|
|
|
|
# TGT ticket can be alive for a week..
|
|
Write-Warning "Password set for $ComputerName. The Kerberos Key Distribution Center should be restarted for the change to take effect."
|
|
}
|
|
catch
|
|
{
|
|
Write-Error "Could not set password for $ComputerName! Set it manually using Set-ADAccountPassword -Identity $($computer.DistinguishedName) -NewPassword (ConvertTo-SecureString -AsPlainText `"$Password`" -Force)"
|
|
}
|
|
}
|
|
else
|
|
{
|
|
# If not set, users won't be able to login..
|
|
Write-Warning "Password NOT set for $ComputerName! Set it manually to `"$Password`" and restart Kerberos Key Distribution Center for the change to take effect."
|
|
}
|
|
|
|
return New-Object -TypeName PSObject -Property $attributes
|
|
}
|
|
}
|
|
|
|
# Aug 21st 2019
|
|
function Set-DesktopSSOEnabled
|
|
{
|
|
<#
|
|
.SYNOPSIS
|
|
Enables or disables Seamless SSO
|
|
|
|
.DESCRIPTION
|
|
Enables or disables Seamless SSO
|
|
|
|
.Parameter AccessToken
|
|
Access Token.
|
|
|
|
.Example
|
|
PS C:\>Get-AADIntAccessTokenForPTA -SaveToCache
|
|
PS C:\>Set-AADIntDesktopSSOEnabled -Enable $true
|
|
|
|
Domains : company.com
|
|
Enabled : True
|
|
ErrorMessage :
|
|
Exists : True
|
|
IsSuccessful : True
|
|
#>
|
|
[cmdletbinding()]
|
|
Param(
|
|
[Parameter(Mandatory=$False)]
|
|
[String]$AccessToken,
|
|
[Parameter(Mandatory=$False)]
|
|
[Bool]$Enable=$True
|
|
)
|
|
Process
|
|
{
|
|
# Get from cache if not provided
|
|
$AccessToken = Get-AccessTokenFromCache -AccessToken $AccessToken -ClientID "cb1056e2-e479-49de-ae31-7812af012ed8" -Resource "https://proxy.cloudwebappproxy.net/registerapp"
|
|
|
|
$tenantId = (Read-Accesstoken $AccessToken).tid
|
|
$url="https://$tenantId.registration.msappproxy.net/register/EnableDesktopSsoFlag"
|
|
|
|
$body=@"
|
|
<DesktopSsoEnablementRequest xmlns="http://schemas.datacontract.org/2004/07/Microsoft.ApplicationProxy.Common.RegistrationCommons.Registration" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
|
|
<AuthenticationToken xmlns="http://schemas.datacontract.org/2004/07/Microsoft.ApplicationProxy.Common.Security.AadSecurity">$AccessToken</AuthenticationToken>
|
|
<Enable>$($Enable.ToString().ToLower())</Enable>
|
|
</DesktopSsoEnablementRequest >
|
|
"@
|
|
$results=Invoke-RestMethod -UseBasicParsing -Uri $url -Body $body -Method Post -ContentType "application/xml; charset=utf-8"
|
|
|
|
|
|
$attributes=@{
|
|
"ErrorMessage" = $results.DesktopSsoEnablementResult.ErrorMessage
|
|
"IsSuccessful" = $($results.DesktopSsoEnablementResult.IsSuccessful -eq "true")
|
|
}
|
|
|
|
return New-Object -TypeName PSObject -Property $attributes
|
|
}
|
|
}
|
|
|
|
# Gets the kerberos domain sync configuration
|
|
# May 11th 2020
|
|
function Get-KerberosDomainSyncConfig
|
|
{
|
|
<#
|
|
.SYNOPSIS
|
|
Gets tenant's Kerberos domain sync configuration
|
|
|
|
.DESCRIPTION
|
|
Gets tenant's Kerberos domain sync configuration using Azure AD Sync API
|
|
|
|
.Parameter AccessToken
|
|
Access Token
|
|
|
|
.Example
|
|
|
|
Get-AADIntKerberosDomainSyncConfig
|
|
|
|
PublicEncryptionKey SecuredEncryptionAlgorithm SecuredKeyId SecuredPartitionId
|
|
------------------- -------------------------- ------------ ------------------
|
|
RUNLMSAAAABOD8OPj7I3nfeuh7ELE47OtA3yvyryQ0wamf5jPy2uGKibaTRKJd/kFexTpJ8siBxszKCXC2sn1Fd9pEG2y7fu 5 2 15001
|
|
#>
|
|
[cmdletbinding()]
|
|
Param(
|
|
[Parameter(Mandatory=$False)]
|
|
[String]$AccessToken,
|
|
[Parameter(Mandatory=$False)]
|
|
[int]$Recursion=1
|
|
)
|
|
Process
|
|
{
|
|
# Accept only three loops
|
|
if($Recursion -gt 3)
|
|
{
|
|
throw "Too many recursions"
|
|
}
|
|
|
|
# Get from cache if not provided
|
|
$AccessToken = Get-AccessTokenFromCache -AccessToken $AccessToken -ClientID "1b730954-1685-4b74-9bfd-dac224a7b894" -Resource "https://graph.windows.net"
|
|
|
|
# Create the body block
|
|
$body=@"
|
|
<GetKerberosDomainSyncConfig xmlns="http://schemas.microsoft.com/online/aws/change/2010/01">
|
|
</GetKerberosDomainSyncConfig>
|
|
"@
|
|
$Message_id=(New-Guid).ToString()
|
|
$Command="GetKerberosDomainSyncConfig"
|
|
|
|
$serverName=$Script:aadsync_server
|
|
|
|
$envelope = Create-SyncEnvelope -AccessToken $AccessToken -Command $Command -Message_id $Message_id -Body $body -Binary -Server $serverName
|
|
|
|
# Call the API
|
|
$response=Call-ADSyncAPI $envelope -Command "$Command" -Tenant_id (Read-AccessToken($AccessToken)).tid -Message_id $Message_id -Server $serverName
|
|
|
|
# Convert binary response to XML
|
|
$xml_doc=BinaryToXml -xml_bytes $response -Dictionary (Get-XmlDictionary -Type WCF)
|
|
|
|
if(IsRedirectResponse($xml_doc))
|
|
{
|
|
return Get-KerberosDomainSyncConfig -AccessToken $AccessToken -Recursion ($Recursion+1)
|
|
}
|
|
else
|
|
{
|
|
# Create a return object
|
|
$res=$xml_doc.Envelope.Body.GetKerberosDomainSyncConfigResponse.GetKerberosDomainSyncConfigResult
|
|
|
|
$attributes=[ordered]@{
|
|
"PublicEncryptionKey" = $res.PublicEncryptionKey
|
|
"SecuredEncryptionAlgorithm" = $res.SecuredEncryptionAlgorithm
|
|
"SecuredKeyId" = $res.SecuredKeyId
|
|
"SecuredPartitionId" = $res.SecuredPartitionId
|
|
}
|
|
|
|
return New-Object psobject -Property $attributes
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
# Gets the kerberos domain
|
|
# May 11th 2020
|
|
function Get-KerberosDomain
|
|
{
|
|
<#
|
|
.SYNOPSIS
|
|
Gets the kerberos domain information.
|
|
|
|
.DESCRIPTION
|
|
Gets the kerberos domain information.
|
|
|
|
.Parameter AccessToken
|
|
Access Token
|
|
|
|
.Parameter DomaiName
|
|
Domain name
|
|
#>
|
|
[cmdletbinding()]
|
|
Param(
|
|
[Parameter(Mandatory=$False)]
|
|
[String]$AccessToken,
|
|
[Parameter(Mandatory=$True)]
|
|
[String]$DomainName,
|
|
[Parameter(Mandatory=$False)]
|
|
[int]$Recursion=1
|
|
)
|
|
Process
|
|
{
|
|
# Accept only three loops
|
|
if($Recursion -gt 3)
|
|
{
|
|
throw "Too many recursions"
|
|
}
|
|
|
|
# Get from cache if not provided
|
|
$AccessToken = Get-AccessTokenFromCache -AccessToken $AccessToken -ClientID "1b730954-1685-4b74-9bfd-dac224a7b894" -Resource "https://graph.windows.net"
|
|
|
|
# Create the body block
|
|
$body=@"
|
|
<GetKerberosDomain xmlns="http://schemas.microsoft.com/online/aws/change/2010/01" i:nil="true" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
|
|
<dnsDomainName>$DomainName</dnsDomainName>
|
|
</GetKerberosDomain>
|
|
"@
|
|
$Message_id=(New-Guid).ToString()
|
|
$Command="GetKerberosDomain"
|
|
|
|
$serverName=$Script:aadsync_server
|
|
|
|
$envelope = Create-SyncEnvelope -AccessToken $AccessToken -Command $Command -Message_id $Message_id -Body $body -Binary -Server $serverName
|
|
|
|
# Call the API
|
|
$response=Call-ADSyncAPI $envelope -Command "$Command" -Tenant_id (Read-AccessToken($AccessToken)).tid -Message_id $Message_id -Server $serverName
|
|
|
|
# Convert binary response to XML
|
|
$xml_doc=BinaryToXml -xml_bytes $response -Dictionary (Get-XmlDictionary -Type WCF)
|
|
|
|
if(IsRedirectResponse($xml_doc))
|
|
{
|
|
return Get-KerberosDomain -AccessToken $AccessToken -Recursion ($Recursion+1) -DomainName $DomainName
|
|
}
|
|
else
|
|
{
|
|
# Create a return object
|
|
$res=$xml_doc.Envelope.Body.GetKerberosDomainSyncConfigResponse.GetKerberosDomainSyncConfigResult
|
|
|
|
$attributes=[ordered]@{
|
|
"PublicEncryptionKey" = $res.PublicEncryptionKey
|
|
"SecuredEncryptionAlgorithm" = $res.SecuredEncryptionAlgorithm
|
|
"SecuredKeyId" = $res.SecuredKeyId
|
|
"SecuredPartitionId" = $res.SecuredPartitionId
|
|
}
|
|
|
|
return New-Object psobject -Property $attributes
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
# Gets monitoring tenant certificate. No idea what this is..
|
|
# May 11th 2020
|
|
function Get-MonitoringTenantCertificate
|
|
{
|
|
|
|
[cmdletbinding()]
|
|
Param(
|
|
[Parameter(Mandatory=$False)]
|
|
[String]$AccessToken,
|
|
[Parameter(Mandatory=$False)]
|
|
[int]$Recursion=1
|
|
)
|
|
Process
|
|
{
|
|
# Accept only three loops
|
|
if($Recursion -gt 3)
|
|
{
|
|
throw "Too many recursions"
|
|
}
|
|
|
|
# Get from cache if not provided
|
|
$AccessToken = Get-AccessTokenFromCache -AccessToken $AccessToken -ClientID "1b730954-1685-4b74-9bfd-dac224a7b894" -Resource "https://graph.windows.net"
|
|
|
|
# Create the body block
|
|
$body=@"
|
|
<GetMonitoringTenantCertificate xmlns="http://schemas.microsoft.com/online/aws/change/2010/01">
|
|
</GetMonitoringTenantCertificate>
|
|
"@
|
|
$Message_id=(New-Guid).ToString()
|
|
$Command="GetMonitoringTenantCertificate"
|
|
|
|
$serverName=$Script:aadsync_server
|
|
|
|
$envelope = Create-SyncEnvelope -AccessToken $AccessToken -Command $Command -Message_id $Message_id -Body $body -Binary -Server $serverName
|
|
|
|
# Call the API
|
|
$response=Call-ADSyncAPI $envelope -Command "$Command" -Tenant_id (Read-AccessToken($AccessToken)).tid -Message_id $Message_id -Server $serverName
|
|
|
|
# Convert binary response to XML
|
|
$xml_doc=BinaryToXml -xml_bytes $response -Dictionary (Get-XmlDictionary -Type WCF)
|
|
|
|
if(IsRedirectResponse($xml_doc))
|
|
{
|
|
return Get-MonitoringTenantCertificate -AccessToken $AccessToken -Recursion ($Recursion+1)
|
|
}
|
|
else
|
|
{
|
|
# Create a return object
|
|
$res=$xml_doc.Envelope.Body.GetMonitoringTenantCertificateResponse.GetMonitoringTenantCertificateResult
|
|
|
|
|
|
return $res
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
# Gets the windows credentials sync configuration - if the Azure Domain Services is used
|
|
# May 11th 2020
|
|
function Get-WindowsCredentialsSyncConfig
|
|
{
|
|
<#
|
|
.SYNOPSIS
|
|
Gets tenant's Windows credentials synchronization config
|
|
|
|
.DESCRIPTION
|
|
Gets tenant's Windows credentials synchronization config using Azure AD Sync API
|
|
|
|
.Parameter AccessToken
|
|
Access Token
|
|
|
|
.Example
|
|
Get-AADIntWindowsCredentialsSyncConfig
|
|
|
|
EnableWindowsLegacyCredentials EnableWindowsSupplementaCredentials SecretEncryptionCertificate
|
|
------------------------------ ----------------------------------- ---------------------------
|
|
True False MIIDJTCCAg2gAwIBAgIQFwRSInW7I...
|
|
#>
|
|
[cmdletbinding()]
|
|
Param(
|
|
[Parameter(Mandatory=$False)]
|
|
[String]$AccessToken,
|
|
[Parameter(Mandatory=$False)]
|
|
[int]$Recursion=1
|
|
)
|
|
Process
|
|
{
|
|
# Accept only three loops
|
|
if($Recursion -gt 3)
|
|
{
|
|
throw "Too many recursions"
|
|
}
|
|
|
|
# Get from cache if not provided
|
|
$AccessToken = Get-AccessTokenFromCache -AccessToken $AccessToken -ClientID "1b730954-1685-4b74-9bfd-dac224a7b894" -Resource "https://graph.windows.net"
|
|
|
|
# Create the body block
|
|
$body=@"
|
|
<GetWindowsCredentialsSyncConfig xmlns="http://schemas.microsoft.com/online/aws/change/2010/01">
|
|
</GetWindowsCredentialsSyncConfig>
|
|
"@
|
|
$Message_id=(New-Guid).ToString()
|
|
$Command="GetWindowsCredentialsSyncConfig"
|
|
|
|
$serverName=$Script:aadsync_server
|
|
|
|
$envelope = Create-SyncEnvelope -AccessToken $AccessToken -Command $Command -Message_id $Message_id -Body $body -Binary -Server $serverName
|
|
|
|
# Call the API
|
|
$response=Call-ADSyncAPI $envelope -Command "$Command" -Tenant_id (Read-AccessToken($AccessToken)).tid -Message_id $Message_id -Server $serverName
|
|
|
|
# Convert binary response to XML
|
|
$xml_doc=BinaryToXml -xml_bytes $response -Dictionary (Get-XmlDictionary -Type WCF)
|
|
|
|
if(IsRedirectResponse($xml_doc))
|
|
{
|
|
return Get-WindowsCredentialsSyncConfig -AccessToken $AccessToken -Recursion ($Recursion+1)
|
|
}
|
|
else
|
|
{
|
|
# Create a return object
|
|
$res=$xml_doc.Envelope.Body.GetWindowsCredentialsSyncConfigResponse.GetWindowsCredentialsSyncConfigResult
|
|
|
|
$attributes=[ordered]@{
|
|
"EnableWindowsLegacyCredentials" = $res.EnableWindowsLegacyCredentials -eq "true"
|
|
"EnableWindowsSupplementaCredentials" = $res.EnableWindowsSupplementaCredentials -eq "true"
|
|
"SecretEncryptionCertificate" = $res.SecretEncryptionCertificate
|
|
|
|
}
|
|
|
|
try
|
|
{
|
|
# Parse the certificate information
|
|
$binCert = Convert-B64ToByteArray -B64 $attributes["SecretEncryptionCertificate"]
|
|
$certificate = [System.Security.Cryptography.X509Certificates.X509Certificate2]::new([byte[]]$binCert)
|
|
$attributes["Certificate"] = $certificate
|
|
}
|
|
catch
|
|
{}
|
|
|
|
return New-Object psobject -Property $attributes
|
|
}
|
|
}
|
|
}
|
|
|
|
# Gets tenant's sync device configuration
|
|
# May 11th 2020
|
|
function Get-SyncDeviceConfiguration
|
|
{
|
|
<#
|
|
.SYNOPSIS
|
|
Gets tenant's synchronization device configuration
|
|
|
|
.DESCRIPTION
|
|
Gets tenant's synchronization device configuration using Azure AD Sync API
|
|
|
|
.Parameter AccessToken
|
|
Access Token
|
|
|
|
.Example
|
|
Get-AADIntSyncDeviceConfiguration
|
|
|
|
PublicIssuerCertificates CloudPublicIssuerCertificates
|
|
------------------------ -----------------------------
|
|
{$null} {MIIDejCCAmKgAwIBAgIQzsvx7rE77rJM...
|
|
#>
|
|
[cmdletbinding()]
|
|
Param(
|
|
[Parameter(Mandatory=$False)]
|
|
[String]$AccessToken,
|
|
[Parameter(Mandatory=$False)]
|
|
[int]$Recursion=1
|
|
)
|
|
Process
|
|
{
|
|
# Accept only three loops
|
|
if($Recursion -gt 3)
|
|
{
|
|
throw "Too many recursions"
|
|
}
|
|
|
|
# Get from cache if not provided
|
|
$AccessToken = Get-AccessTokenFromCache -AccessToken $AccessToken -ClientID "1b730954-1685-4b74-9bfd-dac224a7b894" -Resource "https://graph.windows.net"
|
|
|
|
# Create the body block
|
|
$body=@"
|
|
<GetDeviceConfiguration xmlns="http://schemas.microsoft.com/online/aws/change/2010/01">
|
|
</GetDeviceConfiguration>
|
|
"@
|
|
$Message_id=(New-Guid).ToString()
|
|
$Command="GetDeviceConfiguration"
|
|
|
|
$serverName=$Script:aadsync_server
|
|
|
|
$envelope = Create-SyncEnvelope -AccessToken $AccessToken -Command $Command -Message_id $Message_id -Body $body -Binary -Server $serverName
|
|
|
|
# Call the API
|
|
$response=Call-ADSyncAPI $envelope -Command "$Command" -Tenant_id (Read-AccessToken($AccessToken)).tid -Message_id $Message_id -Server $serverName
|
|
|
|
# Convert binary response to XML
|
|
$xml_doc=BinaryToXml -xml_bytes $response -Dictionary (Get-XmlDictionary -Type WCF)
|
|
|
|
if(IsRedirectResponse($xml_doc))
|
|
{
|
|
return Get-DeviceConfiguration -AccessToken $AccessToken -Recursion ($Recursion+1)
|
|
}
|
|
else
|
|
{
|
|
# Create a return object
|
|
$res=$xml_doc.Envelope.Body.GetDeviceConfigurationResponse.GetDeviceConfigurationResult
|
|
|
|
$resCloudCerts = $res.CloudPublicIssuerCertificates
|
|
$resCerts = $res.PublicIssuerCertificates
|
|
$cloudCerts = @()
|
|
$certs = @()
|
|
|
|
foreach($cert in $resCloudCerts)
|
|
{
|
|
$cloudCerts += $cert.base64Binary
|
|
}
|
|
|
|
foreach($cert in $resCerts)
|
|
{
|
|
$certs += $cert.base64Binary
|
|
}
|
|
|
|
return New-Object psobject -Property @{"CloudPublicIssuerCertificates" = $cloudCerts; "PublicIssuerCertificates" = $certs}
|
|
|
|
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
# Get sync capabilities
|
|
# May 12th 2020
|
|
function Get-SyncCapabilities
|
|
{
|
|
<#
|
|
.SYNOPSIS
|
|
Gets tenant's synchronization capabilities
|
|
|
|
.DESCRIPTION
|
|
Gets tenant's synchronization capabilities using Azure AD Sync API
|
|
|
|
.Parameter AccessToken
|
|
Access Token
|
|
#>
|
|
[cmdletbinding()]
|
|
Param(
|
|
[Parameter(Mandatory=$False)]
|
|
[String]$AccessToken,
|
|
[Parameter(Mandatory=$False)]
|
|
[int]$Recursion=1
|
|
)
|
|
Process
|
|
{
|
|
# Accept only three loops
|
|
if($Recursion -gt 3)
|
|
{
|
|
throw "Too many recursions"
|
|
}
|
|
|
|
# Get from cache if not provided
|
|
$AccessToken = Get-AccessTokenFromCache -AccessToken $AccessToken -ClientID "1b730954-1685-4b74-9bfd-dac224a7b894" -Resource "https://graph.windows.net"
|
|
|
|
# Create the body block
|
|
$body=@"
|
|
<Capabilities xmlns="http://schemas.microsoft.com/online/aws/change/2010/01" />
|
|
"@
|
|
$Message_id=(New-Guid).ToString()
|
|
$Command="Capabilities"
|
|
|
|
$serverName=$Script:aadsync_server
|
|
|
|
$envelope = Create-SyncEnvelope -AccessToken $AccessToken -Command $Command -Message_id $Message_id -Body $body -Binary -Server $serverName
|
|
|
|
# Call the API
|
|
$response=Call-ADSyncAPI $envelope -Command "$Command" -Tenant_id (Read-AccessToken($AccessToken)).tid -Message_id $Message_id -Server $serverName
|
|
|
|
# Convert binary response to XML
|
|
$xml_doc=BinaryToXml -xml_bytes $response -Dictionary (Get-XmlDictionary -Type WCF)
|
|
|
|
if(IsRedirectResponse($xml_doc))
|
|
{
|
|
return Get-SyncObjects2 -AccessToken $AccessToken -Recursion ($Recursion+1)
|
|
}
|
|
else
|
|
{
|
|
# Create a return object
|
|
$res=[xml]$xml_doc.Envelope.Body.CapabilitiesResponse.CapabilitiesResult
|
|
|
|
$cap=$res.ServiceCapability.MicrosoftOnline.Protocol.Application
|
|
|
|
$blacklist = @()
|
|
|
|
foreach($client in $cap.BlackList)
|
|
{
|
|
$blacklist += $client.ClientVersion
|
|
}
|
|
|
|
return @{
|
|
"ADSyncLatestVersion" = $cap.LatestProductVersion
|
|
"ADSyncMinimumVersion" = $cap.MinimumProductVersion
|
|
"ADSyncBlackList" = $blacklist
|
|
"ADSyncBlackListEnabled" = $cap.BlackList.Enabled
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
# Joins on-prem device to Azure AD
|
|
# Jan 15th 2021
|
|
function Join-OnPremDeviceToAzureAD
|
|
{
|
|
<#
|
|
.SYNOPSIS
|
|
Emulates Azure AD Hybrid Join by adding a device to Azure AD via Synchronization API.
|
|
|
|
.DESCRIPTION
|
|
Emulates Azure AD Hybrid Join by adding a device to Azure AD via Synchronization API and generates a corresponding certificate (if not provided).
|
|
|
|
You may use any name, SID, device ID, or certificate you like.
|
|
|
|
The generated certificate can be used to complete the Hybrid Join using Join-AADIntDeviceToAzureAD. The certificate has no password.
|
|
|
|
After the synchronisation, the device appears as "Hybrid Azure AD joined" device which registration state is "Pending". The subject of the certificate must be "CN=<DeviceId>" or the Hybrid Join fails.
|
|
|
|
.Parameter AccessToken
|
|
The access token used to synchronise the device. Must have "Global Admin" or "Directory Synchronization Accounts" role!
|
|
If not given, will be prompted.
|
|
|
|
.Parameter DeviceName
|
|
The name of the device.
|
|
|
|
.Parameter SID
|
|
The SID of the device. Must be a valid SID, like "S-1-5-21-1436731841-1414151352-1310210645-8640". If not given, a random SID will be used.
|
|
|
|
.Parameter Certificate
|
|
A certificate of the device. If not given, a new self-signed certificate will be created and exported to the current folder.
|
|
|
|
.Parameter DeviceId
|
|
The device id of the device. If not given, a random id will be used.
|
|
|
|
.EXAMPLE
|
|
Get-AADIntAccessTokenForAADGraph -SaveToCache
|
|
|
|
PS C\:>Join-AADIntOnPremDeviceToAzureAD -DeviceName "My computer"
|
|
|
|
Device successfully created:
|
|
Device Name: "My computer"
|
|
Device ID: f24f116f-6e80-425d-8236-09803da7dfbe
|
|
Device SID: S-1-5-21-685966194-1071688910-211446493-3729
|
|
Cloud Anchor: Device_e049c29d-8c8f-4016-b959-98f3fccd668c
|
|
Source Anchor: bxFP8oBuXUKCNgmAPaffvg==
|
|
Cert thumbprint: C59B20BCDE103F8B7911592FD7A8DDDD22696CE0
|
|
Cert file name: "f24f116f-6e80-425d-8236-09803da7dfbe-user.pfx"
|
|
|
|
.EXAMPLE
|
|
Get-AADIntAccessTokenForAADGraph -SaveToCache
|
|
|
|
PS C\:>Join-AADIntOnPremDeviceToAzureAD -DeviceName "My computer"
|
|
|
|
Device successfully created:
|
|
Device Name: "My computer"
|
|
Device ID: f24f116f-6e80-425d-8236-09803da7dfbe
|
|
Device SID: S-1-5-21-685966194-1071688910-211446493-3729
|
|
Cloud Anchor: Device_e049c29d-8c8f-4016-b959-98f3fccd668c
|
|
Source Anchor: bxFP8oBuXUKCNgmAPaffvg==
|
|
Cert thumbprint: C59B20BCDE103F8B7911592FD7A8DDDD22696CE0
|
|
Cert file name: "f24f116f-6e80-425d-8236-09803da7dfbe-user.pfx"
|
|
|
|
PS C\:>Join-AADIntDeviceToAzureAD -TenantId 4362599e-fd46-44a9-997d-53bc7a3b2947 -DeviceName "My computer" -SID "S-1-5-21-685966194-1071688910-211446493-3729" -PfxFileName .\f24f116f-6e80-425d-8236-09803da7dfbe-user.pfx
|
|
|
|
Device successfully registered to Azure AD:
|
|
DisplayName: "My computer"
|
|
DeviceId: f24f116f-6e80-425d-8236-09803da7dfbe
|
|
Cert thumbprint: A531B73CFBAB2BA26694BA2AD31113211CC2174A
|
|
Cert file name : "f24f116f-6e80-425d-8236-09803da7dfbe.pfx"
|
|
#>
|
|
[cmdletbinding()]
|
|
Param(
|
|
[Parameter(Mandatory=$False)]
|
|
[String]$AccessToken,
|
|
[Parameter(Mandatory=$True)]
|
|
[String]$DeviceName,
|
|
[Parameter(Mandatory=$False)]
|
|
[String]$SID,
|
|
[Parameter(Mandatory=$False)]
|
|
[System.Security.Cryptography.X509Certificates.X509Certificate2]$Certificate,
|
|
[Parameter(Mandatory=$False)]
|
|
[GUID]$DeviceId
|
|
)
|
|
Process
|
|
{
|
|
# Get from cache if not provided
|
|
$AccessToken = Get-AccessTokenFromCache -AccessToken $AccessToken -ClientID "1b730954-1685-4b74-9bfd-dac224a7b894" -Resource "https://graph.windows.net"
|
|
|
|
# Create a random machine SID if not provided
|
|
if([string]::IsNullOrEmpty($SID))
|
|
{
|
|
Write-Verbose "No SID given, creating a random"
|
|
$SID = New-RandomSID
|
|
}
|
|
$sidObject = [System.Security.Principal.SecurityIdentifier]$SID
|
|
$bSid = New-Object Byte[] $sidObject.BinaryLength
|
|
$sidObject.GetBinaryForm($bSid,0)
|
|
$b64SID = Convert-ByteArrayToB64 -Bytes $bSid
|
|
|
|
# Create a random device ID if not provided
|
|
if(!$DeviceId)
|
|
{
|
|
Write-Verbose "No DeviceId given, creating a random"
|
|
$DeviceId = New-Guid
|
|
}
|
|
$b64DeviceId = Convert-ByteArrayToB64 -Bytes $DeviceId.ToByteArray()
|
|
|
|
# Create a self-signed "user" certificate if not provided
|
|
if(!$Certificate)
|
|
{
|
|
Write-Verbose "No Certificate given, creating a new self-signed certificate"
|
|
$Certificate = New-Certificate -SubjectName "CN=$($DeviceId.ToString())"
|
|
Set-BinaryContent -Path "$($DeviceId.ToString())-user.pfx" -Value $Certificate.Export([System.Security.Cryptography.X509Certificates.X509ContentType]::Pfx)
|
|
$certExported = $true
|
|
}
|
|
|
|
if($Certificate.Subject -ne "CN=$DeviceId")
|
|
{
|
|
Write-Warning "The certificate subject ""$($Certificate.Subject)"" does NOT match Device ID ""CN=$DeviceId"""
|
|
Write-Warning "You are NOT able to make hybrid join if the certificate doesn't match!"
|
|
}
|
|
|
|
# Get the public key
|
|
$userCert = Convert-ByteArrayToB64 -Bytes ($Certificate.Export([System.Security.Cryptography.X509Certificates.X509ContentType]::Cert))
|
|
|
|
$response = Set-AzureADObject -AccessToken $AccessToken -accountEnabled $true -SourceAnchor $b64DeviceId -deviceId $b64DeviceId -displayName $DeviceName -onPremiseSecurityIdentifier $b64SID -ObjectType Device -deviceOSType Windows -deviceTrustType ServerAd -userCertificate $userCert -Operation Add
|
|
|
|
if($response.ResultCode -eq "Success")
|
|
{
|
|
# Print out information
|
|
Write-Host "Device successfully created:"
|
|
Write-Host " Device Name: ""$DeviceName"""
|
|
Write-Host " Device ID: $($DeviceId.ToString())"
|
|
Write-Host " Device SID: $SID"
|
|
Write-Host " Cloud Anchor: $($response.CloudAnchor)"
|
|
Write-Host " Source Anchor: $($response.SourceAnchor)"
|
|
Write-Host " Cert thumbprint: $($Certificate.Thumbprint)"
|
|
if($certExported)
|
|
{
|
|
Write-host " Cert file name: ""$($DeviceId.ToString())-user.pfx"""
|
|
}
|
|
}
|
|
else
|
|
{
|
|
$response
|
|
}
|
|
}
|
|
}
|
|
|
|
# Add member to Azure AD group using provisioning API
|
|
# Dec 14th 2022
|
|
function Set-AzureADGroupMember
|
|
{
|
|
<#
|
|
.SYNOPSIS
|
|
Adds or removes an Azure AD object from the given group using Azure AD Sync API.
|
|
|
|
.DESCRIPTION
|
|
Adds or removes an Azure AD object from the given group using Azure AD Sync API.
|
|
|
|
.Parameter AccessToken
|
|
Access Token
|
|
|
|
.Parameter SourceAnchor
|
|
The source anchor for the Azure AD object. Typically Base 64 encoded GUID of on-prem AD object.
|
|
|
|
.Parameter CloudAnchor
|
|
The cloud anchor for the Azure AD object in the form "<type>_<objectid>". For example "User_a98368aa-f0cb-41b5-a7c6-10f18c6c837d"
|
|
|
|
.Parameter GroupSourceAnchor
|
|
The source anchor of the target Azure AD group. Typically Base 64 encoded GUID.
|
|
|
|
.Parameter GroupCloudAnchor
|
|
The cloud anchor of the target Azure AD group in the form "Group_<objectid>".
|
|
|
|
.Parameter Operation
|
|
Group modification operation: Add or Remove
|
|
|
|
#>
|
|
[cmdletbinding()]
|
|
Param(
|
|
[Parameter(Mandatory=$False)]
|
|
[String]$AccessToken,
|
|
[Parameter(Mandatory=$False)]
|
|
[String]$CloudAnchor,
|
|
[Parameter(Mandatory=$False)]
|
|
[String]$SourceAnchor,
|
|
[Parameter(Mandatory=$False)]
|
|
[String]$GroupSourceAnchor,
|
|
[Parameter(Mandatory=$False)]
|
|
[String]$GroupCloudAnchor,
|
|
[Parameter(Mandatory=$True)]
|
|
[ValidateSet('Add','Remove')]
|
|
[String]$Operation,
|
|
[Parameter(Mandatory=$False)]
|
|
[int]$Recursion=1
|
|
)
|
|
Process
|
|
{
|
|
# Check that we have values
|
|
if(-not $CloudAnchor -and -not $SourceAnchor)
|
|
{
|
|
Throw "Either CloudAnchor or SourceAnchor is required"
|
|
}
|
|
if($CloudAnchor -and $SourceAnchor)
|
|
{
|
|
Throw "Provide CloudAnchor or SourceAnchor, not both"
|
|
}
|
|
|
|
if(-not $GroupCloudAnchor -and -not $GroupSourceAnchor)
|
|
{
|
|
Throw "Either GroupCloudAnchor or GroupSourceAnchor is required"
|
|
}
|
|
if($GroupCloudAnchor -and $GroupSourceAnchor)
|
|
{
|
|
Throw "Provide GroupCloudAnchor or GroupSourceAnchor, not both"
|
|
}
|
|
|
|
|
|
# Accept only three loops
|
|
if($Recursion -gt 3)
|
|
{
|
|
throw "Too many recursions"
|
|
}
|
|
# Get from cache if not provided
|
|
$AccessToken = Get-AccessTokenFromCache -AccessToken $AccessToken -ClientID "1b730954-1685-4b74-9bfd-dac224a7b894" -Resource "https://graph.windows.net"
|
|
|
|
if($SourceAnchor)
|
|
{
|
|
$anchorType = 2
|
|
$anchorValue = $SourceAnchor
|
|
}
|
|
else
|
|
{
|
|
$anchorType = 1
|
|
$anchorValue = $CloudAnchor
|
|
}
|
|
|
|
if($GroupSourceAnchor)
|
|
{
|
|
$groupAnchorType = "SourceAnchor"
|
|
$groupAnchorValue = $GroupSourceAnchor
|
|
}
|
|
else
|
|
{
|
|
$groupAnchorType = "CloudAnchor"
|
|
$groupAnchorValue = $GroupCloudAnchor
|
|
}
|
|
|
|
# Set the operation value
|
|
if($Operation -eq "Add")
|
|
{
|
|
$operationValue = 1
|
|
}
|
|
else
|
|
{
|
|
$operationValue = 2
|
|
}
|
|
|
|
# Create the body block
|
|
$body=@"
|
|
<ProvisionAzureADSyncObjects2 xmlns="http://schemas.microsoft.com/online/aws/change/2010/01">
|
|
<syncRequest xmlns:b="http://schemas.microsoft.com/online/aws/change/2014/06" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
|
|
<b:SyncObjects>
|
|
<b:AzureADSyncObject>
|
|
<b:PropertyValues xmlns:c="http://schemas.microsoft.com/2003/10/Serialization/Arrays">
|
|
<c:KeyValueOfstringanyType>
|
|
<c:Key>$groupAnchorType</c:Key>
|
|
<c:Value i:type="d:string" xmlns:d="http://www.w3.org/2001/XMLSchema">$groupAnchorValue</c:Value>
|
|
</c:KeyValueOfstringanyType>
|
|
<c:KeyValueOfstringanyType>
|
|
<c:Key>member</c:Key>
|
|
<c:Value i:type="SyncReferenceChangeCollection">
|
|
<referenceChanges>
|
|
<SyncReferenceChange>
|
|
<Reference>
|
|
<Anchor>$anchorValue</Anchor>
|
|
<referenceTypeInt>$anchorType</referenceTypeInt>
|
|
</Reference>
|
|
<operationInt>$operationValue</operationInt>
|
|
</SyncReferenceChange>
|
|
</referenceChanges>
|
|
</c:Value>
|
|
</c:KeyValueOfstringanyType>
|
|
</b:PropertyValues>
|
|
<b:SyncObjectType>Group</b:SyncObjectType>
|
|
<b:SyncOperation>Set</b:SyncOperation>
|
|
</b:AzureADSyncObject>
|
|
</b:SyncObjects>
|
|
</syncRequest>
|
|
</ProvisionAzureADSyncObjects2>
|
|
"@
|
|
|
|
$Message_id=(New-Guid).ToString()
|
|
$Command="ProvisionAzureADSyncObjects2"
|
|
|
|
$serverName=$aadsync_server
|
|
|
|
$envelope = Create-SyncEnvelope -AccessToken $AccessToken -Command $Command -Message_id $Message_id -Body $body -Binary -Server $serverName
|
|
|
|
# Call the API
|
|
$response=Call-ADSyncAPI $envelope -Command "$Command" -Tenant_id (Read-AccessToken($AccessToken)).tid -Message_id $Message_id -Server $serverName
|
|
|
|
# Convert binary response to XML
|
|
$xml_doc=BinaryToXml -xml_bytes $response -Dictionary (Get-XmlDictionary -Type WCF)
|
|
|
|
|
|
if(IsRedirectResponse($xml_doc))
|
|
{
|
|
return Set-AzureADObject -AccessToken $AccessToken -Recursion ($Recursion+1) -sourceAnchor $sourceAnchor -ObjectType $ObjectType -userPrincipalName $userPrincipalName -surname $surname -onPremisesSamAccountName $onPremisesSamAccountName -onPremisesDistinguishedName $onPremisesDistinguishedName -onPremiseSecurityIdentifier $onPremisesDistinguishedName -netBiosName $netBiosName -lastPasswordChangeTimestamp $lastPasswordChangeTimestamp -givenName $givenName -dnsDomainName $dnsDomainName -displayName $displayName -countryCode $countryCode -commonName $commonName -accountEnabled $accountEnabled -cloudMastered $cloudMastered -usageLocation $usageLocation -CloudAnchor $CloudAnchor
|
|
}
|
|
|
|
# Check whether this is an error message
|
|
if($xml_doc.Envelope.Body.Fault)
|
|
{
|
|
Throw $xml_doc.Envelope.Body.Fault.Reason.Text.'#text'
|
|
}
|
|
|
|
# Return
|
|
$xml_doc.Envelope.Body.ProvisionAzureADSyncObjects2Response.ProvisionAzureADSyncObjects2Result.SyncObjectResults.AzureADSyncObjectResult
|
|
}
|
|
} |