Resource/Policy.ps1

class PolicyBase {
    [string] $Name
    [string] $Description
    [Notification] $Notification
    [string[]] $CustomizeFeatures
    PolicyBase([string] $Name)
    {
       $this.Name=$Name
    }
}

class TeamsChatPolicy:PolicyBase {
    [bool] $EnableRecreateAllChats
    [string] $RecreateChatsOnOrAfter
    [bool] $ArchiveAllChatsToHTMLFiles
    [bool] $CreateTeamsChatToShowHTMLFiles
    [bool] $EnableSyncPinUnpinStatus
    [bool] $EnableMigrateExternalUsers
    [bool] $EnableMigrateChatTabs
    [string] $TimeZoneForMessages
    [bool] $EnableMigrateChatFileAttachments
    [ConflictResolution] $ChatFileAttachmentsConflictResolution
    [ManageLabelOption] $ChatFilesSensitivityLabels
    [string] $ChatFileLabelMappingName
    [bool] $EnableStopIfChatUserNoOneDrive
    [string] $FinalDomain
    [string] $URLMappingName
    [UserMappingRule[]] $UserMappingRules
    [string] $UserMappingName

    TeamsChatPolicy([string] $Name) :base($Name)
    {
       $this.Name=$Name
       $this.RecreateChatsOnOrAfter = (Get-Date).AddMonths(-2).ToString("MM/dd/yyyy")
       $this.ArchiveAllChatsToHTMLFiles=$true
       $this.CreateTeamsChatToShowHTMLFiles=$true
       $this.EnableMigrateChatTabs=$true
       $this.TimeZoneForMessages="UTC"
       $this.ChatFileAttachmentsConflictResolution=[ConflictResolution]::OverwriteByLastModifiedTime
       $this.EnableStopIfChatUserNoOneDrive=$true
       Write-Host "Confirm if the destination tenant's final domain is required to replace file links in chat messages"
       $continueChat=$true
       while($continueChat)
       {
          $answer =Read-Host "Required(Y) or Not Required(N)"
            if($answer -eq "Y")
            {
               $continueRequire=$true
               while ( $continueRequire) 
               {
                 $choose =Read-Host "Final Domain (F) or URL Mapping (U)"
                   if( $choose-eq "F")
                   {
                    $this.FinalDomain= GetInput "Enter a destination final domain"
                    $continueRequire=$false
                   }
                   elseif ($choose-eq "U")
                   {
                     $this.URLMappingName= GetInput "Enter a URL mapping Name"
                     $continueRequire=$false
                   }
               }
               $continueChat=$false
            }
            elseif ($answer -eq "N")
            {
               $continueChat=$false
            }
        }
       $result=ForceGetUserMappingOrRule
       $this.UserMappingName=$result.UserMappingName
       $this.UserMappingRules=$result.UserMappingRules
       Write-Host "The default time zone for the message is (UTC) Coordinated Universal Time. If you want to change the time zone, define it in the TimeZoneForMessage property." -ForegroundColor Yellow
    }
}

class M365GroupPolicy:PolicyBase {
    [bool] $EnablePlanner
    [bool] $EnableMSEntraRoles
    [bool] $EnableMembership
    [bool] $EnableMembersGuestUser
    [bool] $EnableMigrateAllDataInSPTeamSite
    [bool] $EnableMigrateDefaultDocumentLibraryOnly
    [bool] $EnableMigrateGroupMailbox
    [bool] $EnableSimplePolicy
    [string] $SharePointPolicyName
    [string] $ExchangePolicyName
    [ConflictResolution] $SPTeamSiteConflictResolution
    [ConflictResolution] $SPTeamSiteFilesConflictResolution
    [ConflictResolution] $ItemConflictResolution
    [bool] $EnableReplaceEmailAddresses
    [bool] $EnableReplaceMeetingLink
    [bool] $EnableSyncPrivacySettings
    [ManageLabelOption] $FilesSensitivityLabels
    [string] $FilesLabelMappingName
    [ManageLabelOption] $GroupsSensitivityLabels
    [string] $GroupsLabelMappingName
    [bool] $RemoveSourceIRMFromFiles
    [UserMappingRule[]] $UserMappingRules
    [string] $UserMappingName

