<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Wiredwolf Canada</title>
	<atom:link href="https://catastrophe.wiredwolf.com/feed/" rel="self" type="application/rss+xml" />
	<link>https://catastrophe.wiredwolf.com/</link>
	<description></description>
	<lastBuildDate>Fri, 18 Oct 2024 23:45:57 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.9.4</generator>
	<item>
		<title>Office 365 &#8211; Migrating Distribution Groups</title>
		<link>https://catastrophe.wiredwolf.com/office-365-migrating-distribution-groups/</link>
		
		<dc:creator><![CDATA[admin]]></dc:creator>
		<pubDate>Sat, 27 Apr 2024 00:54:48 +0000</pubDate>
				<category><![CDATA[Microsoft Exchange]]></category>
		<category><![CDATA[Microsoft Office 365]]></category>
		<category><![CDATA[PowerShell]]></category>
		<guid isPermaLink="false">https://catastrophe.wiredwolf.com/?p=22459</guid>

					<description><![CDATA[<p>Distribution Groups stuck in Active Directory can cause issues after an Exchange migration.   Users can no longer access the management of a Distribution Group in Outlook Synchronized Distribution Groups in Office 365 cannot be modified in Office 365 - as a synchronized object you must update in Active Directory Adding external contacts to a synchronized  [...]</p>
<p>The post <a href="https://catastrophe.wiredwolf.com/office-365-migrating-distribution-groups/">Office 365 &#8211; Migrating Distribution Groups</a> appeared first on <a href="https://catastrophe.wiredwolf.com">Wiredwolf Canada</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>Distribution Groups stuck in Active Directory can cause issues after an Exchange migration.&nbsp;&nbsp;</p>
<ul>
<li>Users can no longer access the management of a Distribution Group in Outlook</li>
<li>Synchronized Distribution Groups in Office 365 cannot be modified in Office 365 &#8211; as a synchronized object you must update in Active Directory</li>
<li>Adding external contacts to a synchronized Distribution Group becomes difficult as you cannot synchronize contacts with Azure AD Connect</li>
</ul>
<p>The solution is relatively simple &#8211; convert all Distribution Groups to Cloud objects.</p>
<p>This script was designed to do exactly that.</p>
<blockquote>
<div>
<div>&lt;#</div>
<div>#########################################################################################</div>
<div>##</div>
<div>## Name: &nbsp; &nbsp; &nbsp; &nbsp;DG_Cloud.PS1</div>
<div>##</div>
<div>## Version: &nbsp; &nbsp; 1.0</div>
<div>##</div>
<div>## Description: $ Installs required components for Exchange Online Powershell Management</div>
<div>## &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;$ Creates a &#8220;Working&#8221; folder for Sea to Sky (C:\STS) for backups.</div>
<div>## &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;$ Creates an &#8220;Exports&#8221; folder for the temp files needed to migrate the</div>
<div>## &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;Distribution Lists.</div>
<div>## &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;$ Backs up the Distribution List Names and Attributes to DG_Details_Backup.csv</div>
<div>## &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;$ Backs up the Distribution List Members to DG_Members_Backup.csv</div>
<div>## &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;$ Capable of running mulitple times and retaining existing backups &#8211; creates</div>
<div>## &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;new backups each time it&#8217;s run if any new groups are detected</div>
<div>## &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;$ Selectively Creates a copy of each Distribution Group called Cloud_$Group</div>
<div>## &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;that are specifically Distribution Groups and not Mail-Enabled Security</div>
<div>## &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;groups.</div>
<div>## &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;$ Deletes the selected Distribtuion Groups from Active Directory</div>
<div>## &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;$ Initiates an Azure AD Connect to remove the AD objects from Cloud Environment</div>
<div>## &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;$ Forces wait period of 5 minutes to allow Azure AD to synchronize with Exchange</div>
<div>## &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;$ Completes process by renaming Cloud_$Group back to original name</div>
<div>##</div>
<div>## Usage: &nbsp; &nbsp; &nbsp; Execute script in PowerShell with elevated privileges</div>
<div>##</div>
<div>## Author: Jason Zondag</div>
<div>##</div>
<div>## Disclaimer: &nbsp;Has not been tried in every conceivable environment &#8211; always check the results</div>
<div>## &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;and fall back on the backups created to recreate the Distribution Groups if</div>
<div>## &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;necessary</div>
<div>##</div>
<div>#########################################################################################</div>
<div>###### ALTERNATIVE CODE FOR MFA LOGIN TO OFFICE 365 &nbsp;####################################</div>
<div>#Connect &amp; Login to ExchangeOnline (MFA)</div>
<div>$getsessions = Get-PSSession | Select-Object -Property State, Name</div>
<div>$isconnected = (@($getsessions) -like &#8216;@{State=Opened; Name=ExchangeOnlineInternalSession*&#8217;).Count -gt 0</div>
<div>If ($isconnected -ne &#8220;True&#8221;) {</div>
<div>Connect-ExchangeOnline</div>
<div>}</div>
<div>#########################################################################################</div>
<div>#&gt;</div>
<div>clear</div>
<div>Write-Host &#8220;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-&#8221; -ForegroundColor Cyan</div>
<div>Write-Host &#8220;!!!!!IMPORTANT!!!!!!&#8221; -ForeGroundColor Red</div>
<div>Write-Host &#8220;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-&#8221; -ForegroundColor Cyan</div>
<div>Write-Host &#8220;!!!!!IMPORTANT!!!!!!&#8221; -ForeGroundColor Red</div>
<div>Write-Host &#8220;YOU MUST RUN THIS SCRIPT FROM THE DOMAIN CONTROLLER THAT IS RUNNING AZURE AD CONNECT&#8221; -ForeGroundColor Red</div>
<div>sleep 5</div>
<div>Write-Host &#8220;IF YOU ARE NOT PLEASE USE CTRL + C TO ESCAPE AND RUN FROM THE APPROPRIATE DOMAIN CONTROLLER&#8221; -ForeGroundColor Red</div>
<div>Write-Host &#8220;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-&#8221; -ForegroundColor Cyan</div>
<div>Write-Host &#8220;It&#8217;s also important to note that this only affects Distribution Lists and not Mail-Enabled&#8221; -ForeGroundColor Green</div>
<div>Write-Host &#8220;Security Groups. &nbsp;Mail-Enabled Security Groups must be handled differently.&#8221; -ForeGroundColor Green</div>
<div>Write-Host &#8220;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-&#8221; -ForegroundColor Cyan</div>
<div>sleep 15</div>
<div>Pause</div>
<div>Write-Host &#8220;Connecting to Exchange Online &#8211; installing all required PowerShell Modules and initiaing a connection&#8221; -ForegroundColor Green</div>
<div># &#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;</div>
<div># Load PowerShell Modules</div>
<div># &#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;</div>
<div>Set-ExecutionPolicy RemoteSigned -Force</div>
<div>Import-Module ActiveDirectory</div>
<div>[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12</div>
<div>Install-Module -Name ExchangeOnlineManagement -Force</div>
<div>Import-Module ExchangeOnlineManagement</div>
<div>#Connect &amp; Login to ExchangeOnline (MFA)</div>
<div>$getsession = get-pssession | select-object -Property State | select -expandproperty state</div>
<div>If ($getsession -ne &#8220;Opened&#8221;) {</div>
<div>Connect-ExchangeOnline</div>
<div>}</div>
<div>Write-Host &#8220;Completed&#8221; -ForegroundColor Green</div>
<div>Write-Host &#8220;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-&#8221; -ForegroundColor Cyan</div>
<div>Write-Host</div>
<div>Write-host</div>
<div>Write-Host &#8220;______________________________________________________________________________________________&#8221; -ForegroundColor Cyan</div>
<div>Write-Host &#8220;Synchronized Distribution Groups with no ManagedBy settings will be defaulted to Organization&#8221; -ForeGroundColor Yellow</div>
<div>Write-Host &#8220;Management. This value cannot be translated.&#8221; -ForeGroundColor Yellow</div>
<div>Write-host</div>
<div>Write-Host &#8220;You must set a default account value to replace Organization Management.&#8221; -ForeGroundColor Green</div>
<div>Write-Host &#8220;The default account must be a valid licensed address for this tenant. &nbsp;IE. seatosky@domain.com &#8221; -ForeGroundColor Green</div>
<div>$ManagedByDefault = Read-host &#8220;Enter the email address of a valid licensed account for this tenant:&#8221;</div>
<div>Write-Host &#8220;______________________________________________________________________________________________&#8221; -ForegroundColor Cyan</div>
<div># Disable Azure AD Connect from initiating a sync while this process is underway</div>
<div>Set-ADSyncScheduler -SyncCycleEnabled $false</div>
<div>Write-host &#8220;Azure AD Connect Schedule Sync has been disabled temporarily.&#8221;</div>
<div># &#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;</div>
<div># Create Working and Export folders</div>
<div># &#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;</div>
<div>Write-Host &#8220;Creating a Working Directory C:\DG-Migrate and an Exports Directory within the Working Directory&#8221; -ForegroundColor Green</div>
<div># Create a working directory</div>
<div>$orginfo = Get-OrganizationConfig | select -expandproperty Name</div>
<div>$WorkingDirectory = &#8220;C:\DG-Migrate\&#8221; + $orginfo + &#8220;\&#8221;</div>
<div>$ExportDirectory = $WorkingDirectory + &#8220;ExportedAddresses\&#8221;</div>
<div>If(!(Test-Path -Path $WorkingDirectory )){</div>
<div># if WorkingDirectory doesn&#8217;t exist neither does ExportDirectory &#8211; create them both</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Write-Host &#8221; &nbsp;Creating Directory: $WorkingDirectory&#8221;</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; New-Item -ItemType directory -Path $WorkingDirectory | Out-Null</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Write-Host &#8221; &nbsp;Creating Directory: $ExportDirectory&#8221;</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; New-Item -ItemType directory -Path $ExportDirectory | Out-Null</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; } else {</div>
<div># WorkingDirectory may exist but that doesn&#8217;t mean ExportDirectory does &#8211; create if it doesn&#8217;t exist</div>
<div>If(!(Test-Path -Path $ExportDirectory )){</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Write-Host &#8221; &nbsp;Creating Directory: $ExportDirectory&#8221;</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; New-Item -ItemType directory -Path $ExportDirectory | Out-Null</div>
<div>}</div>
<div>}</div>
<div>Write-Host &#8220;Completed&#8221; -ForegroundColor Green</div>
<div>Write-Host &#8220;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-&#8221; -ForegroundColor Cyan</div>
<div>Write-Host &#8220;Creating a backup of all AD Synchronized Distribution Lists and placing into the Working Directory&#8221; -ForegroundColor Green</div>
<div># &#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;</div>
<div># Export all the Distribution Group Information to a separate file</div>
<div># &#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;</div>
<div>$check = (get-distributiongroup | Where {($_.IsDirSynced -eq $true) -AND ($_.RecipientType -eq &#8220;MailUniversalDistributionGroup&#8221;)})</div>
<div>if ((($check | Measure-Object).count) -ne 0) {</div>
<div># Not 0 so we found some Distribution Groups to migrate</div>
<div># We don&#8217;t want to overwrite an existing backup set &#8211; rename any existing files with a time stamp</div>
<div>&nbsp; &nbsp; if (Test-Path ($WorkingDirectory + &#8220;DG_Details_Backup.csv&#8221;)) {</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; $filename = ($WorkingDirectory + &#8220;DG_Details_Backup.csv&#8221;)</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; $fileObj = get-item $filename</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; $DateStamp = get-date -uformat &#8220;%Y-%m-%d@%H-%M-%S&#8221;</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; $extOnly = $fileObj.extension</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; if ($extOnly.length -eq 0) {</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; $nameOnly = $fileObj.Name</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; rename-item &#8220;$fileObj&#8221; &#8220;$nameOnly-$DateStamp&#8221;</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; else {</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; $nameOnly = $fileObj.Name.Replace( $fileObj.Extension,&#8221;)</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; rename-item &#8220;$fileName&#8221; &#8220;$nameOnly-$DateStamp$extOnly&#8221;</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; } &nbsp; &nbsp; &nbsp; }</div>
<div>$check | select `</div>
<div>&nbsp; &nbsp; GroupType, `</div>
<div>&nbsp; &nbsp; SamAccountName, `</div>
<div>&nbsp; &nbsp; IsDirSynced, `</div>
<div>&nbsp; &nbsp; @{label=&#8221;ManagedBy&#8221;;expression={</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; ($_.managedby `</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; | % { get-mailbox -identity $_ | select-object -ExpandProperty PrimarySMTPAddress } `</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; | Where-Object {$_ -like &#8220;*@*&#8221;}) -join &#8216;;&#8217;}</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; }, `</div>
<div>&nbsp; &nbsp; MemberJoinRestriction, `</div>
<div>&nbsp; &nbsp; MemberDepartRestriction, `</div>
<div>&nbsp; &nbsp; ReportToOriginatorEnabled, `</div>
<div>&nbsp; &nbsp; Description, `</div>
<div>&nbsp; &nbsp; AddressListMembership, `</div>
<div>&nbsp; &nbsp; Alias, `</div>
<div>&nbsp; &nbsp; DisplayName, `</div>
<div>&nbsp; &nbsp; PrimarySMTPAddress, `</div>
<div>&nbsp; &nbsp; @{label=&#8221;EmailAddressess&#8221;;expression={</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; ($_.EmailAddresses | Where-Object {$_ -like &#8220;*smtp:*&#8221; }) -join &#8216;;&#8217;}</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; },`</div>
<div>&nbsp; &nbsp; ExternalDirectoryObjectId, `</div>
<div>&nbsp; &nbsp; HiddenFromAddressListsEnabled, `</div>
<div>&nbsp; &nbsp; LegacyExchangeDN, `</div>
<div>&nbsp; &nbsp; MaxSendSize, `</div>
<div>&nbsp; &nbsp; MaxReceiveSize, `</div>
<div>&nbsp; &nbsp; ModeratedBy, `</div>
<div>&nbsp; &nbsp; ModerationEnabled, `</div>
<div>&nbsp; &nbsp; PoliciesIncluded, `</div>
<div>&nbsp; &nbsp; PoliciesExcluded, `</div>
<div>&nbsp; &nbsp; EmailAddressPolicyEnabled, `</div>
<div>&nbsp; &nbsp; RecipientType, `</div>
<div>&nbsp; &nbsp; RecipientTypeDetials, `</div>
<div>&nbsp; &nbsp; RequireSenderAuthenticationEnabled, `</div>
<div>&nbsp; &nbsp; WindowsEmailAddress, `</div>
<div>&nbsp; &nbsp; Identity, `</div>
<div>&nbsp; &nbsp; Id, `</div>
<div>&nbsp; &nbsp; Name, `</div>
<div>&nbsp; &nbsp; DistinguishedName, `</div>
<div>&nbsp; &nbsp; ExchangeObjectId, `</div>
<div>&nbsp; &nbsp; Guid `</div>
<div>| Export-CSV ($WorkingDirectory + &#8220;DG_Details_Backup.csv&#8221;) -NoTypeInformation</div>
<div>sleep 20</div>
<div>&nbsp; &nbsp; }</div>
<div>else {</div>
<div>&nbsp; &nbsp; Write-Host &#8220;There are no appropriate Distribution Lists to migrate. &nbsp;Cancelling migration.&#8221;</div>
<div>&nbsp; &nbsp; Break</div>
<div>}</div>
<div>Write-Host &#8220;Completed&#8221; -ForegroundColor Green</div>
<div>Write-Host &#8220;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-&#8221; -ForegroundColor Cyan</div>
<div>Write-Host &#8220;Creating a backup of Distribution List Membership and placing in the Working Directory&#8221; -ForegroundColor Green</div>
<div># &#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;</div>
<div># Export all the Distribution Group Members to a separate file</div>
<div># &#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;</div>
<div>$output = @()</div>
<div>$Identities = import-csv ($WorkingDirectory + &#8220;DG_Details_Backup.csv&#8221;) | select Name,PrimarySmtpAddress,Managedby,GroupType,RecipientType</div>
<div>If ($Identities) {</div>
<div>Foreach($group in $Identities) {</div>
<div>&nbsp; &nbsp; $Members = Get-DistributionGroupMember $group.PrimarySmtpAddress -resultsize unlimited</div>
<div>&nbsp; &nbsp; if (@($Members.count) -eq 0) {</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; #$managers = ($group | Select @{Name=&#8217;DistributionGroupManagers&#8217;;Expression={[string]::join(&#8220;;&#8221;, ($_.Managedby))}})</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; $userObj = New-Object PSObject</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; $userObj | Add-Member NoteProperty -Name &#8220;DisplayName&#8221; -Value EmptyGroup</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; $userObj | Add-Member NoteProperty -Name &#8220;Alias&#8221; -Value EmptyGroup</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; $userObj | Add-Member NoteProperty -Name &#8220;RecipientType&#8221; -Value EmptyGroup</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; $userObj | Add-Member NoteProperty -Name &#8220;Recipient OU&#8221; -Value EmptyGroup</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; $userObj | Add-Member NoteProperty -Name &#8220;Primary SMTP address&#8221; -Value EmptyGroup</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; $userObj | Add-Member NoteProperty -Name &#8220;Distribution Group&#8221; -Value $group.Name</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; $userObj | Add-Member NoteProperty -Name &#8220;Distribution Group Primary SMTP address&#8221; -Value $group.PrimarySmtpAddress</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; $userObj | Add-Member NoteProperty -Name &#8220;Distribution Group Managers&#8221; -Value $managers.DistributionGroupManagers</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; $userObj | Add-Member NoteProperty -Name &#8220;Distribution Group Type&#8221; -Value $group.GroupType</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; $userObj | Add-Member NoteProperty -Name &#8220;Distribution Group Recipient Type&#8221; -Value $group.RecipientType</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; $output+=$UserObj</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; }</div>
<div>&nbsp; &nbsp; else {</div>
<div>&nbsp; &nbsp; Foreach($Member in $members) {</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; #$managers = $group | Select @{Name=&#8217;DistributionGroupManagers&#8217;;Expression={[string]::join(&#8220;;&#8221;, ($_.Managedby))}}</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; $userObj = New-Object PSObject</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; $userObj | Add-Member NoteProperty -Name &#8220;DisplayName&#8221; -Value $Member.Name</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; $userObj | Add-Member NoteProperty -Name &#8220;Alias&#8221; -Value $Member.Alias</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; $userObj | Add-Member NoteProperty -Name &#8220;RecipientType&#8221; -Value $Member.RecipientType</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; $userObj | Add-Member NoteProperty -Name &#8220;Recipient OU&#8221; -Value $Member.OrganizationalUnit</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; $userObj | Add-Member NoteProperty -Name &#8220;Primary SMTP address&#8221; -Value $Member.PrimarySmtpAddress</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; $userObj | Add-Member NoteProperty -Name &#8220;Distribution Group&#8221; -Value $group.Name</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; $userObj | Add-Member NoteProperty -Name &#8220;Distribution Group Primary SMTP address&#8221; -Value $group.PrimarySmtpAddress</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; $userObj | Add-Member NoteProperty -Name &#8220;Distribution Group Managers&#8221; -Value $managers.DistributionGroupManagers</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; $userObj | Add-Member NoteProperty -Name &#8220;Distribution Group Type&#8221; -Value $group.GroupType</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; $userObj | Add-Member NoteProperty -Name &#8220;Distribution Group Recipient Type&#8221; -Value $group.RecipientType</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; $output+=$UserObj</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; }</div>
<div>&nbsp; &nbsp; }</div>
<div>&nbsp; &nbsp;}</div>
<div>&nbsp; &nbsp;# We don&#8217;t want to overwrite an existing backup set &#8211; rename any existing files with a time stamp</div>
<div>&nbsp; &nbsp; if (Test-Path ($WorkingDirectory + &#8220;DG_Members_Backup.csv&#8221;)) {</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; $filename = ($WorkingDirectory + &#8220;DG_Members_Backup.csv&#8221;)</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; $fileObj = get-item $filename</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; $DateStamp = get-date -uformat &#8220;%Y-%m-%d@%H-%M-%S&#8221;</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; $extOnly = $fileObj.extension</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; if ($extOnly.length -eq 0) {</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; $nameOnly = $fileObj.Name</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; rename-item &#8220;$fileObj&#8221; &#8220;$nameOnly-$DateStamp&#8221;</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; else {</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; $nameOnly = $fileObj.Name.Replace( $fileObj.Extension,&#8221;)</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; rename-item &#8220;$fileName&#8221; &#8220;$nameOnly-$DateStamp$extOnly&#8221;</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; } &nbsp; &nbsp; &nbsp; }</div>
<div>&nbsp; &nbsp; $output | Export-CSV ($WorkingDirectory + &#8220;DG_Members_Backup.csv&#8221;) -NoTypeInformation</div>
<div>&nbsp; &nbsp;}</div>
<div># &#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-</div>
<div>sleep 15</div>
<div>Write-Host &#8220;Completed&#8221; -ForegroundColor Green</div>
<div>Write-Host &#8220;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-&#8221; -ForegroundColor Cyan</div>
<div># &#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;</div>
<div># Create the Cloud copies of the Distribution Lists</div>
<div># &#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;</div>
<div>Write-Host &#8220;Creating Cloud copies of each AD Synced Distribution List&#8221; -ForegroundColor Green</div>
<div>$Identities = import-csv ($WorkingDirectory + &#8220;DG_Details_Backup.csv&#8221;) | select -expandproperty PrimarySmtpAddress</div>
<div># Create the cloud versions</div>
<div>If ($Identities) {</div>
<div>&nbsp; &nbsp; foreach ($group in $identities) {</div>
<div>&nbsp; &nbsp; If (((Get-DistributionGroup $group -Resultsize Unlimited -ErrorAction &#8216;SilentlyContinue&#8217;).IsValid) -eq $true) {</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; $OldDG = Get-DistributionGroup $group</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; [System.IO.Path]::GetInvalidFileNameChars() | ForEach {$Group = $Group.Replace($_,&#8217;_&#8217;)}</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; $OldName = [string]$OldDG.Name</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; $OldDisplayName = [string]$OldDG.DisplayName</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; $OldPrimarySmtpAddress = [string]$OldDG.PrimarySmtpAddress</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; $OldAlias = [string]$OldDG.Alias</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if ((![string]$OldDG.managedby) -or ([string]$OldDG.managedby -eq &#8220;Organization Management&#8221;)) {</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; [string]$OldDG.managedby=$ManagedByDefault</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; $OldMembers = (Get-DistributionGroupMember $OldDG.PrimarySmtpAddress).primarysmtpaddress &#8220;EmailAddress&#8221; &gt; &#8220;$ExportDirectory\$OldName.csv&#8221;</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; $OldDG.EmailAddresses &gt;&gt; &#8220;$ExportDirectory\$OldName.csv&#8221;</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; &#8220;x500:&#8221;+$OldDG.LegacyExchangeDN &gt;&gt; &#8220;$ExportDirectory\$OldName.csv&#8221;</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; Write-Host &#8221; &nbsp;Creating Group: Cloud-$OldDisplayName&#8221; -ForegroundColor Green</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; New-DistributionGroup `</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; -Name &#8220;Cloud-$OldName&#8221; `</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; -Alias &#8220;Cloud-$OldAlias&#8221; `</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; -DisplayName &#8220;Cloud-$OldDisplayName&#8221; `</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; -ManagedBy $OldDG.ManagedBy `</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; -Members $OldMembers `</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; -PrimarySmtpAddress &#8220;Cloud-$OldPrimarySmtpAddress&#8221; | Out-Null</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; Sleep -Seconds 3</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; Write-Host &#8221; &nbsp;Setting Values For: Cloud-$OldDisplayName&#8221; -ForegroundColor Green</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; Set-DistributionGroup `</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; -Identity &#8220;Cloud-$OldPrimarySmtpAddress&#8221; `</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; -AcceptMessagesOnlyFromSendersOrMembers $OldDG.AcceptMessagesOnlyFromSendersOrMembers `</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; -RejectMessagesFromSendersOrMembers $OldDG.RejectMessagesFromSendersOrMembers `</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; Set-DistributionGroup `</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; -Identity &#8220;Cloud-$OldPrimarySmtpAddress&#8221; `</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; -AcceptMessagesOnlyFrom $OldDG.AcceptMessagesOnlyFrom `</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; -AcceptMessagesOnlyFromDLMembers $OldDG.AcceptMessagesOnlyFromDLMembers `</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; -BypassModerationFromSendersOrMembers $OldDG.BypassModerationFromSendersOrMembers `</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; -BypassNestedModerationEnabled $OldDG.BypassNestedModerationEnabled `</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; -CustomAttribute1 $OldDG.CustomAttribute1 `</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; -CustomAttribute2 $OldDG.CustomAttribute2 `</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; -CustomAttribute3 $OldDG.CustomAttribute3 `</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; -CustomAttribute4 $OldDG.CustomAttribute4 `</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; -CustomAttribute5 $OldDG.CustomAttribute5 `</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; -CustomAttribute6 $OldDG.CustomAttribute6 `</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; -CustomAttribute7 $OldDG.CustomAttribute7 `</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; -CustomAttribute8 $OldDG.CustomAttribute8 `</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; -CustomAttribute9 $OldDG.CustomAttribute9 `</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; -CustomAttribute10 $OldDG.CustomAttribute10 `</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; -CustomAttribute11 $OldDG.CustomAttribute11 `</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; -CustomAttribute12 $OldDG.CustomAttribute12 `</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; -CustomAttribute13 $OldDG.CustomAttribute13 `</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; -CustomAttribute14 $OldDG.CustomAttribute14 `</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; -CustomAttribute15 $OldDG.CustomAttribute15 `</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; -ExtensionCustomAttribute1 $OldDG.ExtensionCustomAttribute1 `</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; -ExtensionCustomAttribute2 $OldDG.ExtensionCustomAttribute2 `</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; -ExtensionCustomAttribute3 $OldDG.ExtensionCustomAttribute3 `</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; -ExtensionCustomAttribute4 $OldDG.ExtensionCustomAttribute4 `</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; -ExtensionCustomAttribute5 $OldDG.ExtensionCustomAttribute5 `</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; -GrantSendOnBehalfTo $OldDG.GrantSendOnBehalfTo `</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; -HiddenFromAddressListsEnabled $True `</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; -MailTip $OldDG.MailTip `</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; -MailTipTranslations $OldDG.MailTipTranslations `</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; -MemberDepartRestriction $OldDG.MemberDepartRestriction `</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; -MemberJoinRestriction $OldDG.MemberJoinRestriction `</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; -ModeratedBy $OldDG.ModeratedBy `</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; -ModerationEnabled $OldDG.ModerationEnabled `</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; -RejectMessagesFrom $OldDG.RejectMessagesFrom `</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; -RejectMessagesFromDLMembers $OldDG.RejectMessagesFromDLMembers `</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; -ReportToManagerEnabled $OldDG.ReportToManagerEnabled `</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; -ReportToOriginatorEnabled $OldDG.ReportToOriginatorEnabled `</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; -RequireSenderAuthenticationEnabled $OldDG.RequireSenderAuthenticationEnabled `</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; -SendModerationNotifications $OldDG.SendModerationNotifications `</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; -SendOofMessageToOriginatorEnabled $OldDG.SendOofMessageToOriginatorEnabled `</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; -BypassSecurityGroupManagerCheck</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; sleep 3</div>
<div>&nbsp; &nbsp; } &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;</div>
<div>&nbsp; &nbsp; Else {</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; Write-Host &#8221; &nbsp;ERROR: The distribution group &#8216;$Group&#8217; was not found&#8221; -ForegroundColor Red</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; Write-Host</div>
<div>&nbsp; &nbsp; }</div>
<div>}</div>
<div>}</div>
<div># &#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;</div>
<div># Delete all the Distribution Groups in Active Directory</div>
<div># &#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;</div>
<div>Write-Host &#8220;All Distribution Lists have been replicated in the Cloud with Cloud_ as a prefix&#8221; -ForegroundColor Green</div>
<div>Write-Host &#8220;Completed&#8221; -ForegroundColor Green</div>
<div>Write-Host &#8220;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-&#8221; -ForegroundColor Cyan</div>
<div>Write-host &#8220;If you encountered any errors during the creation of the Cloud-Group process you may hit CTRL + C now to kill the process.&#8221; -ForegroundColor Red -BackgroundColor Black</div>
<div>Write-host &#8220;If you kill the process now to fix any issues you should remove the Cloud-Group objects from Azure AD and start fresh.&#8221; -ForegroundColor Red -BackgroundColor Black</div>
<div>Write-host &#8220;WARNING &#8211; The Azure AZ Connect Sync Schedule is currently Suspended. You must complete the script or manually restart the Schedule.&#8221; -ForegroundColor Black -BackgroundColor Red</div>
<div>Write-Host &#8220;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-&#8221; -ForegroundColor Cyan</div>
<div>Write-host &#8220;Press Enter to delete the migrated Distribution Lists from Active Directory&#8221; -ForegroundColor Cyan</div>
<div>pause</div>
<div>If (test-path ($WorkingDirectory + &#8220;DG_Details_Backup.csv&#8221;)) {</div>
<div>&nbsp; &nbsp; $Identities = import-csv ($WorkingDirectory + &#8220;DG_Details_Backup.csv&#8221;) | select -expandproperty Identity</div>
<div>&nbsp; &nbsp; foreach ($group in $identities) {</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; Remove-ADGroup -identity &#8220;$group&#8221; -confirm:$false</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; sleep 2</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; }</div>
<div>}</div>
<div>Write-Host &#8220;All Distribution Lists have been removed from Active Directory&#8221; -ForegroundColor Green</div>
<div>Write-Host &#8220;Completed&#8221; -ForegroundColor Green</div>
<div>Write-Host &#8220;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-&#8221; -ForegroundColor Cyan</div>
<div>sleep 15</div>
<div>Pause</div>
<div># &#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;</div>
<div># Initiate a Delta Sync with Azure AD Connect and set a timer of 5 minutes</div>
<div># &#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;</div>
<div>Write-Host &#8220;Synchronizing Changes with Azure AD Connect. &nbsp;Please allow 5 minutes for process to complete. &nbsp;You will be prompted when to continue.&#8221; -ForegroundColor Green</div>
<div>Start-AdSyncSyncCycle -PolicyType Delta</div>
<div>Write-Host &#8220;PLEASE BE PATIENT &#8211; Confirm the Distribution Lists have been removed from Office 365 Azure AD before continuing&#8221; -ForegroundColor Green</div>
<div>sleep 300</div>
<div>Write-Host &#8220;Completed&#8221; -ForegroundColor Green</div>
<div>Write-Host &#8220;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-&#8221; -ForegroundColor Cyan</div>
<div>Pause</div>
<div># &#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;</div>
<div># Complete the process by renaming the Cloud copies to the original names</div>
<div># &#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;</div>
<div>Write-Host &#8220;Updating the placeholder Distribution Lists to replace the original AD synchronized Distribution Lists&#8221; -ForegroundColor Green</div>
<div>If (test-path $ExportDirectory) {</div>
<div>&nbsp; &nbsp; $Identities = import-csv ($WorkingDirectory + &#8220;DG_Details_Backup.csv&#8221;) | select -expandproperty Identity</div>
<div>&nbsp; &nbsp; foreach ($group in $identities) {</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; $TempDG = Get-DistributionGroup &#8220;Cloud-$Group&#8221;</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; $TempPrimarySmtpAddress = $TempDG.PrimarySmtpAddress</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; [System.IO.Path]::GetInvalidFileNameChars() | ForEach {$Group = $Group.Replace($_,&#8217;_&#8217;)}</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; $OldAddresses = @(Import-Csv &#8220;$ExportDirectory\$Group.csv&#8221;)</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; $NewAddresses = $OldAddresses | ForEach {$_.EmailAddress.Replace(&#8220;X500&#8243;,&#8221;x500&#8221;)}</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; $NewDGName = $TempDG.Name.Replace(&#8220;Cloud-&#8220;,&#8221;&#8221;)</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; $NewDGDisplayName = $TempDG.DisplayName.Replace(&#8220;Cloud-&#8220;,&#8221;&#8221;)</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; $NewDGAlias = $TempDG.Alias.Replace(&#8220;Cloud-&#8220;,&#8221;&#8221;)</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; $NewPrimarySmtpAddress = ($NewAddresses | Where {$_ -clike &#8220;SMTP:*&#8221;}).Replace(&#8220;SMTP:&#8221;,&#8221;&#8221;)</div>
<div>&nbsp; &nbsp; Write-Host &#8220;Converting Cloud-$Group to $Group&#8221;</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; Set-DistributionGroup `</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; -Identity $TempDG.Name `</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; -Name $NewDGName `</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; -Alias $NewDGAlias `</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; -DisplayName $NewDGDisplayName `</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; -PrimarySmtpAddress $NewPrimarySmtpAddress `</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; -HiddenFromAddressListsEnabled $False `</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; -BypassSecurityGroupManagerCheck</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; Set-DistributionGroup `</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; -Identity $NewDGName `</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; -EmailAddresses @{Add=$NewAddresses} `</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; -BypassSecurityGroupManagerCheck</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; Set-DistributionGroup `</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; -Identity $NewDGName `</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; -EmailAddresses @{Remove=$TempPrimarySmtpAddress} `</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; -BypassSecurityGroupManagerCheck</div>
<div>&nbsp; &nbsp; sleep 3</div>
<div>&nbsp; &nbsp; }</div>
<div>&nbsp; &nbsp; }</div>
<div>Write-Host &#8220;Completed&#8221; -ForegroundColor Green</div>
<div>Write-Host &#8220;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-&#8221; -ForegroundColor Cyan</div>
<div># Re-Enable AD Sync Schedule</div>
<div>Set-ADSyncScheduler -SyncCycleEnabled $true</div>
<div>Write-Host &#8220;The conversion process happens in Exchange and can take a while to reflect in Azure AD&#8221;</div>
<div>Write-Host &#8220;Check to make sure that Azure AD is updated and now showing all of the Distribution Lists are converted to Cloud objects&#8221;</div>
<div>Pause</div>
</div>
</blockquote>
<div>
<div>&nbsp;</div>
</div>
<p>The post <a href="https://catastrophe.wiredwolf.com/office-365-migrating-distribution-groups/">Office 365 &#8211; Migrating Distribution Groups</a> appeared first on <a href="https://catastrophe.wiredwolf.com">Wiredwolf Canada</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Proxmox &#8211; Post 2</title>
		<link>https://catastrophe.wiredwolf.com/proxmox-post-2/</link>
		
		<dc:creator><![CDATA[admin]]></dc:creator>
		<pubDate>Sat, 17 Feb 2024 05:40:45 +0000</pubDate>
				<category><![CDATA[Microsoft Server]]></category>
		<category><![CDATA[Networking]]></category>
		<category><![CDATA[Proxmox]]></category>
		<guid isPermaLink="false">https://catastrophe.wiredwolf.com/?p=22477</guid>

					<description><![CDATA[<p>As the trials continue, so do the posts. For this post I am relaying what I've discovered about trying to migrate a VM from VMware to Promox VE. The original VM: 4 vCore x 1 socket 8 GB memory 1 x 250 GiB VMDK 1 x 1.1 TiB VMDK Windows 2016 Standard Granted, I have  [...]</p>
<p>The post <a href="https://catastrophe.wiredwolf.com/proxmox-post-2/">Proxmox &#8211; Post 2</a> appeared first on <a href="https://catastrophe.wiredwolf.com">Wiredwolf Canada</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>As the trials continue, so do the posts.</p>
<p>For this post I am relaying what I&#8217;ve discovered about trying to migrate a VM from VMware to Promox VE.</p>
<p>The original VM:</p>
<ul>
<li>4 vCore x 1 socket</li>
<li>8 GB memory</li>
<li>1 x 250 GiB VMDK</li>
<li>1 x 1.1 TiB VMDK</li>
<li>Windows 2016 Standard</li>
</ul>
<p>Granted, I have made things more difficult by having multiple network segments.&nbsp; In my last post I think I mentioned setting up multiple VLANs, Bridges, and Interfaces in Proxmox.&nbsp; This was because I&#8217;ve gone back and forth between two networks, and my Synology NAS straddles both.&nbsp; I ended up adding the Synology NAS to as storage to the same Promox host twice, once per network, to hopefully make network access more efficient to the mass storage device.</p>
<p>Over the past week I have been working with the ovftool to migrate the file server to Promox, each time taking in excess of 24 hours and inevitable session timeouts, causing the import to stall and ultimately fail.&nbsp; Since you cannot pick up where you left off (that would be a sweet feature to ask for) the process has been started multiple times.&nbsp; This last attempt today, however, completed in just 8 hours successfully, so evidently my theory on network traffic efficiency won out.</p>
<p>The first step &#8211; use the VMware ovftool (installed to Proxmox VE server) to import the desired VM:</p>
<p>ovftool &#8211;overwrite vi://192.168.155.81/FileServ /pve-zfs</p>
<p>The second step &#8211; use qm to import the VM to Promox:</p>
<p>qm importovf 210 /pve-zfs/FileServ.ovf /pve-zfs</p>
<p>Once completed, the VM showed up in Proxmox as expected, as ID 210, but without a network interface (which is also expected).</p>
<p>First challenge &#8211; machine is not bootable.</p>
<p>Referencing Promox support documentation on importing VMs:&nbsp; <a href="https://pve.proxmox.com/wiki/Migrate_to_Proxmox_VE#Post_Migration">Migrate to Proxmox VE &#8211; Proxmox VE</a></p>
<p>Convert the SCSI disks to IDE or SATA.&nbsp;&nbsp;</p>
<ul>
<li>Detach disk</li>
<li>Change format to IDE or Sata</li>
<li>Connect&nbsp;</li>
</ul>
<p>Still no joy.&nbsp; Disks were converted fine.&nbsp;&nbsp;<br />
I then added a CDROM to the VM and booted to a Windows Server 2016 install media ISO &#8211; confirmed that C:\ is the %systemroot%.&nbsp;&nbsp;</p>
<p>I then went back to the hardware and made a few edits:</p>
<ol>
<li>Converted the Machine version to q35 from default i440fx</li>
<li>Converted the SCSI Controller to PVSCSI</li>
<li>Converted the Processors from kvm64 to host</li>
</ol>
<p>Still no joy &#8211; machine does appear to boot it just can&#8217;t find a boot partition.</p>
<p>Maybe the OVF will give me some insights? Before getting that desperate, I tried one last thing &#8211; I switched the BIOS from SeaBIOS to OVMF (UEFI).</p>
<p>Bingo &#8211; the machine instantly booted successfully.</p>
<p>Lesson learned &#8211; don&#8217;t rely on the qm importovf to properly read the OVF and translate it to the same profile in Proxmox.&nbsp; Make sure you have your hardware profile from VMware before&nbsp;</p>
<p>The post <a href="https://catastrophe.wiredwolf.com/proxmox-post-2/">Proxmox &#8211; Post 2</a> appeared first on <a href="https://catastrophe.wiredwolf.com">Wiredwolf Canada</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Proxmox &#8211; Post 1</title>
		<link>https://catastrophe.wiredwolf.com/proxmox-post-1/</link>
		
		<dc:creator><![CDATA[admin]]></dc:creator>
		<pubDate>Thu, 15 Feb 2024 01:46:48 +0000</pubDate>
				<category><![CDATA[Proxmox]]></category>
		<category><![CDATA[cluster]]></category>
		<category><![CDATA[clusterig]]></category>
		<category><![CDATA[network]]></category>
		<category><![CDATA[proxmox]]></category>
		<category><![CDATA[storage]]></category>
		<guid isPermaLink="false">https://catastrophe.wiredwolf.com/?p=22474</guid>

					<description><![CDATA[<p>As of a couple of weeks ago I have started working with Promox.  Now that the 'free' options for premium hypervisors are starting to dry up (Win2022 Hyper-V - does not exist; VMware is pulling all non-subscription licensing) I can see there will be a growing trend towards less mainstream hypervisors. Proxmox has actually been  [...]</p>
<p>The post <a href="https://catastrophe.wiredwolf.com/proxmox-post-1/">Proxmox &#8211; Post 1</a> appeared first on <a href="https://catastrophe.wiredwolf.com">Wiredwolf Canada</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>As of a couple of weeks ago I have started working with Promox.&nbsp; Now that the &#8216;free&#8217; options for premium hypervisors are starting to dry up (Win2022 Hyper-V &#8211; does not exist; VMware is pulling all non-subscription licensing) I can see there will be a growing trend towards less mainstream hypervisors.</p>
<p>Proxmox has actually been around for a while.&nbsp; In essence, it&#8217;s a web-skinned Linux KVM.&nbsp; Proxmox is built on a Debian build, and heavily relies on the operator to be somewhat proficient with Linux.&nbsp; While it takes some of the guesswork out of the Linux experience, there is no way an operator will be able to fully run Proxmox without dropping to bash occasionally.</p>
<p>Initially I was very impressed with Proxmox.&nbsp; It installs very easily and the web console is both intuitive and provides quite a lot of help.&nbsp; After watching a few training videos I could see that importing VMs can be done numerous ways, and that networking appeared to be fairly flexible.As I continued down the road of converting my ESXi 6.7 hosts, though, I started to see where Proxmox still has a ways to go.</p>
<p>My first trial was to install Windows 10.&nbsp; This went quite smoothly and I was able to load the VirtIO disk drivers into the install without too much difficulty, and get the VM booted.&nbsp; After that, though, I encountered numerous issues with the VirtIO supported drivers and the Windows 10 image.&nbsp; After the driver package and guest software for VirtIO was installed, it took no less than 2 hours to boot the image again.&nbsp; It took quite a while to figure out that the issues were with VirtIO, so 3 builds later I was on the SeaBIOS with the default drivers and found that to be stable.&nbsp; Not as quick as with the VirtIO driver set (when it did eventually boot to a desktop) but stable and far more reasonable boot times.</p>
<p>My second trial was to add storage.&nbsp; This was relatively easy.&nbsp; On my second Proxmox node I used an old Datto Siris box I&#8217;d reset.&nbsp; It has JBOD, no RAID to speak of, so I set up ZFS.&nbsp; Within a couple of minutes I&#8217;d wiped out the VMFS 6 partitions and set up a single solid ZFS volume.&nbsp; I also have a Synology NAS &#8211; 4 NICs, 4 Bays.&nbsp; I went the easy route and just set up an SMB/CIFS share and connected to Proxmox.</p>
<p>The third trial was a bit more daunting.&nbsp; On my first Promox node I&#8217;d already created a Windows 10 instance.&nbsp; My second node had nothing on it.&nbsp; I set up the second node to be the Cluster to join, then discovered that the first node couldn&#8217;t join the cluster because it already had a VM running on it.&nbsp; So I set up the first node to be the Cluster to join, and discovered that in the Web UI there is absolutely no way to &#8216;unjoin&#8217; or even remove the cluster config.&nbsp; Now I had 2 nodes, each believing they were the master node.</p>
<p>After a lot of searching I finally figured out how to remove the cluster info from both servers so I could try again.</p>
<p>To Remove a Node:</p>
<ol>
<li>SSH to host (better to <strong>not</strong> use the Web UI Shell for this)</li>
<li>Check nodes<br />
pvecm nodes</li>
<li>Identify the node you want to remove by it&#8217;s Node id</li>
<li>Remove the node<br />
pvecm delnode &lt;nodeid&gt;</li>
<li>Now we want to remove the Cluster
<ol>
<li>Set the cluster to indicate that there&#8217;s now only the 1 member<br />
pvecm expected 1</li>
<li>Stop the Cluster Service<br />
systemctl stop pve-cluster</li>
<li>Start the PVE local service<br />
pmxcfs -l</li>
<li>Delete the PVE cluster conf files and the lock file<br />
rm -r /etc/pve/cluster.conf /etc/pve/corosync.conf<br />
rm -r /etc/cluster/cluster.conf /etc/corosync/corosync.conf<br />
rm /var/lib/pve-cluster/.pmxcfs.lockfile</li>
<li>Stop the Cluster Service (again)<br />
systemctl stop pve-cluster</li>
<li>Reboot</li>
</ol>
</li>
</ol>
<p>The first time I did this the /etc/pve/cluster.conf and /etc/cluster/cluster.conf files did exist.&nbsp; Of course I&#8217;ve blown this up several times now, so I&#8217;ve come to realize that these files no longer exist.&nbsp; Execute the commands anyway (doesn&#8217;t hurt anything) and ignore the warnings that these two files cannot be found.</p>
<p>I did eventually get the cluster to connect the nodes and I did find that the Promox server quite conveniently shifted to the Datacenter console to manage both the nodes and the storage, which actually is quite convenient.&nbsp; Unfortunately unless both nodes are identical it&#8217;s not quite so simple to seamlessly move VMs around, but it does work quite well.</p>
<p>My fourth trial was to migrate VMs from my VMware ESXi host to Proxmox.&nbsp; The first one I selected was a VM called &#8216;Unifi Controller&#8217;.&nbsp; It&#8217;s a VM I no longer use, but it&#8217;s small making it relatively portable, and being that it&#8217;s no longer in use there was no danger of losing anything critical.</p>
<p>This ended up being quite the exercise.&nbsp; The space in the name caused a lot of problems.&nbsp; Exporting the VM from the VMware Web UI was no issue, nor uploading to Promox. The Debian OS had no trouble with it either, just making it /Unifi\ Controller/, but the &#8216;qm importovf&#8217; native to Proxmox couldn&#8217;t handle it, converting the space to %20 instead.&nbsp; Eventually I worked out that if I renamed the .ovf and .vmdk files to something else without a space, and edited the .ovf file to match the file names I&#8217;d already reset, then the &#8216;qm importovf&#8217; command was able to blast through the conversation from VMDK to RAW format and add the VM to the node.&nbsp; Obviously I did have to recreate the network config, but that was relatively easy and the VM came up without issue.</p>
<p>Convert the VMDK disk to RAW:<br />
qemu-img convert -f vmdk /pve-zfs/Unifi\ Controller-1.vmdk -O raw /pve-zfs/Unifi\ Controller/Unifi\ Controller-1.raw -p</p>
<p>Import the VM to Promox (after renaming the files to unificontroller.xxx):<br />
qm importovf 200 unificontroller.ovf pve-zfs</p>
<p>The problem with this process is the download/upload of the OVF from VMware.&nbsp; If you&#8217;re exporting a 1 TB server and you&#8217;re on a laptop with only 500 GiB storage, then you can&#8217;t get there from here.&nbsp; So the more logical approach is to import the OVF directly from ESXi to Proxmox, which turns out to be fairly easily done through a program called ovftool (VMware &#8211; download and install from their site to your Proxmox server):</p>
<p>ovftool &#8211;overwrite vi://192.168.1.5/FileServ /pve-zfs</p>
<p>I had to add &#8211;overwrite as this process has failed numerous times and when it does &#8211; you have to start over.</p>
<p>The syntax is easy:</p>
<ul>
<li>ovftool &#8211; command</li>
<li>&#8211;overwrite &#8211; option</li>
<li>vi://192.168.1.5/FileServ &#8211; virtual interface IP address of your ESXi host and /VM-Name</li>
<li>/pvs-zfs &#8211; the storage name you are importing to</li>
</ul>
<p>The only problem I have with this process is that it&#8217;s very slow.&nbsp; The VM must be shut down to execute the export/import and I&#8217;ve found this process on a 1 TB transfer can easily take a day.&nbsp; I&#8217;m going to keep trying to figure out if there&#8217;s a way to speed this up.&nbsp; If I do, I will document it here.</p>
<p>&nbsp;</p>
<p>The post <a href="https://catastrophe.wiredwolf.com/proxmox-post-1/">Proxmox &#8211; Post 1</a> appeared first on <a href="https://catastrophe.wiredwolf.com">Wiredwolf Canada</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Azure AD Connect &#8211; Fixing the Sync</title>
		<link>https://catastrophe.wiredwolf.com/azure-ad-connect-fixing-the-sync/</link>
		
		<dc:creator><![CDATA[admin]]></dc:creator>
		<pubDate>Wed, 17 May 2023 17:03:54 +0000</pubDate>
				<category><![CDATA[Active Directory]]></category>
		<category><![CDATA[Azure]]></category>
		<category><![CDATA[Microsoft Office 365]]></category>
		<category><![CDATA[Microsoft Server]]></category>
		<guid isPermaLink="false">https://catastrophe.wiredwolf.com/?p=22432</guid>

					<description><![CDATA[<p>Windows Active Directory to Azure Active Directory generally works pretty well but there are times when the sync generates a new Azure AD user instead of linking to an existing account in Azure.  This generally happens when you're onboarding a client environment to Azure where they already have a number of accounts in MS 365  [...]</p>
<p>The post <a href="https://catastrophe.wiredwolf.com/azure-ad-connect-fixing-the-sync/">Azure AD Connect &#8211; Fixing the Sync</a> appeared first on <a href="https://catastrophe.wiredwolf.com">Wiredwolf Canada</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>Windows Active Directory to Azure Active Directory generally works pretty well but there are times when the sync generates a new Azure AD user instead of linking to an existing account in Azure.&nbsp; This generally happens when you&#8217;re onboarding a client environment to Azure where they already have a number of accounts in MS 365 that are in use, and now need to be directly linked to the on-premises Active Directory.</p>
<p>Typically I use the EmailAddress (Mail) attribute to cross-link the accounts.</p>
<p>Example:</p>
<p>Azure AD user:&nbsp; Smiles J. McDuff</p>
<p>Azure AD email:&nbsp; smiley@mydomain.com, sjmcduff@mydomain.onmicrosoft.com</p>
<p>In AD I then create the user &#8211;</p>
<p>AD User:&nbsp; &nbsp;Smiles McDuff</p>
<p>SamAccountName:&nbsp; sjmcduff</p>
<p>Set Email Address:&nbsp; smiley@mydomain.com</p>
<p>When I do an Azure AD Connect Sync the new AD user should match to the Azure AD user and overwrite the user and attributes to that based in Active Directory.</p>
<p>Sometimes that doesn&#8217;t happen, such as making a typo with the email address, and instead of cross-linking the accounts between AD and AAD, a new user account is created in AAD.&nbsp; Now things get tricky, because no matter how many times you delete the incorrect account in Azure AD, the next sync will just recreate it.</p>
<p>The solution is to capture the ObjectGUID attribute for the user in Active Directory and set that as the ImmutableID for the user in Azure.</p>
<p>Command:</p>
<blockquote><p>Get-ADUser sjmcduff | select-object userPrincipalName, objectGuid</p></blockquote>
<p>Result:</p>
<blockquote><p>UserPrincipleName&nbsp; :&nbsp; sjmcduff@mywindowsdomain.com</p>
<p>objectGuid : b316d357-25fd-4912-9896-faf007a16289</p></blockquote>
<p>Now convert that Guid to something we can use as an ImmutableID &#8211;</p>
<blockquote><p>[Convert]::ToBase64String([guid]::New(&#8220;b316d357-25fd-4912-9896-faf007a16289&#8221;).ToByteArray())</p></blockquote>
<p>Result:&nbsp;&nbsp;</p>
<blockquote><p>V9MWs/0lEkmYlvrwB6FiiQ==</p></blockquote>
<p>This is our new ImmutableID value for the Azure AD user account.</p>
<blockquote><p>connect-msolservice</p>
<p>Get-MsolUser -UserPrincipalName &#8220;sjmcduff@mydomain.onmicrosoft.com&#8221; | select-object userPrincipalName, ImmutableId</p></blockquote>
<p>Result:</p>
<blockquote><p>UserPrincipalName : sjmcduff@mydomain.onmicrosoft.com<br />
ImmutableId :&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; [Confirm ImmutableID is blank -if not record it in your notes]</p></blockquote>
<p>Command:</p>
<blockquote><p>Set-MsolUser -UserPrincipalName &#8220;sjmcduff@mydomain.onmicrosoft.com&#8221; -ImmutableId &#8220;V9MWs/0lEkmYlvrwB6FiiQ==&#8221;</p></blockquote>
<p>Comand:</p>
<blockquote><p>Get-MsolUser -UserPrincipalName &#8220;sjmcduff@mydomain.onmicrosoft.com&#8221; | fl userPrincipalName,ImmutableId</p></blockquote>
<p><strong><em>Confirm ImmutableID matches</em></strong></p>
<p>Now when you sync, the accounts should pair up properly.</p>
<p>The post <a href="https://catastrophe.wiredwolf.com/azure-ad-connect-fixing-the-sync/">Azure AD Connect &#8211; Fixing the Sync</a> appeared first on <a href="https://catastrophe.wiredwolf.com">Wiredwolf Canada</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Windows Security &#8211; LAPS</title>
		<link>https://catastrophe.wiredwolf.com/windows-security-laps/</link>
		
		<dc:creator><![CDATA[admin]]></dc:creator>
		<pubDate>Tue, 22 Nov 2022 03:03:29 +0000</pubDate>
				<category><![CDATA[Active Directory]]></category>
		<category><![CDATA[Best Practices]]></category>
		<category><![CDATA[Microsoft Server]]></category>
		<category><![CDATA[Microsoft Workstation]]></category>
		<category><![CDATA[Security]]></category>
		<category><![CDATA[Windows 10]]></category>
		<guid isPermaLink="false">https://catastrophe.wiredwolf.com/?p=22027</guid>

					<description><![CDATA[<p>It's actually been around for a while now - LAPS or Local Administrator Password Solution - but honestly, it's not something I've ever encountered in all the networks I've ever managed.  I was introduced to LAPS when I ran Ping Castle against my own environment, as a strongly recommended solution to implement. The concept is  [...]</p>
<p>The post <a href="https://catastrophe.wiredwolf.com/windows-security-laps/">Windows Security &#8211; LAPS</a> appeared first on <a href="https://catastrophe.wiredwolf.com">Wiredwolf Canada</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>It&#8217;s actually been around for a while now &#8211; LAPS or Local Administrator Password Solution &#8211; but honestly, it&#8217;s not something I&#8217;ve ever encountered in all the networks I&#8217;ve ever managed.&nbsp; I was introduced to LAPS when I ran Ping Castle against my own environment, as a strongly recommended solution to implement.</p>
<p>The concept is pretty simple &#8211; LAPS sets a Local Administrator password policy against all the computers (except domain controllers) in a domain environment.&nbsp; A typical policy:</p>
<ul>
<li>Password is reset every X number of days</li>
<li>Password meets complexity requirements</li>
<li>Password meets length requirements</li>
</ul>
<p>In my own environment I set a policy of 90 days and 24 completely randomized characters.</p>
<p>I found lots of resource online for getting started:</p>
<ul>
<li><a href="https://4sysops.com/archives/how-to-install-and-configure-microsoft-laps/">How to install and configure Microsoft LAPS – 4sysops</a></li>
<li><a href="https://techcommunity.microsoft.com/t5/itops-talk-blog/step-by-step-guide-how-to-configure-microsoft-local/ba-p/2806185">How to Configure Microsoft Local Administrator Password Solution (LAPS)</a></li>
</ul>
<p>Both sites are great at detailing out the process.</p>
<p>The drawbacks I encountered:</p>
<ol>
<li>If deploying the MSI package via GPO you do have to reboot the system for the install to happen</li>
<li>It doesn&#8217;t work for Domain-Joined systems in Azure unless you&#8217;ve planned for this in advance</li>
</ol>
<p>When you deploy an Azure Windows VM, the &#8216;administrator&#8217; account is reserved by Azure, so you&#8217;re prompted to create your own.&nbsp; Out of the box configurations for LAPS utilizes the Administrator account, so unless you plan ahead and have a policy that resets the default Administrator to a different username, and incorporated the same username into your LAPS policy, your Azure Windows VMs will accept the policy but never return a password.</p>
<p>Truthfully I did cheat this last bit by just adding the Administrator username to the Azure VM and giving it a random password.&nbsp; LAPS then did grab that account and reset the password according to policy.&nbsp; This isn&#8217;t the right way to do it though, so I don&#8217;t recommend it.&nbsp; Best practice would be to set your own Local Administrator username and implement across your domain, and incorporate into your LAPS policy.</p>
<p>Oct 18, 2024 &#8211; update</p>
<p>I keep hitting my head against needing to validate LAPS passwords.&nbsp; Sometimes I don&#8217;t want the password, I want to know how many or which systems have a LAPS password:</p>
<blockquote><p>Get-ADComputer -LDAPFilter &#8220;(ms-mcs-AdmPwd=*)&#8221; | select-object name</p>
<p>(Get-ADComputer -LDAPFilter &#8220;(ms-mcs-AdmPwd=*)&#8221;).count</p></blockquote>
<p>Inversely to find out how many or which systems do not have a LAPS password:</p>
<blockquote><p>Get-ADComputer -LDAPFilter &#8220;(!(ms-mcs-AdmPwd=*))&#8221; | select-object name</p>
<p>(Get-ADComputer -LDAPFilter &#8220;(!(ms-mcs-AdmPwd=*))&#8221;).count</p></blockquote>
<p>The post <a href="https://catastrophe.wiredwolf.com/windows-security-laps/">Windows Security &#8211; LAPS</a> appeared first on <a href="https://catastrophe.wiredwolf.com">Wiredwolf Canada</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Azure Repair and Recovery for Active Directory</title>
		<link>https://catastrophe.wiredwolf.com/azure-repair-and-recovery-for-active-directory/</link>
		
		<dc:creator><![CDATA[admin]]></dc:creator>
		<pubDate>Sat, 12 Nov 2022 20:48:04 +0000</pubDate>
				<category><![CDATA[Active Directory]]></category>
		<category><![CDATA[Azure]]></category>
		<category><![CDATA[Microsoft Server]]></category>
		<guid isPermaLink="false">https://catastrophe.wiredwolf.com/?p=22003</guid>

					<description><![CDATA[<p>This is a similar post to one a few days ago. In that post I detailed an experience I had with a recovered DC that refused to boot in Azure, and how I solved the issue. This post is more specific to creating a clean room, restoring your controllers to the clean room to run  [...]</p>
<p>The post <a href="https://catastrophe.wiredwolf.com/azure-repair-and-recovery-for-active-directory/">Azure Repair and Recovery for Active Directory</a> appeared first on <a href="https://catastrophe.wiredwolf.com">Wiredwolf Canada</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>This is a <a href="https://catastrophe.wiredwolf.com/recover-a-domain-controller-in-azure/" target="_blank" rel="noopener">similar post</a> to one a few days ago. In that post I detailed an experience I had with a recovered DC that refused to boot in Azure, and how I solved the issue.</p>
<p>This post is more specific to creating a clean room, restoring your controllers to the clean room to run tests on them, particularly to make sure they&#8217;re replicating with each other, and restore them to the original Resource Group.&nbsp; In this post we are using Commvault as the backup medium and restoring to Azure using a Commvault Media Agent.</p>
<h4>Step 1: Create a Clean Room in Azure</h4>
<p>The clean room in Azure is a Resource Group into which Commvault will restore backups of the Active Directory Domain Controllers. This Resource Group should be created in the same region as the production environment.</p>
<p style="padding-left: 40px;">Resource Group:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; RG-CACENTRAL-AD-Cleanroom<br />
Within the Resource Group create:<br />
Virtual Network: &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; VNET-CACENTRAL-AD-Cleanroom<br />
Scope: &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;10.200.0.0/16<br />
Subnet 1:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;SNET-CACENTRAL-AD-Cleanroom<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 10.200.0.0/24<br />
Subnet 2:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; AzureBastionSubnet<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;10.200.254.0/26<br />
Storage Account:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; cleanroomstorage1<br />
Public IP:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;PIP-Bastion-Cleanroom</p>
<p>NOTES</p>
<p style="padding-left: 40px;">• Subnet 2 is automatically created when Bastion is deployed, and the name of the subnet must be AzureBastionSubnet<br />
• The storage account is a requirement of Commvault as a place holder for the recovery VHDs<br />
• Commvault also requires the name of the virtual network/subnet<br />
• Throughout this exercise the vnet was never given its own security group or public IP address. This is a sandbox area with no internet access to ensure there can be no tampering with the domain controllers while they’re being recovered.<br />
Bastion Deployment</p>
<h4>Step 2 &#8211; Add Bastion</h4>
<p>• In the resource group click on Create<br />
• Enter “Bastion” in the search field<br />
• Select Microsoft Bastion<br />
• Click on Create to add the Bastion Plan to the Resource Group</p>
<p style="padding-left: 40px;">o Subscription: pre-selected<br />
o Resource Group: pre-selected<br />
o Name: RG-CACENTRAL-CLEANROOM-BASTION<br />
o Region: pre-selected (but make sure it matches)<br />
o Tier: Basic<br />
o Virtual Network: select VNET-CACENTRAL-AD-Cleanroom<br />
o Subnet: select AzureBastionSubnet<br />
o Public IP select ‘Create new’<br />
o Public IP Name PIP-Bastion-Cleanroom</p>
<p>It can take a few minutes for Bastion to deploy.</p>
<p>The Clean Room has now been set up. Please note that the only Public IP to this environment is with Bastion and Bastion cannot be accessed outside of the Azure Portal.</p>
<h4>Step 3 &#8211; Restoring the VMs to the Clean Room</h4>
<p>Using Commvault the restoration process will deposit/create replica VMs to the resource group.</p>
<p style="padding-left: 40px;">• Use the option to <strong>not start the VM</strong> when the restore is completed so you can check each one individually for corruption.<br />
• Try to restore from the same time frame for each domain controller<br />
IE – restore for Nov 11 2022 8 PM for one, then every other one should be the same or close<br />
• Recommend you start with your PDCE role holder and restore each subsequent domain controller to a time or date after the PDCE</p>
<h4>Step 4 &#8211; Verifying Azure Cleanroom VMs</h4>
<p>The restored VMs can be booted up but may require additional modifications to function in the different vnet. This can be set on the vnet object or each network interface object.</p>
<p>• Update the Network Interface for each VM<br />
o IP configurations – set to Static and note the IP address<br />
o DNS Servers – add the static IP assigned to each VM as DNS servers<br />
o Update the root IP A address of the domain, test pinging to ensure it resolves to the correct address<br />
Use Bastion to connect to each domain controller. Note that your username should not be your pre-2000 (DOMAIN\username) but rather your modern username (username@internaldomain.loc)</p>
<p>• Suggest you open Active Directory Sites and Services and add the SNET-CACENTRAL-AD-Cleanroom subnet and link to the site name of the original domain controller</p>
<p>Testing AD</p>
<p>In this environment I was able to connect to each domain controller and immediately execute commands to verify AD replication.&nbsp;</p>
<p style="padding-left: 40px;">• Start Elevated CMD<br />
• Execute: repadmin /syncall<br />
• Execute: repadmin /showrepl<br />
• Execute: repadmin /replsummary</p>
<p>I also created a bogus DNS entry under our domain and confirmed that it did replicate between controllers. Once confirmed be sure to remove this entry.</p>
<p>There was an issue with NETLOGON/SYSVOL replication with DFS Replication. In DNS the root A host record of the local domain is the original IP address of the PDCE and doesn’t automatically update. We resolved this issue in our test environment by adjusting this IP from the original to the new IP of the PDCE. Immediately all servers showed the NETLOGON and SYSVOL folders, and policies were being replicated between the domain controllers.</p>
<p><em>After moving back to the production environment make sure this DNS record is updated to its original value.</em></p>
<h4>Step 5 &#8211; Migration back to Production Environment</h4>
<p>The process to migrate the VMs to the Production Environment is simplified by only migrating the managed disks. To move the virtual disks the easiest way is to snapshot then create a replica from the snapshot in the target Resource Group.</p>
<p style="padding-left: 40px;">• Shut down the VMs in the Clean Room<br />
• Click on the disk to replicate in the Clean Room<br />
• Click on Create Snapshot<br />
• Give the snapshot a name but leave it in the same Resource group<br />
• Set the networking to ‘Disable public and private access’<br />
• Review + create<br />
• Create</p>
<p>Now click on the snapshot in the Resource group</p>
<p style="padding-left: 40px;">• Click on Create Disk<br />
• Give the disk a name that identifies it as the replacement OS disk for the DC being restored<br />
• Select the Resource group where the DC is being restored<br />
• Select an appropriate size for the disk<br />
• Set the networking to ‘Disable public and private access’<br />
• Review + create<br />
• Create</p>
<p>Go back to your production Resource group and ensure the servers you are about to restore are shut down</p>
<p style="padding-left: 40px;">• Start with your PDCE Virtual machine<br />
• Click on Disks<br />
• In the OS Disk section, click on Swap OS Disk</p>
<p>Pay attention to the disk name, size, and which resource group it is in, to ensure correct selection.</p>
<p><strong>Note:</strong><br />
<em>You must confirm the OS Disk Swap by typing in the name of the VM</em></p>
<p>Click on OK at the bottom of the screen and wait for deployment to complete.</p>
<p>You will need to repeat this process for each VM being restored.</p>
<h4>Final Steps</h4>
<p>Once you have restored all the domain controllers in this fashion, start them up one at a time starting with your PDCE, and execute your AD tests again.</p>
<p>Because you are only swapping disks and not recreating the VMs you do not need to worry about configuration changes to the VM itself. All that information is retained. The analogy here would be that you have restored to a new drive to an earlier point in time, removed the corrupted or locked drive, and replaced with the new drive.</p>
<p>Tech Notes:</p>
<ul>
<li>I found it can sometimes take Azure a little while for the disks to appear in the dropdowns (sometimes a few minutes)</li>
<li>I strongly recommend that you test this process biannually up to the point of restoring the disks to the production VMs.</li>
<li>Bastion also has its own costs so if you aren’t going to be using it regularly then you can add when needed and remove when your recovery process has been concluded.&nbsp; Keeping the Clean Room Resource Group doesn&#8217;t really cost anything but it&#8217;s so easy to recreate you might as well remove it as well.</li>
<li>After recovery you should ensure your backups are continuing as expected &#8211; check your backup logs and run new backups manually as a test</li>
</ul>
<p>The post <a href="https://catastrophe.wiredwolf.com/azure-repair-and-recovery-for-active-directory/">Azure Repair and Recovery for Active Directory</a> appeared first on <a href="https://catastrophe.wiredwolf.com">Wiredwolf Canada</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Active Directory Backups</title>
		<link>https://catastrophe.wiredwolf.com/active-directory-backups/</link>
		
		<dc:creator><![CDATA[admin]]></dc:creator>
		<pubDate>Fri, 11 Nov 2022 16:59:09 +0000</pubDate>
				<category><![CDATA[Active Directory]]></category>
		<category><![CDATA[Best Practices]]></category>
		<category><![CDATA[Microsoft Server]]></category>
		<guid isPermaLink="false">https://catastrophe.wiredwolf.com/?p=21999</guid>

					<description><![CDATA[<p>Recently I was tasked with doing an AD audit in which I ran a utility called Ping Castle which indicated that Active Directory NTDS should be backed up frequently. This would be so you can roll back AD to a consistent state should you need to recover deleted items or deleted attributes.  Typically administrators would  [...]</p>
<p>The post <a href="https://catastrophe.wiredwolf.com/active-directory-backups/">Active Directory Backups</a> appeared first on <a href="https://catastrophe.wiredwolf.com">Wiredwolf Canada</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>Recently I was tasked with doing an <a href="https://catastrophe.wiredwolf.com/active-directory-auditing/" target="_blank" rel="noopener">AD audit</a> in which I ran a utility called <a href="https://www.pingcastle.com/" target="_blank" rel="noopener">Ping Castle</a> which indicated that Active Directory NTDS should be backed up frequently. This would be so you can roll back AD to a consistent state should you need to recover deleted items or deleted attributes.&nbsp; Typically administrators would restore the entire server to a point in time, but with AD this can cause differences in AD between servers to become a problem.&nbsp; Having a backup of AD on all servers at the same time would be helpful in restoring AD back to a consistent state.</p>
<p>The process is actually very simple using ntdsutil.</p>
<p>Open an elevated CMD (as Administrator</p>
<p>ntdsutil</p>
<p>activate instance ntds</p>
<p>snapshot</p>
<p>create</p>
<p>That&#8217;s it &#8211; the manual process.</p>
<p>To autotmate this process Petri has a good article on the process:&nbsp; <a href="https://petri.com/automating-creation-active-directory-snapshots/" target="_blank" rel="noopener">https://petri.com/automating-creation-active-directory-snapshots/</a></p>
<p>Basically create a batch file in your scripts folder called ad-snapshot.bat</p>
<blockquote><p>@echo off</p>
<p>ntdsutil snapshot &#8220;activate instance ntds&#8221; create quit quit</p>
<p>exit</p></blockquote>
<p>Use the Task Scheduler to create a job to fire this job once a week.&nbsp;</p>
<p>The post <a href="https://catastrophe.wiredwolf.com/active-directory-backups/">Active Directory Backups</a> appeared first on <a href="https://catastrophe.wiredwolf.com">Wiredwolf Canada</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Recover a Domain Controller in Azure</title>
		<link>https://catastrophe.wiredwolf.com/recover-a-domain-controller-in-azure/</link>
		
		<dc:creator><![CDATA[admin]]></dc:creator>
		<pubDate>Fri, 11 Nov 2022 00:52:26 +0000</pubDate>
				<category><![CDATA[Active Directory]]></category>
		<category><![CDATA[Azure]]></category>
		<category><![CDATA[Microsoft Server]]></category>
		<guid isPermaLink="false">https://catastrophe.wiredwolf.com/?p=21991</guid>

					<description><![CDATA[<p>I was recently tasked with creating a cleanroom restore process in Azure to recover Active Directory servers in the event of a catastrophic event, such as an entire AD domain being crypto-locked.  We needed to confirm that we could restore all the domain controllers, and that the domain controllers came up and were able to  [...]</p>
<p>The post <a href="https://catastrophe.wiredwolf.com/recover-a-domain-controller-in-azure/">Recover a Domain Controller in Azure</a> appeared first on <a href="https://catastrophe.wiredwolf.com">Wiredwolf Canada</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>I was recently tasked with creating a cleanroom restore process in Azure to recover Active Directory servers in the event of a catastrophic event, such as an entire AD domain being crypto-locked.&nbsp; We needed to confirm that we could restore all the domain controllers, and that the domain controllers came up and were able to immediately replicate AD/DNS, etc.&nbsp;</p>
<p>The production environment:</p>
<ul>
<li>One AD server is already in Azure</li>
<li>One AD server is hosted on-premises in a VMware hypervisor</li>
<li>Backup is done on both servers with CommVault</li>
</ul>
<p>We created a Resource Group and populated with the following:</p>
<ul>
<li>Virtual Network separate from our production network with 2 subnets with no Public IP
<ul>
<li>Cleanroom Subnet</li>
<li>AzureBastionSubnet</li>
</ul>
</li>
<li>Bastion</li>
<li>StorageAccount</li>
</ul>
<p>Using CommVault both servers were restored to the vnet subnet for the Cleanroom.</p>
<p>The Azure server came up right away with no issues.&nbsp; AD DS was healthy.</p>
<p>The VMware hosted server did not come up &#8211; it could not be booted and gave a BSOD of 0xc00002e2 which is Directory Services could not be started.&nbsp; Uh oh &#8211; this is a problem.</p>
<p>Azure can be a little difficult when it comes to a BSOD on a server.&nbsp; There is no &#8216;console&#8217; per-say so there&#8217;s no way to reboot the server and start pounding on the F8 key to start in Directory Services Recovery Mode.&nbsp; Even after an exhaustive Google search session, there wasn&#8217;t a lot to be found.&nbsp; Lots of articles on how to enable DSRM on a failed domain controller if the option wasn&#8217;t available, but very little on how to actually get a VM to boot into DSRM.</p>
<p>The good news is it is doable, but it&#8217;s quite complex and involved.&nbsp; Here are the steps I performed to achieve my goal:</p>
<h3>Step 1 &#8211; create a recovery VM</h3>
<p>This is done by utilizing Azure CLI commands.</p>
<p><a href="https://learn.microsoft.com/en-us/troubleshoot/azure/virtual-machines/repair-windows-vm-using-azure-virtual-machine-repair-commands">Repair a Windows VM by using the Azure Virtual Machine repair commands &#8211; Virtual Machines | Microsoft Learn</a></p>
<p>I used the CLI option in the top right of Azure to open a CLI shell into Azure.&nbsp; This is the simplest way to get a shell without having to install a whole bunch of Azure modules into PowerShell and navigate MFA authentication, etc.</p>
<blockquote><p>az extension add -n vm-repair</p></blockquote>
<p>This installs the extension so you can run the repair and create the repair environment</p>
<p>If you&#8217;ve already installed the extension at some point you may need to update it:</p>
<blockquote><p>az extension update -n vm-repair</p></blockquote>
<p>You will be prompted to enable a Public IP &#8211; if you aren&#8217;t using Bastion you might as well indicate Y &#8211; you will need it to access your recovery VM.</p>
<p>Now you run some simple commands&nbsp;</p>
<blockquote><p>az vm repair create -g MyResourceGroup -n myVM &#8211;repair-username username &#8211;repair-password &#8216;password!234&#8217; &#8211;enable-nested &#8211;verbose</p></blockquote>
<ul>
<li>where MyResourceGroup is the resource group the failed VM is currently residing</li>
<li>where myVM is the name of the failed VM</li>
<li>where repair-username is the is admin user you want to use for your recovery VM&nbsp;</li>
<li>where repair-password is the admin password you want to use for your recovery VM (I suggest you use a strong password)</li>
<li>where &#8211;enable-nested sets up your recovery VM (a 2016 server) with Hyper-V installed</li>
</ul>
<p>Once these commands complete, you&#8217;ll have a new Resource Group called repair-<em>myVM</em>&#8211;<em>timestamp</em> which has your bootable 2016 recovery VM and a copy of your failed drives &#8211; the non-bootable AD controller system drives and whatever other disks were attached to the failed VM from the restore.</p>
<p>Boot up the myVM recovery VM if it isn&#8217;t already and use RDP (or Bastion if you have it) to connect to the server using the repair-username and repair-password you specified in the previous command.</p>
<ul>
<li>Your Windows 2016 Server should have Hpyer-V installed to it.&nbsp; If not, install it.</li>
<li>Open Computer Management -&gt; Disk Management and make sure the drive(s) you need to use for Hyper-V VM are set to &#8220;Offline&#8221;</li>
<li>Open Hyper-V and create a VM with appropriate vCPU cores and memory (acknowledging the limits of your Server 2016 recovery instance)
<ul>
<li>Do not create a drive &#8211; choose to add a drive later</li>
</ul>
</li>
<li>Edit your new Hyper-V VM
<ul>
<li>Click on IDE Controller 1</li>
<li>Click on Add Hard Drive</li>
<li>Select the physical disk available</li>
<li>Click on Apply then OK</li>
</ul>
</li>
<li>Right click on the VM and click on Connect</li>
<li>Start the VM</li>
<li>When it starts booting up start tapping F8 to get into the boot menu</li>
<li>Select Directory Services Recovery Mode</li>
<li>Log in with your DSRM (Administrator) password</li>
</ul>
<p>You can now perform the steps to correct whatever issues you&#8217;re having with AD.</p>
<p>When completed and you can boot the VM up and log in with your domain credentials, shut down the VM, then shut down your Recovery VM.</p>
<p>Go back to your Azure CLI&nbsp;</p>
<blockquote><p><span class="hljs-keyword">az vm repair restore </span><span class="hljs-parameter">-g</span> MyResourceGroup <span class="hljs-parameter">-n</span> MyVM <span class="hljs-parameter">&#8211;verbose</span></p></blockquote>
<p>Obviously, you will again substitute your values for MyResourceGroup and MyVM (same as above).</p>
<p>This final command will completely remove the recovery environment including the repair-ResourceGroup that was created from your Azure tenant.&nbsp;&nbsp;</p>
<p>You should now be able to boot your recovered AD Domain Controller in Azure.</p>
<h3>If your issue was not with Active Directory or you&#8217;re having issues recovering Active Directory due to disk issues:</h3>
<p><a href="https://learn.microsoft.com/en-us/troubleshoot/azure/virtual-machines/troubleshoot-directory-service-initialization-failure">Troubleshoot Windows stop error – directory service initialization failure &#8211; Virtual Machines | Microsoft Learn</a></p>
<p>I found this to be an extremely helpful article on fixing disks with NTDS installed.&nbsp;</p>
<ul>
<li>You can use the 2016 Server to run DISKPART on the attached drive to set the active partition (Gen 1 VMs)</li>
<li>You can use REG QUERY to find the location of the NTDS (typically C:\Windows\NTDS but it can be moved such as I found in my case which complicated things considerably)</li>
<li>You can run BCDEDIT commands to&nbsp;<em>force</em> the VM to boot to DSRM (safeboot dsrepair) so you can re-attach the disk to your VM in Azure and not worry about not being able to hit F8 on a console
<ul>
<li>Note &#8211; you will have to also reverse this or the BCD will&nbsp;<em>always</em> boot to DS Recovery Mode</li>
<li>Note &#8211; you still have to have your DSRM password to log in</li>
</ul>
</li>
</ul>
<p>Other Helpful Resources</p>
<p>I used all of these online resources to solve my problem.</p>
<p><a href="https://learn.microsoft.com/en-us/troubleshoot/azure/virtual-machines/troubleshoot-common-blue-screen-error">Blue screen errors when booting an Azure VM &#8211; Virtual Machines | Microsoft Learn</a></p>
<p><a href="https://learn.microsoft.com/en-us/troubleshoot/azure/virtual-machines/troubleshoot-recovery-disks-portal-windows">Troubleshoot a Windows VM in the Azure portal &#8211; Virtual Machines | Microsoft Learn</a></p>
<p><a href="https://learn.microsoft.com/en-us/troubleshoot/windows-server/identity/reset-directory-services-restore-mode-admin-pwd">How to reset the Directory Services Restore Mode administrator account password &#8211; Windows Server | Microsoft Learn</a></p>
<p><a href="https://support.hostway.com/hc/en-us/articles/360001126259-How-to-fix-Error-0xc00002e2-after-rebooting-Windows-Domain-Controller">How to fix Error 0xc00002e2 after rebooting Windows Domain Controller – Hostway Help Center</a></p>
<p><a href="https://learn.microsoft.com/en-us/troubleshoot/azure/virtual-machines/serial-console-windows">Azure Serial Console for Windows &#8211; Virtual Machines | Microsoft Learn</a></p>
<p><a href="https://www.cloud-aware.net/2022/08/fixing-non-bootable-azure-virtual.html">Fixing non-bootable Azure Virtual Machine (cloud-aware.net)</a></p>
<p><a href="https://www.kapilarya.com/how-to-boot-windows-server-in-directory-services-restore-mode-dsrm">How to boot Windows Server in Directory Services Restore Mode (DSRM) (kapilarya.com)</a></p>
<p><a href="https://www.dell.com/support/kbdoc/en-ca/000137796/recovering-the-active-directory-database-in-windows-server-2012-r2#:~:text=The%20default%20path%20for%20ntds,C%3A%5CWindows%5CSystem32%20folder.">Recovering the Active Directory database in Windows Server 2012 R2 | Dell Canada</a></p>
<p><a href="https://www.nakivo.com/blog/hyper-v-nested-virtualization-on-azure-complete-guide/">How to Deploy Hyper-V Nested Virtualization on Azure: Full Overview (nakivo.com)</a></p>
<p><a href="https://learn.microsoft.com/en-us/azure/virtual-machines/windows/attach-managed-disk-portal">Attach a managed data disk to a Windows VM &#8211; Azure &#8211; Azure Virtual Machines | Microsoft Learn</a></p>
<p>The post <a href="https://catastrophe.wiredwolf.com/recover-a-domain-controller-in-azure/">Recover a Domain Controller in Azure</a> appeared first on <a href="https://catastrophe.wiredwolf.com">Wiredwolf Canada</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Active Directory Auditing</title>
		<link>https://catastrophe.wiredwolf.com/active-directory-auditing/</link>
		
		<dc:creator><![CDATA[admin]]></dc:creator>
		<pubDate>Thu, 20 Oct 2022 19:23:54 +0000</pubDate>
				<category><![CDATA[Active Directory]]></category>
		<category><![CDATA[Microsoft Server]]></category>
		<category><![CDATA[Microsoft Workstation]]></category>
		<guid isPermaLink="false">https://catastrophe.wiredwolf.com/?p=21948</guid>

					<description><![CDATA[<p>Running security audits against client active directory domains can be pretty daunting.  There's so much to look at and it takes a long time to go through it and be thorough.  There are, however, several tools available that can simplify this process. I started testing Ping Castle on my own Active Directory (mostly a lab  [...]</p>
<p>The post <a href="https://catastrophe.wiredwolf.com/active-directory-auditing/">Active Directory Auditing</a> appeared first on <a href="https://catastrophe.wiredwolf.com">Wiredwolf Canada</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>Running security audits against client active directory domains can be pretty daunting.&nbsp; There&#8217;s so much to look at and it takes a long time to go through it and be thorough.&nbsp; There are, however, several tools available that can simplify this process.</p>
<p>I started testing Ping Castle on my own Active Directory (mostly a lab environment) and scored way worse than I expected.&nbsp; Ping Castle is like golf &#8211; the lower the score the better.&nbsp; It&#8217;s scored based on findings in 4 categories, each category scored out of 100, with an overall total of 100 for all 4 categories.&nbsp; I scored a 92/100.&nbsp; Abysmal.&nbsp;</p>
<p>Ping Castle shines in that it backs up all of its findings (which are extensive) with documentation to support best-practices, knowledge base articles on the subject, and recommendations.&nbsp; One of those findings was the lack of good auditing configured on my AD domain.&nbsp; The following list is what is recommended.&nbsp; This is referenced in <a href="https://adsecurity.org/?p=3299">adsecurity.org</a> but I couldn&#8217;t find the exact settings.&nbsp; After more searching I did find it eventually on some other site, and re-running the analysis confirms my settings are now correct.&nbsp;&nbsp;</p>
<p>Here are those settings for future reference:</p>
<p><strong>Default Domain Controller Policy</strong></p>
<p>Computer Configuration\Policies\Windows Settings\Security Settings\Local Policies\Audit Policy</p>
<p style="padding-left: 40px;">Audit account logon events: Failure<br />
Audit account management: Success &amp; Failure<br />
Audit directory service access: Failure<br />
Audit logon events: Failure<br />
Audit policy change: Success &amp; Failure<br />
Audit privilege use: Success &amp; Failure<br />
Audit system events: Success &amp; Failure</p>
<p>Computer Configuration\Policies\Windows Settings\Security Settings\Advanced Audit Policy Configuration\Audit Policies</p>
<p style="padding-left: 40px;">Account Logon</p>
<p style="padding-left: 80px;">Audit Credential Validation: Success &amp; Failure<br />
Audit Kerberos Authentication: Service Success &amp; Failure<br />
Audit Kerberos Service Ticket: Operations Success &amp; Failure</p>
<p style="padding-left: 40px;">Account Management</p>
<p style="padding-left: 80px;">Audit Computer Account Management: Success &amp; Failure<br />
Audit Other Account Management Events: Success &amp; Failure<br />
Audit Security Group Management: Success &amp; Failure<br />
Audit User Account Management: Success &amp; Failure</p>
<p style="padding-left: 40px;">Detailed Tracking</p>
<p style="padding-left: 80px;">Audit DPAPI Activity Success &amp; Failure<br />
Audit Process Creation Success &amp; Failure</p>
<p style="padding-left: 40px;">DS Access</p>
<p style="padding-left: 80px;">Audit Directory Service Access Success &amp; Failure<br />
Audit Directory Service Changes Success &amp; Failure</p>
<p style="padding-left: 40px;">Logon/Logoff</p>
<p style="padding-left: 80px;">Audit Account Lockout Success<br />
Audit Logoff Success<br />
Audit Logon Success &amp; Failure<br />
Audit Special Logon Success &amp; Failure</p>
<p style="padding-left: 40px;">Policy Change</p>
<p style="padding-left: 80px;">Audit Audit Policy Change Success &amp; Failure<br />
Audit Authentication Policy Change Success &amp; Failure</p>
<p style="padding-left: 40px;">System</p>
<p style="padding-left: 80px;">Audit IPsec Driver Success &amp; Failure<br />
Audit Other System Events Success &amp; Failure<br />
Audit Security State Change Success &amp; Failure<br />
Audit Security System Extension Success &amp; Failure<br />
Audit System Integrity Success &amp; Failure</p>
<p>I would imagine the Default Domain Policy should also be updated, or another GPO applied at the root of the domain specifically for auditing, but those settings would be more lax as we&#8217;d only be interested in logging the actual logins to the workstations as opposed to these settings which are tracking changes to security around authentication and key objects.</p>
<p>On the workstation these policy settings should be sufficient:</p>
<p><strong>Computer Configuration\Policies\Windows Settings\Security Settings\Local Policies\Audit Policy</strong></p>
<p style="padding-left: 40px;">Audit account logon events: Failure<br />
Audit account management: Success &amp; Failure<br />
Audit directory service access: Failure<br />
Audit logon events: Failure<br />
Audit policy change: Success &amp; Failure<br />
Audit privilege use: Success &amp; Failure<br />
Audit system events: Success &amp; Failure</p>
<p>I will write more about the Ping Castle findings later.</p>
<p>The post <a href="https://catastrophe.wiredwolf.com/active-directory-auditing/">Active Directory Auditing</a> appeared first on <a href="https://catastrophe.wiredwolf.com">Wiredwolf Canada</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>PowerShell &#8211; Active Directory &#8211; New Email Domain</title>
		<link>https://catastrophe.wiredwolf.com/powershell-active-directory-new-email-domain/</link>
		
		<dc:creator><![CDATA[admin]]></dc:creator>
		<pubDate>Mon, 15 Aug 2022 17:11:27 +0000</pubDate>
				<category><![CDATA[Microsoft Office 365]]></category>
		<category><![CDATA[Microsoft Server]]></category>
		<category><![CDATA[PowerShell]]></category>
		<guid isPermaLink="false">https://catastrophe.wiredwolf.com/?p=21776</guid>

					<description><![CDATA[<p>Environment: Active Directory with Azure Active Directory Connect and Exchange Online I had a job recently where I needed to change everyone's email address to a new domain but I couldn't accomplish this from Exchange Online as all the users are synchronized with Active Directory on-premises. I wrote a quick script that addresses this: &lt;#  [...]</p>
<p>The post <a href="https://catastrophe.wiredwolf.com/powershell-active-directory-new-email-domain/">PowerShell &#8211; Active Directory &#8211; New Email Domain</a> appeared first on <a href="https://catastrophe.wiredwolf.com">Wiredwolf Canada</a>.</p>
]]></description>
										<content:encoded><![CDATA[<div class="fusion-fullwidth fullwidth-box fusion-builder-row-1 fusion-flex-container nonhundred-percent-fullwidth non-hundred-percent-height-scrolling" style="--awb-border-radius-top-left:0px;--awb-border-radius-top-right:0px;--awb-border-radius-bottom-right:0px;--awb-border-radius-bottom-left:0px;--awb-flex-wrap:wrap;" ><div class="fusion-builder-row fusion-row fusion-flex-align-items-flex-start fusion-flex-content-wrap" style="max-width:1144px;margin-left: calc(-4% / 2 );margin-right: calc(-4% / 2 );"><div class="fusion-layout-column fusion_builder_column fusion-builder-column-0 fusion_builder_column_1_1 1_1 fusion-flex-column" style="--awb-bg-size:cover;--awb-width-large:100%;--awb-margin-top-large:0px;--awb-spacing-right-large:1.92%;--awb-margin-bottom-large:0px;--awb-spacing-left-large:1.92%;--awb-width-medium:100%;--awb-spacing-right-medium:1.92%;--awb-spacing-left-medium:1.92%;--awb-width-small:100%;--awb-spacing-right-small:1.92%;--awb-spacing-left-small:1.92%;"><div class="fusion-column-wrapper fusion-flex-justify-content-flex-start fusion-content-layout-column"><div class="fusion-text fusion-text-1"><p>Environment: Active Directory with Azure Active Directory Connect and Exchange Online</p>
<p>I had a job recently where I needed to change everyone's email address to a new domain but I couldn't accomplish this from Exchange Online as all the users are synchronized with Active Directory on-premises. </p>
<p>I wrote a quick script that addresses this:</p>
<blockquote>
<p><#<br />
PowerShell<br />
Objectives<br />
– query AD with all users with a 'mail' attribute<br />
– update the 'mail' attribute to the new domain<br />
– set the proxyaddress SMTP matching the mail attribute to lowercase smtp<br />
– set the proxyaddress SMTP to the new domain</p>
<p>Author: Jason Zondag<br />
Date: 2202.08.11<br />
#><br />
# Set Variables<br />
$newMailDomain = "newdomain.com"<br />
$oldMailDomain = "olddomain.com"</p>
<p># Make sure we have AD access<br />
Import-Module ActiveDirectory</p>
<p># Set the SearchBase<br />
$SearchBase = "DC=domain,DC=local"</p>
<p># Get the required data and loop<br />
$Users = Get-ADUser -Filter "mail -like '*'" -SearchBase "$SearchBase" -ResultSetSize $null -properties mail,proxyaddresses,samaccountname,givenname,sn<br />
ForEach ($User in $Users) </p>
</blockquote>
<p>Note – you must run this script with elevated privileges. </p>
<p>I would also recommend that you add the command to stop AAD Sync during the course of running the script.</p>
</div></div></div></div></div>
<p>The post <a href="https://catastrophe.wiredwolf.com/powershell-active-directory-new-email-domain/">PowerShell &#8211; Active Directory &#8211; New Email Domain</a> appeared first on <a href="https://catastrophe.wiredwolf.com">Wiredwolf Canada</a>.</p>
]]></content:encoded>
					
		
		
			</item>
	</channel>
</rss>
