functions/Install-HydrationEpac.ps1
|
<#
.SYNOPSIS This function deploys Enterprise Policy as Code locally, and configures the repo to be uploaded to the platform of your choice. .DESCRIPTION The Install-HydrationEpac function deploys the Enterprise Policy as Code. It takes several optional parameters: DefinitionsRootFolder, StarterKit, AnswerFile, Output, StopPoint, UseUtc, Interactive, and SkipTests. .PARAMETER TenantIntermediateRoot The name of the Tenant Intermediate Root. This parameter is mandatory. .PARAMETER DefinitionsRootFolder The path to the Definitions directory. Defaults to "./Definitions". .PARAMETER StarterKit The path to the StarterKit directory. Defaults to "./StarterKit". .PARAMETER Output The path to the Output directory. Defaults to "./Output". .PARAMETER AnswerFile The path to the Answer file. This parameter is optional and does not have a default value. .PARAMETER UseUtc Switch to use UTC time. .PARAMETER Interactive Switch to enable interactive mode. .PARAMETER SkipTests Switch to skip preliminary tests. .EXAMPLE Install-HydrationEpac -TenantIntermediateRoot "/path/to/root" This example deploys the Enterprise Policy as Code using the specified Tenant Intermediate Root and default directories, which is appropriate if being run from the root of the new repo. .LINK https://aka.ms/epac https://github.com/Azure/enterprise-azure-policy-as-code/tree/main/Docs/start-hydration-kit.md #> function Install-HydrationEpac { [CmdletBinding()] [Alias("Build-EpacRepo", "ber")] param ( [Parameter(Mandatory = $true, HelpMessage = "The path to the Tenant Intermediate Root. This parameter is mandatory.")] [string] $TenantIntermediateRoot, [Parameter(Mandatory = $false, HelpMessage = "The path to the Definitions directory. Defaults to './Definitions'.")] [string] $DefinitionsRootFolder = "./Definitions", # [Parameter(Mandatory = $false, HelpMessage = "The path to the StarterKit directory. Defaults to './StarterKit'.")] # [string] # $StarterKit = "./StarterKit", [Parameter(Mandatory = $false, HelpMessage = "The path to the Output directory. Defaults to './Output'.")] [string] $Output = "./Output", [Parameter(Mandatory = $false, HelpMessage = "The path to the Answer file. This parameter is optional and does not have a default value.")] [string] $AnswerFile, [Parameter(Mandatory = $false, HelpMessage = "Switch to use UTC time.")] [switch] $UseUtc, [Parameter(Mandatory = $false, HelpMessage = "Switch to enable interactive mode.")] [switch] $Interactive, [Parameter(Mandatory = $false, HelpMessage = "Switch to skip preliminary tests.")] [switch] $SkipTests ) # Define UI Width for display automation if ($host.UI.RawUI.WindowSize.Width -lt 80 -or $host.UI.RawUI.WindowSize.Width -eq "" -or $null -eq $host.UI.RawUI.WindowSize.Width) { $TerminalWidth = 80 } else { $TerminalWidth = $host.UI.RawUI.WindowSize.Width } $stageBlocks = Get-HydrationMessageBlock -TerminalWidth $TerminalWidth $epacDevName = "epac-dev" Clear-Host $sleepTime = 10 # Define Verbosity $InformationPreference = "Continue" if ($TenantIntermediateRoot -eq $(Get-AzContext).Tenant.Id) { Write-Error "The Tenant Intermediate Root specified is the same as the current connected Tenant ID. This is not recommended as it can lead to a less secure outcome, and for this reason this script does not support the configuration. Please choose an intermediate root that is not your Tenant Root." } if(!(Test-Path $DefinitionsRootFolder)){ $null = New-Item -Path $DefinitionsRootFolder -ItemType Directory -Force } $DefinitionsRootFolder = (Resolve-Path $DefinitionsRootFolder).Path $repoRootPath = Split-Path $DefinitionsRootFolder Set-Location $repoRootPath if(!(Test-Path $DefinitionsRootFolder)){ $null = New-Item -Path $DefinitionsRootFolder -ItemType Directory -Force } if(!(Test-Path $Output)){ $null = New-Item -Path $Output -ItemType Directory -Force } $Output = (Resolve-Path $Output).Path $exportedDefinitionsPath = @{ root = Join-Path $Output "Export" definitions = Join-Path $Output "Export" "Definitions" policyAssignments = Join-Path $Output "Export" "Definitions" "policyAssignments" policyDefinitions = Join-Path $Output "Export" "Definitions" "policyDefinitions" policySetDefinitions = Join-Path $Output "Export" "Definitions" "policySetDefinitions" policyExemptions = Join-Path $Output "Export" "Definitions" "policyExemptions" exportPolicyToEpac = Join-Path $Output "Export" "policyAssignments" starterKit = Join-Path $repoRootPath "StarterKit" } # Move to repo root as EPAC generally prefers to be run from this location $logDirectory = Join-Path $Output "Logs" $logFilePath = Join-Path $logDirectory "Install-HydrationEpac.log" # $questionsFilePath = Join-Path $StarterKit "HydrationKit" "questions.jsonc" if(!(Test-Path $logDirectory)){ $null = New-Item -Path $logDirectory -ItemType Directory -Force } # Import, fail to import, or create the answer file if ($AnswerFile) { if (!(Test-Path $AnswerFile)) { Write-Error "Answer file not found at $AnswerFile, exiting..." return } $allInterviewAnswers = Get-Content $AnswerFile ` | ConvertFrom-Json -Depth 10 -AsHashtable $answerFilePath = Resolve-Path $AnswerFile } else { $answerFilePath = Join-Path $Output ` "HydrationAnswer" ` "AnswerFile.json" if (!(Test-Path $(Split-Path $answerFilePath))) { $null = New-Item -Path $(Split-Path $answerFilePath) -ItemType Directory -Force } else { if($Interactive){ Write-Warning "A file already exists at $answerFilePath, and will be overwritten unless you use Ctrl+C to exit..." Read-Host "Press Enter to continue..." } Write-HydrationLogFile -EntryType logEntryDataAsPresented ` -EntryData "Overwriting Answer File at $answerFilePath" ` -LogFilePath $logFilePath ` -UseUtc:$UseUtc } "" | Set-Content $answerFilePath -Force } # Crete an ordered hashtable to hold log entries until the directories are tested $endSummary = [PSCustomObject]@{ preliminaryTestResults = [ordered]@{} gatherData = [ordered]@{} generateAnswerFile = [ordered]@{} importAnswerFile = [ordered]@{} populateRepoDefinitions = [ordered]@{} createRepo = [ordered]@{} testRepo = [ordered]@{} testAndPlan = [ordered]@{} buildRepo = [ordered]@{} deployRepo = [ordered]@{} deployPipelines = [ordered]@{} deployPolicyAssignments = [ordered]@{} deployPolicyDefinitions = [ordered]@{} deployPolicyRoles = [ordered]@{} } $interviewQuestionSets = [ordered]@{ initial = $true optionalCreatePrimaryIntermediateRoot = $false optionalCreateMainCaf3Hierarchy = $false tenantList = $false # Future state pacSelector = $true updatesByPacSelector = $true policyDecisionsByPacSelector = $true pipeline = $true } # $roleMessages = @{ # "PassedEpacAllDeploy" = "- The script will be unable to assist with building the new management groups for EPAC to test with during development. However, RBAC Authorization tests passed, and you have the necessary permissions to deploy the EPAC solution to existing management groups. It is recommended that you ensure that you already have your epac development hierarchy in place prior to continuing so that these initial test deployments can be run." # "PassedEpacPolicyDeploy" = "- The script will be unable to deploy any roles to support policies that are deployed by EPAC. This can result in an unsupported state, so no deployments step will be offered." # "PassedEpacRoleDeploy" = "- The script will be unable to deploy any policies used by the roles that will be deployed by EPAC. This can result in an unsupported state, so no deployment step will be offered." # "PassedEpacPlan" = "- The script will be unable to deploy any aspect of the EPAC solution to Azure. Only a plan step will be offered." # "Failed" = "- The script will be unable to plan a deployment to support the EPAC solution. No plan step will be offered, and there will be no attempt to gather data from Azure programatically to simplify this process." # } $limitation = [ordered]@{ Write = @{ Status = $false Message = "The script will be unable to create the epac testing environment for testing as part of this process. Consider manually creating these management groups prior to continuing..." optionalCaf3Message = "This script will be unable to create the requisite management groups for the new caf3 management group hierarchy due to limitations of the account used for the current connection to Azure, these questions will be skipped. Consider creating the new Tenant Intermediate Root Group and restarting the process" optionalTirMessage = "This script will be unable to create the recommended management groups for the new caf3 management group hierarchy due to limitations of the account used for the current connection to Azure, these questions will be skipped. STRONGLY consider creating the new Tenant Intermediate Root Group and restarting the process." } Policy = @{ Status = $false Message = "The script will be unable to deploy policy, and will not continue beyond the plan phase." } Role = @{ Status = $false Message = "The script will be unable to deploy roles that are needed to support policy deployment, and will not continue beyond the plan phase." } Plan = @{ Status = $false Message = "The script is unable to read the environment to generate a plan, so deployment of Azure Policy will not be tested against the EPAC development environment specified." } Gather = @{ Status = $false Message = "The script will be unable to gather data from Azure programatically to simplify this process. No insights from Azure can be gained to assist in guiding you through this process... Stopping now and rerunning the tests after connecting to Azure with an account that has more comprehensive rights, at least Read, to Azure, is STRONGLY recommended." } Git = @{ Status = $false Message = "The script will be unable to programatically download the StarterKit from GitHub. Please ensure that you have an updated copy of StarterKit in the same directory as the Definitions folder, based on the choices made in this script." } } ##################### INSERT INITIAL BLOCK HERE # $stageBlocks = Get-HydrationMessageBlock # foreach ($key in $stageBlocks2.keys) { # $stageBlocks2.$key.TerminalWidth = $TerminalWidth # $stageBlocks.add($key, $stageBlocks2.$key) # } # Remove-Variable stageBlocks2 -ErrorAction SilentlyContinue # $stageBlocks = [ordered]@{ # } Clear-Host ################################################################################ ################################################################################ # Initiate UI: Header $summary = [ordered]@{} $uiStart = $stageBlocks.uiStart New-HydrationSeparatorBlock @uiStart Write-HydrationLogFile -EntryType newStage ` -EntryData $stageBlocks.uiStart.DisplayText ` -LogFilePath $logFilePath ` -UseUtc:$UseUtc ` -Silent # Initiate UI: Body # TODO Add a welcome message here that is more informative than the current placeholder. Write-Host "Welcome to the Enterprise Policy as Code (EPAC) Hydration Kit. This script is intended to help guide you through the EPAC Deployment process." -ForegroundColor Yellow Write-Host "Please report any issues to the EPAC team." -ForegroundColor Yellow # Initiate UI: Footer New-HydrationContinuePrompt -Interactive:$Interactive -SleepTime:$sleepTime Clear-Host ################################################################################ ################################################################################ # Preliminary Tests: Path Header $summary = [ordered]@{} $runPreliminaryTests = $stageBlocks.runPreliminaryTests New-HydrationSeparatorBlock @runPreliminaryTests Write-HydrationLogFile -EntryType newStage ` -EntryData $stageBlocks.runPreliminaryTests.DisplayText ` -LogFilePath $logFilePath ` -UseUtc:$UseUtc ` -Silent ################################################################################ # Preliminary Tests: Path Body Write-Host "Beginning path tests to help ensure that the script can run successfully..." -ForegroundColor Yellow # Test for and create directories if they do not exist, this allows realtime logging to file to begin if ($SkipTests) { Write-HydrationLogFile -EntryType logEntryDataAsPresented -EntryData "Skipping tests..." -LogFilePath $logFilePath -UseUtc:$UseUtc -ForegroundColor Magenta } else { # foreach ($path in $pathsToTest) { # $pathTest = Test-HydrationPath -LocalPath $path ` # -UseUtc:$UseUtc ` # -LogFilePath $logFilePath ` # -Silent # if ($pathTest -eq "Failed") { # Write-Error "The path $path could not be created. Please choose a location to work in that you have write access to and restart the process." # $summary.Add($path, "Failed") # } # else { # $summary.Add($path, "Passed") # } # } Write-HydrationLogFile -EntryType logEntryDataAsPresented -EntryData "Summary of $($stageblocks.runPreliminaryTests.DisplayText)" -LogFilePath $logFilePath -UseUtc:$UseUtc -Silent foreach ($entry in $summary.keys) { Write-HydrationLogFile -EntryType testResult -EntryData "$entry -- $($summary.$entry)" -UseUtc:$UseUtc -LogFilePath $logFilePath -Silent Start-Sleep -Seconds $sleepTime } # Begin Azure Network Connection Tests Write-Host "Beginning connectivity tests to help ensure that the script can run successfully..." -ForegroundColor Yellow $connectionTestUrlList = [ordered]@{ internetConnection = "13.107.42.14" # Used in Microsoft Automated Testing (per CoPilot) githubConnection = "www.github.com" azureManagement = (Get-AzContext).Environment.ResourceManagerUrl | Select-String -Pattern "https?://([^/]+)/?" | ForEach-Object { $_.Matches[0].Groups[1].Value } azureAuthentication = (Get-AzContext).Environment.ActiveDirectoryAuthority | Select-String -Pattern "https?://([^/]+)/?" | ForEach-Object { $_.Matches[0].Groups[1].Value } } foreach ($url in $connectionTestUrlList.Keys) { $connectionTest = Test-HydrationConnection -FullyQualifiedDomainName $connectionTestUrlList.$url -UseUtc:$UseUtc -LogFilePath $logFilePath -Silent if ($connectionTest -eq "Failed") { # Write-Error "The test for $url failed when tested with the value $($connectionTestUrlList.$url). Please ensure that you have an active internet connection and that the Azure services are available. This will not prevent continuing with the process, but it will prevent use of much of the accelerator, as well as gathering of data to provide guidance in this process, and is not recommended." $summary.Add($url, "Failed") } else { $summary.Add($url, "Passed") } Write-HydrationLogFile -EntryType testResult -EntryData "$url -- $($summary.$url)" -UseUtc:$UseUtc -LogFilePath $logFilePath -Silent } # Begin Git Installation Test Write-Host "Beginning git tests to help ensure that the script can run successfully..." -ForegroundColor Yellow try { if (($(git --help))) { $summary.add("gitInstall", "Passed") } } catch { $summary.add("gitInstall", "Failed") if(!((Get-ChildItem ./StarterKit/* -Recurse).count -gt 0)){ Write-Host "Download the repo at https://github.com/Azure/enterprise-azure-policy-as-code and extract it to the root directory to continue. The StarterKit is required for pipeline deployment..." -ForegroundColor Red Write-Error "Without either git to download the repo, or a Module and [repoRootDirectory]/StarterKit folder, this installer will not work as intended. Please choose one of the two supported paths forward, and run the installer again." return } } Write-Host "Reviewing Returns..." -ForegroundColor Yellow # Process failed tests for warning messages and script limitations calculations $failedTests = @{} foreach ($key in $summary.keys) { if ($summary.$key -like "Failed*") { $failedTests.Add($key, $summary.$key) } } # if ($failedTests.keys.count -gt 0 -or (!($($summary.RBACAuthorization -eq "PassedHydrationDeploy")))) { if ($failedTests.keys.count -gt 0) { Write-Host "The following items should be considered before continuing:" -ForegroundColor Red foreach ($key in $failedTests.keys) { switch ($key) { "githubConnection" { Write-Host " - The test for a connection to GitHub failed. You will need to ensure that the StarterKit folder has been downloaded to the same directory that your Definitions folder is now in, based on the choices made in this script.` `n - This is not a blocker if you have a fresh copy of the StarterKit in the same directory as the Definitions folder, and you already have access to the module. However, this script requires the presence of that folder in the repo to continue, either through automated or manual means.` `n - Download Location: https://github.com/Azure/enterprise-azure-policy-as-code/tree/main/StarterKit" -ForegroundColor Yellow } "azureManagement" { Write-Host " - The test for the management connection to Azure failed. The script will be unable to plan a deployment to support the EPAC solution. No plan step will be offered, and there will be no attempt to gather data from Azure programatically to simplify this process." -ForegroundColor Red $limitation.azureManagement.Status = $true } "azureAuthentication" { Write-Host " - The test for the authentication connection to Azure failed. The script will be unable to plan a deployment to support the EPAC solution. No plan step will be offered, and there will be no attempt to gather data from Azure programatically to simplify this process." -ForegroundColor Red $limitation.azureAuthentication.Status = $true } "gitInstall" { Write-Host " - The test for git software has failed. You will need to ensure that the StarterKit folder has been manually downloaded to the same directory that your Definitions folder is now in, based on the choices made in this script.` `n - This is not a blocker if you have a fresh copy of the StarterKit in the same directory as the Definitions folder, and you already have access to the module. However, this script requires the presence of that folder in the repo to continue, either through automated or manual means.` `n - Download Location: https://github.com/Azure/enterprise-azure-policy-as-code/tree/main/StarterKit" -ForegroundColor Yellow $limitation.git.Status = $true } } } } else { Write-Host "All tests indicate that the connection is in an optimal state for the EPAC installation process. Additional data will be gathered using these connections, and errors after this point will generally indicate a unique condition such as an inheritance block/override for RBAC authority." -ForegroundColor Green } ################################################################################ # Preliminary Tests: Footer # Summary separator block $displayPreliminaryTests = $stageBlocks.displayPreliminaryTests New-HydrationSeparatorBlock @displayPreliminaryTests Write-HydrationLogFile -EntryType newStage -EntryData $stageBlocks.displayPreliminaryTests.DisplayText -LogFilePath $logFilePath -UseUtc:$UseUtc -Silent # Display Summary $testSummarySeparator = "-" foreach ($hashkey in $summary.keys) { $hashString = $( -join ($hashKey, " ", ($testSummarySeparator * ($TerminalWidth - ($hashKey.Length + $($summary.$hashKey).Length + 2))), " ", $($summary.$hashKey))) if ($summary.$hashkey -like "Failed*") { Write-Host $hashString -ForegroundColor Red } else { Write-Host $hashString -ForegroundColor Green } Write-HydrationLogFile -EntryType logEntryDataAsPresented -EntryData "Summary: $hashString" -LogFilePath $logFilePath -UseUtc:$UseUtc -Silent } Write-Host "`n`n" # Pause for review based on interactive message from input New-HydrationContinuePrompt -Interactive:$Interactive -SleepTime:$sleepTime } ################################################################################ ################################################################################ # Begin Data Gather Process to Support EPAC Deployment $summary = [ordered]@{} $gatherData = $stageBlocks.gatherData New-HydrationSeparatorBlock @gatherData Write-HydrationLogFile -EntryType newStage -EntryData $stageBlocks.gatherData.DisplayText -LogFilePath $logFilePath -UseUtc:$UseUtc -Silent Write-Host "Beginning data gathering process..." -ForegroundColor Yellow # Gather Data $gatherData = [ordered]@{} $environmentEntry = [ordered]@{ pacSelector = "" intermediateRootGroupName = $TenantIntermediateRoot tenantId = $(Get-AzContext).Tenant.Id cloud = $(Get-AzContext).Environment.Name keepDfcSecurityAssignments = $false caf3Status = "Untested" intermediateRootStatus = "Untested" } # Current Tenant Information try { $gatherData.Add('currentTenantPacSelector', $(Get-DeepCloneAsOrderedHashtable -InputObject $environmentEntry)) $gatherData.currentTenantPacSelector.pacSelector = "tenant01" # Set Default for primary tenant, primary root (that will be cloned to epac-dev environment) } catch { Write-Error "Cannot find the EPAC helper command `'Get-DeepCloneAsOrderedHashtable`', please ensure that the EPAC module or is installed and available, or that the scripts directory is available and the helpers have been dot sourced for use." return } try { $psdList = Get-AzPolicySetDefinition -ManagementGroupName $(Get-AzContext).Tenant.Id } catch { Write-Error "Failed to gather Azure Policy Set Definition List, please ensure that you have a connection to Azure and try again." return } $gatherData.Add('policySetDefinitions', $psdList) Remove-Variable psdList -ErrorAction SilentlyContinue try { $pdList = Get-AzPolicyDefinition -ManagementGroupName $(Get-AzContext).Tenant.Id } catch { Write-Error "Failed to gather Azure Policy Set Definition List, please ensure that you have a connection to Azure and try again." return } $gatherData.Add('policyDefinitions', $pdList) Remove-Variable pdList -ErrorAction SilentlyContinue try { $locationList = (Get-AzLocation -ErrorAction SilentlyContinue).location if ($locationList.count -gt 0) { $gatherData.Add('locationList', @($LocationList) -join ", ") } } catch { Write-Error "Failed to gather Azure Location List, please ensure that you have a connection to Azure and try again." return } try { $tenantIntermediateRootTestResult = Get-AzManagementGroupRestMethod -GroupID $TenantIntermediateRoot } catch { Write-Warning "Error returned retrieving the Tenant Intermediate Root. This generally means that it has not been created, and the interview process will continue with the assumption that it needs to be created." } if (!($tenantIntermediateRootTestResult.Id)) { $gatherData.currentTenantPacSelector.intermediateRootStatus = $environmentEntry.intermediateRootStatus = "Available" Write-HydrationLogFile -EntryType logEntryDataAsPresented -EntryData "The Tenant Intermediate Root Management Group does not exist, and will need to be created to be used." -LogFilePath $logFilePath -UseUtc:$UseUtc -silent $interviewQuestionSets.optionalCreatePrimaryIntermediateRoot = $true Write-HydrationLogFile -EntryType logEntryDataAsPresented -EntryData "Create Intermediate Root: `"$TenantIntermediateRoot`" does not exist, adding discussion items..." -LogFilePath $logFilePath -UseUtc:$UseUtc -ForegroundColor Yellow } else { $gatherData.currentTenantPacSelector.intermediateRootStatus = $environmentEntry.intermediateRootStatus = "Passed" Write-HydrationLogFile -EntryType logEntryDataAsPresented -EntryData "The TenantIntermediateRoot Management Group `"$TenantIntermediateRoot`" is confirmed." -LogFilePath $logFilePath -UseUtc:$UseUtc -silent $interviewQuestionSets.optionalCreatePrimaryIntermediateRoot = $false Write-HydrationLogFile -EntryType logEntryDataAsPresented -EntryData "Create Intermediate Root Test: `"$TenantIntermediateRoot`" exists , no deployment is needed..." -LogFilePath $logFilePath -UseUtc:$UseUtc -ForegroundColor Green } Write-HydrationLogFile -EntryType logEntryDataAsPresented -EntryData "Data Gathered: $($gatherData | Convertto-Json -depth 100 -compress)" -LogFilePath $logFilePath -UseUtc:$UseUtc -Silent Write-Host "`n" $endSummary.gatherData = $summary New-HydrationContinuePrompt -Interactive:$Interactive -SleepTime:$sleepTime # } ################################################################################ ################################################################################ # Download Repo Contents Get-HydrationEpacRepo -RepoRoot $RepoRoot try{ Copy-Item -Path $repoRoot "temp" "StarterKit" -Destination $exportedDefinitionsPath.starterKit -Recurse -Force } catch{ Write-Warning "Failed to copy StarterKit directory: $($_.Exception.Message)" } # # $stageBlocks2 = Get-Content $(Join-Path $StarterKit 'HydrationKit' 'blockDefinitions.jsonc') | ConvertFrom-Json -Depth 5 -AsHashtable if ($answers.useModuleorScript -eq "LocalScript") { $scriptsSourcePath = Join-Path -Path $repoRootPath -ChildPath "temp" "Scripts" $scriptDestinationPath = Join-Path -Path $repoRootPath -ChildPath "Scripts" Copy-Item $scriptsSourcePath $scriptDestinationPath -Recurse -Force Write-HydrationLogFile -EntryType logEntryDataAsPresented -EntryData " Copied Scripts directory to repo for use in ongoing operations..." -LogFilePath $logFilePath -UseUtc:$UseUtc } ################################################################################ ################################################################################ # Run Interview Process if ($AnswerFile) { Write-HydrationLogFile -EntryType logEntryDataAsPresented -EntryData "Answer File Provided: $AnswerFile" -LogFilePath $logFilePath -UseUtc:$UseUtc -ForegroundColor Yellow try { $allInterviewAnswers = Get-Content $AnswerFile | ConvertFrom-Json -Depth 10 -AsHashtable } catch { Write-HydrationLogFile -EntryType logEntryDataAsPresented -EntryData $Error[0].Exception.Message -LogFilePath $logFilePath -UseUtc:$UseUtc -ForegroundColor Red Write-HydrationLogFile -EntryType logEntryDataAsPresented -EntryData "Failed to import the answer file, please review the error message and rerun the process." -LogFilePath $logFilePath -UseUtc:$UseUtc -ForegroundColor Red } } else { ################################################################################ # Detection logic for this can be added later if AI doesn't become the primary avenue. # $interviewquestionSets.createIntermediateRootHierarchy = $true $interviewQuestionSets.optionalCreatePrimaryIntermediateRoot = $true $interviewQuestionSets.optionalCreateMainCaf3Hierarchy = $true $interviewQuestionSets.optionalRequireCaf3HierarchyRename = $true ################################################################################ ################################################################################ # Gather Deployment Decisions for/from Answer File # Run Interview Process Clear-Host $stageBlock = $stageBlocks.generateAnswerFile $allInterviewAnswers = [ordered]@{} New-HydrationSeparatorBlock @stageBlock Write-HydrationLogFile -EntryType newStage -EntryData $stageBlock.DisplayText -LogFilePath $logFilePath -UseUtc:$UseUtc -Silent ################################################################################ # Main Tenant Loops ############ # Confirm Tenant $loopId = "initial" Write-HydrationLogFile -EntryType newStage -EntryData "Processing QuestionSet: $loopId" -LogFilePath $logFilePath -UseUtc:$UseUtc -Silent if ($interviewQuestionSets.$loopId) { $loopNotes = @( "Current Tenant ID: $((Get-AzContext).tenant.Id)" ) try { $interview = New-HydrationAnswerSet -LoopId $loopId -Notes $loopNotes -UseUtc:$UseUtc -LogFilePath $logFilePath -TerminalWidth:$TerminalWidth -ErrorAction Stop } catch { Write-HydrationLogFile -EntryType logEntryDataAsPresented -EntryData $error[0].Exception.Message -LogFilePath $logFilePath -UseUtc:$UseUtc -Silent Write-HydrationLogFile -EntryType logEntryDataAsPresented -EntryData "Failed to process QuestionSet $loopId" -LogFilePath $logFilePath -UseUtc:$UseUtc -Silent Write-Error "Error in the interview process for $loopId, please review the error message and rerun the process." return } Write-HydrationLogFile -EntryType answerSetProvided -EntryData "$($interview | Convertto-Json -depth 100 -compress)" -LogFilePath $logFilePath -UseUtc:$UseUtc -Silent Remove-Variable loopId -ErrorAction SilentlyContinue Remove-Variable loopNotes -ErrorAction SilentlyContinue Write-Host "`nQuestion Set is complete, responses outlined below..." -ForegroundColor Yellow foreach ($intKey in $interview.keys) { $allInterviewAnswers.Add($intKey, $interview.$intKey) $testSummarySeparator = "-" $hashString = $( -join ($intKey, " ", ($testSummarySeparator * ($TerminalWidth - ($intKey.Length + $($interview.$intKey).Length + 2))), " ", $($interview.$intKey))) Write-HydrationLogFile -EntryType logEntryDataAsPresented -EntryData "$hashString" -LogFilePath $logFilePath -UseUtc:$UseUtc -ForegroundColor Green } # Process Responses if ($interview.initialTenantId -eq "Yes") { $allInterviewAnswers.initialTenantId = (Get-AzContext).tenant.id Write-HydrationLogFile -EntryType logEntryDataAsPresented -EntryData "`nNo Tenant ID was provided, using the current Tenant ID...`n" -LogFilePath $logFilePath -UseUtc:$UseUtc -ForegroundColor Yellow } else { Write-HydrationLogFile -EntryType logEntryDataAsPresented -EntryData "`nDiffering Tenant ID provided, exiting to use the provided Tenant ID...`n" -LogFilePath $logFilePath -UseUtc:$UseUtc -ForegroundColor Yellow Write-Error "Reconnect to the appropriate Tenant ID using `'Connect-AzAccount -TenantId [YourTenantId]`', and then rerun the process." return } if ($interview.pacOwnerId -eq "" -or $null -eq $interview.pacOwnerId) { Write-HydrationLogFile -EntryType logEntryDataAsPresented -EntryData "`nNo Owner ID was provided, generating a new GUID for the Owner ID...`n" -LogFilePath $logFilePath -UseUtc:$UseUtc -ForegroundColor Yellow $interview.pacOwnerId = $allInterviewAnswers.pacOwnerId = (New-Guid).Guid $testSummarySeparator = "-" $ikey = "PacOwnerId Generated" $hashString = $( -join ($ikey, " ", ($testSummarySeparator * ($TerminalWidth - ($ikey.Length + $($interview.pacOwnerId).Length + 2))), " ", $($interview.pacOwnerId))) Write-HydrationLogFile -EntryType logEntryDataAsPresented -EntryData "$hashString" -LogFilePath $logFilePath -UseUtc:$UseUtc -ForegroundColor Green } New-HydrationContinuePrompt -Interactive:$Interactive -SleepTime:$sleepTime $allInterviewAnswers.initialTenantCloud = (Get-AzContext).environment.name $allInterviewAnswers.initialTenantIntermediateRoot = $TenantIntermediateRoot } $loopId = "optionalCreatePrimaryIntermediateRoot" Write-HydrationLogFile -EntryType newStage -EntryData "Processing QuestionSet: $loopId" -LogFilePath $logFilePath -UseUtc:$UseUtc -Silent if ($environmentEntry.intermediateRootStatus -eq "Available" -and $interviewQuestionSets.$loopId) { $loopNotes = @( "$TenantIntermediateRoot Name Status: $($environmentEntry.intermediateRootStatus)" ) try { $interview = New-HydrationAnswerSet -LoopId $loopId -Notes $loopNotes -UseUtc:$UseUtc -LogFilePath $logFilePath -TerminalWidth:$TerminalWidth -ErrorAction Stop } catch { Write-HydrationLogFile -EntryType logEntryDataAsPresented -EntryData $error[0].Exception.Message -LogFilePath $logFilePath -UseUtc:$UseUtc -Silent Write-HydrationLogFile -EntryType logEntryDataAsPresented -EntryData "Failed to process QuestionSet $loopId" -LogFilePath $logFilePath -UseUtc:$UseUtc -Silent Write-Error "Error in the interview process for $loopId, please review the error message and rerun the process." return } Write-HydrationLogFile -EntryType answerSetProvided -EntryData "$($interview | Convertto-Json -depth 100 -compress)" -LogFilePath $logFilePath -UseUtc:$UseUtc -Silent Remove-Variable loopId -ErrorAction SilentlyContinue Remove-Variable loopNotes -ErrorAction SilentlyContinue Write-Host "`nQuestion Set is complete, responses outlined below..." -ForegroundColor Yellow foreach ($intKey in $interview.keys) { $allInterviewAnswers.Add($intKey, $interview.$intKey) $testSummarySeparator = "-" $hashString = $( -join ($intKey, " ", ($testSummarySeparator * ($TerminalWidth - ($intKey.Length + $($interview.$intKey).Length + 2))), " ", $($interview.$intKey))) Write-HydrationLogFile -EntryType logEntryDataAsPresented -EntryData "$hashString" -LogFilePath $logFilePath -UseUtc:$UseUtc -ForegroundColor Green } New-HydrationContinuePrompt -Interactive:$Interactive -SleepTime:$sleepTime } ############ # corePacSelectors Loop $loopId = "corePacSelectors" Write-HydrationLogFile -EntryType newStage -EntryData "Processing QuestionSet: $loopId" -LogFilePath $logFilePath -UseUtc:$UseUtc -Silent if (!($endSummary.gatherData.locationList -eq "" -or $null -eq $endSummary.gatherData.locationList)) { $loopNotes = @("Location List: $($endSummary.gatherData.locationList)") } else { # $loopNotes = @("Location List: Rbac Test Failed, no location list gathered.") } try { $interview = New-HydrationAnswerSet -LoopId $loopId -Notes $loopNotes -UseUtc:$UseUtc -LogFilePath $logFilePath -TerminalWidth:$TerminalWidth -ErrorAction Stop } catch { Write-HydrationLogFile -EntryType logEntryDataAsPresented -EntryData $error[0].Exception.Message -LogFilePath $logFilePath -UseUtc:$UseUtc -Silent Write-HydrationLogFile -EntryType logEntryDataAsPresented -EntryData "Failed to process QuestionSet $loopId" -LogFilePath $logFilePath -UseUtc:$UseUtc -Silent Write-Error "Error in the interview process for $loopId, please review the error message and rerun the process." return } Write-HydrationLogFile -EntryType answerSetProvided -EntryData "$($interview | Convertto-Json -depth 100 -compress)" -LogFilePath $logFilePath -UseUtc:$UseUtc -Silent Write-Host "`nQuestion Set is complete, responses outlined below..." -ForegroundColor Yellow foreach ($intKey in $interview.keys) { $allInterviewAnswers.Add($intKey, $interview.$intKey) $testSummarySeparator = "-" $hashString = $( -join ($intKey, " ", ($testSummarySeparator * ($TerminalWidth - ($intKey.Length + $($interview.$intKey).Length + 2))), " ", $($interview.$intKey))) Write-HydrationLogFile -EntryType logEntryDataAsPresented -EntryData "$hashString" -LogFilePath $logFilePath -UseUtc:$UseUtc -ForegroundColor Green } Remove-Variable loopId -ErrorAction SilentlyContinue Remove-Variable loopNotes -ErrorAction SilentlyContinue if ($interview.mainTenantMainPacSelectorName -eq "" -or $null -eq $interview.mainTenantMainPacSelectorName -or -not $interview.mainTenantMainPacSelectorName) { Write-HydrationLogFile -EntryType logEntryDataAsPresented -EntryData "No pacSelectorName was provided, using 'tenant01'..." -LogFilePath $logFilePath -UseUtc:$UseUtc -Silent Write-HydrationLogFile -EntryType logEntryDataAsPresented -EntryData "`nNo pacSelectorName was provided, setting default value...`n" -LogFilePath $logFilePath -UseUtc:$UseUtc -ForegroundColor Yellow $interview.mainTenantMainPacSelectorName = $allInterviewAnswers.mainTenantMainPacSelectorName = "tenant01" $testSummarySeparator = "-" $ikey = "pacSelectorName" $hashString = $( -join ($ikey, " ", ($testSummarySeparator * ($TerminalWidth - ($ikey.Length + $($interview.mainTenantMainPacSelectorName).Length + 2))), " ", $($interview.mainTenantMainPacSelectorName))) Write-HydrationLogFile -EntryType logEntryDataAsPresented -EntryData "$hashString" -LogFilePath $logFilePath -UseUtc:$UseUtc -ForegroundColor Green } if ($interview.epacParent -eq "" -or $null -eq $interview.epacParent -or -not $interview.epacParent) { Write-HydrationLogFile -EntryType logEntryDataAsPresented -EntryData "No Parent for EPAC was provided, using Tenant Root for the Parent..." -LogFilePath $logFilePath -UseUtc:$UseUtc -Silent Write-HydrationLogFile -EntryType logEntryDataAsPresented -EntryData "`nNo epacParent was provided, setting default value...`n" -LogFilePath $logFilePath -UseUtc:$UseUtc -ForegroundColor Yellow $interview.epacParent = $allInterviewAnswers.epacParent = $gatherData.currentTenantPacSelector.tenantId $testSummarySeparator = "-" $ikey = "epacParent" $hashString = $( -join ($ikey, " ", ($testSummarySeparator * ($TerminalWidth - ($ikey.Length + $($interview.epacParent).Length + 2))), " ", $($interview.epacParent))) Write-HydrationLogFile -EntryType logEntryDataAsPresented -EntryData "$hashString" -LogFilePath $logFilePath -UseUtc:$UseUtc -ForegroundColor Green } New-HydrationContinuePrompt -Interactive:$Interactive -SleepTime:$sleepTime ############ # epacModifier Loop $loopId = "epacModifier" Write-HydrationLogFile -EntryType newStage -EntryData "Processing QuestionSet: $loopId" -LogFilePath $logFilePath -UseUtc:$UseUtc -Silent try { $interview = New-HydrationAnswerSet -LoopId $loopId -UseUtc:$UseUtc -LogFilePath $logFilePath -TerminalWidth:$TerminalWidth -ErrorAction Stop } catch { Write-HydrationLogFile -EntryType logEntryDataAsPresented -EntryData $error[0].Exception.Message -LogFilePath $logFilePath -UseUtc:$UseUtc -Silent Write-HydrationLogFile -EntryType logEntryDataAsPresented -EntryData "Failed to process QuestionSet $loopId" -LogFilePath $logFilePath -UseUtc:$UseUtc -Silent Write-Error "Error in the interview process for $loopId, please review the error message and rerun the process." return } Write-HydrationLogFile -EntryType answerSetProvided -EntryData "$($interview | Convertto-Json -depth 100 -compress)" -LogFilePath $logFilePath -UseUtc:$UseUtc -Silent Write-Host "`nQuestion Set is complete, responses outlined below..." -ForegroundColor Yellow foreach ($intKey in $interview.keys) { $allInterviewAnswers.Add($intKey, $interview.$intKey) $testSummarySeparator = "-" $hashString = $( -join ($intKey, " ", ($testSummarySeparator * ($TerminalWidth - ($intKey.Length + $($interview.$intKey).Length + 2))), " ", $($interview.$intKey))) Write-HydrationLogFile -EntryType logEntryDataAsPresented -EntryData "$hashString" -LogFilePath $logFilePath -UseUtc:$UseUtc -ForegroundColor Green } $epacDevRoot = $( -join ($interview.epacPrefix, $TenantIntermediateRoot, $interview.epacSuffix)) if ($epacDevRoot -eq $TenantIntermediateRoot) { Write-HydrationLogFile -EntryType logEntryDataAsPresented -EntryData "`nEPAC Development Root $epacDevRoot is equal to the specified Tenant Intermediate Root group, $TenantIntermediateRootGroup. This will result in a name collision. Please choose a prefix and/or suffx when you run this process again." -LogFilePath $logFilePath -UseUtc:$UseUtc -ForegroundColor Red New-HydrationContinuePrompt -Interactive:$Interactive -SleepTime:$sleepTime return } else { $testSummarySeparator = "-" $ival = $( -join ($interview.epacPrefix, $TenantIntermediateRoot, $interview.epacSuffix)) $ikey = 'EpacDevelopmentRoot' $hashString = $( -join ($ikey, " ", ($testSummarySeparator * ($TerminalWidth - ($ikey.Length + $ival.Length + 2))), " ", $ival)) Write-HydrationLogFile -EntryType logEntryDataAsPresented -EntryData "$hashString" -LogFilePath $logFilePath -UseUtc:$UseUtc -ForegroundColor Green $allInterviewAnswers.Add($ikey, $ival) # Write-HydrationLogFile -EntryType logEntryDataAsPresented -EntryData "`nEPAC Development Root: " -LogFilePath $logFilePath -UseUtc:$UseUtc -ForegroundColor Green } Remove-Variable ikey -ErrorAction SilentlyContinue Remove-Variable ival -ErrorAction SilentlyContinue Remove-Variable loopId -ErrorAction SilentlyContinue Remove-Variable loopNotes -ErrorAction SilentlyContinue New-HydrationContinuePrompt -Interactive:$Interactive -SleepTime:$sleepTime ############ # policyDecisions Loop $loopId = "policyDecisions" Write-HydrationLogFile -EntryType newStage -EntryData "Processing QuestionSet: $loopId" -LogFilePath $logFilePath -UseUtc:$UseUtc -Silent try { $interview = New-HydrationAnswerSet -LoopId $loopId -UseUtc:$UseUtc -LogFilePath $logFilePath -TerminalWidth:$TerminalWidth -ErrorAction Stop } catch { Write-HydrationLogFile -EntryType logEntryDataAsPresented -EntryData $error[0].Exception.Message -LogFilePath $logFilePath -UseUtc:$UseUtc -Silent Write-HydrationLogFile -EntryType logEntryDataAsPresented -EntryData "Failed to process QuestionSet $loopId" -LogFilePath $logFilePath -UseUtc:$UseUtc -Silent Write-Error "Error in the interview process for $loopId, please review the error message and rerun the process." return } Write-HydrationLogFile -EntryType answerSetProvided -EntryData "$($interview | Convertto-Json -depth 100 -compress)" -LogFilePath $logFilePath -UseUtc:$UseUtc -Silent Write-Host "`nQuestion Set is complete, responses outlined below..." -ForegroundColor Yellow foreach ($intKey in $interview.keys) { $allInterviewAnswers.Add($intKey, $interview.$intKey) $testSummarySeparator = "-" $hashString = $( -join ($intKey, " ", ($testSummarySeparator * ($TerminalWidth - ($intKey.Length + $($interview.$intKey).Length + 2))), " ", $($interview.$intKey))) Write-HydrationLogFile -EntryType logEntryDataAsPresented -EntryData "$hashString" -LogFilePath $logFilePath -UseUtc:$UseUtc -ForegroundColor Green } Remove-Variable loopId -ErrorAction SilentlyContinue Remove-Variable loopNotes -ErrorAction SilentlyContinue New-HydrationContinuePrompt -Interactive:$Interactive -SleepTime:$sleepTime ############ # pipelineDecisions Loop $loopId = "pipelineDecisions" Write-HydrationLogFile -EntryType newStage -EntryData "Processing QuestionSet: $loopId" -LogFilePath $logFilePath -UseUtc:$UseUtc -Silent try { $interview = New-HydrationAnswerSet -LoopId $loopId -UseUtc:$UseUtc -LogFilePath $logFilePath -TerminalWidth:$TerminalWidth -ErrorAction Stop } catch { Write-HydrationLogFile -EntryType logEntryDataAsPresented -EntryData $error[0].Exception.Message -LogFilePath $logFilePath -UseUtc:$UseUtc -Silent Write-HydrationLogFile -EntryType logEntryDataAsPresented -EntryData "Failed to process QuestionSet $loopId" -LogFilePath $logFilePath -UseUtc:$UseUtc -Silent Write-Error "Error in the interview process for $loopId, please review the error message and rerun the process." return } Write-HydrationLogFile -EntryType answerSetProvided -EntryData "$($interview | Convertto-Json -depth 100 -compress)" -LogFilePath $logFilePath -UseUtc:$UseUtc -Silent Write-Host "`nQuestion Set is complete, responses outlined below..." -ForegroundColor Yellow foreach ($intKey in $interview.keys) { $allInterviewAnswers.Add($intKey, $interview.$intKey) $testSummarySeparator = "-" $hashString = $( -join ($intKey, " ", ($testSummarySeparator * ($TerminalWidth - ($intKey.Length + $($interview.$intKey).Length + 2))), " ", $($interview.$intKey))) Write-HydrationLogFile -EntryType logEntryDataAsPresented -EntryData "$hashString" -LogFilePath $logFilePath -UseUtc:$UseUtc -ForegroundColor Green } # Release flow is too complicated for the current logic, and should be part of a new module generating the pipeline integration with the platform. $allInterviewAnswers.Add("pipelineFlow", "Github") Remove-Variable loopId -ErrorAction SilentlyContinue Remove-Variable loopNotes -ErrorAction SilentlyContinue New-HydrationContinuePrompt -Interactive:$Interactive -SleepTime:$sleepTime ############ # otherPipeline Loop if ($AllInterviewAnswers.pipelinePlatform -eq "Other") { $loopId = "otherPipeline" Write-HydrationLogFile -EntryType newStage -EntryData "Processing QuestionSet: $loopId" -LogFilePath $logFilePath -UseUtc:$UseUtc -Silent try { $interview = New-HydrationAnswerSet -LoopId $loopId -UseUtc:$UseUtc -LogFilePath $logFilePath -TerminalWidth:$TerminalWidth -ErrorAction Stop } catch { Write-HydrationLogFile -EntryType logEntryDataAsPresented -EntryData $error[0].Exception.Message -LogFilePath $logFilePath -UseUtc:$UseUtc -Silent Write-HydrationLogFile -EntryType logEntryDataAsPresented -EntryData "Failed to process QuestionSet $loopId" -LogFilePath $logFilePath -UseUtc:$UseUtc -Silent Write-Error "Error in the interview process for $loopId, please review the error message and rerun the process." return } Write-HydrationLogFile -EntryType answerSetProvided -EntryData "$($interview | Convertto-Json -depth 100 -compress)" -LogFilePath $logFilePath -UseUtc:$UseUtc -Silent Write-Host "`nQuestion Set is complete, responses outlined below..." -ForegroundColor Yellow foreach ($intKey in $interview.keys) { $allInterviewAnswers.Add($intKey, $interview.$intKey) $testSummarySeparator = "-" $hashString = $( -join ($intKey, " ", ($testSummarySeparator * ($TerminalWidth - ($intKey.Length + $($interview.$intKey).Length + 2))), " ", $($interview.$intKey))) Write-HydrationLogFile -EntryType logEntryDataAsPresented -EntryData "$hashString" -LogFilePath $logFilePath -UseUtc:$UseUtc -ForegroundColor Green } Remove-Variable loopId -ErrorAction SilentlyContinue Remove-Variable loopNotes -ErrorAction SilentlyContinue New-HydrationContinuePrompt -Interactive:$Interactive -SleepTime:$sleepTime } else { $allInterviewAnswers.Add("pipelineCustomPath", "NotApplicable") } # Output Answer File $writeAnswerFile = $stageBlocks.writeAnswerFile New-HydrationSeparatorBlock @writeAnswerFile Write-HydrationLogFile -EntryType logEntryDataAsPresented -EntryData "File Location: $answerFilePath"-LogFilePath $logFilePath -UseUtc:$UseUtc -ForegroundColor Yellow try { $allInterviewAnswers | ConvertTo-Json -Depth 100 | Set-Content -Path $answerFilePath -Force } catch { Write-Error "Unable to write the answer file to $answerFilePath. Please ensure that you have write access to the location and try again. This was tested during the preliminary checks, so this is an odd situation. It is possible that a write lock, or some other lock, has occurred." return } # Summary separator block $displayAnswerData = $stageBlocks.displayAnswerData New-HydrationSeparatorBlock @displayAnswerData Write-HydrationLogFile -EntryType newStage -EntryData $displayAnswerData.DisplayText -LogFilePath $logFilePath -UseUtc:$UseUtc -Silent } # Display Summary $testSummarySeparator = "-" Write-HydrationLogFile -EntryType logEntryDataAsPresented -EntryData "Summary:" -LogFilePath $logFilePath -UseUtc:$UseUtc -ForegroundColor Yellow foreach ($hashkey in $allInterviewAnswers.keys) { $hashString = $( -join ($hashKey, " ", ($testSummarySeparator * ($TerminalWidth - ($hashKey.Length + $($allInterviewAnswers.$hashKey).Length + 2))), " ", $($allInterviewAnswers.$hashKey))) Write-HydrationLogFile -EntryType logEntryDataAsPresented -EntryData "$hashString" -LogFilePath $logFilePath -UseUtc:$UseUtc -ForegroundColor Green } New-HydrationContinuePrompt -Interactive:$Interactive -SleepTime:$sleepTime # Test New MG Values try { $gatherData.currentTenantPacSelector.caf3Status = $environmentEntry.caf3Status = ` Test-HydrationCaf3Hierarchy ` -TenantId $(Get-AzContext).Tenant.Id ` -TenantIntermediateRoot $TenantIntermediateRoot ` -MgPrefix:$allInterviewAnswers.mainCaf3Prefix ` -MgSuffix:$allInterviewAnswers.mainCaf3Suffix ` -LogFilePath $logFilePath } catch { Write-Error $Error[0].Exception.Message return } try { $gatherData.currentTenantPacSelector.caf3Status = $environmentEntry.caf3Status = ` Test-HydrationCaf3Hierarchy ` -TenantId $(Get-AzContext).Tenant.Id ` -TenantIntermediateRoot $TenantIntermediateRoot ` -MgPrefix:$allInterviewAnswers.epacPrefix ` -MgSuffix:$allInterviewAnswers.epacSuffix ` -LogFilePath $logFilePath } catch { Write-Error $Error[0].Exception.Message return } ################################################################################ # Execute hydration process Clear-Host $blockData = $stageBlocks.beginHydrationProcess New-HydrationSeparatorBlock @blockData Write-HydrationLogFile -EntryType newStage -EntryData "Hydrating EPAC based on the answers provided below:" -LogFilePath $logFilePath -UseUtc:$UseUtc -ForegroundColor Yellow foreach ($hashkey in $allInterviewAnswers.keys) { $hashString = $( -join ($hashKey, " ", ($testSummarySeparator * ($TerminalWidth - ($hashKey.Length + $($allInterviewAnswers.$hashKey).Length + 2))), " ", $($allInterviewAnswers.$hashKey))) Write-HydrationLogFile -EntryType logEntryDataAsPresented -EntryData "$hashString" -LogFilePath $logFilePath -UseUtc:$UseUtc -ForegroundColor Green } ######################### # Create Definitions Folder Structure Write-HydrationLogFile -EntryType logEntryDataAsPresented -EntryData "Creating Definitions folder structure..." -LogFilePath $logFilePath -UseUtc:$UseUtc -ForegroundColor Yellow try { New-HydrationDefinitionsFolder -DefinitionsRootFolder $DefinitionsRootFolder -ErrorAction Stop } catch { Write-HydrationLogFile -EntryType logEntryDataAsPresented -EntryData $Error[0].Exception.Message -LogFilePath $logFilePath -UseUtc:$UseUtc -ForegroundColor Red Write-HydrationLogFile -EntryType logEntryDataAsPresented -EntryData "Unable to create Definitions folder. Please ensure that you have write access to $(Get-Location) and try again." -LogFilePath $logFilePath -UseUtc:$UseUtc -ForegroundColor Red Write-HydrationLogFile -EntryType logEntryDataAsPresented -EntryData "Use answer file at $answerFilePath to rerun the script without prompting for questions to retry this process once the problem is resolved." -LogFilePath $logFilePath -UseUtc:$UseUtc -ForegroundColor Yellow return } Write-Host " Definitions folder structure has been validated..." -ForegroundColor Green ######################### # Create Global Settings File # Generate Environment List $allInterviewAnswers.initialTenantId = (Get-AzContext).tenant.id $allInterviewAnswers.initialTenantCloud = (Get-AzContext).environment.name # $allInterviewAnswers.initialTenantIntermediateRoot = $TenantIntermediateRoot $globalSettingsInputs = [ordered]@{ PacOwnerId = $allInterviewAnswers.pacOwnerId DefinitionsRootFolder = $DefinitionsRootFolder ManagedIdentityLocation = $allInterviewAnswers.managedIdAssignmentLocation MainPacSelector = $allInterviewAnswers.mainTenantMainPacSelectorName EpacPacSelector = $epacDevName # TODO: Improvement, add a question to the mainpacselector loop for the epacPacSelector Cloud = (Get-AzContext).environment.name # This never need not be prompted, choice of initialTenantId will choose this implicitly TenantId = $allInterviewAnswers.initialTenantId MainDeploymentRoot = $allInterviewAnswers.initialTenantIntermediateRoot EpacDevelopmentRoot = $allInterviewAnswers.EpacDevelopmentRoot Strategy = 'ownedOnly' # This is kept at ownedOnly on purpose. People should be comfortable enough with the deployment to update this before changing to full to help prevent accidents # RepoRoot = $(Resolve-Path $DefinitionsRootFolder | Split-Path -Parent) LogFilePath = $logFilePath UseUtc = $UseUtc KeepDfcSecurityAssignments = $false # TODO: Improvement, add a question to the mainpacselector loop for the dfcSecurityAssignments } Write-HydrationLogFile -EntryType logEntryDataAsPresented -EntryData "Creating Global Settings file..." -LogFilePath $logFilePath -UseUtc:$UseUtc -ForegroundColor Yellow try { $null = New-HydrationGlobalSettingsFile @globalSettingsInputs -ErrorAction Stop } catch { Write-Error "Unable to create global-settings file. This is likely a flaw in the choices made above that should have been caught in earlier tests. Please retain your answer file and report this to the EPAC team, and attempt this process again." return } Write-HydrationLogFile -EntryType logEntryDataAsPresented -EntryData "Creating Pipeline..." -LogFilePath $logFilePath -UseUtc:$UseUtc -ForegroundColor Yellow Remove-Variable usePipelineCustomPath -ErrorAction SilentlyContinue if ($allInterviewAnswers.pipelineCustomPath -eq "NotApplicable"){ try { if($allInterviewAnswers.codeExecutionType -eq "Scripts"){ if(Test-Path $(Join-Path $repoRootPath "Scripts" "Operations" "New-PipelinesFromStarterKit.ps1")) { & (Join-Path $repoRootPath "Scripts" "Operations" "New-PipelinesFromStarterKit.ps1") -StarterKitFolder $exportedDefinitionsPath.starterKit ` -PipelineType $allInterviewAnswers.pipelinePlatform ` -BranchingFlow $allInterviewAnswers.pipelineFlow ` -ScriptType $allInterviewAnswers.codeExecutionType ` -ErrorAction Stop ` -SuppressConfirm:$true }elseif(Test-Path (Join-Path $repoRootPath "temp" "Scripts" "Operations" "New-PipelinesFromStarterKit.ps1")){ & (Join-Path $repoRootPath "temp" "Scripts" "Operations" "New-PipelinesFromStarterKit.ps1") -StarterKitFolder $exportedDefinitionsPath.starterKit ` -PipelineType $allInterviewAnswers.pipelinePlatform ` -BranchingFlow $allInterviewAnswers.pipelineFlow ` -ScriptType $allInterviewAnswers.codeExecutionType ` -ErrorAction Stop ` -SuppressConfirm:$true } } elseif($allInterviewAnswers.codeExecutionType -eq "Module"){ New-PipelinesFromStarterKit -StarterKitFolder $exportedDefinitionsPath.starterKit ` -PipelineType $allInterviewAnswers.pipelinePlatform ` -BranchingFlow $allInterviewAnswers.pipelineFlow ` -ScriptType $allInterviewAnswers.codeExecutionType ` -ErrorAction Stop ` -SuppressConfirm:$true } } catch { Write-Host "Failure, Error Information: $($_.Exception.Message)" -ForegroundColor Red Write-Error "Unable to create pipeline. This is likely a flaw in the choices made above that should have been caught in earlier tests. Please retain your answer file and report this to the EPAC team, and attempt this process again." return } } else { try { if(Test-Path $(Join-Path $repoRootPath "Scripts" "Operations" "New-PipelinesFromStarterKit.ps1")) { & (Join-Path $repoRootPath "Scripts" "Operations" "New-PipelinesFromStarterKit.ps1") -StarterKitFolder $exportedDefinitionsPath.starterKit ` -PipelinesFolder:$usePipelineCustomPath ` -PipelineType $allInterviewAnswers.pipelinePlatform ` -BranchingFlow $allInterviewAnswers.pipelineFlow ` -ScriptType $allInterviewAnswers.codeExecutionType ` -ErrorAction Stop ` -SuppressConfirm:$true }elseif(Test-Path "./Scripts/Operations/New-PipelinesFromStarterKit.ps1"){ & "./Scripts/Operations/New-PipelinesFromStarterKit.ps1" -StarterKitFolder $exportedDefinitionsPath.starterKit ` -PipelinesFolder:$usePipelineCustomPath ` -PipelineType $allInterviewAnswers.pipelinePlatform ` -BranchingFlow $allInterviewAnswers.pipelineFlow ` -ScriptType $allInterviewAnswers.codeExecutionType ` -ErrorAction Stop }else{ New-PipelinesFromStarterKit -StarterKitFolder $exportedDefinitionsPath.starterKit ` -PipelinesFolder:$usePipelineCustomPath ` -PipelineType $allInterviewAnswers.pipelinePlatform ` -BranchingFlow $allInterviewAnswers.pipelineFlow ` -ScriptType $allInterviewAnswers.codeExecutionType ` -ErrorAction Stop } } catch { Write-Error "Unable to create pipeline. This is likely a flaw in the choices made above that should have been caught in earlier tests. Please retain your answer file and report this to the EPAC team, and attempt this process again." return } } ## Build MG Structure if ($allInterviewAnswers.createMainIntermediateRoot -eq "Yes") { Write-HydrationLogFile -EntryType logEntryDataAsPresented -EntryData "Creating Main Intermediate Root..." -LogFilePath $logFilePath -UseUtc:$UseUtc -ForegroundColor Yellow do { try { New-AzManagementGroup -GroupName $allInterviewAnswers.initialTenantIntermediateRoot -ErrorAction Stop } catch { $AzMgRetry = $true } if (Get-AzManagementGroupRestMethod -GroupId $allInterviewAnswers.initialTenantIntermediateRoot) { $AzMgRetry = $false } }until($AzMgRetry -eq $false) } if ($allInterviewAnswers.createMainCaf3Hierarchy -eq "Yes") { Write-HydrationLogFile -EntryType logEntryDataAsPresented -EntryData "Creating Main CAF3 Management Groups..." -LogFilePath $logFilePath -UseUtc:$UseUtc -ForegroundColor Yellow New-HydrationCaf3Hierarchy -DestinationRootName $allInterviewAnswers.initialTenantIntermediateRoot -Prefix $allInterviewAnswers.mainCaf3Prefix -Suffix $allInterviewAnswers.maincaf3Suffix $updatedTenantIntermediateRoot = $allInterviewAnswers.initialTenantIntermediateRoot } ## Build EPAC MG Structure if (!($skipEpacMgDeploy)) { Write-HydrationLogFile -EntryType logEntryDataAsPresented -EntryData "Creating Epac Management Groups in $(-join($allInterviewAnswers.epacPrefix,$allInterviewAnswers.initialTenantIntermediateRoot,$allInterviewAnswers.epacSuffix)), a child of $($allInterviewAnswers.epacParent)..." -LogFilePath $logFilePath -UseUtc:$UseUtc -ForegroundColor Yellow Copy-HydrationManagementGroupHierarchy -SourceGroupName $allInterviewAnswers.initialTenantIntermediateRoot -DestinationParentGroupName $allInterviewAnswers.epacParent -Prefix:$allInterviewAnswers.epacPrefix -Suffix:$allInterviewAnswers.epacSuffix | Out-Null } ## Import Existing Policy Assignments (if applicable) if ($allInterviewAnswers.importExistingPolicies -eq "Yes") { Write-HydrationLogFile -EntryType logEntryDataAsPresented -EntryData "Importing Existing Policy Assignments..." -LogFilePath $logFilePath -UseUtc:$UseUtc -ForegroundColor Yellow Write-HydrationLogFile -EntryType logEntryDataAsPresented -EntryData " Exporting existing content for PacSelector `'$($allInterviewAnswers.mainTenantMainPacSelectorName)`', for which the root is defined as $( $allInterviewAnswers.initialTenantIntermediateRoot)" -LogFilePath $logFilePath -UseUtc:$UseUtc -ForegroundColor Yellow try{ if(Test-Path $(Join-Path $repoRootPath "Scripts" "Operations" "Export-AzPolicyResources.ps1")) { & (Join-Path "$repoRootPath" "Scripts" "Operations" "Export-AzPolicyResources.ps1") -DefinitionsRootFolder $DefinitionsRootFolder ` -ExemptionFiles 'csv' ` -FileExtension 'jsonc' ` -IncludeAutoAssigned ` -IncludeChildScopes ` -InputPacSelector $allInterviewAnswers.mainTenantMainPacSelectorName ` -Mode 'export' ` -OutputFolder $Output ` -Interactive:$FALSE ` -ErrorAction Stop }elseif(Test-Path $(Join-Path $repoRootPath "temp" "Scripts" "Operations" "Export-AzPolicyResources.ps1")){ & (Join-Path "$repoRootPath" "temp" "Scripts" "Operations" "Export-AzPolicyResources.ps1") -DefinitionsRootFolder $DefinitionsRootFolder ` -ExemptionFiles 'csv' ` -FileExtension 'jsonc' ` -IncludeAutoAssigned ` -IncludeChildScopes ` -InputPacSelector $allInterviewAnswers.mainTenantMainPacSelectorName ` -Mode 'export' ` -OutputFolder $Output ` -Interactive:$FALSE ` -ErrorAction Stop }else{ Export-AzPolicyResources.ps1 -DefinitionsRootFolder $DefinitionsRootFolder ` -ExemptionFiles 'csv' ` -FileExtension 'jsonc' ` -IncludeAutoAssigned ` -IncludeChildScopes ` -InputPacSelector $allInterviewAnswers.mainTenantMainPacSelectorName ` -Mode 'export' ` -OutputFolder $Output ` -Interactive:$FALSE ` -ErrorAction Stop } }catch{ Write-HydrationLogFile -EntryType logEntryDataAsPresented -EntryData $Error[0].Exception.Message -LogFilePath $logFilePath -UseUtc:$UseUtc -ForegroundColor Red return } Write-HydrationLogFile -EntryType logEntryDataAsPresented -EntryData " Export process complete. Copying non-assignment content to definitions folder..." -LogFilePath $logFilePath -UseUtc:$UseUtc -ForegroundColor Yellow # Rename to align with naming expected by Export-PolicyToEPAC, simplifies linux support. Rename-Item -Path (Join-Path $Output "export") -NewName "Export" -Force -ErrorAction SilentlyContinue # $exportedDefinitionsPath = Join-Path $Output "Export" "Definitions" if (!(Test-Path $exportedDefinitionsPath.definitions)) { Write-Error "Unable to find the folder $($exportedDefinitionsPath.definitions). You should go to https://portal.azure.com and confirm whether or not assignments exist that are assigned within the referenced scope $($answerFilePath.initialTenantIntermediateRoot) and its children." $noExport = Read-Host "Type 'Confirmed' and press enter to continue, otherwise simply press enter to quit..." if (!($noExport -eq "Confirmed")) { { Write-HydrationLogFile -EntryType logEntryDataAsPresented -EntryData "You have confirmed that the export does not contain your contents. Confirm access, and run the script again, choosing to use the answer file at $answerFilePath." -LogFilePath $logFilePath -UseUtc:$UseUtc -ForegroundColor Red } } } elseif (Test-Path $exportedDefinitionsPath.definitions) { # This should be triggered now $nonAssignmentExportFolders = Get-ChildItem $exportedDefinitionsPath.definitions -Directory -Exclude policyAssignments if ($nonAssignmentExportFolders.count -gt 0) { Write-HydrationLogFile -EntryType logEntryDataAsPresented -EntryData " Copying $($nonAssignmentExportFolders.count) non-assignment content to definitions folder..." -LogFilePath $logFilePath -UseUtc:$UseUtc -ForegroundColor Yellow foreach ($sourceDir in $nonAssignmentExportFolders) { $updatedFiles = Get-ChildItem -Path $sourceDir -Recurse -Include "*.json", "*.jsonc", '*.csv' Write-HydrationLogFile -EntryType logEntryDataAsPresented -EntryData " Copying content from $sourceDir to $DefinitionsRootFolder" -LogFilePath $logFilePath -UseUtc:$UseUtc -ForegroundColor Yellow foreach ($uFile in $updatedFiles) { # Calculate the relative path of the item $relativePath = $uFile.FullName.Substring($(Split-Path $sourceDir.FullName).Length + 1) # Create the full destination path $destinationPath = Join-Path -Path $DefinitionsRootFolder -ChildPath $relativePath # Create the necessary destination directories $destinationDirPath = Split-Path -Path $destinationPath if (!(Test-Path -Path $destinationDirPath)) { $null = New-Item -Path $destinationDirPath -ItemType Directory Write-HydrationLogFile -EntryType logEntryDataAsPresented ` -EntryData " Created $destinationDirPath" ` -LogFilePath $logFilePath ` -UseUtc:$UseUtc ` -Silent } # Copy the file or directory Copy-Item -Path $uFile.FullName -Destination $destinationPath -Force Write-HydrationLogFile -EntryType logEntryDataAsPresented ` -EntryData " Copied $uFile to $destinationPath" ` -LogFilePath $logFilePath ` -UseUtc:$UseUtc ` -Silent if (!(Test-Path -Path $destinationPath)) { Write-HydrationLogFile -EntryType logEntryDataAsPresented ` -EntryData " Failed to copy $uFile to $destinationPath, this should be copied manually or the task should be run again." ` -LogFilePath $logFilePath ` -UseUtc:$UseUtc ` -ForegroundColor Red } } } } else{ Write-HydrationLogFile -EntryType logEntryDataAsPresented -EntryData " No non-assignment content found to copy." -LogFilePath $logFilePath -UseUtc:$UseUtc -ForegroundColor Yellow } } Remove-Variable noExport -ErrorAction SilentlyContinue } # Add Audit standards at root of hierarchies # if ($allInterviewAnswers.importPciDssPolicies -eq "Yes" -or $allInterviewAnswers.importMcsbPolicies -eq "Yes" -or $allInterviewAnswers.importNist80053Policies -eq "Yes") { Write-HydrationLogFile -EntryType logEntryDataAsPresented -EntryData "Adding Specified PolicySet and (optional) Policy defintions" -LogFilePath $logFilePath -UseUtc:$UseUtc -ForegroundColor Yellow $auditStandardList = @("/providers/Microsoft.Authorization/policySetDefinitions/1f3afdf9-d0c9-4c3d-847f-89da613e70a8") if ($allInterviewAnswers.importPciDssPolicies -eq "Yes") { $auditStandardList += "/providers/Microsoft.Authorization/policySetDefinitions/c676748e-3af9-4e22-bc28-50feed564afb" } if ($allInterviewAnswers.importNist80053Policies -eq "Yes") { $auditStandardList += "/providers/Microsoft.Authorization/policySetDefinitions/179d1daa-458f-4e47-8086-2a68d0d6c38f" } if ($allInterviewAnswers.importAdditionalPolicySets) { $splitList = $allInterviewAnswers.importAdditionalPolicySets -split "," foreach ($guid in $splitList) { # Note: Rather than supporting questions about whether or not "" are appropriate for wrapping the GUID, we just manage it with RegEx. $guid = $guid -replace '[,"'' ]', '' $parsedGuid = [System.Guid]::Empty if (-not [System.Guid]::TryParse($guid, [ref]$parsedGuid)) { Write-Error "$guid is not a valid GUID, skipping..." } else { $auditStandardList += "/providers/Microsoft.Authorization/policySetDefinitions/$guid" } } } foreach ($polSet in $auditStandardList) { Write-HydrationLogFile -EntryType logEntryDataAsPresented -EntryData " Exporting $polSet" -LogFilePath $logFilePath -UseUtc:$UseUtc -ForegroundColor Yellow $lCount = 0 do { $succ1 = $true try { if ($gatherData.policySetDefinitions.name -contains $($polSet.split("/")[-1])) { # Handle Scripts based install if(Test-Path $(Join-Path $repoRootPath "Scripts" "Operations" "Export-PolicyToEPAC.ps1")) { & (Join-Path "$repoRootPath" "Scripts" "Operations" "Export-PolicyToEPAC.ps1") -PolicySetDefinitionId $polSet ` -OutputFolder $Output ` -AutoCreateParameters $TRUE ` -UseBuiltIn $TRUE ` -OverwriteScope $(@("/providers/Microsoft.Management/managementGroups", $allInterviewAnswers.initialTenantIntermediateRoot) -join "/") ` -OverwritePacSelector $allInterviewAnswers.mainTenantMainPacSelectorName ` -OverwriteOutput $FALSE -ErrorAction Stop }elseif(Test-Path $(Join-Path $repoRootPath "temp" "Scripts" "Operations" "Export-PolicyToEPAC.ps1")){ & (Join-Path "$repoRootPath" "temp" "Scripts" "Operations" "Export-PolicyToEPAC.ps1") -PolicySetDefinitionId $polSet ` -OutputFolder $Output ` -AutoCreateParameters $TRUE ` -UseBuiltIn $TRUE ` -OverwriteScope $(@("/providers/Microsoft.Management/managementGroups", $allInterviewAnswers.initialTenantIntermediateRoot) -join "/") ` -OverwritePacSelector $allInterviewAnswers.mainTenantMainPacSelectorName ` -OverwriteOutput $FALSE -ErrorAction Stop }else{ Export-PolicyToEPAC -PolicySetDefinitionId $polSet ` -OutputFolder $Output ` -AutoCreateParameters $TRUE ` -UseBuiltIn $TRUE ` -OverwriteScope $(@("/providers/Microsoft.Management/managementGroups", $allInterviewAnswers.initialTenantIntermediateRoot) -join "/") ` -OverwritePacSelector $allInterviewAnswers.mainTenantMainPacSelectorName ` -OverwriteOutput $FALSE -ErrorAction Stop } } elseif ($gatherData.policyDefinitions.name -contains $($polSet.split("/")[-1])) { Export-PolicyToEPAC -PolicyDefinitionId $polSet ` -OutputFolder $Output ` -AutoCreateParameters $TRUE ` -UseBuiltIn $TRUE ` -OverwriteScope $(@("/providers/Microsoft.Management/managementGroups", $allInterviewAnswers.initialTenantIntermediateRoot) -join "/") ` -OverwritePacSelector $allInterviewAnswers.mainTenantMainPacSelectorName ` -OverwriteOutput $FALSE ` -ErrorAction Stop } else { Write-HydrationLogFile -EntryType logEntryDataAsPresented -EntryData " PolicySet or PolicyDefinition does not appear to be a valid built-in policySet, skipping..." -LogFilePath $logFilePath -UseUtc:$UseUtc -ForegroundColor Red } } catch { $succ1 = $false $lCount++ } }until($succ1 -eq $true -or $lCount -gt 5) if (!($succ1)) { Write-HydrationLogFile -EntryType logEntryDataAsPresented -EntryData " Failed to Export Current Azure Policy Assignments. Confirm access level and pressence of assignments to export." -LogFilePath $logFilePath -UseUtc:$UseUtc -ForegroundColor Yellow } } if(!(Test-Path $exportedDefinitionsPath.policyAssignments)){ $null = New-Item -Path $exportedDefinitionsPath.policyAssignments -ItemType Directory -Force -ErrorAction SilentlyContinue } Move-Item -Path $(Join-Path $exportedDefinitionsPath.exportPolicyToEpac "*") -Destination $exportedDefinitionsPath.policyAssignments -Force -ErrorAction SilentlyContinue Remove-Item -Path $exportedDefinitionsPath.exportPolicyToEpac -Force -ErrorAction SilentlyContinue Copy-Item -Path $(Join-Path $exportedDefinitionsPath.policyAssignments "*") -Destination $(Join-Path -Path $DefinitionsRootFolder "policyAssignments") -Recurse -Force Write-HydrationLogFile -EntryType logEntryDataAsPresented -EntryData " Copied exported assignments to Definitions folder structure..." -LogFilePath $logFilePath -UseUtc:$UseUtc -ForegroundColor Yellow try{New-HydrationAssignmentPacSelector -SourcePacSelector $allInterviewAnswers.mainTenantMainPacSelectorName ` -NewPacSelector $epacDevName ` -MGHierarchyPrefix:$allInterviewAnswers.epacPrefix ` -MGHierarchySuffix:$allInterviewAnswers.epacSuffix ` -Definitions $DefinitionsRootFolder ` -Output $Output ` -ErrorAction Stop } catch{ Write-HydrationLogFile -EntryType logEntryDataAsPresented -EntryData $Error[0].Exception.Message -LogFilePath $logFilePath -UseUtc:$UseUtc -ForegroundColor Red return } Write-HydrationLogFile -EntryType logEntryDataAsPresented -EntryData "Updated exported and newly created assignments with epac-dev pacSelector information based on assignments in PacSelector `'$($allInterviewAnswers.mainTenantMainPacSelectorName)...`'" -LogFilePath $logFilePath -UseUtc:$UseUtc -ForegroundColor Yellow if ((Get-ChildItem $(Join-Path $Output "UpdatedAssignments")).count -gt 0) { Copy-Item -Path $(Join-Path $Output "UpdatedAssignments" "*") -Destination $(Join-Path -Path $DefinitionsRootFolder "policyAssignments") -Recurse -Force Write-HydrationLogFile -EntryType logEntryDataAsPresented -EntryData " Copied updated assignments to Definitions folder structure..." -LogFilePath $logFilePath -UseUtc:$UseUtc -ForegroundColor Yellow # $updatedAssignmentList = Get-ChildItem -Path $(Join-Path $Output "UpdatedAssignments") -Recurse -Include "*.json", "*.jsonc" -ErrorAction SilentlyContinue # if ($updatedAssignmentList.count -gt 0) { # Write-HydrationLogFile -EntryType logEntryDataAsPresented -EntryData " Copying updated $($UpdatedAssignmentList.count) assignments to definitions folder..." -LogFilePath $logFilePath -UseUtc:$UseUtc -ForegroundColor Yellow # Write-HydrationLogFile -EntryType logEntryDataAsPresented -EntryData "Copying new content to $DefinitionsRootFolder" -LogFilePath $logFilePath -UseUtc:$UseUtc -ForegroundColor Yellow # } } else { Write-HydrationLogFile -EntryType logEntryDataAsPresented ` -EntryData " No updated assignments found, skipping assignment update, but it is recommended that that you consider running this again with choices that include generation of an assignment so you have a template to review..." ` -LogFilePath $logFilePath ` -UseUtc:$UseUtc ` -ForegroundColor Yellow } ################################################################################ # Closing Tasks # Clear-Host New-HydrationSeparatorBlock -DisplayText "End Hydration Process" -Location Bottom Write-HydrationLogFile -EntryType logEntryDataAsPresented -EntryData "Hydration process complete." -LogFilePath $logFilePath -UseUtc:$UseUtc -ForegroundColor Magenta ################################################################################ # Test in EPAC-Dev New-HydrationSeparatorBlock -DisplayText "Deploy to epac-dev" -Location Top Write-Host "These steps require that the Az and EnterprisePolicyAsCode modules be available" -ForegroundColor Yellow Write-Host "When you are ready to deploy the changes to epac-dev, please perform the following tasks to complete deployment to the $epacDevName pacSelector:" -ForegroundColor Yellow Write-Host " Build-DeploymentPlans -PacEnvironmentSelector $epacDevName -OutputFolder $Output -DefinitionsRootFolder $DefinitionsRootFolder" Write-Host " Deploy-PolicyPlan -PacEnvironmentSelector $epacDevName -DefinitionsRootFolder $DefinitionsRootFolder" Write-Host " Deploy-RolesPlan -PacEnvironmentSelector $epacDevName -DefinitionsRootFolder $DefinitionsRootFolder" Write-Host "`nIf you were notified that default values were missing for items above (scroll back to review), you will need to update those values prior to running the code above." -BackgroundColor Yellow -ForegroundColor Black Write-Host "`n" Write-Host "Parameter Update Guidance: https://github.com/Azure/enterprise-azure-policy-as-code/blob/main/Docs/policy-assignments.md" Write-Host "Advanced Parameter Management Guidance: https://github.com/Azure/enterprise-azure-policy-as-code/blob/main/Docs/policy-assignments-csv-parameters.md" Write-Host "`nNext Steps: CI/CD Integration" -ForegroundColor Yellow Write-Host " General Guidance: https://azure.github.io/enterprise-azure-policy-as-code/ci-cd-overview/" Write-Host " Branching Flow Guidance: https://github.com/Azure/enterprise-azure-policy-as-code/blob/main/Docs/ci-cd-branching-flows.md" Write-Host " Review high level CI/CD Options. While the hydration kit only supports a standard two stage deployment plan, you may want to consider a release plan for your environment." Write-Host " Azure DevOps: https://azure.github.io/enterprise-azure-policy-as-code/ci-cd-ado-pipelines/" Write-Host " Review Azure DevOps Pipeline implementation options and guidance." Write-Host " GitHub Actions: https://azure.github.io/enterprise-azure-policy-as-code/ci-cd-github-actions/" Write-Host " Review Github Actions implementation options and guidance." Write-Host "`nNext Steps: Additional Policy Assignments" -ForegroundColor Yellow Write-Host " Sync-AlzPolicies: https://github.com/Azure/enterprise-azure-policy-as-code/blob/main/Docs/integrating-with-alz.md#scenario-2---alz-policy-deployment-with-epac" Write-Host " Import the ALZ Policy Set using Sync-AlzPolicies, and update the parameters which do not have default values to add policies that will aid in modification of your environment to baseline Microsoft standards." Write-Host " Create Additional Assignments: https://github.com/Azure/enterprise-azure-policy-as-code/blob/main/Docs/operational-scripts.md" Write-Host " Review the command Export-PolicyToEPAC to simplify additional assignment creation.`n" Write-Host "Type 'CleanUp' to delete temporary files created during this process, or simply press enter to retain them for review..." -foregroundColor Yellow -BackgroundColor Black if($Interactive){$cleanupOption = Read-Host "`nCleanup?"} if ($cleanupOption -eq "CleanUp") { Remove-Item -Path $(Join-Path $repoRootPath "temp") -Recurse -Force -ErrorAction SilentlyContinue Write-HydrationLogFile -EntryType logEntryDataAsPresented -EntryData "Temporary files removed." -LogFilePath $logFilePath -UseUtc:$UseUtc -ForegroundColor Green } else{ Write-HydrationLogFile -EntryType logEntryDataAsPresented -EntryData "Temporary files retained at $(Join-Path $repoRootPath "temp") for review." -LogFilePath $logFilePath -UseUtc:$UseUtc -ForegroundColor Yellow Write-Host "Task Complete!" -ForegroundColor Green } Write-Host "Output files retained at $(Join-Path $repoRootPath "Output") for review, but are excluded from replication to the repo." -ForegroundColor Green return } |