我需要为创建特定 M365 租户的每个新组创建访问评审。手动创建这些非常耗时,因此我想在 Azure 自动化运行手册中利用 MS Graph API。

然而我遇到了以下错误:

{“error”:{“code”:””,”message”:”试图执行未经授权的操作。”,”innerError”:{“date”:”2024-07-01T13:54:10″,”re​​quest-id”:”fbbb3311-e8c8-45b4-b7fd-d077ce23424c”,”client-request-id”:”fbbb3311-e8c8-45b4-b7fd-d077ce23424c”}}}(响应状态代码不表示成功:403(禁止)。)

运行手册从操作组触发,并将组特定详细信息从 webhook 捕获到变量中。我能够使用 MS graph 在运行手册中查询该组。因此通过 graph API 提取数据没有问题。

这是我的 Runbook(注意:我将使用变量来填充用户/组信息,但这已被剥离以便进行故障排除):

<#
    .DESCRIPTION
        A runbook to create an access review for the given O365 group.

    .NOTES
    
#>

param
(
[Parameter (Mandatory=$false)]
[object] $WebhookData
)

$ErrorActionPreference = 'Stop'

if ($WebhookData){
    # Collect properties of WebhookData.
    $WebhookName    =   $WebhookData.WebhookName
    $WebhookBody    =   $WebhookData.RequestBody
    $WebhookHeaders =   $WebhookData.RequestHeader.Message

    # Webhook name that called This
    Write-Output "This runbook was started from webhook $WebhookName."

    # Convert the WebhookBody into PS object
    $WebhookBody = (ConvertFrom-Json -InputObject $WebhookBody)
    $URI = $WebhookBody.Data.AlertContext.Condition.AllOf.LinkToSearchResultsApi

    $userAssignedMID = "MI-ALL-DEV-AutomationManagedID"
    $userAssignedMIDClientID = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" # User Assigned Managed Identity for Automation
    $connectionName = "AzureRunAsConnection"
    $TenantID = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
    $subscriptionID = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
    $resourceGroup = "RG-ALL-DEV-ResourceGroup"

    #Auth token - HTTP Post
    $Url = $env:IDENTITY_ENDPOINT
    $Headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
    $Headers.Add("Metadata", "True")
    $Headers.Add("X-IDENTITY-HEADER", $env:IDENTITY_HEADER) 
    $Body = @{
        'resource'='https://graph.microsoft.com/'
        'client_id'=$userAssignedMIDClientID
    }
    $accessToken = (Invoke-RestMethod $url -Method 'POST' -Headers $Headers -ContentType 'application/x-www-form-urlencoded' -Body $body ).access_token
    $authHeader = @{
        'Authorization' = "Bearer $accessToken"
    }
        
    # Ensure we do not inherit an AzContext in runbook
    Disable-AzContextAutosave -Scope Process
    # Connect to Azure with user-assigned managed identity
    $AzureContext = (Connect-AzAccount -Identity -AccountId $userAssignedMIDClientID).context
    $ConnectionResult = Set-AzContext -SubscriptionName $subscriptionID -DefaultProfile $AzureContext

    #Pull in alert rule kql query search results via the LoganAlytics API.
    $APIdata = Invoke-AzRestMethod -Uri $URI
    
    #Convert Search results content from JSON to PS objects.
    $SearchResults = ConvertFrom-Json $APIdata.content

    #Set variables from the search data.
    $SearchResults = $SearchResults.tables.rows
    $GroupName = $SearchResults[0]
    $GroupID = $SearchResults[1]
    $CreatedBy = $SearchResults[2]

    Write-output "####### New Group Created #########"
    Write-Output "GroupName: $Groupname, Group ID: $GroupID, Created By: $CreatedBy"

    $GroupUrl = 'https://graph.microsoft.com/v1.0/groups/' + $GroupID + '/owners'
    Write-Output "Group URL is: $GroupUrl"
    #Connect to graph via the rest api. Find and pull back group owner(s).
    $Response = Invoke-RestMethod -Uri $GroupUrl -Method Get -Headers @{'Authorization'= "Bearer $accessToken"}
    $GroupOwner = $response.value.id
    Write-Output "Group owner: $GroupOwner"

    #Review all members of a group with the ID collected from the KQL query now in variable $GroupID.
    #$GroupOwner is the reviewer
    #It recurs monthly and continues indefinitely.
    $ReviewName = "AR-" + $Groupname + "-AllGuests"
    $ScopeQueryFilter = "`$filter=(userType eq 'Guest')"
    $ScopeQuery = '/groups/' + $GroupID + '/transitiveMembers/?' + $ScopeQueryFilter
    Write-Output "Scope Query is: $ScopeQuery"
    $ReviewersQuery = '/Users/' + $GroupOwner
    Write-Output "Reviewer Query is: $ReviewersQuery"
    $FallbackReviewersQuery = '/Groups/' + $GroupOwner
    Write-Output "Fallback Reviewer Query is: $FallbackReviewersQuery"
    
    $Body = '{
        "displayName": "Test Access Review",
        "descriptionForAdmins": "scheduled access review for guest users",
        "descriptionForReviewers": "If you have any questions, contact xxx@xxx.com",
        "scope": {
            "@odata.type": "#microsoft.graph.accessReviewQueryScope",
            "query": "/groups/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/transitiveMembers",
            "queryType": "MicrosoftGraph"
        },
        "reviewers": [
            {
            "query": "/users/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
            "queryType": "MicrosoftGraph"
            }
        ],
        "settings": {
            "mailNotificationsEnabled": true,
            "reminderNotificationsEnabled": true,
            "justificationRequiredOnApproval": true,
            "defaultDecisionEnabled": false,
            "defaultDecision": "None",
            "instanceDurationInDays": 3,
            "recommendationsEnabled": true,
            "recurrence": {
                "pattern": {
                    "type": "absoluteMonthly",
                    "interval": 3
                },
                "range": {
                    "type": "noEnd",
                    "startDate": "2024-06-01T12:00:00.667Z"
                }
            }
        }
    }'
    $Headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
    $Headers.Add("Authorization", "Bearer $accessToken")
    $CreateReview = Invoke-RestMethod -Headers $Headers -Uri "https://graph.microsoft.com/v1.0/identityGovernance/accessReviews/definitions" -Body $Body -Method POST -ContentType "application/json" 
    Write-Output "Review Result: $CreateReview"

} else {
    Write-Output "No data received"
}`

任何帮助均感激不尽。

注意:我已经编辑了这个问题,因为我发现了原始问题的错误(不要假设你知道属性值并忘记你猜到了)。

当前的问题在于身份验证。身份验证适用于初始请求,但我认为我尚未对正确的图形 API 资源进行身份验证以创建访问审查。

有人能帮助我了解我需要使用什么资源进行身份验证吗?

$(function() {
$(“.js-gps-inline-related-questions .spacer”).on(“click”, function () {
fireRelatedEvent($(this).index() + 1, $(this).data(‘question-id’));
});

function fireRelatedEvent(position, questionId) {
StackExchange.using(“gps”, function() {
StackExchange.gps.track(‘related_questions.click’,
{
position: position,
originQuestionId: 78691632,
relatedQuestionId: +questionId,
location: ‘inline’,
source: ‘Baseline_Fallback’
});
});
}
});

function toggleInlineRelated(showMore) {
var inlineRelatedLess = document.getElementById(“inline_related_var_a_less”);
var inlineRelatedMore = document.getElementById(“inline_related_var_a_more”);

var inlineRelatedSeeMore = document.getElementById(“inline_related_see_more”);
var inlineRelatedSeeLess = document.getElementById(“inline_related_see_less”);

if (showMore) {
inlineRelatedLess.classList.add(“d-none”);
inlineRelatedSeeMore.classList.add(“d-none”);

inlineRelatedMore.classList.remove(“d-none”);
inlineRelatedSeeLess.classList.remove(“d-none”);
}
else {
inlineRelatedMore.classList.add(“d-none”);
inlineRelatedSeeLess.classList.add(“d-none”);

inlineRelatedLess.classList.remove(“d-none”);
inlineRelatedSeeMore.classList.remove(“d-none”);
}
}

0