DeployGPO.ps1 (196 lines of code) (raw):

# Copyright (c) Microsoft Corporation. # Licensed under the MIT License. #requires -module ActiveDirectory <# .DESCRIPTION This script needs to be executed in a Domain Controller and makes the following actions: - Deploys the Azure Arc Servers Onboarding GPO in the local domain as '[MSFT] Azure Arc Servers Onboarding<Timestamp>' - Copies the EnableAzureArc.ps1 onboarding script to the network Share .PARAMETER DomainFQDN FQDN of the Domain to Deploy e.g. contoso.com .PARAMETER ReportServerFQDN FQDN of the Server that will act as report Server (and source files) .PARAMETER ServicePrincipalSecret Service Principal's secret Use this link to create a new one: https://docs.microsoft.com/en-us/azure/azure-arc/servers/onboard-service-principal#create-a-service-principal-for-onboarding-at-scale .PARAMETER ArcRemoteShare Remote share that holds deployment source files and reporting files .PARAMETER AgentProxy proxy address used by the Agent .EXAMPLE This example deploys the GPO to the contoso.com domain and copies the onboarding script EnableAzureArc.ps1 to the remote share AzureArcOnBoard in the Server.contoso.com server .\DeployGPO.ps1 -DomainFQDN contoso.com -ReportServerFQDN Server.contoso.com -ArcRemoteShare AzureArcOnBoard -ServicePrincipalSecret $ServicePrincipalSecret -ServicePrincipalClientId $ServicePrincipalClientId -SubscriptionId $SubscriptionId --ResourceGroup $ResourceGroup -Location $Location -TenantId $TenantId [-AgentProxy $AgentProxy] #> Param ( [Parameter(Mandatory = $True)] [System.String]$DomainFQDN, [Parameter(Mandatory = $True)] [System.String]$ReportServerFQDN, # "server.contoso.com" [Parameter(Mandatory = $True)] [System.String]$ServicePrincipalClientId, [Parameter(Mandatory = $True)] [System.String]$ServicePrincipalSecret, [Parameter(Mandatory = $True)] [System.String]$SubscriptionId, [Parameter(Mandatory = $True)] [System.String]$ResourceGroup, [Parameter(Mandatory = $True)] [System.String]$Location, [Parameter(Mandatory = $True)] [System.String]$TenantId, [Parameter(Mandatory = $True)] [System.String]$ArcRemoteShare, [System.String]$AgentProxy, [Hashtable]$Tags, [System.String]$PrivateLinkScopeId, [switch]$AssessOnly ) $ErrorActionPreference = "Stop" $GPOName = "[MSFT] Azure Arc Servers Onboarding" #Create the remote folders AzureArcDeploy & AzureArcLogging $FolderRemotepath = "\\$ReportServerFQDN\$ArcRemoteShare" if (-not (Test-Path $FolderRemotepath -ErrorAction SilentlyContinue)) { throw "The Path $FolderRemotepath does't exist, please creat it before running this script!!" } else { Write-Host "Remote path $FolderRemotepath found!" -ForegroundColor Green } Write-Host "Creating remote folder's structure..." -ForegroundColor Green try { New-Item -ItemType Directory -Path "$FolderRemotepath\AzureArcDeploy" -Force -ErrorAction Stop | Out-Null New-Item -ItemType Directory -Path "$FolderRemotepath\AzureArcLogging" -Force -ErrorAction Stop | Out-Null } catch { Write-Host "Could not create remote folders in path $FolderRemotepath`n$(($_.Exception).Message)" -ForegroundColor Red ; exit } $AzureArcDeployPath = "$FolderRemotepath\AzureArcDeploy" $AzureArcLoggingPath = "$FolderRemotepath\AzureArcLogging" #region assign appropiate permissions to the folders Write-Host "Assigning appropriate permissions..." -ForegroundColor Green #Remove inheritance $Acl = Get-ACL -Path $AzureArcDeployPath $Acl.SetAccessRuleProtection($True, $True) Set-Acl -Path $AzureArcDeployPath -AclObject $Acl $Acl = Get-ACL -Path $AzureArcLoggingPath $Acl.SetAccessRuleProtection($True, $True) Set-Acl -Path $AzureArcLoggingPath -AclObject $Acl #Add Access to Domain Computers and Domain Controllers $DomainNetbios = (Get-ADDomain $DomainFQDN).NetBIOSName $DomainComputersSID = (Get-ADDomain).DomainSID.Value + '-515' $DomainComputersName = (Get-ADGroup -Filter "SID -eq `'$DomainComputersSID`'").Name $DomainControllersSID = (Get-ADDomain).DomainSID.Value + '-516' $DomainControllersName = (Get-ADGroup -Filter "SID -eq `'$DomainControllersSID`'").Name $identity = "$DomainNetbios\$DomainComputersName" $identity2 = "$DomainNetbios\$DomainControllersName" #Deploy Path $NewAcl = Get-ACL -Path $AzureArcDeployPath $fileSystemAccessRules = @( [System.Security.AccessControl.FileSystemAccessRule]::new($identity, 'ReadandExecute', "ContainerInherit,ObjectInherit", 'None', 'Allow') [System.Security.AccessControl.FileSystemAccessRule]::new($identity2, 'ReadandExecute', "ContainerInherit,ObjectInherit", 'None', 'Allow') ) foreach ($fileSystemAccessRule in $fileSystemAccessRules) { $NewAcl.SetAccessRule($fileSystemAccessRule) Set-Acl -Path $AzureArcDeployPath -AclObject $NewAcl } #Logging Path $NewAcl = Get-ACL -Path $AzureArcLoggingPath $fileSystemAccessRules = @( [System.Security.AccessControl.FileSystemAccessRule]::new($identity, 'ReadandExecute,Write,Modify', "ContainerInherit,ObjectInherit", 'None', 'Allow') [System.Security.AccessControl.FileSystemAccessRule]::new($identity2, 'ReadandExecute,Write,Modify', "ContainerInherit,ObjectInherit", 'None', 'Allow') ) foreach ($fileSystemAccessRule in $fileSystemAccessRules) { $NewAcl.SetAccessRule($fileSystemAccessRule) Set-Acl -Path $AzureArcLoggingPath -AclObject $NewAcl } #endregion #region Replacing Custom Data in the scheduled task #Replacing the data in the scheduled task $BackupPath = "$PSScriptRoot\ArcGPO" $Backupid = ((Get-ChildItem -Path $BackupPath | Sort-Object -Property LastWriteTime -Descending) | Select-Object -First 1 -ExpandProperty Name) -replace "{" -replace "}" $ScheduledTaskfile = "$BackupPath\{$Backupid}\DomainSysvol\GPO\Machine\Preferences\ScheduledTasks\ScheduledTasks.xml" Write-Host "`nReplacing the data the scheduled task..." -ForegroundColor Green try { $xmlcontent = Get-Content -Path $ScheduledTaskfile -ErrorAction Stop $xmlcontent -replace "{ReportServerFQDN}", $ReportServerFQDN | Out-File $ScheduledTaskfile -Encoding utf8 -Force -ErrorAction Stop Write-Host "Report Server FQDN $ReportServerFQDN was successfully set in the scheduled task" -ForegroundColor Green $xmlcontent = Get-Content -Path $ScheduledTaskfile -ErrorAction Stop $xmlcontent -replace "{ArcRemoteShare}", "$ArcRemoteShare" | Out-File $ScheduledTaskfile -Encoding utf8 -Force -ErrorAction Stop Write-Host "Arc Remote share $ArcRemoteShare was successfully set in the scheduled task..." -ForegroundColor Green if ($PSBoundParameters.ContainsKey('AssessOnly')) { $xmlcontent = Get-Content -Path $ScheduledTaskfile -ErrorAction Stop $xmlcontent -replace "ArcRemoteShare $ArcRemoteShare", "ArcRemoteShare $ArcRemoteShare -AssessOnly" | Out-File $ScheduledTaskfile -Encoding utf8 -Force -ErrorAction Stop Write-Host "AssessMode was successfully set in the scheduled task..." -ForegroundColor Green } } catch { Write-Host "Could not modify Scheduled task:`n$(($_.Exception).Message)" -ForegroundColor Red ; exit } Write-Host "`nAdding ReportServerFQDN $ReportServerFQDN to the scheduled task ..." -ForegroundColor Green try { $xmlcontent = Get-Content -Path $ScheduledTaskfile -ErrorAction Stop ($xmlcontent -replace "EnableAzureArc.ps1 -ArcRemoteShare", "EnableAzureArc.ps1 -ReportServerFQDN $ReportServerFQDN -ArcRemoteShare") | Out-File $ScheduledTaskfile -Encoding utf8 -Force -ErrorAction Stop Write-Host "ReportServerFQDN was successfully added to the scheduled task" -ForegroundColor Green } catch { Write-Host "Could not add ReportServerFQDN:`n$(($_.Exception).Message)" -ForegroundColor Red ; exit } #endregion #Creating the new GPO Write-Host "`nCreating the new GPO..." -ForegroundColor Green try { $GPONamewithTimestamp = "$GPOName$(Get-Date -Format yyyyMMddhhmmss)" New-GPO -Name $GPONamewithTimestamp -ErrorAction Stop Write-Host "GPO `'$GPONamewithTimestamp`' was successfully created in Domain $DomainFQDN" -ForegroundColor Green } catch { Write-Host "The Group Policy could not be created:`n$(($_.Exception).Message)" -ForegroundColor Red ; break } # Encrypting the ServicePrincipalSecret to be decrypted only by the Domain Controllers and the Domain Computers security groups $DomainComputersSID = "SID=" + $DomainComputersSID $DomainControllersSID = "SID=" + $DomainControllersSID $descriptor = @($DomainComputersSID, $DomainControllersSID) -join " OR " Import-Module $PSScriptRoot\AzureArcDeployment.psm1 $encryptedSecret = [DpapiNgUtil]::ProtectBase64($descriptor, $ServicePrincipalSecret) #Copying Script to Source files Subfolder path Write-Host "`nCopying Script EnableAzureArc.ps1 to path $AzureArcDeployPath ..." -ForegroundColor Green try { if (Test-Path "$AzureArcDeployPath\EnableAzureArc.ps1" -ErrorAction SilentlyContinue) { Write-Host "File `'$AzureArcDeployPath\EnableAzureArc.ps1`' already exists." -ForegroundColor Red; throw } else { Copy-Item -Path "$PSScriptRoot\EnableAzureArc.ps1" -Destination $AzureArcDeployPath -ErrorAction Stop Write-Host "Onboarding script `'EnableAzureArc.ps1`' successfully copied to $AzureArcDeployPath" -ForegroundColor Green } if (Test-Path "$AzureArcDeployPath\AzureArcDeployment.psm1" -ErrorAction SilentlyContinue) { Write-Host "File `'$AzureArcDeployPath\AzureArcDeployment.psm1`' already exists." -ForegroundColor Red; throw } else { Copy-Item -Path "$PSScriptRoot\AzureArcDeployment.psm1" -Destination $AzureArcDeployPath -ErrorAction Stop Write-Host "Onboarding script `'AzureArcDeployment.psm1`' successfully copied to $AzureArcDeployPath" -ForegroundColor Green } if (Test-Path "$AzureArcDeployPath\AzureConnectedMachineAgent.msi" -ErrorAction SilentlyContinue) { Write-Host "File `'$AzureArcDeployPath\AzureConnectedMachineAgent.msi`' already exists." -ForegroundColor Red; throw } else { Copy-Item -Path "$FolderRemotepath\AzureConnectedMachineAgent.msi" -Destination $AzureArcDeployPath -ErrorAction Stop Write-Host "Install file `'AzureConnectedMachineAgent.msi`' successfully copied to $AzureArcDeployPath" -ForegroundColor Green } $infoTable = @{"ServicePrincipalClientId"="$ServicePrincipalClientId";"SubscriptionId"="$SubscriptionId";"ResourceGroup"="$ResourceGroup";"Location"="$Location";"TenantId"="$TenantId";"PrivateLinkScopeId"="$PrivateLinkScopeId"; "AgentProxy"="$AgentProxy"; "Tags"=$tags} $infoTableJSON = $infoTable | ConvertTo-Json -Compress if (Test-Path "$AzureArcDeployPath\ArcInfo.json" -ErrorAction SilentlyContinue) { Write-Host "File `'$AzureArcDeployPath\ArcInfo.json`' already exists." -ForegroundColor Red; throw } else { $infoTableJSON | Out-File -FilePath "$AzureArcDeployPath\ArcInfo.json" Write-Host "JSON file with onboarding info `'ArcInfo.json`' successfully copied to $AzureArcDeployPath" -ForegroundColor Green } $encryptedSecret | Out-File -FilePath (Join-Path -Path $AzureArcDeployPath -ChildPath "encryptedServicePrincipalSecret") -Force } catch { Write-Host "Onboarding script could not be copied:`n$(($_.Exception).Message)" -ForegroundColor Red ; break } #Import the setting from the backup Write-Host "`nImport the setting from the backup..." -ForegroundColor Green try { Import-GPO -Path $BackupPath -TargetName $GPONamewithTimestamp -BackupId $Backupid -ErrorAction Stop Write-Host "GPO Setting were successfully imported.`nOpen GPO Management Console and Check for '$GPONamewithTimestamp`' Group policy" -ForegroundColor Green gpmc.msc } catch { Write-Host "The Group Policy setting could not be imported:`n$(($_.Exception).Message)" -ForegroundColor Red ; break }