    M365GroupPolicy([string] $Name):base($Name)
    {
       $this.Name=$Name
       $this.EnablePlanner=$true
       $this.EnableMembership=$true
       $this.EnableMembersGuestUser=$true
       $this.EnableMigrateAllDataInSPTeamSite=$true
       $this.EnableMigrateGroupMailbox=$true
       $this.EnableSimplePolicy=$true
       $this.SPTeamSiteConflictResolution=[ConflictResolution]::Merge
       $this.SPTeamSiteFilesConflictResolution=[ConflictResolution]::OverwriteByLastModifiedTime
       $this.ItemConflictResolution=[ConflictResolution]::Overwrite
       $this.GroupsSensitivityLabels=[ManageLabelOption]::RemoveOnly
       $this.RemoveSourceIRMFromFiles=$true
       $result=ForceGetUserMappingOrRule
       $this.UserMappingName=$result.UserMappingName
       $this.UserMappingRules=$result.UserMappingRules
    }
}

class OneDrivePolicy:PolicyBase {
    [bool] $EnableDefaultDocumentsLibrary
    [bool] $EnableAllInOneDrive
    [bool] $EnableMeetingRecordings
    [bool] $EnableDirectPermissions
    [bool] $EnableSharedLinkPermissions
    [bool] $EnableDataShareWithYouPermissions
    [string] $FilterPolicyName
    [ConflictResolution] $ContainerConflictResolution
    [ConflictResolution] $ContentConflictResolution
    [ManageLabelOption] $FilesSensitivityLabels
    [string] $FilesLabelMappingName
    [bool] $RemoveSourceIRMFromFiles
    [string] $DefaultDestinationUser
    [bool] $EnableSynchronizeDeletion
    [UserMappingRule[]] $UserMappingRules
    [string] $UserMappingName
    [string] $ContentTypeMappingName

    OneDrivePolicy([string] $Name):base($Name)
    {
       $this.Name=$Name
       $this.EnableDefaultDocumentsLibrary=$true
       $this.EnableDirectPermissions=$true
       $this.EnableSharedLinkPermissions=$true
       $this.ContainerConflictResolution=[ConflictResolution]::Merge
       $this.ContentConflictResolution=[ConflictResolution]::OverwriteByLastModifiedTime
       $this.RemoveSourceIRMFromFiles=$true
       $result=ForceGetUserMappingOrRule
       $this.UserMappingName=$result.UserMappingName
       $this.UserMappingRules=$result.UserMappingRules
    }
}

class TeamsPolicy:PolicyBase {
    [bool] $EnableApps
    [bool] $EnableChannelConversations
    [bool] $EnableTabs
    [bool] $EnableTags
    [bool] $EnablePlanner
    [bool] $EnableShifts
    [bool] $EnableMembership
    [bool] $EnableMembersGuestUser
    [bool] $EnableMembersExternalUser
    [bool] $EnableMeetingRecordings
    [bool] $EnableRecreateMessageAndHierarchy
    [string] $RecreateOnOrAfter
    [bool] $EnableArchiveMessageToAnHTMLFile
    [string] $TimeZoneForMessages
    [bool] $EnableMigrateAllDataInSPTeamSite
    [bool] $EnableMigrateDefaultDocumentLibraryOnly
    [bool] $EnableMigrateGroupMailbox
    [bool] $EnableSimplePolicy
    [string] $SharePointPolicyName
    [string] $ExchangePolicyName
    [ConflictResolution] $SPTeamSiteConflictResolution
    [ConflictResolution] $SPTeamSiteFilesConflictResolution
    [ConflictResolution] $ItemConflictResolution
    [bool] $EnableReplaceEmailAddresses
    [bool] $EnableReplaceMeetingLink
    [bool] $EnableSyncPrivacySettings
    [ManageLabelOption] $FilesSensitivityLabels
    [string] $FilesLabelMappingName
    [ManageLabelOption] $TeamsSensitivityLabels
    [string] $TeamsLabelMappingName
    [bool] $RemoveSourceIRMFromFiles
    [UserMappingRule[]] $UserMappingRules
    [string] $UserMappingName
    [string] $URLMappingName

