Get Plan and Bucket ID

You can get the Plan ID from the link in the browser, it mostly starts with “rALlID_”. If you have the Plan ID, then you can get the Bucket ID with the following command:

Import-Module Microsoft.Graph.Planner 

$plannerPlanId = "rALlID_"

Get-MgPlannerPlanBucket -PlannerPlanId $plannerPlanId 

Plannner Task Creation

You can create planner tasks in powershell with the MS Graph Planner Module (Microsoft.Graph.Planner) and the custom module.

Grouping the vulnerabilities

...
$result = $response.Result  
$groupedTasks = $result | Group-Object -Property "HostName"

foreach ($vuln in $groupedTasks) {
    $TaskDescription = @{}

    $sortedTasks = $vuln.Group | Sort-Object -Property VulnerabilitySeverityLevel -Descending 

    $totalTasks = $sortedTasks.Count

    # Process in chunks of 5
    for ($i = 0; $i -lt $totalTasks; $i += 5) {
        $batch = $vuln.Group[$i..($i+4)]

        $firstVuln = $batch | Select-Object -First 1 # to get the HostName and contact for the server
        $contact = $firstVuln.contact
        $hostName = $firstVuln.HostName

Get the owner

...
$contact = if ([string]::IsNullOrWhiteSpace($contact) -or $contact -eq "NULL") { "admin@flo7000.ch" } else { $contact }
$user = Get-MgUser -Filter "Mail eq '$($contact)'"

$assignments = @{
    $user.Id = @{
        "@odata.type" = "#microsoft.graph.plannerAssignment"
        orderHint    = " !"
    }
}

Set the description

...
$TaskDescription = @{}

$taskList = $batch | ForEach-Object { 
    " 🚨 $($_.CveID) with SeverityLevel $($_.VulnerabilitySeverityLevel) and CVSS Score of $($_.CvssScore) " + "`n" +
    "Affected Software: $($_.SoftwareName) by $($_.SoftwareVendor)" + "`n" +
    ($(if (![string]::IsNullOrWhiteSpace($_.RecommendedSecurityUpdate)) { 
        "Recommended Security Update: $($_.RecommendedSecurityUpdate)" + "`n"
    } else { "" })) +
    ($(if (![string]::IsNullOrWhiteSpace($_.FirstDiskPath)) { 
        "Disk Path: $($_.FirstDiskPath)" + "`n"
    } else { "" })) +
    ($(if (![string]::IsNullOrWhiteSpace($_.FirstRegistryPath)) { 
        "Registry Path: $($_.FirstRegistryPath)" + "`n"
    } else { "" }))
}

$formattedTasks = $taskList -join "`n"

$TextBody = @"
Serverowner: $($user.UserPrincipalName)

$formattedTasks

For more Informations:
- Process: Help-Artikel
- Vulnerability: NIST Database
"@

$TaskDescription.Clear()
$TaskDescription.Add('description', $TextBody)

There isn’t a description of the vulnerability added because the links are added as reference and to see all Descriptions, we’ll use DescriptionViewer (new Service that gets the descriptions with PHP & Python) in the future! This is because we want to group as many vulnerabilities as possible because Microsoft has description length (32k characters) and planner task limits (9k per plan).

Set the priority and End Date

...
$priorityOrder = @("Critical", "High", "Medium", "Low")
$highestPriorityTask = $batch | Sort-Object { $priorityOrder.IndexOf($_.VulnerabilitySeverityLevel) } | Select-Object -First 1
$highestPrio = $highestPriorityTask.VulnerabilitySeverityLevel # Gets the prio in text
$allCVEs = $batch.CveId

switch ($highestPrio) { # Gets the prio in a number
    "Critical" { $taskPrio = "1"; $daysToSolve = "+7"; break }
    "High"     { $taskPrio = "3"; $daysToSolve = "+10"; break }
    "Medium"   { $taskPrio = "6"; $daysToSolve = "+14"; break }
    "Low"      { $taskPrio = "9"; $daysToSolve = "+21"; break }
    default    { $taskPrio = "10"; $daysToSolve = "+100" }
}

Create the task

$allCVEs = $batch.CveId # Get all CVEs for the references

$planId = "rALlID_"
$bucketId = "your-bucket-id"
$helpArtikelLink = "https%3A//docs%2Eflows%2Eli/svn"
$helpArtikelAlias = "Documentation :)"

New-VulnerabilityNotification -GraphToken $graphToken -PlanID $planId -BucketID $bucketId -AllCVEs $allCVEs -hostName $hostName -TaskDescription $TaskDescription  -taskAssignment $assignments -severityLevel $highestPrio -taskPrio $taskPrio -daysToSolve $daysToSolve -helpArtikelLink $helpArtikelLink -helpArtikelAlias $helpArtikelAlias