Personal Plans in Microsoft Planner

Previously, Microsoft Planner supported two types of plans:

  • Group plan
    A group plan is connected to a Microsoft 365 group, and all group members can access the plan. A Microsoft 365 group can include multiple plans.

  • Lightweight plan
    A Lightweight plan is a restricted plan without a Microsoft 365 group and can be created via Code / PowerShell. The plan is connected to a roster container. A roster container can only include one plan at a time. The system deletes the roster container and plan if no more members are in the plan. A Lightweight Plan cannot be connected to any Microsoft 365 group later.

There is now a third type: Personal Plans


Information about Personal Plans

A Personal plan is connected to an individual in the organization. The plan does not support other members and cannot be shared with others.
Compared to a Lightweight Plan, a Personal plan can be subsequently connected to a Microsoft 365 group if, for example, a person leaves the organization. Once connected to a Microsoft 365 group, a plan can no longer become a Personal plan.

What else should you know about Personal plans?

  • A Personal plan does not include settings. Side effects are renaming the plan is not possible for the user, the user cannot delete the plan,…
  • Personal plan does not support comments on tasks
  • Personal plan does not support document upload
  • Personal plan cannot be shared
  • A Personal plan is deleted as soon as the owner’s account is deleted
  • A Personal plan can be transferred to an existing Microsoft 365 group, not to another person. All members of the group have access to the plan.


Creation of a new Personal Plan

A Personal plan is created via PowerShell and Microsoft Graph. The plan is connected to a Planner User Container. The user container is important to create the right plan type. Without the specification for User Container, the system would create a plan with Microsoft 365 group. In addition to User, there are Group, Roster, Project, and DriveItem containers.

The creation via PowerShell requires the permissions Tasks.ReadWrite (Delegated) or Tasks.ReadWrite.All (Application) for Microsoft Graph. A new Planner Plan must be created.

Consider the permissions:

  • Delegated
    Your account is authorized to create a Personal plan for your own account. The creation for another account is not possible.

  • Application
    You must use an Azure App Registration for the authorization. You can use the app to create Personal plans for other accounts. The account is the owner of the plan.

In my example, I create a Personal plan for my own account via Create plannerPlan.

PowerShell
Import-Module Microsoft.Graph.Authentication
Connect-MgGraph -Scopes Tasks.ReadWrite

$UPN = "<UserPrincipalName>"
$Body = @"
{
  "container": {
    "url": "https://graph.microsoft.com/beta/users/$UPN"
  },
  "title": "TA Personal Plan"
}
"@

$Url = "https://graph.microsoft.com/beta/planner/plans"
$PersonalPlanResult = Invoke-MgGraphRequest -Method POST -Uri $Url -Body $Body -ContentType "application/json"  
$PersonalPlanResult


The plan was created with ID aR8o7jof-UyicwSN2AZvyJgAEbWQ.

PowerShell


I open Planner on the web… and can’t find the plan. Even after 24 hours, the plan is not in the view of all plans.
After an analysis, I assume the plan requires an initial opening call for the listing. The system does not carry out the process automatically during creation. Two workarounds make it possible to show the plan.

Show Personal Plan via Planner on the web (workaround)

A plan has the following URL format in Planner on the web:

I am trying to open the plan directly via the ID without specifying a group:

Planner opens the plan I am looking for, with all the restrictions described above. The plan remains in Planner.

Personal plan in Planner
Personal plan in Planner


Show Personal Plan via PowerShell (workaround)

If you look at the URL from before on the web, Planner has added a group ID VyvrBNrdHECLopziVThfP5gAB17-.

An analysis shows the ID VyvrBNrdHECLopziVThfP5gAB17- is included in the Get plannerUser query.

PowerShell
$Url = "https://graph.microsoft.com/beta/users/$UPN/planner"
$UserPlans = Invoke-MgGraphRequest -Method GET -Uri $Url  -ContentType "application/json"  
$UserPlans

PowerShell

The documentation states that the ID is a plannerUser object. Today, the ID only supports changes to a plan for your account (application permissions are not supported). This allows a Personal plan to be pinned or inserted into the most recently used plans. This can help to display a missing plan, at least for my account.

In my example, I updated the last access. This will move the plan to the top of the history and be displayed in Planner on the web.

PowerShell
$DateTime = (Get-Date).ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ss.fffZ')
$Body = @"
{
  "recentPlanReferences": 
    {  
        "$($PersonalPlanResult.id)": {
        "@odata.type": "#microsoft.graph.plannerRecentPlanReference",
        "lastAccessedDateTime": "$DateTime",
        "planTitle": "$($PersonalPlanResult.title)"
        }  
    }
}
"@

$Url = "https://graph.microsoft.com/beta/me/planner"
$Header = @{ "If-Match" = $UserPlans.'@odata.etag'  }
Invoke-MgRestMethod -Method PATCH -Uri $Url -Body $Body -Headers $Header -ContentType "application/json" 


Wait 30 to 60 seconds, and the Personal plan will be displayed in Planner on the web. 👍


Find a Personal Plan

Microsoft has not yet described in documentation how it is possible to find a Personal plan. There is documentation listing all plans per group. It works with a modification to User.

PowerShell
$Url = "https://graph.microsoft.com/beta/users/$UPN/planner/plans"
$AllUserPlans = Invoke-MgGraphRequest -Method GET -Uri $Url  -ContentType "application/json"  
$AllUserPlans


The results show all plans created by the account. If a Personal plan was created for other accounts, the plan is also listed in the results.
Now, the filtering for Personal plans. Microsoft still has a bug during creation. According to the documentation, the type for Personal plans should be User. Today, the system still notes the value unknownFutureValue. With unknownFutureValue, the result is a mixture of plans created via Loop, lightweight, and Personal plans. Filtering the container URL helps to narrow it down. It shows all Personal plans created by the account. I used my account in the sample. I created various plans for my demo and later couldn’t find the plans in Planner on the web.

PowerShell
$AllPersonalPlans = $AllUserPlans.value | ?{$_.container.url -like "*users*" }
$AllPersonalPlans | select title,id,container | sort title

PowerShell

Delete a Personal Plan

The IDs of the plans are known from the previous query. The ID is required for deleting a plan. Deleting a plan takes place in two steps.

  1. You query the values of the plan. A current ETag is included in the response.
  2. The ETag must be sent as confirmation for the deletion request. Insert the ETag into a header. The plan is deleted. Without a current ETag, the system will decline the request.
PowerShell
$PlannerPlanID = "RIgdnzReL0upbluhiEUS8ZgACLnX"
$url = "https://graph.microsoft.com/beta/planner/plans/$PlannerPlanID"
$PlannerPlanResult = Invoke-MgGraphRequest -Method GET -Uri $Url -ContentType "application/json"  

$Header = @{ "If-Match" = $PlannerPlanResult.'@odata.etag' }
Invoke-MgRestMethod -Method DELETE -Uri $Url -Headers $Header -ContentType "application/json" 


Connect Personal Plan to a Microsoft 365 Group

As a final step, connecting a Personal plan to a Microsoft 365 group may be necessary when employees leave the company. The Personal plan container must be moved to a group. The moveToContainer documentation describes the process.

PowerShell
$PlannerPlanID = "y45XIqCdPkGIYHYFUUhueJgAEIBD"
$M365GroupID = "374767f3-111e-47c4-8ac8-0ccda91779fb"

$Url = "https://graph.microsoft.com/beta/planner/plans/$PlannerPlanID"
$PlannerPlanResult = Invoke-MgGraphRequest -Method GET -Uri $Url -ContentType "application/json"  

$Body = @"
{
    "container": {
        "containerId": "$M365GroupID",
        "type": "group"
    }
}
"@

$Header = @{ "If-Match" = $PlannerPlanResult.'@odata.etag' }
$Url = "https://graph.microsoft.com/beta/planner/plans/$PlannerPlanID/moveToContainer"
$MoveResult = Invoke-MgGraphRequest -Method POST -Uri $Url -Body $Body -Headers $Header -ContentType "application/json"  
$MoveResult 


The result shows the Personal plan is now part of a group. The update in Planner on the web can take a minute. This completes the process, and all group members can access the plan.

PowerShell
Share
Avatar photo

Tobias Asböck

Tobias is a Senior System Engineer with around ten years of professional experience with Microsoft 365 products such as SharePoint Online, OneDrive for Business, Teams Collaboration, Entra ID, Information Protection, Universal Print, and Microsoft 365 Licensing. He also has 15+ years of experience planning, administering, and operating SharePoint Server environments. Tobias is a PowerShell Scripter with certifications for Microsoft 365 products. In his spare time, Tobias is busy with updates in the Microsoft 365 world or on the road with his road bike and other sports activities. If you have additional questions, please contact me via LinkedIn or [email protected].

Leave a Reply

Your email address will not be published. Required fields are marked *