    TeamsPolicy([string] $Name):base($Name)
    {
       $this.Name=$Name
       $this.EnableApps=$true
       $this.EnableChannelConversations=$true
       $this.EnableTabs=$true
       $this.EnableTags=$true
       $this.EnablePlanner=$true
       $this.EnableShifts=$true
       $this.EnableRecreateMessageAndHierarchy=$true
       $this.EnableArchiveMessageToAnHTMLFile=$true
       $this.TimeZoneForMessages="UTC"
       $this.EnableMigrateAllDataInSPTeamSite=$true
       $this.EnableMigrateGroupMailbox=$true
       $this.EnableSimplePolicy=$true
       $this.SPTeamSiteConflictResolution=[ConflictResolution]::Merge
       $this.SPTeamSiteFilesConflictResolution=[ConflictResolution]::OverwriteByLastModifiedTime
       $this.ItemConflictResolution=[ConflictResolution]::Overwrite
       $this.TeamsSensitivityLabels=[ManageLabelOption]::RemoveOnly
       $this.RemoveSourceIRMFromFiles=$true
       $result=ForceGetUserMappingOrRule
       $this.UserMappingName=$result.UserMappingName
       $this.UserMappingRules=$result.UserMappingRules
       Write-Host "The default time zone for the message is (UTC) Coordinated Universal Time. If you want to change the time zone, define it in the TimeZoneForMessage property." -ForegroundColor Yellow
    }
}

class SharePointPolicy:PolicyBase {
    [bool] $EnableSubsites
    [bool] $EnableSiteCollectionAndFeatures
    [bool] $EnableSiteAndListContentTypes
    [bool] $EnableSiteCollectionAndSettings
    [bool] $EnableNavigationBrandingTheme
    [bool] $EnableSiteAndListColumns
    [bool] $EnableManageMetadataService
    [bool] $EnableApps
    [bool] $EnableListsAndLibraries
    [bool] $EnableListSettings
    [bool] $EnablePublicViews
    [bool] $EnablePowerApps
    [bool] $EnableFoldersFilesItemsAttachments
    [bool] $EnableModernWebPartsAndPages
    [bool] $EnableClassicWebPartsAndPages
    [bool] $EnableSitePermissions
    [bool] $EnableListPermissions
    [bool] $EnableContentPermissions
    [bool] $EnableSharedLinkPermissions
    [bool] $EnableDataShareWithYouInOneDrivePermissions
    [bool] $EnableMapSPGroupsToM365Group
    [bool] $EnableWorkFlowDefinitions
    [bool] $EnableUserAlertsAndRules
    [string] $FilterPolicyName
    [ConflictResolution] $ContainerConflictResolution
    [ConflictResolution] $ContentConflictResolution
    [ManageLabelOption] $FilesSensitivityLabels
    [string] $FilesLabelMappingName
    [ManageLabelOption] $SitesAndLibrariesSensitivityLabels
    [string] $SitesAndLibrariesFilesLabelMappingName
    [bool] $RemoveSourceIRMFromFiles
    [string] $DefaultDestinationUser
    [bool] $EnableSynchronizeDeletion
    [UserMappingRule[]] $UserMappingRules
    [string] $UserMappingName
    [string] $ColumnMappingName
    [string] $ContentTypeMappingName
    [string] $TemplateMappingName
    [string] $URLMappingName

