Create a gist now

Instantly share code, notes, and snippets.

Azure Active Directory (AAD) Connect tooling.
#requires -Version 2.0
function Invoke-ADSyncDeltaCycle
{
<#
.SYNOPSIS
Trigger an AAD Connect Sync
.DESCRIPTION
Trigger an AAD Connect Sync on a remote computer
.PARAMETER AADComputer
The computer where AAD Connect is installed, needs to be an FQDN
.EXAMPLE
PS C:\> Invoke-ADSyncDeltaCycle -AADComputer 'aad.domain.tld'
Trigger an AAD Connect Sync on aad.domain.tld
.NOTES
Showcase: We do NOT use the old school import of "C:\Program Files\Microsoft Azure AD Sync\Bin\ADSync\ADSync.psd1"
#>
param
(
[Parameter(ValueFromPipeline = $true,
Position = 1)]
[ValidateNotNullOrEmpty()]
[ValidatePattern('(?=^.{4,253}$)(^((?!-)[a-zA-Z0-9-]{0,62}[a-zA-Z0-9]\.)+[a-zA-Z]{2,63}$)')]
[string]$AADComputer = 'default.domain.tld'
)
BEGIN
{
try
{
# Create a remote powerShell session.
$session = (New-PSSession -ComputerName $AADComputer)
Write-Verbose -Message "Session created on $AADComputer"
}
catch
{
# Doh! Did you enable remote Powerhell by executing the following:
# Enable-PSRemoting -Force
#
# Did you check the Firewall on the remote system?
# Remember?
# Enable-NetFirewallRule -DisplayGroup 'Windows Remote Management (Compatibility)' -PolicyStore PersistentStore
Write-Error -Message ('{0} - Line Number: {1}' -f $_.Exception.Message, $_.InvocationInfo.ScriptLineNumber) -ErrorAction Stop
}
}
PROCESS
{
try
{
# If you want to use this within the remote session, you have to tell the REMOTE computer,
# that debug/verbose should be displayed. Not only the local one!
Write-Verbose -Message "Start AD Sync on $AADComputer"
Invoke-Command -Session $session -ScriptBlock {
try
{
# Import the remote(!) Module, not on the local system...
$null = (Import-Module -Name 'ADSync' -Force -ErrorAction Stop -WarningAction SilentlyContinue)
# Execute the command we want on the remote system
$null = (Start-ADSyncSyncCycle -PolicyType Delta -InteractiveMode $false -ErrorAction Stop -WarningAction SilentlyContinue)
}
catch
{
# No Error Handling on the remote system
# Could be a simple throw, as you like
Write-Error -Message $_.Exception.Message -ErrorAction Stop
}
}
Write-Verbose -Message "Done with the AD Sync on $AADComputer"
}
catch
{
# Cleanup, if needed
if ($session)
{
$null = (Remove-PSSession -Id $session.Id)
}
# Here is the REAL Error handling: On the local system.
# You might want to retry something or inform someone... Mail? Eventlog?
Write-Error -Message ('{0} - Line Number: {1}' -f $_.Exception.Message, $_.InvocationInfo.ScriptLineNumber) -ErrorAction Stop
}
}
END
{
if ($session)
{
Write-Verbose -Message 'Cleanup the session'
# Something you should do: Do NOT leave the remote session open and stop executing
# Yep, it will get a timeout, but you shoul cleanup the session as soon as you are sone!
$null = (Remove-PSSession -Id $session.Id)
}
}
}
function Invoke-ADSyncCleanup
{
<#
.SYNOPSIS
Trigger an AAD Connect cleanup
.DESCRIPTION
Trigger an AAD Connect cleanup on a remote computer
.PARAMETER AADComputer
The computer where AAD Connect is installed, needs to be an FQDN
.PARAMETER Days
timespan that we pass to the PurgeRunHistoryInterval parameter. The default is 7
.EXAMPLE
PS C:\> Invoke-ADSyncCleanup -AADComputer 'aad.enatec.local' -Days '30'
Removes the entries of the last 30 days from the logs of the host aad.enatec.local
.NOTES
Helper function to keep the system clean ;-)
#>
param
(
[Parameter(ValueFromPipeline = $true,
Position = 1)]
[ValidateNotNullOrEmpty()]
[ValidatePattern('(?=^.{4,253}$)(^((?!-)[a-zA-Z0-9-]{0,62}[a-zA-Z0-9]\.)+[a-zA-Z]{2,63}$)')]
[string]$AADComputer = 'default.domain.tld',
[Parameter(ValueFromPipeline = $true,
Position = 2)]
[ValidateNotNullOrEmpty()]
$Days = '7'
)
BEGIN
{
try
{
# Open the remote session
$session = (New-PSSession -ComputerName $AADComputer)
Write-Verbose -Message "Session created on $AADComputer"
}
catch
{
# Whoopsie, unable to open the remote session!
Write-Error -Message ('{0} - Line Number: {1}' -f $_.Exception.Message, $_.InvocationInfo.ScriptLineNumber) -ErrorAction Stop
}
}
PROCESS
{
try
{
Write-Verbose -Message "Cleanup the last $Days of logs on $AADComputer"
<#
The difference here:
We use the 'param' and the 'ArgumentList' to handover the value of 'days' to the remote computer.
You might want to use a variable that you declaire on your LOCAL system,
That variable can conaint any local variable you like and will be executed on the
remote system (think: Like a simple string that is already filled)
#>
Invoke-Command -Session $session -ScriptBlock {
param($Days)
try
{
$null = (Import-Module -Name 'ADSync' -Force -ErrorAction Stop -WarningAction SilentlyContinue)
# Execute the command we want (remote)
Start-ADSyncPurgeRunHistory -PurgeRunHistoryInterval $Days
}
catch
{
# No Error Handling on the remote system
# Could be a simple throw, as you like
Write-Error -Message $_.Exception.Message -ErrorAction Stop
}
} -ArgumentList $Days
# The ArgumentList the what the remote site get in the param.
# We use this to handover the Variable from local to the remote system.
# See the comment above the ScriptBlock
}
catch
{
# Cleanup, if needed
if ($session)
{
$null = (Remove-PSSession -Id $session.Id)
}
# Whoopsie!
Write-Error -Message ('{0} - Line Number: {1}' -f $_.Exception.Message, $_.InvocationInfo.ScriptLineNumber) -ErrorAction Stop
}
}
END
{
if ($session)
{
Write-Verbose -Message 'Cleanup the session'
$null = (Remove-PSSession -Id $session.Id)
}
}
}
<#
Azure Active Directory (AAD) Connect tooling.
With Azure Active Directory (AAD) connect you can syncronize an On-Premises Active Directory with the Microsoft Cloud.
You might want to do that if you use Office 365 or any other Azure based Microsoft Cloud Service.
Requirements to use the functions:
- Domain joined PC (if not, you need to tweak a bit. But it IS possible!)
- Remote PowerShell is enabled on the remote system (Execute "Enable-PSRemoting -Force")
- The Firewalls allow the communication.
The functions in this script where created during a PowerShell workshop with the admin of a customer.
The admins asked for a solution and we created this approach from scratch together.
We developed a lot of functions around the AAD Connect and ADFS Tools from Microsoft.
They searched and found sample code and snippets where the "C:\Program Files\Microsoft Azure AD Sync\Bin\ADSync\ADSync.psd1" is parsed.
That still works, but newer ADSync versions do NOT need that anymore and by using a native "Import-Module -Name 'ADSync'" you might be
more flexible in the future and you do not need to know where the PSD1 file is on the remote computer.
You might ask yourself: Why is the code not optimized and have so many comments?
The answer: This is code to teach others how to build simple tools ;-) Teaching without so much comments makes no sense, correct?
And trust me: It was way more complex during the workshop!!! and I mean a lot more...
Nothing fancy, but it should to the job!
TODO: We need to find a better REGEX for the FQDN check! The one we use is not perfect and might need some tweaks!
#>
<#
Copyright (c) 2017, Joerg Hochwald
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors
may be used to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
By using the Software, you agree to the License, Terms and Conditions above!
#>
<#
DISCALIMER:
This is a third party Software!
The developer of this Software is NOT sponsored by or affiliated with
Microsoft Corp (MSFT) or any of it's subsidiaries in any way
The Software is not supported by Microsoft Corp (MSFT)!
#>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment