From ee3f2fbddba548e7c58cb7e68b2e122e627e09f9 Mon Sep 17 00:00:00 2001 From: Anthony Watherston Date: Fri, 21 Jun 2024 13:19:41 +1000 Subject: [PATCH] Hydration kits updates, ALZ fixes (#684) Co-authored-by: Anthony Watherston --- .../ALZ-Sandbox-Default.jsonc | 21 ++- Scripts/Helpers/Add-HelperScripts.ps1 | 1 + ...Build-ScopeTableForDeploymentRootScope.ps1 | 2 +- .../Build-ScopeTableForManagementGroup.ps1 | 27 ++-- ...-HydrationChildManagementGroupNameList.ps1 | 6 +- .../New-HydrationManagementGroupChildren.ps1 | 33 ++++- .../Remove-HydrationChildHierarchy.ps1 | 42 ++++++ .../Get-AzManagementGroupRestMethod.ps1 | 46 +++++++ ...Copy-HydrationManagementGroupHierarchy.ps1 | 12 +- .../HydrationKit/New-HydrationAnswerFile.ps1 | 8 +- .../New-HydrationCaf3Hierarchy.ps1 | 127 +++++++----------- ...ve-HydrationManagementGroupRecursively.ps1 | 62 ++++++--- 12 files changed, 249 insertions(+), 138 deletions(-) create mode 100644 Scripts/Helpers/Remove-HydrationChildHierarchy.ps1 create mode 100644 Scripts/Helpers/RestMethods/Get-AzManagementGroupRestMethod.ps1 diff --git a/Scripts/CloudAdoptionFramework/policyAssignments/ALZ-Sandbox-Default.jsonc b/Scripts/CloudAdoptionFramework/policyAssignments/ALZ-Sandbox-Default.jsonc index dbe5e708..a1233f5b 100644 --- a/Scripts/CloudAdoptionFramework/policyAssignments/ALZ-Sandbox-Default.jsonc +++ b/Scripts/CloudAdoptionFramework/policyAssignments/ALZ-Sandbox-Default.jsonc @@ -19,18 +19,15 @@ }, "parameters": { "listOfResourceTypesNotAllowed": [ - "microsoft.consumption/tags", - "microsoft.authorization/roleassignments", - "microsoft.authorization/roledefinitions", - "microsoft.authorization/policyassignments", - "microsoft.authorization/locks", - "microsoft.authorization/policydefinitions", - "microsoft.authorization/policysetdefinitions", - "microsoft.resources/tags", - "microsoft.authorization/roleeligibilityschedules", - "microsoft.authorization/roleeligibilityscheduleinstances", - "microsoft.authorization/roleassignmentschedules", - "microsoft.authorization/roleassignmentscheduleinstances" + "microsoft.network/expressroutecircuits", + "microsoft.network/expressroutegateways", + "microsoft.network/expressrouteports", + "microsoft.network/virtualwans", + "microsoft.network/virtualhubs", + "microsoft.network/vpngateways", + "microsoft.network/p2svpngateways", + "microsoft.network/vpnsites", + "microsoft.network/virtualnetworkgateways" ] }, "nonComplianceMessages": [ diff --git a/Scripts/Helpers/Add-HelperScripts.ps1 b/Scripts/Helpers/Add-HelperScripts.ps1 index bbac2f80..96f37105 100644 --- a/Scripts/Helpers/Add-HelperScripts.ps1 +++ b/Scripts/Helpers/Add-HelperScripts.ps1 @@ -105,6 +105,7 @@ . "$PSScriptRoot/Write-AssignmentDetails.ps1" . "$PSScriptRoot/Write-ErrorsFromErrorInfo.ps1" +. "$PSScriptRoot/RestMethods/Get-AzManagementGroupRestMethod.ps1" . "$PSScriptRoot/RestMethods/Get-AzPolicyAssignmentRestMethod.ps1" . "$PSScriptRoot/RestMethods/Get-AzPolicyExemptionsRestMethod.ps1" . "$PSScriptRoot/RestMethods/Get-AzResourceListRestMethod.ps1" diff --git a/Scripts/Helpers/Build-ScopeTableForDeploymentRootScope.ps1 b/Scripts/Helpers/Build-ScopeTableForDeploymentRootScope.ps1 index 5501843f..77fb7979 100644 --- a/Scripts/Helpers/Build-ScopeTableForDeploymentRootScope.ps1 +++ b/Scripts/Helpers/Build-ScopeTableForDeploymentRootScope.ps1 @@ -103,7 +103,7 @@ function Build-ScopeTableForDeploymentRootScope { -ScopeTable $scopeTable } else { - $managementGroup = Get-AzManagementGroup -GroupName $deploymentRootScopeManagementGroupName -Expand -Recurse -ErrorAction Stop + $managementGroup = Get-AzManagementGroupRestMethod -GroupId $deploymentRootScopeManagementGroupName -Expand -Recurse -ErrorAction Stop $scopeDetails = Build-ScopeTableForManagementGroup ` -ManagementGroup $managementGroup ` -ResourceGroupsBySubscriptionId $resourceGroupsBySubscriptionId ` diff --git a/Scripts/Helpers/Build-ScopeTableForManagementGroup.ps1 b/Scripts/Helpers/Build-ScopeTableForManagementGroup.ps1 index ec0d21db..ec8e463a 100644 --- a/Scripts/Helpers/Build-ScopeTableForManagementGroup.ps1 +++ b/Scripts/Helpers/Build-ScopeTableForManagementGroup.ps1 @@ -17,13 +17,20 @@ function Build-ScopeTableForManagementGroup { $notScopesList = [System.Collections.ArrayList]::new() $notScopesTable = @{} $excludedScopesTable = @{} - $managementGroupType = $ManagementGroup.Type - $managementGroupId = $ManagementGroup.Id - $managementGroupName = $ManagementGroup.Name - $managementGroupDisplayName = $ManagementGroup.DisplayName - $managementGroupChildren = $ManagementGroup.Children #endregion initialize variables + #region get management group details + $managementGroupType = $ManagementGroup.type + $managementGroupId = $ManagementGroup.id + $managementGroupName = $ManagementGroup.name + $managementGroupDisplayName = $ManagementGroup.displayName + $managementGroupChildren = $ManagementGroup.children + if ($ManagementGroup.properties) { + $managementGroupDisplayName = $ManagementGroup.properties.displayName + $managementGroupChildren = $ManagementGroup.properties.children + } + #endregion get management group details + #region build scope details $thisNotScope = $null if ($null -ne $ParentScopeDetails) { @@ -62,7 +69,7 @@ function Build-ScopeTableForManagementGroup { excludedScopesTable = $excludedScopesTable isExcluded = $IsExcluded isInGlobalNotScope = $IsInGlobalNotScope - state = $resourceContainer.properties.state + state = "Enabled" location = "global" } if ($IsExcluded) { @@ -81,12 +88,12 @@ function Build-ScopeTableForManagementGroup { #region recurse down the tree if ($null -ne $managementGroupChildren) { foreach ($child in $managementGroupChildren) { - $childId = $child.Id + $childId = $child.id $childScopeDetails = $null - if ($child.Type -eq "/subscriptions") { + if ($child.type -eq "/subscriptions") { $childScopeDetails = Build-ScopeTableForSubscription ` - -SubscriptionId $child.Name ` - -SubscriptionName $child.DisplayName ` + -SubscriptionId $child.name ` + -SubscriptionName $child.displayName ` -ResourceGroupsBySubscriptionId $ResourceGroupsBySubscriptionId ` -PacEnvironment $PacEnvironment ` -ScopeTable $ScopeTable ` diff --git a/Scripts/Helpers/Get-HydrationChildManagementGroupNameList.ps1 b/Scripts/Helpers/Get-HydrationChildManagementGroupNameList.ps1 index e09cd2b2..fce0f252 100644 --- a/Scripts/Helpers/Get-HydrationChildManagementGroupNameList.ps1 +++ b/Scripts/Helpers/Get-HydrationChildManagementGroupNameList.ps1 @@ -24,13 +24,13 @@ function Get-HydrrationChildManagementGroupNameList { $ManagementGroupName ) $childMgsList = @() - $mgs = Get-AzManagementGroup $ManagementGroupName -Recurse -Expand + $mgs = Get-AzManagementGroupRestMethod -GroupId $ManagementGroupName -Expand -Recurse do { $childMgs = @() foreach ($mg in $mgs) { $childMgsList += $mg - foreach ($cMg in $mg.Children) { - $childMgs += $cMg.Children | Where-Object { $_.Type -eq "Microsoft.Management/managementGroups" } + foreach ($cMg in $mg.properties.children) { + $childMgs += $cMg.properties.children | Where-Object { $_.Type -eq "Microsoft.Management/managementGroups" } } } Clear-Variable mgs diff --git a/Scripts/Helpers/New-HydrationManagementGroupChildren.ps1 b/Scripts/Helpers/New-HydrationManagementGroupChildren.ps1 index a051e1cd..6829ac85 100644 --- a/Scripts/Helpers/New-HydrationManagementGroupChildren.ps1 +++ b/Scripts/Helpers/New-HydrationManagementGroupChildren.ps1 @@ -7,7 +7,7 @@ The New-HydrationManagementGroupChildren function takes a parent group name and a list of child names. It then creates new child groups under the specified parent group. .PARAMETER Hierarchy - An output from get-azmanagementgroup -GroupName $ManagementGroup -expand -Recurse, this will be used as the source for the management group copy job. + An output from Get-AzManagementGroupRestMethod -GroupId $ManagementGroup -Expand -Recurse , this will be used as the source for the management group copy job. .PARAMETER Suffix The prefix to be used for naming in the destination of the management group copy job. .PARAMETER Prefix @@ -38,7 +38,7 @@ function New-HydrationManagementGroupChildren { } Write-Debug "Suffix: $Suffix" Write-Debug "Prefix: $Prefix" - foreach ($child in $Hierarchy.Children) { + foreach ($child in $Hierarchy.properties.children) { $destParentGroupId = $( -join ("/providers/Microsoft.Management/managementGroups/", $Prefix, $Hierarchy.Name, $Suffix)) if ($child.Type -eq "Microsoft.Management/managementGroups") { $newMGName = $( -join ($Prefix, $child.Name, $Suffix)) @@ -47,15 +47,36 @@ function New-HydrationManagementGroupChildren { do { $i++ if ($i -gt 1) { - Write-Warning " Last attempt failed, this is attempt number $i..." + Start-Sleep -seconds 30 + Write-Warning " Last attempt appears to have failed, retry $i of 10..." } # Error action included because timeouts happen frequently, but mean nothing. Rather than have responses cause concern, we simply suppress the error and test. It takes longer, but this should be a task that is run very infrequently outside of a lab environment. - $newMg = New-AzManagementGroup -GroupName $newMGName -DisplayName $( -join ($Prefix, $child.DisplayName, $Suffix)) -ParentId $destParentGroupId -ErrorAction SilentlyContinue - }until($(Get-AzManagementGroup -GroupName $newMGName) -or $i -eq 10) + try { + $null = $newMg = New-AzManagementGroup -GroupName $newMGName -DisplayName $( -join ($Prefix, $child.DisplayName, $Suffix)) -ParentId $destParentGroupId -ErrorAction SilentlyContinue + } + catch { + Write-Information "Error: $_" + Write-Information "This is generally transient, so we test after, this is just here for debugging" + } + try { + $null = $testResult = Get-AzManagementGroupRestMethod -GroupId $newMGName -ErrorAction SilentlyContinue + } + catch { + $testResult = $false + } + }until($testResult.name -eq $newMGName -or $i -eq 10) $mgChildren = $child.Children | Where-Object { $_.Type -eq "Microsoft.Management/managementGroups" } if ($mgChildren.count -gt 0) { Write-Information " Creating child Management Groups of $(-join($Prefix,$child.Name,$Suffix)) from $($mgChildren.Name -join ", ")..." - New-HydrationManagementGroupChildren -Hierarchy $child -Prefix $Prefix -Suffix $Suffix + do { + try { + $null = $childHierarchy = Get-AzManagementGroupRestMethod -GroupId $child.Name -Expand -Recurse + } + catch { + $testResult = $false + } + }until($testResult.name -or $i -eq 10) + New-HydrationManagementGroupChildren -Hierarchy $childHierarchy -Prefix $Prefix -Suffix $Suffix } } } diff --git a/Scripts/Helpers/Remove-HydrationChildHierarchy.ps1 b/Scripts/Helpers/Remove-HydrationChildHierarchy.ps1 new file mode 100644 index 00000000..8972f85d --- /dev/null +++ b/Scripts/Helpers/Remove-HydrationChildHierarchy.ps1 @@ -0,0 +1,42 @@ +function Remove-HydrationChildHierarchy { + [CmdletBinding()] + param ( + [Parameter(Mandatory = $true)] + $ChildHierarchy + ) + foreach ($child in $ChildHierarchy) { + if ($child.Type -eq "Microsoft.Management/managementGroups") { + # Error action included because timeouts happen frequently, but mean nothing. Rather than have responses cause concern, we simply suppress the error. + if ($child.children) { + Write-Information " Removing child objects of $($child.Name) -- $($child.children.Name -join ", ")..." + try { + Write-Debug "Starting Inner Loop" + $null = Remove-HydrationChildHierarchy -ChildHierarchy $child.children #-erroraction silentlycontinue + Write-Debug "Leaving Inner Loop" + } + catch { + write-error $_ + } + } + do { + Write-Information " Removing $($child.Name)..." + $null = Remove-AzManagementGroup -GroupName $($child.Name) + try { + $null = Get-AzManagementGroupRestMethod -GroupId $($child.Name) -ErrorAction SilentlyContinue + } + catch { + if ($_.Exception.Message -match "NotFound") { + Write-Information " $($child.Name) confirmed to be removed..." + $complete = $true + } + else { + Write-Information " $($child.Name) generated an error during deletion, retrying $(6-$i) more times..." + $complete = $false + $i++ + } + } + # } + }until($true -eq $complete -or $i -eq 6) + } + } +} \ No newline at end of file diff --git a/Scripts/Helpers/RestMethods/Get-AzManagementGroupRestMethod.ps1 b/Scripts/Helpers/RestMethods/Get-AzManagementGroupRestMethod.ps1 new file mode 100644 index 00000000..e33bf826 --- /dev/null +++ b/Scripts/Helpers/RestMethods/Get-AzManagementGroupRestMethod.ps1 @@ -0,0 +1,46 @@ + +function Get-AzManagementGroupRestMethod { + [CmdletBinding()] + param ( + [string] $ApiVersion = "2020-05-01", + [switch] $Expand, + [switch] $Recurse, + [string] $GroupID + ) + + # Print a message to indicate that the function is being called + Write-Debug "Get-AzManagementGroupRestMethod is being called" + + # Assemble the API path + $path = "/providers/Microsoft.Management/managementGroups/$($GroupID)?api-version=$($ApiVersion)" + if ($Recurse) { + $path += "&`$recurse=True" + } + if ($Expand) { + $path += "&`$expand=children" + } + + # Print the GroupID and API path for debugging + Write-Debug "GroupID: $GroupID" + Write-Debug "API Path: $path" + + # Invoke the REST API + $response = Invoke-AzRestMethod -Path $path -Method GET + + # Process response + $statusCode = $response.StatusCode + if ($statusCode -lt 200 -or $statusCode -ge 300) { + $content = $response.Content + Write-Error "Get Management Group error $($statusCode) -- $($content)" -ErrorAction Stop + } + + # Convert the response content to a JSON object + $jsonContent = $response.Content | ConvertFrom-Json -Depth 100 + + # $jsonText = $jsonContent | ConvertTo-Json -Depth 100 + + return $jsonContent +} + + + diff --git a/Scripts/HydrationKit/Copy-HydrationManagementGroupHierarchy.ps1 b/Scripts/HydrationKit/Copy-HydrationManagementGroupHierarchy.ps1 index f8d7ea28..072692f2 100644 --- a/Scripts/HydrationKit/Copy-HydrationManagementGroupHierarchy.ps1 +++ b/Scripts/HydrationKit/Copy-HydrationManagementGroupHierarchy.ps1 @@ -46,15 +46,17 @@ $InformationPreference = "Continue" if (!($Suffix) -and !($Prefix)) { Write-Error "You must modify the name with either a Suffix, a Prefix, or both in order to replicate within the current tenant without naming collision errors." } -$destParent = Get-AzManagementGroup -GroupName $DestinationParentGroupName -ErrorAction SilentlyContinue -if (!(Get-AzManagementGroup -GroupName $DestinationParentGroupName)) { - Write-Information "Destination Management Group $DestinationParentGroupName was not found." +try { + $null = $destParent = Get-AzManagementGroupRestMethod -GroupID $DestinationParentGroupName -ErrorAction SilentlyContinue +} +catch { + Write-Information $_.Exception.Message Write-Error "Cannot continue, a valid `$DestinationParentGroupName must be specified to tell the cmdlet where to anchor your new hierarchy." return } Write-Information "Beginning Duplication to $DestinationParentGroupName..." -$hierarchy = Get-AzManagementGroup -GroupName $SourceGroupName -Expand -Recurse +$hierarchy = Get-AzManagementGroupRestMethod -GroupID $SourceGroupName -Expand -Recurse Write-Information " Creating $(-join($Prefix,$hierarchy.Name,$Suffix))..." # Error action included because timeouts happen frequently, but mean nothing. Rather than have responses cause concern, we simply suppress the error. -$createdMg = New-AzManagementGroup -GroupName $( -join ($Prefix, $hierarchy.Name, $Suffix)) -DisplayName $( -join ($Prefix, $hierarchy.DisplayName, $Suffix)) -ParentId $destParent.Id -ErrorAction SilentlyContinue +$createdMg = New-AzManagementGroup -GroupName $( -join ($Prefix, $hierarchy.Name, $Suffix)) -DisplayName $( -join ($Prefix, $hierarchy.properties.displayName, $Suffix)) -ParentId $destParent.Id -ErrorAction SilentlyContinue New-HydrationManagementGroupChildren -Hierarchy $hierarchy -Prefix $Prefix -Suffix $Suffix diff --git a/Scripts/HydrationKit/New-HydrationAnswerFile.ps1 b/Scripts/HydrationKit/New-HydrationAnswerFile.ps1 index e60196e3..06db45aa 100644 --- a/Scripts/HydrationKit/New-HydrationAnswerFile.ps1 +++ b/Scripts/HydrationKit/New-HydrationAnswerFile.ps1 @@ -107,7 +107,7 @@ Clear-Variable repeat Write-Information "`nResult Verified for variable $($tenantEntry.pacSelector), which will be used to identify the deployment to this Tenant.`n" # Define intermediateRootGroupName -$tenantRootObject = Get-AzManagementGroup -GroupName $tenantEntry.tenantId -recurse -expand +$tenantRootObject = Get-AzManagementGroupRestMethod -GroupId $tenantEntry.tenantId -recurse -expand $tenantChildrenString = $tenantRootObject.Children.Name -join ", " do { Write-Host "`n################################################################################" @@ -123,7 +123,7 @@ do { $tenantEntry.intermediateRootGroupName = Read-Host "What is the ID of your Intermediate Management Group Root?" $repeat = $true - $test = Get-AzManagementGroup -GroupName $tenantEntry.intermediateRootGroupName + $test = Get-AzManagementGroupRestMethod -GroupId $tenantEntry.intermediateRootGroupName }until($null -ne $test) Clear-Variable repeat Write-Information "`nResult Verified for variable intermediateRootGroupName: $($tenantEntry.intermediateRootGroupName)`n" @@ -139,7 +139,7 @@ do { Write-Host "Recommendation: $($tenantEntry.intermediateRootGroupName)" $tenantEntry.initialPolicyScope = Read-Host "What is the ID of the Management Group for this PacSelector?" $repeat = $true - $test = Get-AzManagementGroup -GroupName $tenantEntry.intermediateRootGroupName -ErrorAction SilentlyContinue + $test = Get-AzManagementGroupRestMethod -GroupID $tenantEntry.intermediateRootGroupName -ErrorAction SilentlyContinue }until($null -ne $test) Clear-Variable repeat Clear-Variable test @@ -164,7 +164,7 @@ do { Write-Host "Recommendation: $($returnData.initialTenantId)" $returnData.epacParentGroupName = Read-Host "What is the ID of the Management Group that you would like to use?" $repeat = $true - $test = Get-AzManagementGroup -GroupName $returnData.epacParentGroupName -ErrorAction SilentlyContinue + $test = Get-AzManagementGroupRestMethod -GroupId $returnData.epacParentGroupName -ErrorAction SilentlyContinue }until($null -ne $test) Clear-Variable repeat Clear-Variable test diff --git a/Scripts/HydrationKit/New-HydrationCaf3Hierarchy.ps1 b/Scripts/HydrationKit/New-HydrationCaf3Hierarchy.ps1 index 85a2c367..b88fc623 100644 --- a/Scripts/HydrationKit/New-HydrationCaf3Hierarchy.ps1 +++ b/Scripts/HydrationKit/New-HydrationCaf3Hierarchy.ps1 @@ -1,3 +1,4 @@ + <# .SYNOPSIS This function creates a new hierarchy of management groups based on the CAF 3.0 model. @@ -35,84 +36,60 @@ param ( $Suffix ) $InformationPreference = "Continue" -$IRMGChildList = @("Platform", "LandingZones", "Decomissioned", "Sandbox") -$PlatformMGList = @("Identity", "Management", "Connectivity") -$LandingZoneMGList = @("Corp", "Online") -$tRootGroupId = $( -join ("/providers/Microsoft.Management/managementGroups/", $DestinationRootName)) -foreach ($t in $IRMGChildList) { - $rootGroupId = $tRootGroupId - $i = 0 - $name = $( -join ($Prefix, $t, $Suffix)) - $alreadyExists = Get-AzManagementGroup -GroupName $name -ErrorAction SilentlyContinue - if ($alreadyExists) { - Write-Information "Management Group $name already exists in $($alreadyExists.ParentName)." - continue - } - do { - if ($repeat) { - $complete = Get-AzManagementGroup -GroupName $name -ErrorAction SilentlyContinue - } - $newMg = New-AzManagementGroup -GroupName $name -DisplayName $name -ParentId $rootGroupId - if (!($newMg)) { - $repeat = $true - $i++ - } - }until($newMg -or $complete -or $i -eq 3) - if ($i -eq 3) { - Write-Error "Failed to create $name Management Group" - return - } - Write-Information "Created $name Management Group in $rootGroupId" +$mgLists = [ordered]@{ + $DestinationRootName = @("Platform", "LandingZones", "Decomissioned", "Sandbox") + Platform = @("Identity", "Management", "Connectivity") + LandingZones = @("Corp", "Online") } -$pRootGroupId = $( -join ("/providers/Microsoft.Management/managementGroups/", $Prefix, "Platform", $Suffix)) -foreach ($p in $PlatformMGList) { - $rootGroupId = $pRootGroupId - $i = 0 - $name = $( -join ($Prefix, $p, $Suffix)) - $alreadyExists = Get-AzManagementGroup -GroupName $name -ErrorAction SilentlyContinue - if ($alreadyExists) { - Write-Information "Management Group $name already exists in $($alreadyExists.ParentName)." - continue +foreach ($listName in $mgLists.Keys) { + if ($DestinationRootName -eq $listName) { + $parentName = $listName } - do { - if ($repeat) { - $complete = Get-AzManagementGroup -GroupName $name -ErrorAction SilentlyContinue - } - $newMg = New-AzManagementGroup -GroupName $name -DisplayName $name -ParentId $rootGroupId - if (!($newMg)) { - $repeat = $true - $i++ - } - }until($newMg -or $complete -or $i -eq 3) - if ($i -eq 3) { - Write-Error "Failed to create $name Management Group in $rootGroupId" - return + else { + $parentName = $( -join ($Prefix, $listName, $Suffix)) } - Write-Information "Created $name Management Group" -} -$lRootGroupId = $( -join ("/providers/Microsoft.Management/managementGroups/", $Prefix, "LandingZones", $Suffix)) -foreach ($l in $LandingZoneMGList) { - $rootGroupId = $lRootGroupId - $i = 0 - $name = $( -join ($Prefix, $l, $Suffix)) - $alreadyExists = Get-AzManagementGroup -GroupName $name -ErrorAction SilentlyContinue - if ($alreadyExists) { - Write-Information "Management Group $name already exists in $($alreadyExists.ParentName)." - continue - } - do { - if ($repeat) { - $complete = Get-AzManagementGroup -GroupName $name -ErrorAction SilentlyContinue - } - $newMg = New-AzManagementGroup -GroupName $name -DisplayName $name -ParentId $rootGroupId - if (!($newMg)) { - $repeat = $true - $i++ + $rootGroupId = $( -join ("/providers/Microsoft.Management/managementGroups/", $parentName)) + foreach ($t in $mgLists.($listName)) { + $i = 0 + $name = $( -join ($Prefix, $t, $Suffix)) + Remove-Variable repeat -ErrorAction SilentlyContinue + do { + $null = Remove-variable testResult -ErrorAction SilentlyContinue + $null = Remove-variable complete -ErrorAction SilentlyContinue + try { + $null = $testResult = Get-AzManagementGroupRestMethod -GroupId $name -ErrorAction SilentlyContinue + } + catch { + $complete = $false + } + if ($testResult.name) { + # This exists for several reasons: + # First, timeout errors on response to new-azmanagementgroup are addressed this way. + # Second, this avoids collisions, and notifies of the location if one occurs. + # Third, this accelerates a retry if the first attempt is interrupted. + $complete = $true + Write-Information "Management Group $name confirmed in $($testResult.properties.details.parent.name)." + } + if (!($complete -eq $true)) { + try { + $null = $newMg = New-AzManagementGroup -GroupName $name -DisplayName $name -ParentId $rootGroupId -ErrorAction SilentlyContinue + } + catch { + $null = $newMg = Get-AzManagementGroupRestMethod -GroupId $name -ErrorAction SilentlyContinue + Write-Error $_.Exception.Message + } + } + if (!($newMg)) { + if ($i -gt 0) { + Write-Warning "Failed to Create Management Group $name, this is generally caused by a timeout on the API call, and will automatically retry $(10-$i) more times..." + } + $i++ + } + }until($newMg -or $complete -or $i -eq 10) + if ($i -eq 3) { + Write-Error "Failed to create $name Management Group" + return } - }until($newMg -or $complete -or $i -eq 3) - if ($i -eq 3) { - Write-Error "Failed to create $name Management Group" - return + Write-Information "Verified $name Management Group in $rootGroupId" } - Write-Information "Created $name Management Group in $rootGroupId" } diff --git a/Scripts/HydrationKit/Remove-HydrationManagementGroupRecursively.ps1 b/Scripts/HydrationKit/Remove-HydrationManagementGroupRecursively.ps1 index f919c0eb..e0854381 100644 --- a/Scripts/HydrationKit/Remove-HydrationManagementGroupRecursively.ps1 +++ b/Scripts/HydrationKit/Remove-HydrationManagementGroupRecursively.ps1 @@ -1,3 +1,4 @@ + <# .SYNOPSIS Removes a Management Group and all of its children recursively. @@ -13,30 +14,47 @@ [CmdletBinding()] param ( [Parameter(Mandatory = $true)] - $Hierarchy -) + $HierarchyRootGroupName + +) + $InformationPreference = "Continue" -# $hierarchy = Get-AzManagementGroup -GroupName $Hierarchy -Expand -Recurse -foreach ($child in $Hierarchy.Children) { - if ($child.Type -eq "Microsoft.Management/managementGroups") { - # Error action included because timeouts happen frequently, but mean nothing. Rather than have responses cause concern, we simply suppress the error. - if ($child.Children) { - Write-Information " Removing child objects of $($child.Name) -- $($child.Children.Name -join ", ")..." - Remove-HydrationManagementGroupRecursively $child +$fullHierarchy = Get-AzManagementGroupRestMethod -GroupId $HierarchyRootGroupName -Expand -Recurse +Write-Debug "Starting Outer Loop" +Remove-HydrationChildHierarchy -ChildHierarchy $fullHierarchy.properties.children +Write-Debug "Leaving Outer Loop" +# Test to ensure deletes were completed +if ($(Get-AzManagementGroupRestMethod -GroupId $HierarchyRootGroupName -Expand -Recurse).properties.children.count -gt 0) { + Write-Error "Child Deletions Failed, rerun script." +} +# Delete the root group +do { + try { + $null = Get-AzManagementGroupRestMethod -GroupId $HierarchyRootGroupName -ErrorAction SilentlyContinue + } + catch { + if ($_.Exception.Message -match "NotFound") { + Write-Information " $HierarchyRootGroupName confirmed to be removed..." + $complete = $true + } + } + if (!($true -eq $complete)) { + Write-Information " Removing $HierarchyRootGroupName..." + $null = Remove-AzManagementGroup -GroupName $HierarchyRootGroupName + try { + $null = Get-AzManagementGroupRestMethod -GroupId $HierarchyRootGroupName -ErrorAction SilentlyContinue } - if ($(Get-AzManagementGroup -GroupName $($child.Name) -ErrorAction SilentlyContinue)) { - Write-Information " Removing $($child.Name)..." - $remMg = Remove-AzManagementGroup -GroupName $($child.Name) + catch { + if ($_.Exception.Message -match "NotFound") { + Write-Information " $HierarchyRootGroupName confirmed to be removed..." + $complete = $true + } } - else { - Write-Information " $($child.Name) has already been removed..." + if (!($complete -eq $true)) { + Write-Information " $HierarchyRootGroupName generated an error during deletion, retrying $(6-$i) more times..." + $complete = $false + $i++ } } -} -if ($(Get-AzManagementGroup -GroupName $Hierarchy.Name -ErrorAction SilentlyContinue)) { - Write-Information " Removing $($Hierarchy.Name)..." - $remMg = Remove-AzManagementGroup -GroupName $($Hierarchy.Name) -} -else { - Write-Information " $($Hierarchy.Name) has already been removed..." -} +}until($true -eq $complete -or $i -eq 6) +