    SharePointPolicy([string] $Name):base($Name)
    {
       $this.Name=$Name
       $this.EnableSubsites=$true
       $this.EnableSiteCollectionAndFeatures=$true
       $this.EnableSiteAndListContentTypes=$true
       $this.EnableSiteCollectionAndSettings=$true
       $this.EnableNavigationBrandingTheme=$true
       $this.EnableSiteAndListColumns=$true
       $this.EnableManageMetadataService=$true
       $this.EnableApps=$true
       $this.EnableListsAndLibraries=$true
       $this.EnableListSettings=$true
       $this.EnablePublicViews=$true
       $this.EnableSitePermissions=$true
       $this.EnableListPermissions=$true
       $this.EnableContentPermissions=$true
       $this.EnableSharedLinkPermissions=$true
       $this.EnableWorkFlowDefinitions=$true
       $this.ContainerConflictResolution=[ConflictResolution]::Merge
       $this.ContentConflictResolution=[ConflictResolution]::OverwriteByLastModifiedTime
       $this.RemoveSourceIRMFromFiles=$true
       $this.SitesAndLibrariesSensitivityLabels=[ManageLabelOption]::RemoveOnly
       $result=ForceGetUserMappingOrRule
       $this.UserMappingName=$result.UserMappingName
       $this.UserMappingRules=$result.UserMappingRules
    }
}

class ExchangePolicy:PolicyBase {
    [bool] $EnableMigrateContacts
    [bool] $EnableMigrateMails
    [bool] $EnableMigrateNotes
    [bool] $EnableMigrateRules
    [bool] $EnableMigrateCalendars
    [bool] $EnableMigrateJournals
    [bool] $EnableMigrateTasks
    [bool] $EnableMigrateAutoCompleteList
    [bool] $EnableMigrateAutomaticReplies
    [bool] $EnableMigratePermissions
    [bool] $EnableMigrateRecoverableItemsFolders
    [bool] $EnableMigrateArchiveMailbox
    [bool] $EnableAutoProvisionArchiveMailboxInDestination
    [FolderCondition] $FolderCondition
    [string] $FolderConditionValue
    [bool] $EnableMigrateSpecifiedFoldersAlongWithAll
    [string] $EmailTimeRangeOnAfter
    [string] $EmailTimeRangeOnBefore
    [ConflictResolution] $ItemConflictResolution
    [bool] $EnableSynchronizeDeletion
    [bool] $EnableReplaceEmailAddresses
    [bool] $EnableMigrateMailsBeforeSpecifiedDateToArchive
    [string] $SpecifiedDateToArchive
    [bool] $EnableReplaceMeetingLink
    [string] $MigrateSourceDataToSpecifiedDestinationFolder
    [ManageLabelOption] $EmailSensitivityLabels
    [string] $LabelMappingName
    [string] $UserMappingName

    ExchangePolicy([string] $Name):base($Name)
    {
       $this.Name=$Name
       $this.EnableMigrateContacts=$true
       $this.EnableMigrateMails=$true
       $this.EnableMigrateNotes=$true
       $this.EnableMigrateRules=$true
       $this.EnableMigrateCalendars=$true
       $this.EnableMigrateJournals=$true
       $this.EnableMigrateTasks=$true
       $this.EnableMigrateAutoCompleteList=$true
       $this.EnableMigrateAutomaticReplies=$true
       $this.ItemConflictResolution=[ConflictResolution]::Overwrite
    }
}





class Notification {
    [bool] $SendProjectMigration
    [int] $FrequencyDays
    [datetime] $StartTime 
    [bool] $IsEnableEndTime 
    [datetime] $EndTime 
    [string[]] $ProjectLevelEmailRecipients 
    [string] $ProjectLevelEmailTemplateName 
    [bool] $SendIndividualMapping 
    [IndividualMappingLevel] $IndividualMappingLevel
    [bool] $SendToSourceEmail 
    [bool] $SendToDestinationEmail 
    [bool] $SendToPersonRunMigration
    [bool] $SendToAdditionalRecipients 
    [string[]]  $AdditionalRecipients 
    [string] $IndividualLevelEmailTemplateName
    Notification() {
      
   }
}

enum  IndividualMappingLevel {
   # enum value: "0"
    ForAllMappings
   # enum value: "1"
    ForFailedMappingsOnly
}

enum ConflictResolution {
    Skip
    Overwrite
    Merge
    OverwriteByLastModifiedTime
    Append
}

