PowerShell – find and update a string value within all dynamic group membership rules

If your company benefits from using dynamic groups, then you probably know that feature takes its toll – proper maintenance and updates. It usually happens when there are some organizational changes – updated job titles or another department, just to name a few. The purpose of this script is to take that burden away and make updates at once to every single dynamic group that matches the scope of the adjustment.

Benefits and nuances of the script:

Important! This line (#113) of code is responsible for either dry or wet run of the script. I left it commented on purpose and by default.

# $GroupAdjustments = Invoke-RestMethod @Parameters

Please note, this script assumes you already have Az.Accounts cmdlet installed and connected to it, and of course have proper rights in the system.

# Data input
$OldValue = Read-host "Old value to find and eventually replace (include double quotes if those are part of the string)"
$NewValue = Read-host "New value to replace the old value with (include double quotes if those are part of the string)"

# Start timer
$elapsed = [System.Diagnostics.Stopwatch]::StartNew()

# Graph API authentication 
$resource = "https://graph.microsoft.com"
$context = [Microsoft.Azure.Commands.Common.Authentication.Abstractions.AzureRmProfileProvider]::Instance.Profile.DefaultContext
$accessToken = [Microsoft.Azure.Commands.Common.Authentication.AzureSession]::Instance.AuthenticationFactory.Authenticate($context.Account, $context.Environment, $context.Tenant.Id.ToString(), $null, [Microsoft.Azure.Commands.Common.Authentication.ShowDialog]::Never, $null, $resource).AccessToken

# Get all groups through Graph API - select id,displayName,membershipRule
$uri = "https://graph.microsoft.com/v1.0/groups?$select=id,displayName,membershipRule"
$Groups = Invoke-RestMethod -Method 'Get' -Uri $uri -Headers @{ Authorization = "Bearer " + $accessToken }

# Add found groups to AllGroups collection
$AllGroups = @()
$AllGroups+=$Groups.value

# Keep getting (Graph API is subject to paging) all groups through Graph API - select id,displayName,membershipRule
while($Groups.'@odata.nextLink' -ne $null) {
    $Groups = Invoke-RestMethod -Method 'Get' -Uri $Groups.'@odata.nextLink' -Headers @{ Authorization = "Bearer " + $accessToken }
    
    # Keep adding found groups to AllGroups collection
    $AllGroups+=$Groups.value
}

# Filter groups and leave those with Dynamic rules
$AllGroups = $AllGroups | Where-Object {($_.membershipRule -ne $null)}

# Collection to store found groups
$Collection = @()

# Index variable
$i = 1

ForEach ($Group in $AllGroups) {
    
    # Add the following to the collection - number, display name, id, if CustomAttribute2 is used, if CustomAttribute4 is used, old rule before the change, new rule after the change
    $OldRule = $Group.membershipRule
    $Rule = $Group.membershipRule
    $outObject = "" | Select Number,id,DisplayName,OldRule,NewRule
    $outObject."id" = $Group.id
    $outObject."Number" = $i
    $outObject."DisplayName" = $Group.DisplayName
    If (($Group.membershipRule -match $OldValue)) {
        $Rule = $Rule.replace($OldValue, $NewValue)
        $Process = $true
    }
    Else {
        $Process = $false
    }
    $outObject."OldRule" = $OldRule
    $outObject."NewRule" = $Rule

    # Only add those groups to the collection that will be processed
    If ($Process -eq $true) {
        $i = $i + 1
        $Collection += $outObject
    }
}

# Save collection to a CSV file
$OutputFile = "C:\Updates to Dynamic Groups $(get-date -f yyyy-MM-dd_HH-mm-ss).csv"
$Collection | Export-CSV $OutputFile -NoTypeInformation

# Collection for batch requests
$myBatchRequests = @()

Foreach ($item in $Collection | Select-Object -First 20) {
    Write-host 
    Write-host $item.DisplayName -ForegroundColor Cyan
    Write-host $item.OldRule -ForegroundColor Red
    Write-host $item.NewRule -ForegroundColor Green

    # Calculate url for each group
    $url = "/groups/"+$item.id
    # $item.DisplayName
    # Body
    $myRequest = @{ 
        id     = $item.Number
        method = "PATCH"
        url    = $url
        body = @{
            membershipRule = $item.NewRule
        }
        headers = @{
            "Content-Type" = "application/json"
        }   
    } 
    $myBatchRequests += $myRequest
}

# Combine batch requests for Graph API batching
$allBatchRequests = @{ 
    requests = $myBatchRequests
}

# Convert body to JSON for Graph API
$batchBody = $allBatchRequests | ConvertTo-Json -Depth 5

# Core parameters for Graph API POST request
$Parameters = @{
    ContentType = 'application/json'
    Method = 'POST'
    Body = $batchBody
    Uri = 'https://graph.microsoft.com/v1.0/$batch'
    Headers = @{ Authorization = "Bearer " + $accessToken }
}

# Execute Graph API POST request that changes Membership Rules in Dynamic groups
# $GroupAdjustments = Invoke-RestMethod @Parameters

# Stop timer
Write-Host "Total Time: $($elapsed.Elapsed.ToString())"

RESULT

This Post Has One Comment

Leave a Reply