enum ManageLabelOption{

    KeepExisting
    RemoveOnly
    ApplySame
    ConfigureCustom
}

enum  FolderCondition {
    # enum value: "0"
    All
    # enum value: "1"
    Include
    # enum value: "2"
    Exclude
 }

 enum  UserMappingRule {
      UPN
      DisplayName
      Email
 }

 function GetInput {
    param (
        [string] $message
    )
    $result=""
    while ($true)
    {
       $result=Read-Host $message 
       if(![string]::IsNullOrWhiteSpace($result))
       {
          break;
       }      
    }
    return $result
 }

 function GetUserMappingRule {
    param (
        [string] $message,[UserMappingRule[]] $existRules, [bool] $canOver
    )
    $result=""
    while ($true)
    {
       $result=Read-Host $message
       if($result -eq "S" -and $canOver)
       {
          break;
       }
       if($result -eq "U" -or $result -eq "D" -or $result -eq "M" )
       {
          $resultRule=ConvertMappingRule $result
          if( $existRules -notcontains $resultRule)
          {
            break;
          }
       }      
    }
    return $result
 }

 function ConvertMappingRule {
    param (
        [string] $rule
    )
    if($rule -eq "U")
    {
       return [UserMappingRule]::UPN
    }
    elseif ($rule -eq "D") {
       return [UserMappingRule]::DisplayName
    }
     elseif ($rule -eq "M") {
       return [UserMappingRule]::Email
    }
    else {
        return ""
    }
 }

  function GetRemainMappingRule {
    param (
        [UserMappingRule[]] $rules
    )
    $result=@()
    if($rules -notcontains [UserMappingRule]::UPN )
    {
       $result+="U"
    }
    if($rules -notcontains [UserMappingRule]::DisplayName)
    {
       $result+="D"
    }
    if($rules -notcontains [UserMappingRule]::Email)
    {
       $result+="M"
    }
    return $result
 }
 
  function ForceGetUserMappingOrRule {
    [string] $userMappingName=""
    [UserMappingRule[]] $userMappingRules=@()
       $continue=$true
       while($continue)
       {
          $answer =Read-Host "Specify the method for mapping user and group identities: UserMappingName (U), or UserMappingRule (User Principal name prefix, Display name, Email address prefix) (R) or Both (B)"
          if($answer -eq "U" -or $answer -eq "B")
          {
               $userMappingName = GetInput "Enter a UserMappingName"
               $continue=$false
          }
          if ($answer -eq "R" -or $answer -eq "B")
          {
             
               $continue1=$true
               while ( $continue1) 
               {
                  Write-Host "Add at least one UserMappingRule: User principal name prefix (U), Display name (D), Email address prefix (M)."
                  $choice1 = GetUserMappingRule "Enter the 1st priority rule: U, D or M" $userMappingRules $false 
                  $rule1=ConvertMappingRule $choice1
                  $userMappingRules+=$rule1
                  $remainRules= GetRemainMappingRule $userMappingRules
                  $choice2 =  GetUserMappingRule  ("Enter the 2nd priority rule: "+$remainRules[0]+" or " +$remainRules[1]+ ". If the 2nd priority is not required, enter S to skip") $userMappingRules $true
                  if($choice2-eq "S")
                  {
                    $continue1=$false
                    break;
                  }
                  $rule2=ConvertMappingRule $choice2
                  $userMappingRules+=$rule2
                  $remainRules= GetRemainMappingRule $userMappingRules
                  $choice3 = GetUserMappingRule  ("Enter the 3rd priority rule: "+$remainRules[0]+". If 3rd priority is not required, please enter S to skip") $userMappingRules $true
                  if($choice3-eq "S")
                  {
                    $continue1=$false
                    break;
                  }
                  $rule3=ConvertMappingRule $choice3
                  $userMappingRules+=$rule3
                  $continue1=$false
               }
               $continue=$false
          }
        }
        $result=@{
         UserMappingName=$userMappingName
         UserMappingRules=$userMappingRules
        }
        return $result
 }